Merge branch 'development' into Prefab/DestroyGameEntitySupport

Signed-off-by: srikappa-amzn <srikappa@amazon.com>
This commit is contained in:
srikappa-amzn
2021-09-28 06:57:38 -07:00
1501 changed files with 34384 additions and 17743 deletions
@@ -81,71 +81,6 @@ namespace AzFramework
static constexpr const char s_prefabSystemKey[] = "/Amazon/Preferences/EnablePrefabSystem";
static constexpr const char s_prefabWipSystemKey[] = "/Amazon/Preferences/EnablePrefabSystemWipFeatures";
static constexpr const char s_legacySlicesAssertKey[] = "/Amazon/Preferences/ShouldAssertForLegacySlicesUsage";
// A Helper function that can load an app descriptor from file.
AZ::Outcome<AZStd::unique_ptr<AZ::ComponentApplication::Descriptor>, AZStd::string> LoadDescriptorFromFilePath(const char* appDescriptorFilePath, AZ::SerializeContext& serializeContext)
{
AZStd::unique_ptr<AZ::ComponentApplication::Descriptor> loadedDescriptor;
AZ::IO::SystemFile appDescriptorFile;
if (!appDescriptorFile.Open(appDescriptorFilePath, AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
{
return AZ::Failure(AZStd::string::format("Failed to open file: %s", appDescriptorFilePath));
}
AZ::IO::SystemFileStream appDescriptorFileStream(&appDescriptorFile, true);
if (!appDescriptorFileStream.IsOpen())
{
return AZ::Failure(AZStd::string::format("Failed to stream file: %s", appDescriptorFilePath));
}
// Callback function for allocating the root elements in the file.
AZ::ObjectStream::InplaceLoadRootInfoCB inplaceLoadCb =
[](void** rootAddress, const AZ::SerializeContext::ClassData**, const AZ::Uuid& classId, AZ::SerializeContext*)
{
if (rootAddress && classId == azrtti_typeid<AZ::ComponentApplication::Descriptor>())
{
// ComponentApplication::Descriptor is normally a singleton.
// Force a unique instance to be created.
*rootAddress = aznew AZ::ComponentApplication::Descriptor();
}
};
// Callback function for saving the root elements in the file.
AZ::ObjectStream::ClassReadyCB classReadyCb =
[&loadedDescriptor](void* classPtr, const AZ::Uuid& classId, AZ::SerializeContext* context)
{
// Save descriptor, delete anything else loaded from file.
if (classId == azrtti_typeid<AZ::ComponentApplication::Descriptor>())
{
loadedDescriptor.reset(static_cast<AZ::ComponentApplication::Descriptor*>(classPtr));
}
else if (const AZ::SerializeContext::ClassData* classData = context->FindClassData(classId))
{
classData->m_factory->Destroy(classPtr);
}
else
{
AZ_Error("Application", false, "Unexpected type %s found in application descriptor file. This memory will leak.",
classId.ToString<AZStd::string>().c_str());
}
};
// There's other stuff in the file we may not recognize (system components), but we're not interested in that stuff.
AZ::ObjectStream::FilterDescriptor loadFilter(&AZ::Data::AssetFilterNoAssetLoading, AZ::ObjectStream::FILTERFLAG_IGNORE_UNKNOWN_CLASSES);
if (!AZ::ObjectStream::LoadBlocking(&appDescriptorFileStream, serializeContext, classReadyCb, loadFilter, inplaceLoadCb))
{
return AZ::Failure(AZStd::string::format("Failed to load objects from file: %s", appDescriptorFilePath));
}
if (!loadedDescriptor)
{
return AZ::Failure(AZStd::string::format("Failed to find descriptor object in file: %s", appDescriptorFilePath));
}
return AZ::Success(AZStd::move(loadedDescriptor));
}
}
Application::Application()
File diff suppressed because it is too large Load Diff
@@ -19,6 +19,7 @@
#include <AzCore/IO/CompressionBus.h>
#include <AzCore/Outcome/Outcome.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/containers/set.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/parallel/thread.h>
@@ -26,7 +27,6 @@
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/std/string/osstring.h>
#include <AzFramework/Archive/IArchive.h>
#include <AzFramework/Archive/ZipDirCache.h>
@@ -115,12 +115,12 @@ namespace AZ::IO
struct PackDesc
{
AZ::IO::Path m_pathBindRoot; // the zip binding root
AZStd::string strFileName; // the zip file name (with path) - very useful for debugging so please don't remove
AZ::IO::Path m_strFileName; // the zip file name (with path) - very useful for debugging so please don't remove
// [LYN-2376] Remove once legacy slice support is removed
bool m_containsLevelPak = false; // indicates whether this archive has level.pak inside it or not
const char* GetFullPath() const { return pZip->GetFilePath(); }
AZ::IO::PathView GetFullPath() const { return pZip->GetFilePath(); }
AZStd::intrusive_ptr<INestedArchive> pArchive;
ZipDir::CachePtr pZip;
@@ -129,10 +129,7 @@ namespace AZ::IO
// ArchiveFindDataSet entire purpose is to keep a reference to the intrusive_ptr of ArchiveFindData
// so that it doesn't go out of scope
using ArchiveFindDataSet = AZStd::set<AZStd::intrusive_ptr<AZ::IO::FindData>, AZ::OSStdAllocator>;
// given the source relative path, constructs the full path to the file according to the flags
const char* AdjustFileName(AZStd::string_view src, char* dst, size_t dstSize, uint32_t nFlags, bool skipMods = false) override;
using ArchiveFindDataSet = AZStd::set<AZStd::intrusive_ptr<AZ::IO::FindData>>;
/**
@@ -154,29 +151,17 @@ namespace AZ::IO
//! CompressionBus Handler implementation.
void FindCompressionInfo(bool& found, AZ::IO::CompressionInfo& info, const AZStd::string_view filename) override;
//! Processes an alias command line containing multiple aliases.
void ParseAliases(AZStd::string_view szCommandLine) override;
//! adds or removes an alias from the list - if bAdd set to false will remove it
void SetAlias(AZStd::string_view szName, AZStd::string_view szAlias, bool bAdd) override;
//! gets an alias from the list, if any exist.
//! if bReturnSame==true, it will return the input name if an alias doesn't exist. Otherwise returns nullptr
const char* GetAlias(AZStd::string_view szName, bool bReturnSame = true) override;
// Set the localization folder
void SetLocalizationFolder(AZStd::string_view sLocalizationFolder) override;
const char* GetLocalizationFolder() const override { return m_sLocalizationFolder.c_str(); }
const char* GetLocalizationRoot() const override { return m_sLocalizationRoot.c_str(); }
// lock all the operations
void Lock() override;
void Unlock() override;
// open the physical archive file - creates if it doesn't exist
// returns nullptr if it's invalid or can't open the file
AZStd::intrusive_ptr<INestedArchive> OpenArchive(AZStd::string_view szPath, AZStd::string_view bindRoot = {}, uint32_t nFlags = 0, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr) override;
AZStd::intrusive_ptr<INestedArchive> OpenArchive(AZStd::string_view szPath, AZStd::string_view bindRoot = {}, uint32_t nArchiveFlags = 0, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr) override;
// returns the path to the archive in which the file was opened
const char* GetFileArchivePath(AZ::IO::HandleType fileHandle) override;
AZ::IO::PathView GetFileArchivePath(AZ::IO::HandleType fileHandle) override;
//////////////////////////////////////////////////////////////////////////
@@ -192,40 +177,31 @@ namespace AZ::IO
void RegisterFileAccessSink(IArchiveFileAccessSink* pSink) override;
void UnregisterFileAccessSink(IArchiveFileAccessSink* pSink) override;
bool Init(AZStd::string_view szBasePath) override;
void Release() override;
bool IsInstalledToHDD(AZStd::string_view acFilePath = 0) const override;
// [LYN-2376] Remove 'addLevels' parameter once legacy slice support is removed
bool OpenPack(AZStd::string_view pName, uint32_t nFlags = 0, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) override;
bool OpenPack(AZStd::string_view szBindRoot, AZStd::string_view pName, uint32_t nFlags = 0, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) override;
bool OpenPack(AZStd::string_view pName, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) override;
bool OpenPack(AZStd::string_view szBindRoot, AZStd::string_view pName, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) override;
// after this call, the file will be unlocked and closed, and its contents won't be used to search for files
bool ClosePack(AZStd::string_view pName, uint32_t nFlags = 0) override;
bool OpenPacks(AZStd::string_view pWildcard, uint32_t nFlags = 0, AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr) override;
bool OpenPacks(AZStd::string_view szBindRoot, AZStd::string_view pWildcard, uint32_t nFlags = 0, AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr) override;
bool ClosePack(AZStd::string_view pName) override;
bool OpenPacks(AZStd::string_view pWildcard, AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr) override;
bool OpenPacks(AZStd::string_view szBindRoot, AZStd::string_view pWildcard, AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr) override;
// closes pack files by the path and wildcard
bool ClosePacks(AZStd::string_view pWildcard, uint32_t nFlags = 0) override;
bool ClosePacks(AZStd::string_view pWildcard) override;
//returns if a archive exists matching the wildcard
bool FindPacks(AZStd::string_view pWildcardIn) override;
// prevent access to specific archive files
bool SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard, uint32_t nFlags = 0) override;
bool SetPackAccessible(bool bAccessible, AZStd::string_view pName, uint32_t nFlags = 0) override;
bool SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard) override;
bool SetPackAccessible(bool bAccessible, AZStd::string_view pName) override;
// returns the file modification time
uint64_t GetModificationTime(AZ::IO::HandleType fileHandle) override;
bool LoadPakToMemory(AZStd::string_view pName, EInMemoryArchiveLocation nLoadArchiveToMemory, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pMemoryBlock = nullptr) override;
void LoadPaksToMemory(int nMaxArchiveSize, bool bLoadToMemory) override;
AZ::IO::HandleType FOpen(AZStd::string_view pName, const char* mode, uint32_t nPathFlags = 0) override;
size_t FReadRaw(void* data, size_t length, size_t elems, AZ::IO::HandleType handle) override;
size_t FReadRawAll(void* data, size_t nFileSize, AZ::IO::HandleType handle) override;
AZ::IO::HandleType FOpen(AZStd::string_view pName, const char* mode) override;
size_t FRead(void* data, size_t bytesToRead, AZ::IO::HandleType handle) override;
void* FGetCachedFileData(AZ::IO::HandleType handle, size_t& nFileSize) override;
size_t FWrite(const void* data, size_t length, size_t elems, AZ::IO::HandleType handle) override;
size_t FWrite(const void* data, size_t bytesToWrite, AZ::IO::HandleType handle) override;
size_t FSeek(AZ::IO::HandleType handle, uint64_t seek, int mode) override;
uint64_t FTell(AZ::IO::HandleType handle) override;
int FFlush(AZ::IO::HandleType handle) override;
@@ -234,9 +210,7 @@ namespace AZ::IO
AZ::IO::ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator fileIterator) override;
bool FindClose(AZ::IO::ArchiveFileIterator fileIterator) override;
int FEof(AZ::IO::HandleType handle) override;
char* FGets(char*, int, AZ::IO::HandleType) override;
int Getc(AZ::IO::HandleType) override;
int FPrintf(AZ::IO::HandleType handle, const char* format, ...) override;
size_t FGetSize(AZ::IO::HandleType fileHandle) override;
size_t FGetSize(AZStd::string_view sFilename, bool bAllowUseFileSystem = false) override;
bool IsInPak(AZ::IO::HandleType handle) override;
@@ -248,9 +222,6 @@ namespace AZ::IO
bool IsFolder(AZStd::string_view sPath) override;
IArchive::SignedFileSize GetFileSizeOnDisk(AZStd::string_view filename) override;
// creates a directory
bool MakeDir(AZStd::string_view szPath) override;
// compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate)
// returns one of the Z_* errors (Z_OK upon success)
// MT-safe
@@ -275,22 +246,12 @@ namespace AZ::IO
IResourceList* GetResourceList(ERecordFileOpenList eList) override;
void SetResourceList(ERecordFileOpenList eList, IResourceList* pResourceList) override;
uint32_t ComputeCRC(AZStd::string_view szPath, uint32_t nFileOpenFlags = 0) override;
bool ComputeMD5(AZStd::string_view szPath, uint8_t* md5, uint32_t nFileOpenFlags = 0, bool useDirectFileAccess = false) override;
void DisableRuntimeFileAccess(bool status) override
{
m_disableRuntimeFileAccess[0] = status;
m_disableRuntimeFileAccess[1] = status;
m_disableRuntimeFileAccess = status;
}
bool DisableRuntimeFileAccess(bool status, AZStd::thread_id threadId) override;
bool CheckFileAccessDisabled(AZStd::string_view name, const char* mode) override;
void SetRenderThreadId(AZStd::thread_id renderThreadId) override
{
m_renderThreadId = renderThreadId;
}
// gets the current archive priority
ArchiveLocationPriority GetPakPriority() const override;
@@ -307,11 +268,11 @@ namespace AZ::IO
// Return cached file data for entries inside archive file.
CCachedFileDataPtr GetOpenedFileDataInZip(AZ::IO::HandleType file);
ZipDir::FileEntry* FindPakFileEntry(AZStd::string_view szPath, uint32_t& nArchiveFlags,
ZipDir::CachePtr* pZip = {}, bool bSkipInMemoryArchives = {}) const;
ZipDir::CachePtr* pZip = {}) const;
private:
bool OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view pName, uint32_t nArchiveFlags, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr, bool addLevels = true);
bool OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, uint32_t nArchiveFlags, AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr, bool addLevels = true);
bool OpenPackCommon(AZStd::string_view szBindRoot, AZStd::string_view pName, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr, bool addLevels = true);
bool OpenPacksCommon(AZStd::string_view szDir, AZStd::string_view pWildcardIn, AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr, bool addLevels = true);
ZipDir::FileEntry* FindPakFileEntry(AZStd::string_view szPath) const;
@@ -346,9 +307,6 @@ namespace AZ::IO
AZStd::mutex m_cachedFileRawDataMutex;
// For m_pCachedFileRawDataSet
using RawDataCacheLockGuard = AZStd::scoped_lock<decltype(m_cachedFileRawDataMutex)>;
// The F* emulation functions critical section: protects all F* functions
// that don't have a chance to be called recursively (to avoid deadlocks)
AZStd::mutex m_csMain;
mutable AZStd::shared_mutex m_archiveMutex;
ArchiveArray m_arrArchives;
@@ -360,8 +318,6 @@ namespace AZ::IO
//////////////////////////////////////////////////////////////////////////
IArchive::ERecordFileOpenList m_eRecordFileOpenList = RFOM_Disabled;
using RecordedFilesSet = AZStd::set<AZ::OSString, AZ::IO::AZStdStringLessCaseInsensitive, AZ::OSStdAllocator>;
RecordedFilesSet m_recordedFilesSet;
AZStd::intrusive_ptr<IResourceList> m_pEngineStartupResourceList;
@@ -372,28 +328,16 @@ namespace AZ::IO
float m_fFileAccessTime{}; // Time used to perform file operations
AZStd::vector<IArchiveFileAccessSink*, AZ::OSStdAllocator> m_FileAccessSinks; // useful for gathering file access statistics
bool m_disableRuntimeFileAccess[2]{};
bool m_disableRuntimeFileAccess{};
//threads which we don't want to access files from during the game
AZStd::thread_id m_mainThreadId{};
AZStd::thread_id m_renderThreadId{};
AZStd::fixed_string<128> m_sLocalizationFolder;
AZStd::fixed_string<128> m_sLocalizationRoot;
AZStd::set<uint32_t, AZStd::less<>, AZ::OSStdAllocator> m_filesCachedOnHDD;
// [LYN-2376] Remove once legacy slice support is removed
LevelPackOpenEvent m_levelOpenEvent;
LevelPackCloseEvent m_levelCloseEvent;
};
}
namespace AZ::IO::ArchiveInternal
{
// Utility function to de-alias archive file opening and file-within-archive opening
// if the file specified was an absolute path but it points at one of the aliases, de-alias it and replace it with that alias.
// this works around problems where the level editor is in control but still mounts asset packs (ie, level.pak mounted as @assets@)
AZStd::optional<AZ::IO::FixedMaxPath> ConvertAbsolutePathToAliasedPath(AZStd::string_view sourcePath,
AZStd::string_view aliasToLookFor = "@devassets@", AZStd::string_view aliasToReplaceWith = "@assets@");
}
@@ -5,10 +5,9 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/IO/SystemFile.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/functional.h> // for function<> in the find files callback.
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Archive/ArchiveFileIO.h>
#include <AzFramework/Archive/IArchive.h>
@@ -188,7 +187,7 @@ namespace AZ::IO
return IO::ResultCode::Error;
}
size_t result = m_archive->FReadRaw(buffer, 1, size, fileHandle);
size_t result = m_archive->FRead(buffer, size, fileHandle);
if (bytesRead)
{
*bytesRead = static_cast<AZ::u64>(result);
@@ -213,7 +212,7 @@ namespace AZ::IO
return IO::ResultCode::Error;
}
size_t result = m_archive->FWrite(buffer, 1, size, fileHandle);
size_t result = m_archive->FWrite(buffer, size, fileHandle);
if (bytesWritten)
{
*bytesWritten = static_cast<AZ::u64>(result);
@@ -357,14 +356,8 @@ namespace AZ::IO
return IO::ResultCode::Error;
}
// avoid using AZStd::string if possible - use OSString instead of StringFunc
AZ::OSString destPath(destinationFilePath);
IO::Path destPath(IO::PathView(destinationFilePath).ParentPath());
AZ::OSString::size_type pos = destPath.find_last_of(AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
if (pos != AZ::OSString::npos)
{
destPath.resize(pos);
}
CreatePath(destPath.c_str());
if (!Open(destinationFilePath, IO::OpenMode::ModeWrite | IO::OpenMode::ModeBinary, destinationFile))
@@ -466,31 +459,25 @@ namespace AZ::IO
return IO::ResultCode::Error;
}
AZStd::fixed_string<AZ_MAX_PATH_LEN> total = filePath;
AZ::IO::FixedMaxPath total = filePath;
if (total.empty())
{
return IO::ResultCode::Error;
}
if (!total.ends_with(AZ_CORRECT_FILESYSTEM_SEPARATOR) && !total.ends_with(AZ_WRONG_FILESYSTEM_SEPARATOR))
{
total.append(AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING);
}
total.append(filter);
total /= filter;
AZ::IO::ArchiveFileIterator fileIterator = m_archive->FindFirst(total.c_str());
if (!fileIterator)
{
return IO::ResultCode::Success; // its not an actual fatal error to not find anything.
}
for (;fileIterator; fileIterator = m_archive->FindNext(fileIterator))
for (; fileIterator; fileIterator = m_archive->FindNext(fileIterator))
{
total = AZStd::fixed_string<AZ_MAX_PATH_LEN>::format("%s/%.*s", filePath, aznumeric_cast<int>(fileIterator.m_filename.size()), fileIterator.m_filename.data());
AZStd::optional resolvedAliasLength = ConvertToAlias(total.data(), total.capacity());
if (resolvedAliasLength)
total = filePath;
total /= fileIterator.m_filename;
if (ConvertToAlias(total, total))
{
total.resize_no_construct(*resolvedAliasLength);
if (!callback(total.c_str()))
{
break;
@@ -510,8 +497,13 @@ namespace AZ::IO
const auto fileIt = m_trackedFiles.find(fileHandle);
if (fileIt != m_trackedFiles.end())
{
AZ_Assert(filenameSize >= fileIt->second.length(), "Filename size %" PRIu64 " is larger than the size of the tracked file %s:%zu", fileIt->second.c_str(), fileIt->second.size());
azstrncpy(filename, filenameSize, fileIt->second.c_str(), fileIt->second.length());
const AZStd::string_view trackedFileView = fileIt->second.Native();
if (filenameSize <= trackedFileView.size())
{
return false;
}
size_t trackedFileViewLength = trackedFileView.copy(filename, trackedFileView.size());
filename[trackedFileViewLength] = '\0';
return true;
}
@@ -13,7 +13,6 @@
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/fixed_vector.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/string/osstring.h>
namespace AZ::IO
@@ -65,7 +64,7 @@ namespace AZ::IO
void SetAlias(const char* alias, const char* path) override;
void ClearAlias(const char* alias) override;
AZStd::optional<AZ::u64> ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const override;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const;
bool ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const override;
using FileIOBase::ConvertToAlias;
const char* GetAlias(const char* alias) const override;
bool ResolvePath(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const override;
@@ -78,7 +77,7 @@ namespace AZ::IO
protected:
// we keep a list of file names ever opened so that we can easily return it.
mutable AZStd::recursive_mutex m_operationGuard;
AZStd::unordered_map<IO::HandleType, AZ::OSString, AZStd::hash<IO::HandleType>, AZStd::equal_to<IO::HandleType>, AZ::OSStdAllocator> m_trackedFiles;
AZStd::unordered_map<IO::HandleType, AZ::IO::Path> m_trackedFiles;
AZStd::fixed_vector<char, ArchiveFileIoMaxBuffersize> m_copyBuffer;
IArchive* m_archive;
};
@@ -15,34 +15,6 @@
namespace AZ::IO
{
size_t ArchiveFileIteratorHash::operator()(const AZ::IO::ArchiveFileIterator& iter) const
{
return iter.GetHash();
}
bool AZStdStringLessCaseInsensitive::operator()(AZStd::string_view left, AZStd::string_view right) const
{
// If one or both strings are 0-length, return true if the left side is smaller, false if they're equal or left is larger.
size_t compareLength = (AZStd::min)(left.size(), right.size());
if (compareLength == 0)
{
return left.size() < right.size();
}
// They're both non-zero, so compare the strings up until the length of the shorter string.
int compareResult = azstrnicmp(left.data(), right.data(), compareLength);
// If both strings are equal for the number of characters compared, return true if the left side is shorter, false if
// they're equal or left is longer.
if (compareResult == 0)
{
return left.size() < right.size();
}
// Return true if the left side should come first alphabetically, false if the right side should.
return compareResult < 0;
}
FileDesc::FileDesc(Attribute fileAttribute, uint64_t fileSize, time_t accessTime, time_t creationTime, time_t writeTime)
: nAttrib{ fileAttribute }
, nSize{ fileSize }
@@ -52,10 +24,9 @@ namespace AZ::IO
{
}
ArchiveFileIterator::ArchiveFileIterator(FindData* findData, AZStd::string_view filename, const FileDesc& fileDesc)
// ArchiveFileIterator
ArchiveFileIterator::ArchiveFileIterator(FindData* findData)
: m_findData{ findData }
, m_filename{ filename }
, m_fileDesc{ fileDesc }
{
}
@@ -73,21 +44,36 @@ namespace AZ::IO
return operator++();
}
bool ArchiveFileIterator::operator==(const AZ::IO::ArchiveFileIterator& rhs) const
{
return GetHash() == rhs.GetHash();
}
ArchiveFileIterator::operator bool() const
{
return m_findData && m_lastFetchValid;
}
size_t ArchiveFileIterator::GetHash() const
// FindData::ArchiveFile
FindData::ArchiveFile::ArchiveFile() = default;
FindData::ArchiveFile::ArchiveFile(AZStd::string_view filename, const FileDesc& fileDesc)
: m_filename(filename)
, m_fileDesc(fileDesc)
{
}
size_t FindData::ArchiveFile::GetHash() const
{
return AZStd::hash<AZ::IO::PathView>{}(m_filename.c_str());
}
bool FindData::ArchiveFile::operator==(const ArchiveFile& rhs) const
{
return GetHash() == rhs.GetHash();
}
// FindData::ArchiveFilehash
size_t FindData::ArchiveFileHash::operator()(const ArchiveFile& archiveFile) const
{
return archiveFile.GetHash();
}
// FindData
void FindData::Scan(IArchive* archive, AZStd::string_view szDir, bool bAllowUseFS, bool bScanZips)
{
// get the priority into local variable to avoid it changing in the course of
@@ -119,40 +105,37 @@ namespace AZ::IO
void FindData::ScanFS([[maybe_unused]] IArchive* archive, AZStd::string_view szDirIn)
{
AZStd::string searchDirectory;
AZStd::string pattern;
AZ::IO::PathView directory{ szDirIn };
AZ::IO::FixedMaxPath searchDirectory = directory.ParentPath();
AZ::IO::FixedMaxPath pattern = directory.Filename();
auto ScanFileSystem = [this](const char* filePath) -> bool
{
AZ::IO::PathString directory{ szDirIn };
AZ::StringFunc::Path::GetFullPath(directory.c_str(), searchDirectory);
AZ::StringFunc::Path::GetFullFileName(directory.c_str(), pattern);
}
AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), [&](const char* filePath) -> bool
{
AZ::IO::ArchiveFileIterator fileIterator{ nullptr, AZ::IO::PathView(filePath).Filename().Native(), {} };
ArchiveFile archiveFile{ AZ::IO::PathView(filePath).Filename().Native(), {} };
if (AZ::IO::FileIOBase::GetDirectInstance()->IsDirectory(filePath))
{
fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory;
m_fileSet.emplace(AZStd::move(fileIterator));
archiveFile.m_fileDesc.nAttrib = archiveFile.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::Subdirectory;
m_fileSet.emplace(AZStd::move(archiveFile));
}
else
{
if (AZ::IO::FileIOBase::GetDirectInstance()->IsReadOnly(filePath))
{
fileIterator.m_fileDesc.nAttrib = fileIterator.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly;
archiveFile.m_fileDesc.nAttrib = archiveFile.m_fileDesc.nAttrib | AZ::IO::FileDesc::Attribute::ReadOnly;
}
AZ::u64 fileSize = 0;
AZ::IO::FileIOBase::GetDirectInstance()->Size(filePath, fileSize);
fileIterator.m_fileDesc.nSize = fileSize;
fileIterator.m_fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath);
archiveFile.m_fileDesc.nSize = fileSize;
archiveFile.m_fileDesc.tWrite = AZ::IO::FileIOBase::GetDirectInstance()->ModificationTime(filePath);
// These times are not supported by our file interface
fileIterator.m_fileDesc.tAccess = fileIterator.m_fileDesc.tWrite;
fileIterator.m_fileDesc.tCreate = fileIterator.m_fileDesc.tWrite;
m_fileSet.emplace(AZStd::move(fileIterator));
archiveFile.m_fileDesc.tAccess = archiveFile.m_fileDesc.tWrite;
archiveFile.m_fileDesc.tCreate = archiveFile.m_fileDesc.tWrite;
m_fileSet.emplace(AZStd::move(archiveFile));
}
return true;
});
};
AZ::IO::FileIOBase::GetDirectInstance()->FindFiles(searchDirectory.c_str(), pattern.c_str(), ScanFileSystem);
}
//////////////////////////////////////////////////////////////////////////
@@ -180,7 +163,7 @@ namespace AZ::IO
fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive;
fileDesc.nSize = fileEntry->desc.lSizeUncompressed;
fileDesc.tWrite = fileEntry->GetModificationTime();
m_fileSet.emplace(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc });
m_fileSet.emplace(fname, fileDesc);
}
ZipDir::FindDir findDirectoryEntry(zipCache);
@@ -193,7 +176,7 @@ namespace AZ::IO
}
AZ::IO::FileDesc fileDesc;
fileDesc.nAttrib = AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory;
m_fileSet.emplace(AZ::IO::ArchiveFileIterator{ this, fname, fileDesc });
m_fileSet.emplace(fname, fileDesc);
}
};
@@ -208,30 +191,16 @@ namespace AZ::IO
// so there's really no way to filter out opening the pack and looking at the files inside.
// however, the bind root is not part of the inner zip entry name either
// and the ZipDir::FindFile actually expects just the chopped off piece.
// we have to find whats in common between them and check that:
// we have to find the common path segments between them and check that:
auto resolvedBindRoot = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(it->m_pathBindRoot);
if (!resolvedBindRoot)
AZ::IO::FixedMaxPath bindRoot;
if (!AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(bindRoot, it->m_pathBindRoot))
{
AZ_Assert(false, "Unable to resolve Path for archive %s bind root %s", it->GetFullPath(), it->m_pathBindRoot.c_str());
return;
}
AZ::IO::FixedMaxPath bindRoot{ *resolvedBindRoot };
auto [bindRootIter, sourcePathIter] = AZStd::mismatch(AZStd::begin(bindRoot), AZStd::end(bindRoot),
AZStd::begin(sourcePath), AZStd::end(sourcePath));
if (sourcePathIter == AZStd::begin(sourcePath))
{
// The path has no characters in common , early out the search as filepath is not part of the iterated zip
continue;
}
AZ::IO::FixedMaxPath sourcePathRemainder;
for (; sourcePathIter != AZStd::end(sourcePath); ++sourcePathIter)
{
sourcePathRemainder /= *sourcePathIter;
}
// Example:
// "@assets@\\levels\\*" <--- szDir
// "@assets@\\" <--- mount point
@@ -256,18 +225,26 @@ namespace AZ::IO
// then it means that the pack's mount point itself might be a return value, not the files inside the pack
// in that case, we compare the mount point remainder itself with the search filter
auto [bindRootIter, sourcePathIter] = AZStd::mismatch(bindRoot.begin(), bindRoot.end(),
sourcePath.begin(), sourcePath.end());
if (bindRootIter != bindRoot.end())
{
AZ::IO::FixedMaxPath sourcePathRemainder;
for (; sourcePathIter != sourcePath.end(); ++sourcePathIter)
{
sourcePathRemainder /= *sourcePathIter;
}
// Retrieve next path component of the mount point remainder
if (!bindRootIter->empty() && AZStd::wildcard_match(sourcePathRemainder.Native(), bindRootIter->Native()))
if (!bindRootIter->empty() && bindRootIter->Match(sourcePathRemainder.Native()))
{
AZ::IO::FileDesc fileDesc{ AZ::IO::FileDesc::Attribute::ReadOnly | AZ::IO::FileDesc::Attribute::Archive | AZ::IO::FileDesc::Attribute::Subdirectory };
m_fileSet.emplace(AZ::IO::ArchiveFileIterator{ this, bindRootIter->Native(), fileDesc });
m_fileSet.emplace(AZStd::move(bindRootIter->Native()), fileDesc);
}
}
else
{
AZ::IO::FixedMaxPath sourcePathRemainder = sourcePath.LexicallyRelative(bindRoot);
// if we get here, it means that the search pattern's root and the mount point for this pack are identical
// which means we may search inside the pack.
ScanInZip(it->pZip.get(), sourcePathRemainder.Native());
@@ -280,17 +257,17 @@ namespace AZ::IO
{
if (m_fileSet.empty())
{
AZ::IO::ArchiveFileIterator emptyFileIterator;
emptyFileIterator.m_lastFetchValid = false;
emptyFileIterator.m_findData = this;
return emptyFileIterator;
return {};
}
// Remove Fetched item from the FindData map so that the iteration continues
AZ::IO::ArchiveFileIterator fileIterator{ *m_fileSet.begin() };
AZ::IO::ArchiveFileIterator fileIterator;
auto archiveFileIt = m_fileSet.begin();
fileIterator.m_filename = archiveFileIt->m_filename;
fileIterator.m_fileDesc = archiveFileIt->m_fileDesc;
fileIterator.m_lastFetchValid = true;
fileIterator.m_findData = this;
m_fileSet.erase(m_fileSet.begin());
m_fileSet.erase(archiveFileIt);
return fileIterator;
}
}
@@ -36,56 +36,72 @@ namespace AZ::IO
AZ_DEFINE_ENUM_BITWISE_OPERATORS(AZ::IO::FileDesc::Attribute);
inline constexpr size_t ArchiveFilenameMaxLength = 256;
using ArchiveFileString = AZStd::fixed_string<ArchiveFilenameMaxLength>;
class FindData;
//! This is not really an iterator, but a handle
//! that extends ownership of any found filenames from an archive file or the file system
struct ArchiveFileIterator
{
ArchiveFileIterator() = default;
ArchiveFileIterator(FindData* findData, AZStd::string_view filename, const FileDesc& fileDesc);
explicit ArchiveFileIterator(FindData* findData);
ArchiveFileIterator operator++();
ArchiveFileIterator operator++(int);
bool operator==(const AZ::IO::ArchiveFileIterator& rhs) const;
explicit operator bool() const;
size_t GetHash() const;
inline static constexpr size_t FilenameMaxLength = 256;
AZStd::fixed_string<FilenameMaxLength> m_filename;
ArchiveFileString m_filename;
FileDesc m_fileDesc;
AZStd::intrusive_ptr<FindData> m_findData{};
private:
friend class FindData;
friend class Archive;
AZStd::intrusive_ptr<FindData> m_findData;
bool m_lastFetchValid{};
};
struct ArchiveFileIteratorHash
{
size_t operator()(const AZ::IO::ArchiveFileIterator& iter) const;
};
struct AZStdStringLessCaseInsensitive
{
bool operator()(AZStd::string_view left, AZStd::string_view right) const;
using is_transparent = void;
};
class FindData
: public AZStd::intrusive_base
{
public:
AZ_CLASS_ALLOCATOR(FindData, AZ::SystemAllocator, 0);
FindData() = default;
AZ::IO::ArchiveFileIterator Fetch();
ArchiveFileIterator Fetch();
void Scan(IArchive* archive, AZStd::string_view path, bool bAllowUseFS = false, bool bScanZips = true);
protected:
void ScanFS(IArchive* archive, AZStd::string_view path);
// Populates the FileSet with files within the that match the path pattern that is
// if it refers to a file within a bound archive root or returns the archive root
// path if the path pattern matches it.
void ScanZips(IArchive* archive, AZStd::string_view path);
using FileSet = AZStd::unordered_set<ArchiveFileIterator, ArchiveFileIteratorHash>;
class ArchiveFile
{
public:
friend class FindData;
ArchiveFile();
ArchiveFile(AZStd::string_view filename, const FileDesc& fileDesc);
size_t GetHash() const;
bool operator==(const ArchiveFile& rhs) const;
private:
ArchiveFileString m_filename;
FileDesc m_fileDesc;
};
struct ArchiveFileHash
{
size_t operator()(const ArchiveFile& archiveFile) const;
};
using FileSet = AZStd::unordered_set<ArchiveFile, ArchiveFileHash>;
FileSet m_fileSet;
};
}
@@ -12,12 +12,10 @@
#include <AzCore/EBus/Event.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/smart_ptr/intrusive_base.h>
#include <AzCore/std/smart_ptr/intrusive_ptr.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/fixed_string.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Archive/ArchiveFindData.h>
@@ -106,66 +104,6 @@ namespace AZ::IO
{
AZ_RTTI(IArchive, "{764A2260-FF8A-4C86-B958-EBB0B69D9DFA}");
using FileTime = uint64_t;
// Flags used in file path resolution rules
enum EPathResolutionRules
{
// If used, the source path will be treated as the destination path
// and no transformations will be done. Pass this flag when the path is to be the actual
// path on the disk/in the packs and doesn't need adjustment (or after it has come through adjustments already)
// if this is set, AdjustFileName will not map the input path into the folder (Ex: Shaders will not be converted to Game\Shaders)
FLAGS_PATH_REAL = 1 << 16,
// AdjustFileName will always copy the file path to the destination path:
// regardless of the returned value, szDestpath can be used
FLAGS_COPY_DEST_ALWAYS = 1 << 17,
// Adds trailing slash to the path
FLAGS_ADD_TRAILING_SLASH = 1L << 18,
// if this is set, AdjustFileName will not make relative paths into full paths
FLAGS_NO_FULL_PATH = 1 << 21,
// if this is set, AdjustFileName will redirect path to disc
FLAGS_REDIRECT_TO_DISC = 1 << 22,
// if this is set, AdjustFileName will not adjust path for writing files
FLAGS_FOR_WRITING = 1 << 23,
// if this is set, the archive would be stored in memory (gpu)
FLAGS_PAK_IN_MEMORY = 1 << 25,
// Store all file names as crc32 in a flat directory structure.
FLAGS_FILENAMES_AS_CRC32 = 1 << 26,
// if this is set, AdjustFileName will try to find the file under any mod paths we know about
FLAGS_CHECK_MOD_PATHS = 1 << 27,
// if this is set, AdjustFileName will always check the filesystem/disk and not check inside open archives
FLAGS_NEVER_IN_PAK = 1 << 28,
// returns existing file name from the local data or existing cache file name
// used by the resource compiler to pass the real file name
FLAGS_RESOLVE_TO_CACHE = 1 << 29,
// if this is set, the archive would be stored in memory (cpu)
FLAGS_PAK_IN_MEMORY_CPU = 1 << 30,
// if this is set, the level pak is inside another archive
FLAGS_LEVEL_PAK_INSIDE_PAK = 1 << 31,
};
// Used for widening FOpen functionality. They're ignored for the regular File System files.
enum EFOpenFlags
{
// If possible, will prevent the file from being read from memory.
FOPEN_HINT_DIRECT_OPERATION = 1,
// Will prevent a "missing file" warnings to be created.
FOPEN_HINT_QUIET = 1 << 1,
// File should be on disk
FOPEN_ONDISK = 1 << 2,
// Open is done by the streaming thread.
FOPEN_FORSTREAMING = 1 << 3,
};
//
enum ERecordFileOpenList
@@ -175,8 +113,6 @@ namespace AZ::IO
RFOM_Level, // during level loading till export2game -> resourcelist.txt, used to generate the list for level2level loading
RFOM_NextLevel // used for level2level loading
};
// the size of the buffer that receives the full path to the file
inline static constexpr size_t MaxPath = 1024;
//file location enum used in isFileExist to control where the archive system looks for the file.
enum EFileSearchLocation
@@ -205,63 +141,31 @@ namespace AZ::IO
virtual ~IArchive() = default;
/**
* Deprecated: Use the AZ::IO::FileIOBase::ResolvePath function below that doesn't accept the nFlags or skipMods parameters
* given the source relative path, constructs the full path to the file according to the flags
* returns the pointer to the constructed path (can be either szSourcePath, or szDestPath, or NULL in case of error
*/
//
virtual const char* AdjustFileName(AZStd::string_view src, char* dst, size_t dstSize, uint32_t nFlags, bool skipMods = false) = 0;
virtual bool Init(AZStd::string_view szBasePath) = 0;
virtual void Release() = 0;
// Summary:
// Returns true if given archivepath is installed to HDD
// If no file path is given it will return true if whole application is installed to HDD
virtual bool IsInstalledToHDD(AZStd::string_view acFilePath = 0) const = 0;
// after this call, the archive file will be searched for files when they aren't on the OS file system
// Arguments:
// pName - must not be 0
virtual bool OpenPack(AZStd::string_view pName, uint32_t nFlags = FLAGS_PATH_REAL, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = {},
virtual bool OpenPack(AZStd::string_view pName, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = {},
AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) = 0;
// after this call, the archive file will be searched for files when they aren't on the OS file system
virtual bool OpenPack(AZStd::string_view pBindingRoot, AZStd::string_view pName, uint32_t nFlags = FLAGS_PATH_REAL,
virtual bool OpenPack(AZStd::string_view pBindingRoot, AZStd::string_view pName,
AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = {}, AZ::IO::FixedMaxPathString* pFullPath = nullptr, bool addLevels = true) = 0;
// after this call, the file will be unlocked and closed, and its contents won't be used to search for files
virtual bool ClosePack(AZStd::string_view pName, uint32_t nFlags = FLAGS_PATH_REAL) = 0;
virtual bool ClosePack(AZStd::string_view pName) = 0;
// opens pack files by the path and wildcard
virtual bool OpenPacks(AZStd::string_view pWildcard, uint32_t nFlags = FLAGS_PATH_REAL, AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr) = 0;
virtual bool OpenPacks(AZStd::string_view pWildcard, AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr) = 0;
// opens pack files by the path and wildcard
virtual bool OpenPacks(AZStd::string_view pBindingRoot, AZStd::string_view pWildcard, uint32_t nFlags = FLAGS_PATH_REAL,
virtual bool OpenPacks(AZStd::string_view pBindingRoot, AZStd::string_view pWildcard,
AZStd::vector<AZ::IO::FixedMaxPathString>* pFullPaths = nullptr) = 0;
// closes pack files by the path and wildcard
virtual bool ClosePacks(AZStd::string_view pWildcard, uint32_t nFlags = FLAGS_PATH_REAL) = 0;
virtual bool ClosePacks(AZStd::string_view pWildcard) = 0;
//returns if a archive exists matching the wildcard
virtual bool FindPacks(AZStd::string_view pWildcardIn) = 0;
// Set access status of a archive files with a wildcard
virtual bool SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard, uint32_t nFlags = FLAGS_PATH_REAL) = 0;
virtual bool SetPacksAccessible(bool bAccessible, AZStd::string_view pWildcard) = 0;
// Set access status of a pack file
virtual bool SetPackAccessible(bool bAccessible, AZStd::string_view pName, uint32_t nFlags = FLAGS_PATH_REAL) = 0;
// Load or unload archive file completely to memory.
virtual bool LoadPakToMemory(AZStd::string_view pName, EInMemoryArchiveLocation eLoadToMemory, AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pMemoryBlock = nullptr) = 0;
virtual void LoadPaksToMemory(int nMaxArchiveSize, bool bLoadToMemory) = 0;
// Processes an alias command line containing multiple aliases.
virtual void ParseAliases(AZStd::string_view szCommandLine) = 0;
// adds or removes an alias from the list
virtual void SetAlias(AZStd::string_view szName, AZStd::string_view szAlias, bool bAdd) = 0;
// gets an alias from the list, if any exist.
// if bReturnSame==true, it will return the input name if an alias doesn't exist. Otherwise returns NULL
virtual const char* GetAlias(AZStd::string_view szName, bool bReturnSame = true) = 0;
// lock all the operations
virtual void Lock() = 0;
virtual void Unlock() = 0;
virtual bool SetPackAccessible(bool bAccessible, AZStd::string_view pName) = 0;
// Set and Get the localization folder name (Languages, Localization, ...)
virtual void SetLocalizationFolder(AZStd::string_view sLocalizationFolder) = 0;
@@ -273,28 +177,19 @@ namespace AZ::IO
// ex: AZ::IO::HandleType fileHandle = FOpen( "test.txt","rbx" );
// mode x is a direct access mode, when used file reads will go directly into the low level file system without any internal data caching.
// Text mode is not supported for files in Archives.
// for nFlags @see IArchive::EFOpenFlags
virtual AZ::IO::HandleType FOpen(AZStd::string_view pName, const char* mode, uint32_t nFlags = 0) = 0;
// Read raw data from file, no endian conversion.
virtual size_t FReadRaw(void* data, size_t length, size_t elems, AZ::IO::HandleType fileHandle) = 0;
// Read all file contents into the provided memory, nSizeOfFile must be the same as returned by GetFileSize(handle)
// Current seek pointer is ignored and reseted to 0.
// no endian conversion.
virtual size_t FReadRawAll(void* data, size_t nFileSize, AZ::IO::HandleType fileHandle) = 0;
virtual AZ::IO::HandleType FOpen(AZStd::string_view pName, const char* mode) = 0;
// Get pointer to the internally cached, loaded data of the file.
// WARNING! The returned pointer is only valid while the fileHandle has not been closed.
virtual void* FGetCachedFileData(AZ::IO::HandleType fileHandle, size_t& nFileSize) = 0;
// Write file data, cannot be used for writing into the Archive.
// Use INestedArchive interface for writing into the archivefiles.
virtual size_t FWrite(const void* data, size_t length, size_t elems, AZ::IO::HandleType fileHandle) = 0;
// Read raw data from file, no endian conversion.
virtual size_t FRead(void* data, size_t bytesToRead, AZ::IO::HandleType fileHandle) = 0;
// Write file data, cannot be used for writing into the Archive.
// Use INestedArchive interface for writing into the archive files.
virtual size_t FWrite(const void* data, size_t bytesToWrite, AZ::IO::HandleType fileHandle) = 0;
virtual int FPrintf(AZ::IO::HandleType fileHandle, const char* format, ...) = 0;
virtual char* FGets(char*, int, AZ::IO::HandleType) = 0;
virtual int Getc(AZ::IO::HandleType) = 0;
virtual size_t FGetSize(AZ::IO::HandleType fileHandle) = 0;
virtual size_t FGetSize(AZStd::string_view pName, bool bAllowUseFileSystem = false) = 0;
virtual bool IsInPak(AZ::IO::HandleType fileHandle) = 0;
@@ -318,7 +213,6 @@ namespace AZ::IO
virtual AZStd::intrusive_ptr<AZ::IO::MemoryBlock> PoolAllocMemoryBlock(size_t nSize, const char* sUsage, size_t nAlign = 1) = 0;
// Arguments:
// nFlags is a combination of EPathResolutionRules flags.
virtual ArchiveFileIterator FindFirst(AZStd::string_view pDir, EFileSearchType searchType = eFileSearchType_AllowInZipsOnly) = 0;
virtual ArchiveFileIterator FindNext(AZ::IO::ArchiveFileIterator handle) = 0;
virtual bool FindClose(AZ::IO::ArchiveFileIterator handle) = 0;
@@ -334,9 +228,6 @@ namespace AZ::IO
virtual IArchive::SignedFileSize GetFileSizeOnDisk(AZStd::string_view filename) = 0;
// creates a directory
virtual bool MakeDir(AZStd::string_view szPath) = 0;
// open the physical archive file - creates if it doesn't exist
// returns NULL if it's invalid or can't open the file
// nFlags is a combination of flags from EArchiveFlags enum.
@@ -344,8 +235,8 @@ namespace AZ::IO
AZStd::intrusive_ptr<AZ::IO::MemoryBlock> pData = nullptr) = 0;
// returns the path to the archive in which the file was opened
// returns NULL if the file is a physical file, and "" if the path to archive is unknown (shouldn't ever happen)
virtual const char* GetFileArchivePath(AZ::IO::HandleType fileHandle) = 0;
// returns empty path view if the file is a physical file
virtual AZ::IO::PathView GetFileArchivePath(AZ::IO::HandleType fileHandle) = 0;
// compresses the raw data into raw data. The buffer for compressed data itself with the heap passed. Uses method 8 (deflate)
// returns one of the Z_* errors (Z_OK upon success)
@@ -378,25 +269,7 @@ namespace AZ::IO
// get the current mode, can be set by RecordFileOpen()
virtual IArchive::ERecordFileOpenList GetRecordFileOpenList() = 0;
// computes CRC (zip compatible) for a file
// useful if a huge uncompressed file is generation in non continuous way
// good for big files - low memory overhead (1MB)
// Arguments:
// szPath - must not be 0
// Returns:
// error code
virtual uint32_t ComputeCRC(AZStd::string_view szPath, uint32_t nFileOpenFlags = 0) = 0;
// computes MD5 checksum for a file
// good for big files - low memory overhead (1MB)
// Arguments:
// szPath - must not be 0
// md5 - destination array of uint8_t [16]
// Returns:
// true if success, false on failure
virtual bool ComputeMD5(AZStd::string_view szPath, uint8_t* md5, uint32_t nFileOpenFlags = 0, bool useDirectFileAccess = false) = 0;
// useful for gathering file access statistics, assert if it was inserted already but then it does not become insersted
// useful for gathering file access statistics, assert if it was inserted already but then it does not become inserted
// Arguments:
// pSink - must not be 0
virtual void RegisterFileAccessSink(IArchiveFileAccessSink* pSink) = 0;
@@ -408,8 +281,6 @@ namespace AZ::IO
// When enabled, files accessed at runtime will be tracked
virtual void DisableRuntimeFileAccess(bool status) = 0;
virtual bool DisableRuntimeFileAccess(bool status, AZStd::thread_id threadId) = 0;
virtual bool CheckFileAccessDisabled(AZStd::string_view name, const char* mode) = 0;
virtual void SetRenderThreadId(AZStd::thread_id renderThreadId) = 0;
// gets the current pak priority
virtual ArchiveLocationPriority GetPakPriority() const = 0;
@@ -431,21 +302,6 @@ namespace AZ::IO
using LevelPackCloseEvent = AZ::Event<AZStd::string_view>;
virtual auto GetLevelPackCloseEvent()->LevelPackCloseEvent* = 0;
// Type-safe endian conversion read.
template<class T>
size_t FRead(T* data, size_t elems, AZ::IO::HandleType fileHandle, bool bSwapEndian = false)
{
size_t count = FReadRaw(data, sizeof(T), elems, fileHandle);
SwapEndian(data, count, bSwapEndian);
return count;
}
// Type-independent Write.
template<class T>
void FWrite(T* data, size_t elems, AZ::IO::HandleType fileHandle)
{
FWrite((void*)data, sizeof(T), elems, fileHandle);
}
inline static constexpr IArchive::SignedFileSize FILE_NOT_PRESENT = -1;
};
@@ -9,9 +9,9 @@
#pragma once
#include <AzCore/IO/Path/Path_fwd.h>
#include <AzCore/Math/Crc.h>
#include <AzCore/std/smart_ptr/intrusive_base.h>
#include <AzCore/std/string/string_view.h>
#include <AzFramework/Archive/Codec.h>
namespace AZ::IO
@@ -71,28 +71,10 @@ namespace AZ::IO
// multiple times
FLAGS_DONT_COMPACT = 1 << 5,
// flag is set when complete pak has been loaded into memory
FLAGS_IN_MEMORY = 1 << 6,
FLAGS_IN_MEMORY_CPU = 1 << 7,
FLAGS_IN_MEMORY_MASK = FLAGS_IN_MEMORY | FLAGS_IN_MEMORY_CPU,
// Store all file names as crc32 in a flat directory structure.
FLAGS_FILENAMES_AS_CRC32 = 1 << 8,
// flag is set when pak is stored on HDD
FLAGS_ON_HDD = 1 << 9,
//Override pak - paks opened with this flag go at the end of the list and contents will be found before other paks
//Used for patching
FLAGS_OVERRIDE_PAK = 1 << 10,
// Disable a pak file without unloading it, this flag is used in combination with patches and multiplayer
// to ensure that specific paks stay in the position(to keep the same priority) but beeing disabled
// to ensure that specific paks stay in the position(to keep the same priority) but being disabled
// when running multiplayer
FLAGS_DISABLE_PAK = 1 << 11,
// flag is set when pak is inside another pak
FLAGS_INSIDE_PAK = 1 << 12,
};
using Handle = void*;
@@ -122,7 +104,7 @@ namespace AZ::IO
virtual int StartContinuousFileUpdate(AZStd::string_view szRelativePath, uint64_t nSize) = 0;
// Summary:
// Adds a new file to the zip or update an existing's segment if it is not compressed - just stored
// Adds a new file to the zip or update an existing segment if it is not compressed - just stored
// adds a directory (creates several nested directories if needed)
// ( name might be misleading as if nOverwriteSeekPos is used the update is not continuous )
// Arguments:
@@ -164,7 +146,7 @@ namespace AZ::IO
// Summary:
// Get the full path to the archive file.
virtual const char* GetFullPath() const = 0;
virtual AZ::IO::PathView GetFullPath() const = 0;
// Summary:
// Get the flags of this object.
@@ -174,7 +174,7 @@ namespace AZ::IO
return m_pCache->ReadFile(reinterpret_cast<ZipDir::FileEntry*>(fileHandle), nullptr, pBuffer);
}
const char* NestedArchive::GetFullPath() const
AZ::IO::PathView NestedArchive::GetFullPath() const
{
return m_pCache->GetFilePath();
}
@@ -193,19 +193,9 @@ namespace AZ::IO
if (nFlagsToSet & FLAGS_RELATIVE_PATHS_ONLY)
{
m_nFlags |= FLAGS_RELATIVE_PATHS_ONLY;
}
if (nFlagsToSet & FLAGS_ON_HDD)
{
m_nFlags |= FLAGS_ON_HDD;
}
if (nFlagsToSet & FLAGS_RELATIVE_PATHS_ONLY ||
nFlagsToSet & FLAGS_ON_HDD)
{
// we don't support changing of any other flags
return true;
}
return false;
}
@@ -252,20 +242,12 @@ namespace AZ::IO
return AZ::IO::FixedMaxPathString{ szRelativePath };
}
if ((szRelativePath.size() > 1 && szRelativePath[1] == ':') || (m_nFlags & FLAGS_ABSOLUTE_PATHS))
if ((m_nFlags & FLAGS_ABSOLUTE_PATHS) == FLAGS_ABSOLUTE_PATHS)
{
// make the normalized full path and try to match it against the binding root of this object
auto resolvedPath = AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(szRelativePath);
// Make sure the resolve path is longer than the bind root and that it starts with the bind root
if (!resolvedPath || resolvedPath->Native().size() <= m_strBindRoot.size() || azstrnicmp(resolvedPath->c_str(), m_strBindRoot.c_str(), m_strBindRoot.size()) != 0)
{
return {};
}
// Remove the bind root prefix from the resolved path
resolvedPath->Native().erase(0, m_strBindRoot.size() + 1);
return resolvedPath->Native();
AZ::IO::FixedMaxPath resolvedPath;
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(resolvedPath, szRelativePath);
return resolvedPath.LexicallyProximate(m_strBindRoot).Native();
}
return AZ::IO::FixedMaxPathString{ szRelativePath };
@@ -19,15 +19,15 @@ namespace AZ::IO
{
bool operator()(const INestedArchive* left, const INestedArchive* right) const
{
return azstricmp(left->GetFullPath(), right->GetFullPath()) < 0;
return left->GetFullPath() < right->GetFullPath();
}
bool operator()(AZStd::string_view left, const INestedArchive* right) const
{
return azstrnicmp(left.data(), right->GetFullPath(), left.size()) < 0;
return AZ::IO::PathView(left) < right->GetFullPath();
}
bool operator()(const INestedArchive* left, AZStd::string_view right) const
{
return azstrnicmp(left->GetFullPath(), right.data(), right.size()) < 0;
return left->GetFullPath() < AZ::IO::PathView(right);
}
};
@@ -40,7 +40,7 @@ namespace AZ::IO
NestedArchive(IArchive* pArchive, AZStd::string_view strBindRoot, ZipDir::CachePtr pCache, uint32_t nFlags = 0);
~NestedArchive() override;
auto GetRootFolderHandle() -> Handle;
auto GetRootFolderHandle() -> Handle override;
// Adds a new file to the zip or update an existing one
// adds a directory (creates several nested directories if needed)
@@ -66,26 +66,26 @@ namespace AZ::IO
int RemoveDir(AZStd::string_view szRelativePath) override;
// deletes all files from the archive
int RemoveAll();
int RemoveAll() override;
// finds the file; you don't have to close the returned handle
Handle FindFile(AZStd::string_view szRelativePath);
Handle FindFile(AZStd::string_view szRelativePath) override;
// returns the size of the file (unpacked) by the handle
uint64_t GetFileSize(Handle fileHandle);
uint64_t GetFileSize(Handle fileHandle) override;
// reads the file into the preallocated buffer (must be at least the size of GetFileSize())
int ReadFile(Handle fileHandle, void* pBuffer);
int ReadFile(Handle fileHandle, void* pBuffer) override;
// returns the full path to the archive file
const char* GetFullPath() const;
AZ::IO::PathView GetFullPath() const override;
ZipDir::Cache* GetCache();
uint32_t GetFlags() const;
bool SetFlags(uint32_t nFlagsToSet);
bool ResetFlags(uint32_t nFlagsToReset);
uint32_t GetFlags() const override;
bool SetFlags(uint32_t nFlagsToSet) override;
bool ResetFlags(uint32_t nFlagsToReset) override;
bool SetPackAccessible(bool bAccessible);
bool SetPackAccessible(bool bAccessible) override;
protected:
// returns the pointer to the relative file path to be passed
@@ -95,7 +95,7 @@ namespace AZ::IO
ZipDir::CachePtr m_pCache;
// the binding root may be empty string - in this case, the absolute path binding won't work
AZStd::string m_strBindRoot;
AZ::IO::Path m_strBindRoot;
IArchive* m_archive{};
uint32_t m_nFlags{};
};
@@ -9,7 +9,6 @@
#include <AzCore/Console/Console.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/string/conversions.h>
#include <AzFramework/Archive/ZipFileFormat.h>
@@ -104,24 +103,21 @@ namespace AZ::IO::ZipDir
: m_pCache(pCache)
, m_bCommitted(false)
{
AZ::IO::PathString normalizedPath{ szRelativePath };
AZ::StringFunc::Path::Normalize(normalizedPath);
AZStd::to_lower(AZStd::begin(normalizedPath), AZStd::end(normalizedPath));
// Update the cache string pool with the relative path to the file
auto pathIt = m_pCache->m_relativePathPool.emplace(normalizedPath);
auto pathIt = m_pCache->m_relativePathPool.emplace(AZ::IO::PathView(szRelativePath).LexicallyNormal());
m_szRelativePath = *pathIt.first;
// this is the name of the directory - create it or find it
m_pFileEntry = m_pCache->GetRoot()->Add(m_szRelativePath);
m_pFileEntry = m_pCache->GetRoot()->Add(m_szRelativePath.Native());
if (m_pFileEntry && az_archive_zip_directory_cache_verbosity)
{
AZ_TracePrintf("Archive", R"(File "%s" has been added to archive at root "%s")", normalizedPath.c_str(), pCache->GetFilePath());
AZ_TracePrintf("Archive", R"(File "%s" has been added to archive at root "%s")", pathIt.first->c_str(), pCache->GetFilePath());
}
}
~FileEntryTransactionAdd()
{
if (m_pFileEntry && !m_bCommitted)
{
m_pCache->RemoveFile(m_szRelativePath);
m_pCache->RemoveFile(m_szRelativePath.Native());
m_pCache->m_relativePathPool.erase(m_szRelativePath);
}
}
@@ -131,11 +127,11 @@ namespace AZ::IO::ZipDir
}
AZStd::string_view GetRelativePath() const
{
return m_szRelativePath;
return m_szRelativePath.Native();
}
private:
Cache* m_pCache;
AZStd::string_view m_szRelativePath;
AZ::IO::PathView m_szRelativePath;
FileEntry* m_pFileEntry;
bool m_bCommitted;
};
@@ -587,34 +583,27 @@ namespace AZ::IO::ZipDir
// deletes the file from the archive
ErrorEnum Cache::RemoveFile(AZStd::string_view szRelativePathSrc)
{
// Normalize and lower case the relative path
AZ::IO::PathString szRelativePath{ szRelativePathSrc };
AZ::StringFunc::Path::Normalize(szRelativePath);
AZStd::to_lower(AZStd::begin(szRelativePath), AZStd::end(szRelativePath));
AZStd::string_view normalizedRelativePath = szRelativePath;
// find the last slash in the path
size_t slashOffset = normalizedRelativePath.find_last_of(AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
AZ::IO::PathView szRelativePath{ szRelativePathSrc };
AZStd::string_view fileName; // the name of the file to delete
FileEntryTree* pDir; // the dir from which the subdir will be deleted
if (slashOffset != AZStd::string_view::npos)
if (szRelativePath.HasParentPath())
{
FindDir fd(GetRoot());
// the directory to remove
pDir = fd.FindExact(normalizedRelativePath.substr(0, slashOffset));
pDir = fd.FindExact(szRelativePath.ParentPath());
if (!pDir)
{
return ZD_ERROR_DIR_NOT_FOUND;// there is no such directory
}
fileName = normalizedRelativePath.substr(slashOffset + 1);
fileName = szRelativePath.Filename().Native();
}
else
{
pDir = GetRoot();
fileName = normalizedRelativePath;
fileName = szRelativePath.Native();
}
ErrorEnum e = pDir->RemoveFile(fileName);
@@ -625,7 +614,7 @@ namespace AZ::IO::ZipDir
if (az_archive_zip_directory_cache_verbosity)
{
AZ_TracePrintf("Archive", R"(File "%.*s" has been remove from archive at root "%s")",
aznumeric_cast<int>(fileName.size()), fileName.data(), GetFilePath());
AZ_STRING_ARG(szRelativePath.Native()), GetFilePath());
}
}
return e;
@@ -635,45 +624,38 @@ namespace AZ::IO::ZipDir
// deletes the directory, with all its descendants (files and subdirs)
ErrorEnum Cache::RemoveDir(AZStd::string_view szRelativePathSrc)
{
// Normalize and lower case the relative path
AZ::IO::PathString szRelativePath{ szRelativePathSrc };
AZ::StringFunc::Path::Normalize(szRelativePath);
AZStd::to_lower(AZStd::begin(szRelativePath), AZStd::end(szRelativePath));
AZStd::string_view normalizedRelativePath = szRelativePath;
// find the last slash in the path
size_t slashOffset = normalizedRelativePath.find_last_of(AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
AZ::IO::PathView szRelativePath{ szRelativePathSrc };
AZStd::string_view dirName; // the name of the dir to delete
FileEntryTree* pDir; // the dir from which the subdir will be deleted
if (slashOffset != AZStd::string_view::npos)
if (szRelativePath.HasParentPath())
{
FindDir fd(GetRoot());
// the directory to remove
pDir = fd.FindExact(normalizedRelativePath.substr(0, slashOffset));
pDir = fd.FindExact(szRelativePath.ParentPath());
if (!pDir)
{
return ZD_ERROR_DIR_NOT_FOUND;// there is no such directory
}
dirName = normalizedRelativePath.substr(slashOffset + 1);
dirName = szRelativePath.Filename().Native();
}
else
{
pDir = GetRoot();
dirName = normalizedRelativePath;
dirName = szRelativePath.Native();
}
ErrorEnum e = pDir->RemoveDir(normalizedRelativePath);
ErrorEnum e = pDir->RemoveDir(dirName);
if (e == ZD_ERROR_SUCCESS)
{
m_nFlags |= FLAGS_UNCOMPACTED | FLAGS_CDR_DIRTY;
if (az_archive_zip_directory_cache_verbosity)
{
AZ_TracePrintf("Archive", R"(File "%.*s" has been remove from archive at root "%s")",
aznumeric_cast<int>(normalizedRelativePath.size()), normalizedRelativePath.data(), GetFilePath());
AZ_TracePrintf("Archive", R"(Directory "%.*s" has been remove from archive at root "%s")",
AZ_STRING_ARG(szRelativePath.Native()), GetFilePath());
}
}
return e;
@@ -769,9 +751,7 @@ namespace AZ::IO::ZipDir
// finds the file by exact path
FileEntry* Cache::FindFile(AZStd::string_view szPathSrc, [[maybe_unused]] bool bFullInfo)
{
AZ::IO::PathString szPath{ szPathSrc };
AZ::StringFunc::Path::Normalize(szPath);
AZStd::to_lower(AZStd::begin(szPath), AZStd::end(szPath));
AZ::IO::PathView szPath{ szPathSrc };
ZipDir::FindFile fd(GetRoot());
FileEntry* fileEntry = fd.FindExact(szPath);
@@ -779,19 +759,13 @@ namespace AZ::IO::ZipDir
{
if (az_archive_zip_directory_cache_verbosity)
{
AZ_TracePrintf("Archive", "FindExact failed to find file %s at root %s", szPath.c_str(), GetFilePath());
AZ_TracePrintf("Archive", "FindExact failed to find file %.*s at root %s", AZ_STRING_ARG(szPath.Native()), GetFilePath());
}
return {};
}
return fileEntry;
}
// returns the size of memory occupied by the instance referred to by this cache
size_t Cache::GetSize() const
{
return sizeof(*this) + m_strFilePath.capacity() + m_treeDir.GetSize() - sizeof(m_treeDir);
}
// refreshes information about the given file entry into this file entry
ErrorEnum Cache::Refresh(FileEntryBase* pFileEntry)
{
@@ -800,7 +774,7 @@ namespace AZ::IO::ZipDir
return ZD_ERROR_INVALID_CALL;
}
if (pFileEntry->nFileDataOffset != pFileEntry->INVALID_DATA_OFFSET)
if (pFileEntry->nFileDataOffset != FileEntryBase::INVALID_DATA_OFFSET)
{
return ZD_ERROR_SUCCESS; // the data offset has been successfully read..
}
@@ -16,6 +16,7 @@
#pragma once
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/std/smart_ptr/intrusive_base.h>
#include <AzFramework/Archive/Codec.h>
@@ -89,9 +90,6 @@ namespace AZ::IO::ZipDir
// refreshes information about the given file entry into this file entry
ErrorEnum Refresh(FileEntryBase* pFileEntry);
// returns the size of memory occupied by the instance of this cache
size_t GetSize() const;
// QUICK check to determine whether the file entry belongs to this object
bool IsOwnerOf(const FileEntry* pFileEntry) const
{
@@ -100,9 +98,9 @@ namespace AZ::IO::ZipDir
// returns the string - path to the zip file from which this object was constructed.
// this will be "" if the object was constructed with a factory that wasn't created with FLAGS_MEMORIZE_ZIP_PATH
const char* GetFilePath() const
AZ::IO::PathView GetFilePath() const
{
return m_strFilePath.c_str();
return m_strFilePath;
}
FileEntryTree* GetRoot()
@@ -135,10 +133,10 @@ namespace AZ::IO::ZipDir
FileEntryTree m_treeDir;
AZ::IO::HandleType m_fileHandle;
AZ::IAllocatorAllocate* m_allocator;
AZStd::string m_strFilePath;
AZ::IO::Path m_strFilePath;
// String Pool for persistently storing paths as long as they reside in the cache
AZStd::unordered_set<AZStd::string> m_relativePathPool;
AZStd::unordered_set<AZ::IO::Path> m_relativePathPool;
// offset to the start of CDR in the file,even if there's no CDR there currently
// when a new file is added, it can start from here, but this value will need to be updated then
@@ -41,13 +41,6 @@ namespace AZ::IO::ZipDir
m_encryptedHeaders = ZipFile::HEADERS_NOT_ENCRYPTED;
m_signedHeaders = ZipFile::HEADERS_NOT_SIGNED;
if (m_nFlags & FLAGS_FILENAMES_AS_CRC32)
{
m_bBuildFileEntryMap = false;
m_bBuildFileEntryTree = false;
m_bBuildOptimizedFileEntry = true;
}
if (m_nFlags & FLAGS_READ_INSIDE_PAK)
{
m_fileExt.m_fileIOBase = AZ::IO::FileIOBase::GetInstance();
@@ -88,12 +81,12 @@ namespace AZ::IO::ZipDir
if (m_fileExt.m_fileHandle == AZ::IO::InvalidHandle)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not open file in binary mode for reading");
AZ_Warning("Archive", false, R"(ZD_ERROR_IO_FAILED: Could not open file "%s" in binary mode for reading)", szFileName);
return {};
}
if (!ReadCache(*pCache))
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not read the CDR of the pack file.");
AZ_Warning("Archive", false, R"(ZD_ERROR_IO_FAILED: Could not read the CDR of the pack file "%s".)", pCache->m_strFilePath.c_str());
return {};
}
}
@@ -113,12 +106,12 @@ namespace AZ::IO::ZipDir
size_t nFileSize = (size_t)Tell();
Seek(0, SEEK_SET);
AZ_Assert(nFileSize != 0, "File of size 0 will not be open for reading");
AZ_Warning("Archive", nFileSize != 0, R"(ZD_ERROR_IO_FAILED: File "%s" with size 0 will not be open for reading)", szFileName);
if (nFileSize)
{
if (!ReadCache(*pCache))
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not open file in binary mode for reading");
AZ_Warning("Archive", false, R"(ZD_ERROR_IO_FAILED: Could not open file "%s" in binary mode for reading)", szFileName);
return {};
}
bOpenForWriting = false;
@@ -143,7 +136,7 @@ namespace AZ::IO::ZipDir
if (m_fileExt.m_fileHandle == AZ::IO::InvalidHandle)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Could not open file in binary mode for appending (read/write)");
AZ_Warning("Archive", false, R"(ZD_ERROR_IO_FAILED: Could not open file "%s" in binary mode for appending (read/write))", szFileName);
return {};
}
}
@@ -211,7 +204,7 @@ namespace AZ::IO::ZipDir
if (m_headerExtended.nHeaderSize != sizeof(m_headerExtended))
{
// Extended Header is not valid
THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Bad extended header");
AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Bad extended header");
return false;
}
//We have the header, so read the encryption and signing techniques
@@ -224,7 +217,7 @@ namespace AZ::IO::ZipDir
if (m_headerExtended.nEncryption != ZipFile::HEADERS_NOT_ENCRYPTED && m_encryptedHeaders != ZipFile::HEADERS_NOT_ENCRYPTED)
{
//Encryption technique has been specified in both the disk number (old technique) and the custom header (new technique).
THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Unexpected encryption technique in header");
AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Unexpected encryption technique in header");
return false;
}
else
@@ -240,7 +233,7 @@ namespace AZ::IO::ZipDir
break;
default:
// Unexpected technique
THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Bad encryption technique in header");
AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Bad encryption technique in header");
return false;
}
}
@@ -255,7 +248,7 @@ namespace AZ::IO::ZipDir
break;
default:
// Unexpected technique
THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Bad signing technique in header");
AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Bad signing technique in header");
return false;
}
@@ -266,7 +259,7 @@ namespace AZ::IO::ZipDir
Read(&m_headerSignature, sizeof(m_headerSignature));
if (m_headerSignature.nHeaderSize != sizeof(m_headerSignature))
{
THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Bad signature header");
AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Bad signature header");
return false;
}
}
@@ -274,7 +267,7 @@ namespace AZ::IO::ZipDir
else
{
// Unexpected technique
THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Comment field is the wrong length");
AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: Comment field is the wrong length");
return false;
}
}
@@ -285,7 +278,7 @@ namespace AZ::IO::ZipDir
|| m_CDREnd.nCDRStartDisk != 0
|| m_CDREnd.numEntriesOnDisk != m_CDREnd.numEntriesTotal)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_UNSUPPORTED, "Multivolume archive detected. Current version of ZipDir does not support multivolume archives");
AZ_Warning("Archive", false, "ZD_ERROR_UNSUPPORTED: Multivolume archive detected.Current version of ZipDir does not support multivolume archives");
return false;
}
@@ -295,7 +288,7 @@ namespace AZ::IO::ZipDir
|| m_CDREnd.lCDRSize > m_nCDREndPos
|| m_CDREnd.lCDROffset + m_CDREnd.lCDRSize > m_nCDREndPos)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "The central directory offset or size are out of range, the pak is probably corrupt, try to repare or delete the file");
AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT: The central directory offset or size are out of range, the pak is probably corrupt, try to repare or delete the file");
return false;
}
@@ -394,7 +387,12 @@ namespace AZ::IO::ZipDir
// if there's nothing to search
if (nNewBufPos >= nOldBufPos)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_NO_CDR, "Cannot find Central Directory Record in pak. This is either not a pak file, or a pak file without Central Directory. It does not mean that the data is permanently lost, but it may be severely damaged. Please repair the file with external tools, there may be enough information left to recover the file completely."); // we didn't find anything
AZ_Warning("Archive", false, "ZD_ERROR_NO_CDR: Cannot find Central Directory Record in pak."
" This is either not a pak file, or a pak file without Central Directory."
" It does not mean that the data is permanently lost,"
" but it may be severely damaged."
" Please repair the file with external tools,"
" there may be enough information left to recover the file completely."); // we didn't find anything
return false;
}
@@ -418,7 +416,11 @@ namespace AZ::IO::ZipDir
}
else
{
THROW_ZIPDIR_ERROR(ZD_ERROR_DATA_IS_CORRUPT, "Central Directory Record is followed by a comment of inconsistent length. This might be a minor misconsistency, please try to repair the file. However, it is dangerous to open the file because I will have to guess some structure offsets, which can lead to permanent unrecoverable damage of the archive content");
AZ_Warning("Archive", false, "ZD_ERROR_DATA_IS_CORRUPT:"
" Central Directory Record is followed by a comment of inconsistent length."
" This might be a minor misconsistency, please try to repair the file.However,"
" it is dangerous to open the file because I will have to guess some structure offsets,"
" which can lead to permanent unrecoverable damage of the archive content");
return false;
}
}
@@ -436,7 +438,7 @@ namespace AZ::IO::ZipDir
nOldBufPos = nNewBufPos;
memmove(&pReservedBuffer[CDRSearchWindowSize], pWindow, sizeof(ZipFile::CDREnd) - 1);
}
THROW_ZIPDIR_ERROR(ZD_ERROR_UNEXPECTED, "The program flow may not have possibly lead here. This error is unexplainable"); // we shouldn't be here
AZ_Assert(false, "ZD_ERROR_UNEXPECTED: The program flow may not have possibly lead here. This error is unexplainable"); // we shouldn't be here
return false;
}
@@ -460,13 +462,13 @@ namespace AZ::IO::ZipDir
if (pBuffer.empty()) // couldn't allocate enough memory for temporary copy of CDR
{
THROW_ZIPDIR_ERROR(ZD_ERROR_NO_MEMORY, "Not enough memory to cache Central Directory record for fast initialization. This error may not happen on non-console systems");
AZ_Warning("Archive", false, "ZD_ERROR_NO_MEMORY: Not enough memory to cache Central Directory record for fast initialization. This error may not happen on non-console systems");
return false;
}
if (!ReadHeaderData(&pBuffer[0], m_CDREnd.lCDRSize))
{
THROW_ZIPDIR_ERROR(ZD_ERROR_CORRUPTED_DATA, "Archive contains corrupted CDR.");
AZ_Warning("Archive", false, "ZD_ERROR_CORRUPTED_DATA: Archive contains corrupted CDR.");
return false;
}
@@ -482,7 +484,7 @@ namespace AZ::IO::ZipDir
if ((pFile->nVersionNeeded & 0xFF) > 20)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_UNSUPPORTED, "Cannot read the archive file (nVersionNeeded > 20).");
AZ_Warning("Archive", false, "ZD_ERROR_UNSUPPORTED: Cannot read the archive file (nVersionNeeded > 20).");
return false;
}
//if (pFile->lSignature != pFile->SIGNATURE) // Timur, Dont compare signatures as signatue in memory can be overwritten by the code below
@@ -492,7 +494,8 @@ namespace AZ::IO::ZipDir
// if the record overlaps with the End Of CDR structure, something is wrong
if (pEndOfRecord > pEndOfData)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_CDR_IS_CORRUPT, "Central Directory record is either corrupt, or truncated, or missing. Cannot read the archive directory");
AZ_Warning("Archive", false, "ZD_ERROR_CDR_IS_CORRUPT: Central Directory record is either corrupt, or truncated, or missing."
" Cannot read the archive directory");
return false;
}
@@ -550,18 +553,22 @@ namespace AZ::IO::ZipDir
//////////////////////////////////////////////////////////////////////////
// give the CDR File Header entry, reads the local file header to validate
// and determine where the actual file lies
// and determine where the actual file resides
void CacheFactory::AddFileEntry(char* strFilePath, const ZipFile::CDRFileHeader* pFileHeader, const SExtraZipFileData& extra)
{
if (pFileHeader->lLocalHeaderOffset > m_CDREnd.lCDROffset)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_CDR_IS_CORRUPT, "Central Directory contains file descriptors pointing outside the archive file boundaries. The archive file is either truncated or damaged. Please try to repair the file"); // the file offset is beyond the CDR: impossible
AZ_Warning("Archive", false, "ZD_ERROR_CDR_IS_CORRUPT:"
" Central Directory contains file descriptors pointing outside the archive file boundaries."
" The archive file is either truncated or damaged.Please try to repair the file"); // the file offset is beyond the CDR: impossible
return;
}
if ((pFileHeader->nMethod == ZipFile::METHOD_STORE || pFileHeader->nMethod == ZipFile::METHOD_STORE_AND_STREAMCIPHER_KEYTABLE) && pFileHeader->desc.lSizeUncompressed != pFileHeader->desc.lSizeCompressed)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "File with STORE compression method declares its compressed size not matching its uncompressed size. File descriptor is inconsistent, archive content may be damaged, please try to repair the archive");
AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:"
" File with STORE compression method declares its compressed size not matching its uncompressed size."
" File descriptor is inconsistent, archive content may be damaged, please try to repair the archive");
return;
}
@@ -593,8 +600,7 @@ namespace AZ::IO::ZipDir
if (m_encryptedHeaders != ZipFile::HEADERS_NOT_ENCRYPTED)
{
// use CDR instead of local header
// The pak encryption tool asserts that there is no extra data at the end of the local file header, so don't add any extra data from the CDR header.
fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength;
fileEntry.nFileDataOffset = pFileHeader->lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + pFileHeader->nFileNameLength + pFileHeader->nExtraFieldLength;
}
else
{
@@ -617,7 +623,9 @@ namespace AZ::IO::ZipDir
//|| pFileHeader->nLastModTime != pLocalFileHeader->nLastModTime
)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "The local file header descriptor doesn't match the basic parameters declared in the global file header in the file. The archive content is misconsistent and may be damaged. Please try to repair the archive");
AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:"
" The local file header descriptor doesn't match the basic parameters declared in the global file header in the file."
" The archive content is misconsistent and may be damaged. Please try to repair the archive");
return;
}
@@ -628,7 +636,9 @@ namespace AZ::IO::ZipDir
if (!AZStd::equal(zipFileDataBegin, zipFileDataEnd, reinterpret_cast<const char*>(pFileHeader + 1), CompareNoCase))
{
// either file name, or the extra field do not match
THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "The local file header contains file name which does not match the file name of the global file header. The archive content is misconsistent with its directory. Please repair the archive");
AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:"
" The local file header contains file name which does not match the file name of the global file header."
" The archive content is misconsistent with its directory. Please repair the archive");
return;
}
@@ -642,7 +652,9 @@ namespace AZ::IO::ZipDir
if (fileEntry.nFileDataOffset >= m_nCDREndPos)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_VALIDATION_FAILED, "The global file header declares the file which crosses the boundaries of the archive. The archive is either corrupted or truncated, please try to repair it");
AZ_Warning("Archive", false, "ZD_ERROR_VALIDATION_FAILED:"
" The global file header declares the file which crosses the boundaries of the archive."
" The archive is either corrupted or truncated, please try to repair it");
return;
}
@@ -686,29 +698,29 @@ namespace AZ::IO::ZipDir
case Z_OK:
break;
case Z_MEM_ERROR:
THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_NO_MEMORY, "ZLib reported out-of-memory error");
AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_NO_MEMORY: ZLib reported out-of-memory error");
return;
case Z_BUF_ERROR:
THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_CORRUPTED_DATA, "ZLib reported compressed stream buffer error");
AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_CORRUPTED_DATA: ZLib reported compressed stream buffer error");
return;
case Z_DATA_ERROR:
THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_CORRUPTED_DATA, "ZLib reported compressed stream data error");
AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_CORRUPTED_DATA: ZLib reported compressed stream data error");
return;
default:
THROW_ZIPDIR_ERROR(ZD_ERROR_ZLIB_FAILED, "ZLib reported an unexpected unknown error");
AZ_Warning("Archive", false, "ZD_ERROR_ZLIB_FAILED: ZLib reported an unexpected unknown error");
return;
}
if (nDestSize != fileEntry.desc.lSizeUncompressed)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_CORRUPTED_DATA, "Uncompressed stream doesn't match the size of uncompressed file stored in the archive file headers");
AZ_Warning("Archive", false, "ZD_ERROR_CORRUPTED_DATA: Uncompressed stream doesn't match the size of uncompressed file stored in the archive file headers");
return;
}
uLong uCRC32 = AZ::Crc32((Bytef*)pUncompressed, nDestSize);
if (uCRC32 != fileEntry.desc.lCRC32)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_CRC32_CHECK, "Uncompressed stream CRC32 check failed");
AZ_Warning("Archive", false, "ZD_ERROR_CRC32_CHECK: Uncompressed stream CRC32 check failed");
return;
}
}
@@ -737,7 +749,7 @@ namespace AZ::IO::ZipDir
{
if (FSeek(&m_fileExt, nPos, nOrigin))
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot fseek() to the new position in the file. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this");
AZ_Warning("Archive", false, "ZD_ERROR_IO_FAILED: Cannot fseek() to the new position in the file. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this");
return;
}
}
@@ -747,7 +759,7 @@ namespace AZ::IO::ZipDir
int64_t nPos = FTell(&m_fileExt);
if (nPos == -1)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot ftell() position in the archive. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this");
AZ_Warning("Archive", false, "ZD_ERROR_IO_FAILED: Cannot ftell() position in the archive. This is unexpected error and should not happen under any circumstances. Perhaps some network or disk failure error has caused this");
return 0;
}
return nPos;
@@ -757,7 +769,7 @@ namespace AZ::IO::ZipDir
{
if (FRead(&m_fileExt, pDest, nSize, 1) != 1)
{
THROW_ZIPDIR_ERROR(ZD_ERROR_IO_FAILED, "Cannot fread() a portion of data from archive");
AZ_Warning("Archive", false, "ZD_ERROR_IO_FAILED: Cannot fread() a portion of data from archive");
return false;
}
return true;
@@ -33,20 +33,13 @@ namespace AZ::IO::ZipDir
// if this is set, the archive will be created anew (the existing file will be overwritten)
FLAGS_CREATE_NEW = 1 << 3,
// Cache will be loaded completely into the memory.
FLAGS_IN_MEMORY = 1 << 4,
FLAGS_IN_MEMORY_CPU = 1 << 5,
// Store all file names as crc32 in a flat directory structure.
FLAGS_FILENAMES_AS_CRC32 = 1 << 6,
// if this is set, zip path will be searched inside other zips
FLAGS_READ_INSIDE_PAK = 1 << 7,
};
// initializes the internal structures
// nFlags can have FLAGS_READ_ONLY flag, in this case the object will be opened only for reading
CacheFactory (InitMethodEnum nInitMethod, uint32_t nFlags = 0);
CacheFactory(InitMethodEnum nInitMethod, uint32_t nFlags = 0);
~CacheFactory();
// the new function creates a new cache
@@ -17,7 +17,7 @@
namespace AZ::IO::ZipDir
{
bool FindFile::FindFirst(AZStd::string_view szWildcard)
bool FindFile::FindFirst(AZ::IO::PathView szWildcard)
{
if (!PreFind(szWildcard))
{
@@ -29,7 +29,7 @@ namespace AZ::IO::ZipDir
return SkipNonMatchingFiles();
}
bool FindDir::FindFirst(AZStd::string_view szWildcard)
bool FindDir::FindFirst(AZ::IO::PathView szWildcard)
{
if (!PreFind(szWildcard))
{
@@ -42,37 +42,20 @@ namespace AZ::IO::ZipDir
}
// matches the file wildcard in the m_szWildcard to the given file/dir name
// this takes into account the fact that xxx. is the alias name for xxx
bool FindData::MatchWildcard(AZStd::string_view szName)
bool FindData::MatchWildcard(AZ::IO::PathView szName)
{
if (AZStd::wildcard_match(m_szWildcard, szName))
{
return true;
}
// check if the file object name contains extension sign (.)
size_t extensionOffset = szName.find('.');
if (extensionOffset != AZStd::string_view::npos)
{
return false;
}
// no extension sign - add it
AZStd::fixed_string<AZ_MAX_PATH_LEN> szAlias{ szName };
szAlias.push_back('.');
return AZStd::wildcard_match(m_szWildcard, szAlias);
return szName.Match(m_szWildcard.Native());
}
FileEntry* FindFile::FindExact(AZStd::string_view szPath)
FileEntry* FindFile::FindExact(AZ::IO::PathView szPath)
{
if (!PreFind(szPath))
{
return nullptr;
}
FileEntryTree::FileMap::iterator itFile = m_pDirHeader->FindFile(m_szWildcard.c_str());
FileEntryTree::FileMap::iterator itFile = m_pDirHeader->FindFile(m_szWildcard);
if (itFile == m_pDirHeader->GetFileEnd())
{
m_pDirHeader = nullptr; // we didn't find it, fail the search
@@ -84,7 +67,7 @@ namespace AZ::IO::ZipDir
return m_pDirHeader->GetFileEntry(m_itFile);
}
FileEntryTree* FindDir::FindExact(AZStd::string_view szPath)
FileEntryTree* FindDir::FindExact(AZ::IO::PathView szPath)
{
if (!PreFind(szPath))
{
@@ -97,40 +80,50 @@ namespace AZ::IO::ZipDir
//////////////////////////////////////////////////////////////////////////
// after this call returns successfully (with true returned), the m_szWildcard
// contains the file name/wildcard and m_pDirHeader contains the directory where
// contains the file name/glob and m_pDirHeader contains the directory where
// the file (s) are to be found
bool FindData::PreFind(AZStd::string_view szWildcard)
bool FindData::PreFind(AZ::IO::PathView pathGlob)
{
if (!m_pRoot)
{
return false;
}
// start the search from the root
m_pDirHeader = m_pRoot;
m_szWildcard = szWildcard;
// for each path directory, copy it into the wildcard buffer and try to find the subdirectory
for (AZStd::optional<AZStd::string_view> pathEntry = AZ::StringFunc::TokenizeNext(szWildcard, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR); pathEntry;
pathEntry = AZ::StringFunc::TokenizeNext(szWildcard, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR))
FileEntryTree* entryTreeHeader = m_pRoot;
// If there is a root path in the glob path, attempt to locate it from the root
if (AZ::IO::PathView rootPath = m_szWildcard.RootPath(); !rootPath.empty())
{
// Update wildcard to new path entry
m_szWildcard = *pathEntry;
// If the wildcard parameter that has been passed to TokenizeNext is empty
// Then pathEntry is the final portion of the path
if (!szWildcard.empty())
FileEntryTree* dirEntry = entryTreeHeader->FindDir(rootPath);
if (dirEntry == nullptr)
{
FileEntryTree* dirEntry = m_pDirHeader->FindDir(*pathEntry);
if (!dirEntry)
{
m_pDirHeader = nullptr; // an intermediate directory has not been found continue the search
return false;
}
m_pDirHeader = dirEntry->GetDirectory();
return false;
}
entryTreeHeader = dirEntry->GetDirectory();
pathGlob = pathGlob.RelativePath();
}
AZ::IO::PathView filenameSegment = pathGlob;
// Recurse through the directories within the file tree for each remaining parent path segment
// of pathGlob parameter
auto parentPathIter = pathGlob.begin();
for (auto filenamePathIter = parentPathIter == pathGlob.end() ? pathGlob.end() : AZStd::next(parentPathIter, 1);
filenamePathIter != pathGlob.end(); ++parentPathIter, ++filenamePathIter)
{
FileEntryTree* dirEntry = entryTreeHeader->FindDir(*parentPathIter);
if (dirEntry == nullptr)
{
return false;
}
entryTreeHeader = dirEntry->GetDirectory();
filenameSegment = *filenamePathIter;
}
// At this point the all the intermediate directories have been found
// so update the directory header to point at the last file entry tree
m_pDirHeader = entryTreeHeader;
m_szWildcard = filenameSegment;
return true;
}
@@ -38,11 +38,10 @@ namespace AZ::IO::ZipDir
// after this call returns successfully (with true returned), the m_szWildcard
// contains the file name/wildcard and m_pDirHeader contains the directory where
// the file (s) are to be found
bool PreFind(AZStd::string_view szWildcard);
bool PreFind(AZ::IO::PathView szWildcard);
// matches the file wildcard in the m_szWildcard to the given file/dir name
// this takes into account the fact that xxx. is the alias name for xxx
bool MatchWildcard(AZStd::string_view szName);
bool MatchWildcard(AZ::IO::PathView szName);
// the directory inside which the current object (file or directory) is being searched
FileEntryTree* m_pDirHeader{};
@@ -50,7 +49,7 @@ namespace AZ::IO::ZipDir
FileEntryTree* m_pRoot{}; // the root of the zip file in which to search
// the actual wildcard being used in the current scan - the file name wildcard only!
AZStd::fixed_string<AZ_MAX_PATH_LEN> m_szWildcard;
AZ::IO::FixedMaxPath m_szWildcard;
};
class FindFile
@@ -66,9 +65,9 @@ namespace AZ::IO::ZipDir
{
}
// if bExactFile is passed, only the file is searched, and besides with the exact name as passed (no wildcards)
bool FindFirst(AZStd::string_view szWildcard);
bool FindFirst(AZ::IO::PathView szWildcard);
FileEntry* FindExact(AZStd::string_view szPath);
FileEntry* FindExact(AZ::IO::PathView szPath);
// goes on to the next file entry
bool FindNext();
@@ -94,9 +93,9 @@ namespace AZ::IO::ZipDir
{
}
// if bExactFile is passed, only the file is searched, and besides with the exact name as passed (no wildcards)
bool FindFirst(AZStd::string_view szWildcard);
bool FindFirst(AZ::IO::PathView szWildcard);
FileEntryTree* FindExact(AZStd::string_view szPath);
FileEntryTree* FindExact(AZ::IO::PathView szPath);
// goes on to the next file entry
bool FindNext();
@@ -68,14 +68,14 @@ namespace AZ::IO::ZipDir
{
for (FileEntryTree::SubdirMap::iterator it = pTree->GetDirBegin(); it != pTree->GetDirEnd(); ++it)
{
AddAllFiles(it->second.get(), AZStd::string::format("%.*s%.*s/", aznumeric_cast<int>(strRoot.size()), strRoot.data(), aznumeric_cast<int>(it->first.size()), it->first.data()));
AddAllFiles(it->second.get(), (AZ::IO::Path(strRoot) / it->first).Native());
}
for (FileEntryTree::FileMap::iterator it = pTree->GetFileBegin(); it != pTree->GetFileEnd(); ++it)
{
FileRecord rec;
rec.pFileEntryBase = pTree->GetFileEntry(it);
rec.strPath = AZStd::string::format("%.*s%.*s", aznumeric_cast<int>(strRoot.size()), strRoot.data(), aznumeric_cast<int>(it->first.size()), it->first.data());
rec.strPath = (AZ::IO::Path(strRoot) / it->first).Native();
push_back(rec);
}
}
@@ -187,8 +187,7 @@ namespace AZ::IO::ZipDir::ZipDirStructuresInternal
// If src/dst overlap (in place decompress), then inflate in chunks, copying src locally to ensure
// pointers don't foul each other.
bool bIndependantBlocks = ((pInput + nInputLen) <= pOutput) || (pInput >= (pOutput + nOutputLen));
if (bIndependantBlocks)
if ((pInput + nInputLen) <= pOutput || pInput >= (pOutput + nOutputLen))
{
pZStream->next_in = (Bytef*)pInput;
pZStream->avail_in = aznumeric_cast<uint32_t>(nInputLen);
@@ -260,8 +259,7 @@ namespace AZ::IO::ZipDir::ZipDirStructuresInternal
// If src/dst overlap (in place decompress), then inflate in chunks, copying src locally to ensure
// pointers don't foul each other.
bool bIndependantBlocks = ((pIn + nIn) <= stream.next_out) || (pIn >= (stream.next_out + stream.avail_out));
if (bIndependantBlocks)
if ((pIn + nIn) <= stream.next_out || pIn >= (stream.next_out + stream.avail_out))
{
stream.next_in = pIn;
stream.avail_in = nIn;
@@ -432,18 +430,18 @@ namespace AZ::IO::ZipDir
bool CZipFile::EvaluateSectorSize(const char* filename)
{
char volume[AZ_MAX_PATH_LEN];
AZ::IO::FixedMaxPath volume;
if (AZ::StringFunc::Path::IsRelative(filename))
if (AZ::IO::PathView(filename).IsRelative())
{
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(filename, volume, AZ_ARRAY_SIZE(volume));
AZ::IO::FileIOBase::GetDirectInstance()->ResolvePath(volume, filename);
}
else
{
azstrcpy(volume, AZ_ARRAY_SIZE(volume), filename);
volume = filename;
}
AZ::IO::FixedMaxPathString drive{ AZ::IO::PathView(volume).RootName().Native() };
AZ::IO::FixedMaxPath drive = volume.RootName();
if (drive.empty())
{
return false;
@@ -498,18 +496,18 @@ namespace AZ::IO::ZipDir
//////////////////////////////////////////////////////////////////////////
FileEntryBase::FileEntryBase(const ZipFile::CDRFileHeader& header, const SExtraZipFileData& extra)
{
this->desc = header.desc;
this->nFileHeaderOffset = header.lLocalHeaderOffset;
//this->nFileDataOffset = INVALID_DATA_OFFSET; // we don't know yet
this->nMethod = header.nMethod;
this->nNameOffset = 0; // we don't know yet
this->nLastModTime = header.nLastModTime;
this->nLastModDate = header.nLastModDate;
this->nNTFS_LastModifyTime = extra.nLastModifyTime;
desc = header.desc;
nFileHeaderOffset = header.lLocalHeaderOffset;
nMethod = header.nMethod;
nNameOffset = 0; // we don't know yet
nLastModTime = header.nLastModTime;
nLastModDate = header.nLastModDate;
nNTFS_LastModifyTime = extra.nLastModifyTime;
// make an estimation (at least this offset should be there), but we don't actually know yet
this->nFileDataOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength;
this->nEOFOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength + header.desc.lSizeCompressed;
nFileDataOffset = header.lLocalHeaderOffset + sizeof(ZipFile::LocalFileHeader) + header.nFileNameLength + header.nExtraFieldLength;
nEOFOffset = nFileDataOffset + header.desc.lSizeCompressed;
}
// Uncompresses raw (without wrapping) data that is compressed with method 8 (deflated) in the Zip file
@@ -666,7 +664,9 @@ namespace AZ::IO::ZipDir
DirEntry* pEnd = pBegin + this->numDirs;
DirEntry* pEntry = AZStd::lower_bound(pBegin, pEnd, szName, pred);
#if AZ_TRAIT_LEGACY_CRYPAK_UNIX_LIKE_FILE_SYSTEM
if (pEntry != pEnd && !azstrnicmp(szName.data(), pEntry->GetName(pNamePool), szName.size()))
AZ::IO::PathView searchPath(szName, AZ::IO::WindowsPathSeparator);
AZ::IO::PathView entryPath(pEntry->GetName(pNamePool), AZ::IO::WindowsPathSeparator);
if (pEntry != pEnd && searchPath == entryPath)
#else
if (pEntry != pEnd && szName == pEntry->GetName(pNamePool))
#endif
@@ -690,7 +690,9 @@ namespace AZ::IO::ZipDir
FileEntry* pEnd = pBegin + this->numFiles;
FileEntry* pEntry = AZStd::lower_bound(pBegin, pEnd, szName, pred);
#if AZ_TRAIT_LEGACY_CRYPAK_UNIX_LIKE_FILE_SYSTEM
if (pEntry != pEnd && !azstrnicmp(szName.data(), pEntry->GetName(pNamePool), szName.size()))
AZ::IO::PathView searchPath(szName, AZ::IO::WindowsPathSeparator);
AZ::IO::PathView entryPath(pEntry->GetName(pNamePool), AZ::IO::WindowsPathSeparator);
if (pEntry != pEnd && searchPath == entryPath)
#else
if (pEntry != pEnd && szName == pEntry->GetName(pNamePool))
#endif
@@ -813,8 +815,6 @@ namespace AZ::IO::ZipDir
header.nFileNameLength = aznumeric_cast<uint16_t>(nFileNameLength);
header.nExtraFieldLength = 0;
pFileEntry->nFileDataOffset = pFileEntry->nFileHeaderOffset + sizeof(header) + header.nFileNameLength;
pFileEntry->nEOFOffset = pFileEntry->nFileDataOffset + pFileEntry->desc.lSizeCompressed;
if (!AZ::IO::FileIOBase::GetDirectInstance()->Write(fileHandle, &header, sizeof(header)))
{
return ZD_ERROR_IO_FAILED;
@@ -990,13 +990,6 @@ namespace AZ::IO::ZipDir
}
//////////////////////////////////////////////////////////////////////////
uint32_t FileNameHash(AZStd::string_view filename)
{
AZ::IO::StackString pathname{ filename };
AZStd::replace(AZStd::begin(pathname), AZStd::end(pathname), AZ_WRONG_DATABASE_SEPARATOR, AZ_CORRECT_DATABASE_SEPARATOR);
return AZ::Crc32(pathname);
}
int64_t FSeek(CZipFile* file, int64_t origin, int command)
{
@@ -119,8 +119,6 @@ namespace AZ::IO::ZipDir
const char* m_szDescription;
};
#define THROW_ZIPDIR_ERROR(ZD_ERR, DESC) AZ_Warning("Archive", false, DESC)
// possible initialization methods
enum InitMethodEnum
{
@@ -157,8 +155,6 @@ namespace AZ::IO::ZipDir
int FEof(CZipFile* zipFile);
uint32_t FileNameHash(AZStd::string_view filename);
//////////////////////////////////////////////////////////////////////////
struct SExtraZipFileData
@@ -173,7 +169,7 @@ namespace AZ::IO::ZipDir
inline static constexpr uint32_t INVALID_DATA_OFFSET = 0xFFFFFFFF;
ZipFile::DataDescriptor desc{};
uint32_t nFileDataOffset{}; // offset of the packed info inside the file; NOTE: this can be INVALID_DATA_OFFSET, if not calculated yet!
uint32_t nFileDataOffset{ INVALID_DATA_OFFSET }; // offset of the packed info inside the file; NOTE: this can be INVALID_DATA_OFFSET, if not calculated yet!
uint32_t nFileHeaderOffset{ INVALID_DATA_OFFSET }; // offset of the local file header
uint32_t nNameOffset{}; // offset of the file name in the name pool for the directory
@@ -9,7 +9,6 @@
#include <AzCore/Console/Console.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <AzFramework/Archive/ZipFileFormat.h>
#include <AzFramework/Archive/ZipDirStructures.h>
#include <AzFramework/Archive/ZipDirTree.h>
@@ -18,37 +17,42 @@ namespace AZ::IO::ZipDir
{
// Adds or finds the file. Returns non-initialized structure if it was added,
// or an IsInitialized() structure if it was found
FileEntry* FileEntryTree::Add(AZStd::string_view szPath)
FileEntry* FileEntryTree::Add(AZ::IO::PathView inputPathView)
{
AZStd::optional<AZStd::string_view> pathEntry = AZ::StringFunc::TokenizeNext(szPath, AZ_CORRECT_AND_WRONG_FILESYSTEM_SEPARATOR);
if (!pathEntry)
if (inputPathView.empty())
{
AZ_Assert(false, "An empty file path cannot be added to the zip file entry tree");
return nullptr;
}
// If a path separator was found, add a subdirectory
if (!szPath.empty())
auto inputPathIter = inputPathView.begin();
AZ::IO::PathView firstPathSegment(*inputPathIter);
auto inputPathNextIter = inputPathIter == inputPathView.end() ? inputPathView.end() : AZStd::next(inputPathIter, 1);
AZ::IO::PathView remainingPath = inputPathNextIter != inputPathView.end() ?
AZStd::string_view(inputPathNextIter->Native().begin(), inputPathView.Native().end())
: AZStd::string_view{};
if (!remainingPath.empty())
{
auto dirEntryIter = m_mapDirs.find(*pathEntry);
auto dirEntryIter = m_mapDirs.find(firstPathSegment);
// we have a subdirectory here - create the file in it
if (dirEntryIter == m_mapDirs.end())
{
dirEntryIter = m_mapDirs.emplace(*pathEntry, AZStd::make_unique<FileEntryTree>()).first;
dirEntryIter = m_mapDirs.emplace(firstPathSegment, AZStd::make_unique<FileEntryTree>()).first;
}
return dirEntryIter->second->Add(szPath);
return dirEntryIter->second->Add(remainingPath);
}
// Add the filename
auto fileEntryIter = m_mapFiles.find(*pathEntry);
auto fileEntryIter = m_mapFiles.find(firstPathSegment);
if (fileEntryIter == m_mapFiles.end())
{
fileEntryIter = m_mapFiles.emplace(*pathEntry, AZStd::make_unique<FileEntry>()).first;
fileEntryIter = m_mapFiles.emplace(firstPathSegment, AZStd::make_unique<FileEntry>()).first;
}
return fileEntryIter->second.get();
}
// adds a file to this directory
ErrorEnum FileEntryTree::Add(AZStd::string_view szPath, const FileEntryBase& file)
ErrorEnum FileEntryTree::Add(AZ::IO::PathView szPath, const FileEntryBase& file)
{
FileEntry* pFile = Add(szPath);
if (!pFile)
@@ -63,7 +67,7 @@ namespace AZ::IO::ZipDir
return ZD_ERROR_SUCCESS;
}
// returns the number of files in this tree, including this and sublevels
// returns the number of files in this tree, including this and subdirectories
uint32_t FileEntryTree::NumFilesTotal() const
{
uint32_t numFiles = aznumeric_cast<uint32_t>(m_mapFiles.size());
@@ -91,21 +95,6 @@ namespace AZ::IO::ZipDir
m_mapFiles.clear();
}
size_t FileEntryTree::GetSize() const
{
size_t nSize = sizeof(*this);
for (const auto& [dirname, dirEntry] : m_mapDirs)
{
nSize += dirname.size() + sizeof(decltype(m_mapDirs)::value_type) + dirEntry->GetSize();
}
for (const auto& [filename, fileEntry] : m_mapFiles)
{
nSize += filename.size() + sizeof(decltype(m_mapFiles)::value_type);
}
return nSize;
}
bool FileEntryTree::IsOwnerOf(const FileEntry* pFileEntry) const
{
for (const auto& [path, fileEntry] : m_mapFiles)
@@ -127,7 +116,7 @@ namespace AZ::IO::ZipDir
return false;
}
FileEntryTree* FileEntryTree::FindDir(AZStd::string_view szDirName)
FileEntryTree* FileEntryTree::FindDir(AZ::IO::PathView szDirName)
{
if (auto it = m_mapDirs.find(szDirName); it != m_mapDirs.end())
{
@@ -137,7 +126,7 @@ namespace AZ::IO::ZipDir
return nullptr;
}
FileEntryTree::FileMap::iterator FileEntryTree::FindFile(AZStd::string_view szFileName)
FileEntryTree::FileMap::iterator FileEntryTree::FindFile(AZ::IO::PathView szFileName)
{
return m_mapFiles.find(szFileName);
}
@@ -152,7 +141,7 @@ namespace AZ::IO::ZipDir
return it == GetDirEnd() ? nullptr : it->second.get();
}
ErrorEnum FileEntryTree::RemoveDir(AZStd::string_view szDirName)
ErrorEnum FileEntryTree::RemoveDir(AZ::IO::PathView szDirName)
{
SubdirMap::iterator itRemove = m_mapDirs.find(szDirName);
if (itRemove == m_mapDirs.end())
@@ -164,7 +153,13 @@ namespace AZ::IO::ZipDir
return ZD_ERROR_SUCCESS;
}
ErrorEnum FileEntryTree::RemoveFile(AZStd::string_view szFileName)
ErrorEnum FileEntryTree::RemoveAll()
{
Clear();
return ZD_ERROR_SUCCESS;
}
ErrorEnum FileEntryTree::RemoveFile(AZ::IO::PathView szFileName)
{
FileMap::iterator itRemove = m_mapFiles.find(szFileName);
if (itRemove == m_mapFiles.end())
@@ -10,6 +10,7 @@
#pragma once
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/containers/map.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
@@ -24,12 +25,12 @@ namespace AZ::IO::ZipDir
// adds a file to this directory
// Function can modify szPath input
ErrorEnum Add(AZStd::string_view szPath, const FileEntryBase& file);
ErrorEnum Add(AZ::IO::PathView szPath, const FileEntryBase& file);
// Adds or finds the file. Returns non-initialized structure if it was added,
// or an IsInitialized() structure if it was found
// Function can modify szPath input
FileEntry* Add(AZStd::string_view szPath);
FileEntry* Add(AZ::IO::PathView szPath);
// returns the number of files in this tree, including this and sublevels
uint32_t NumFilesTotal() const;
@@ -45,24 +46,18 @@ namespace AZ::IO::ZipDir
m_mapFiles.swap(rThat.m_mapFiles);
}
size_t GetSize() const;
bool IsOwnerOf(const FileEntry* pFileEntry) const;
// subdirectories
using SubdirMap = AZStd::map<AZStd::string_view, AZStd::unique_ptr<FileEntryTree>>;
using SubdirMap = AZStd::map<AZ::IO::PathView, AZStd::unique_ptr<FileEntryTree>>;
// file entries
using FileMap = AZStd::map<AZStd::string_view, AZStd::unique_ptr<FileEntry>>;
using FileMap = AZStd::map<AZ::IO::PathView, AZStd::unique_ptr<FileEntry>>;
FileEntryTree* FindDir(AZStd::string_view szDirName);
ErrorEnum RemoveDir (AZStd::string_view szDirName);
ErrorEnum RemoveAll ()
{
Clear();
return ZD_ERROR_SUCCESS;
}
FileMap::iterator FindFile(AZStd::string_view szFileName);
ErrorEnum RemoveFile(AZStd::string_view szFileName);
FileEntryTree* FindDir(AZ::IO::PathView szDirName);
ErrorEnum RemoveDir(AZ::IO::PathView szDirName);
ErrorEnum RemoveAll();
FileMap::iterator FindFile(AZ::IO::PathView szFileName);
ErrorEnum RemoveFile(AZ::IO::PathView szFileName);
// the FileEntryTree is simultaneously an entry in the dir list AND the directory header
FileEntryTree* GetDirectory()
{
@@ -75,8 +70,8 @@ namespace AZ::IO::ZipDir
SubdirMap::iterator GetDirBegin() { return m_mapDirs.begin(); }
SubdirMap::iterator GetDirEnd() { return m_mapDirs.end(); }
uint32_t NumDirs() const { return aznumeric_cast<uint32_t>(m_mapDirs.size()); }
AZStd::string_view GetFileName(FileMap::iterator it) { return it->first; }
AZStd::string_view GetDirName(SubdirMap::iterator it) { return it->first; }
AZStd::string_view GetFileName(FileMap::iterator it) { return it->first.Native(); }
AZStd::string_view GetDirName(SubdirMap::iterator it) { return it->first.Native(); }
FileEntry* GetFileEntry(FileMap::iterator it);
FileEntryTree* GetDirEntry(SubdirMap::iterator it);
@@ -866,7 +866,7 @@ namespace AzFramework
FileIsReadOnlyResponse() = default;
FileIsReadOnlyResponse(bool isReadOnly);
unsigned int GetMessageType() const;
unsigned int GetMessageType() const override;
bool m_isReadOnly;
};
@@ -945,7 +945,7 @@ namespace AzFramework
FileModTimeRequest() = default;
FileModTimeRequest(const AZ::OSString& filePath);
unsigned int GetMessageType() const;
unsigned int GetMessageType() const override;
AZ::OSString m_filePath;
};
@@ -75,7 +75,7 @@ namespace AzFramework
{
public:
AZ_RTTI(GenericAssetHandlerBase, "{B153B8B5-25CC-4BB7-A2BD-9A47ECF4123C}", AZ::Data::AssetHandler);
virtual ~GenericAssetHandlerBase() {}
virtual ~GenericAssetHandlerBase() = default;
};
template <typename AssetType>
@@ -186,7 +186,7 @@ namespace AzFramework
}
}
bool CanHandleAsset(const AZ::Data::AssetId& id) const
bool CanHandleAsset(const AZ::Data::AssetId& id) const override
{
AZStd::string assetPath;
EBUS_EVENT_RESULT(assetPath, AZ::Data::AssetCatalogRequestBus, GetAssetPathById, id);
@@ -16,6 +16,7 @@
#include <AzFramework/Components/AzFrameworkConfigurationSystemComponent.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
#include <AzFramework/FileTag/FileTagComponent.h>
#include <AzFramework/Input/Contexts/InputContextComponent.h>
#include <AzFramework/Input/System/InputSystemComponent.h>
#include <AzFramework/Render/GameIntersectorComponent.h>
#include <AzFramework/Scene/SceneSystemComponent.h>
@@ -47,6 +48,7 @@ namespace AzFramework
AzFramework::CreateScriptDebugAgentFactory(),
AzFramework::AssetSystem::AssetSystemComponent::CreateDescriptor(),
AzFramework::InputSystemComponent::CreateDescriptor(),
AzFramework::InputContextComponent::CreateDescriptor(),
#if !defined(AZCORE_EXCLUDE_LUA)
AzFramework::ScriptComponent::CreateDescriptor(),
@@ -28,7 +28,7 @@ namespace AzFramework
// AZ::NonUniformScaleRequests::Handler ...
AZ::Vector3 GetScale() const override;
void SetScale(const AZ::Vector3& scale) override;
void RegisterScaleChangedEvent(AZ::NonUniformScaleChangedEvent::Handler& handler);
void RegisterScaleChangedEvent(AZ::NonUniformScaleChangedEvent::Handler& handler) override;
protected:
// AZ::Component ...
@@ -105,7 +105,7 @@ namespace AzFramework
virtual AZ::Matrix3x4 PopPremultipliedMatrix() { return AZ::Matrix3x4::CreateIdentity(); }
protected:
~DebugDisplayRequests() = default;
virtual ~DebugDisplayRequests() = default;
};
/// Inherit from DebugDisplayRequestBus::Handler to implement the DebugDisplayRequests interface.
@@ -69,7 +69,7 @@ namespace AzFramework
// EntityContext
AZ::Entity* CreateEntity(const char* name) override;
void OnRootEntityReloaded() override;
void OnContextEntitiesAdded(const EntityList& entities);
void OnContextEntitiesAdded(const EntityList& entities) override;
void OnContextReset() override;
bool ValidateEntitiesAreValidForContext(const EntityList& entities) override;
//////////////////////////////////////////////////////////////////////////
@@ -34,6 +34,7 @@ namespace AzFramework
{
}
using AZ::SettingsRegistryInterface::Visitor::Visit;
void Visit(AZStd::string_view path, AZStd::string_view, AZ::SettingsRegistryInterface::Type,
AZStd::string_view value) override
{
@@ -734,7 +734,7 @@ namespace AZ
{
if (AZ::StringFunc::StartsWith(pathStrView, aliasKey))
{
// Reduce of the size result result path by the size of the and add the resolved alias size
// Add to the size of result path by the resolved alias length - the alias key length
AZStd::string_view postAliasView = pathStrView.substr(aliasKey.size());
size_t requiredFixedMaxPathSize = postAliasView.size();
requiredFixedMaxPathSize += aliasValue.size();
@@ -28,34 +28,10 @@ namespace AzFramework
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId::InputChannelId(const char* name)
: m_crc32(name)
{
memset(m_name, 0, AZ_ARRAY_SIZE(m_name));
azstrncpy(m_name, NAME_BUFFER_SIZE, name, MAX_NAME_LENGTH);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId::InputChannelId(const InputChannelId& other)
: m_crc32(other.m_crc32)
{
memset(m_name, 0, AZ_ARRAY_SIZE(m_name));
azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputChannelId& InputChannelId::operator=(const InputChannelId& other)
{
azstrcpy(m_name, NAME_BUFFER_SIZE, other.m_name);
m_crc32 = other.m_crc32;
return *this;
}
////////////////////////////////////////////////////////////////////////////////////////////////
const char* InputChannelId::GetName() const
{
return m_name;
return m_name.c_str();
}
////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,6 +11,7 @@
#include <AzCore/Math/Crc.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/hash.h>
#include <AzCore/std/string/fixed_string.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
@@ -22,8 +23,7 @@ namespace AzFramework
public:
////////////////////////////////////////////////////////////////////////////////////////////
// Constants
static const int NAME_BUFFER_SIZE = 64;
static const int MAX_NAME_LENGTH = NAME_BUFFER_SIZE - 1;
static constexpr int MAX_NAME_LENGTH = 64;
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
@@ -39,21 +39,28 @@ namespace AzFramework
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] name Name of the input channel (will be truncated if exceeds MAX_NAME_LENGTH)
explicit InputChannelId(const char* name = "");
//! \param[in] name Name of the input channel (will be ignored if exceeds MAX_NAME_LENGTH)
explicit constexpr InputChannelId(AZStd::string_view name = "")
: m_name(name)
, m_crc32(name)
{
}
////////////////////////////////////////////////////////////////////////////////////////////
//! Copy constructor
//! \param[in] other Another instance of the class to copy from
InputChannelId(const InputChannelId& other);
////////////////////////////////////////////////////////////////////////////////////////////
//! Copy assignment operator
//! \param[in] other Another instance of the class to copy from
InputChannelId& operator=(const InputChannelId& other);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default destructor
constexpr InputChannelId(const InputChannelId& other) = default;
constexpr InputChannelId(InputChannelId&& other) = default;
constexpr InputChannelId& operator=(const InputChannelId& other)
{
m_name = other.m_name;
m_crc32 = other.m_crc32;
return *this;
}
constexpr InputChannelId& operator=(InputChannelId&& other)
{
m_name = AZStd::move(other.m_name);
m_crc32 = AZStd::move(other.m_crc32);
other.m_crc32 = 0;
return *this;
}
~InputChannelId() = default;
////////////////////////////////////////////////////////////////////////////////////////////
@@ -77,7 +84,7 @@ namespace AzFramework
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
char m_name[NAME_BUFFER_SIZE]; //!< Name of the input channel
AZStd::fixed_string<MAX_NAME_LENGTH> m_name; //!< Name of the input channel
AZ::Crc32 m_crc32; //!< Crc32 of the input channel
};
} // namespace AzFramework
@@ -55,6 +55,10 @@ namespace AzFramework
// Allocator
AZ_CLASS_ALLOCATOR(InputContext, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputContext, "{D17A85B2-405F-40AB-BBA7-F118256D39AB}", InputDevice);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
//! \param[in] name Unique, will be truncated if exceeds InputDeviceId::MAX_NAME_LENGTH = 64
@@ -0,0 +1,172 @@
/*
* 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/Input/Contexts/InputContextComponent.h>
#include <AzFramework/Input/Mappings/InputMappingAnd.h>
#include <AzFramework/Input/Mappings/InputMappingOr.h>
#include <AzCore/Debug/Trace.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContextComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("InputContextService", 0xa2734425));
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContextComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<InputContextComponent, AZ::Component>()
->Version(0)
->Field("Unique Name", &InputContextComponent::m_uniqueName)
->Field("Input Mappings", &InputContextComponent::m_inputMappings)
->Field("Local Player Index", &InputContextComponent::m_localPlayerIndex)
->Field("Input Listener Priority", &InputContextComponent::m_inputListenerPriority)
->Field("Consumes Processed Input", &InputContextComponent::m_consumesProcessedInput)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<InputContextComponent>("Input Context",
"An input context is a collection of input mappings, which map 'raw' input to custom input channels (ie. events).")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Category, "Input")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->DataElement(AZ::Edit::UIHandlers::Default, &InputContextComponent::m_uniqueName, "Unique Name",
"The name of the input context, unique among all active input contexts and input devices.\n"
"This will be truncated if its length exceeds that of InputDeviceId::MAX_NAME_LENGTH = 64")
->DataElement(AZ::Edit::UIHandlers::Default, &InputContextComponent::m_inputMappings, "Input Mappings",
"The list of all input mappings that will be created when the input context is activated.")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::SpinBox, &InputContextComponent::m_localPlayerIndex, "Local Player Index",
"The local player index that this context will receive input from (0 based, -1 means all controllers).\n"
"Will only work on platforms such as PC where the local user id corresponds to the local player index.\n"
"For other platforms, SetLocalUserId must be called at runtime with the id of a logged in user.")
->Attribute(AZ::Edit::Attributes::Min, -1)
->Attribute(AZ::Edit::Attributes::Max, 3)
->DataElement(AZ::Edit::UIHandlers::SpinBox, &InputContextComponent::m_inputListenerPriority, "Input Listener Priority",
"The priority used to sort the input context relative to all other input event listeners.\n"
"Higher numbers indicate greater priority.")
->Attribute(AZ::Edit::Attributes::Min, InputChannelEventListener::GetPriorityLast())
->Attribute(AZ::Edit::Attributes::Max, InputChannelEventListener::GetPriorityFirst())
->DataElement(AZ::Edit::UIHandlers::CheckBox, &InputContextComponent::m_consumesProcessedInput, "Consumes Processed Input",
"Should the input context consume input that is processed by any of its input mappings?")
;
}
}
InputMapping::ConfigBase::Reflect(context);
InputMappingAnd::Config::Reflect(context);
InputMappingOr::Config::Reflect(context);
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputContextComponent::~InputContextComponent()
{
Deactivate();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContextComponent::Init()
{
// The local player index that this component will receive input from (0 base, -1 wildcard)
// can be set from data, but will only work on platforms where the local user id corresponds
// to a local player index. For other platforms SetLocalUserId must be called at runtime with
// the id of a logged in local user, which will overwrite anything that is set here from data.
const LocalUserId localUserId = (m_localPlayerIndex == -1) ? LocalUserIdAny : aznumeric_cast<AZ::u32>(m_localPlayerIndex);
SetLocalUserId(localUserId);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContextComponent::Activate()
{
InputContextComponentRequestBus::Handler::BusConnect(GetEntityId());
CreateInputContext();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContextComponent::Deactivate()
{
ResetInputContext();
InputContextComponentRequestBus::Handler::BusDisconnect(GetEntityId());
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContextComponent::SetLocalUserId(LocalUserId localUserId)
{
// Create a new filter, or reset any existing one if we have been passed LocalUserIdAny.
if (localUserId != LocalUserIdAny)
{
m_localUserIdFilter = AZStd::make_shared<InputChannelEventFilterInclusionList>(InputChannelEventFilter::AnyChannelNameCrc32,
InputChannelEventFilter::AnyDeviceNameCrc32,
aznumeric_cast<AZ::u32>(m_localPlayerIndex));
}
else
{
m_localUserIdFilter.reset();
}
// Set the filter if the input context has already been created.
if (m_inputContext)
{
m_inputContext->SetFilter(m_localUserIdFilter);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContextComponent::CreateInputContext()
{
if (m_uniqueName.empty())
{
AZ_Error("InputContextComponent", false, "Cannot create input context with empty name.");
return;
}
if (InputDeviceRequests::FindInputDevice(InputDeviceId(m_uniqueName.c_str())))
{
AZ_Error("InputContextComponent", false,
"Cannot create input context '%s' with non-unique name.", m_uniqueName.c_str());
return;
}
if (m_inputMappings.empty())
{
AZ_Error("InputContextComponent", false,
"Cannot create input context '%s' with no input mappings.", m_uniqueName.c_str());
return;
}
// Create the input context.
InputContext::InitData initData;
initData.autoActivate = true;
initData.filter = m_localUserIdFilter;
initData.priority = m_inputListenerPriority;
initData.consumesProcessedInput = m_consumesProcessedInput;
m_inputContext = AZStd::make_unique<InputContext>(m_uniqueName.c_str(), initData);
// Create and add all input mappings.
for (const InputMapping::ConfigBase* inputMapping : m_inputMappings)
{
inputMapping->CreateInputMappingAndAddToContext(*m_inputContext);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputContextComponent::ResetInputContext()
{
m_inputContext.reset();
}
} // namespace AzFramework
@@ -0,0 +1,129 @@
/*
* 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
*
*/
#pragma once
#include <AzFramework/Input/Contexts/InputContext.h>
#include <AzFramework/Input/Mappings/InputMapping.h>
#include <AzFramework/Input/User/LocalUserId.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/ComponentBus.h>
#include <AzCore/RTTI/ReflectContext.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
class InputContextComponentRequests : public AZ::ComponentBus
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Set the local user id that the InputContextComponent should process input from
//! \param[in] localUserId Local user id the InputContextComponent should process input from
virtual void SetLocalUserId(LocalUserId localUserId) = 0;
};
using InputContextComponentRequestBus = AZ::EBus<InputContextComponentRequests>;
////////////////////////////////////////////////////////////////////////////////////////////////
//! An InputContextComponent is used to configure (at edit time) the data necessary to create an
//! InputContext (at run time). The life cycle of any InputContextComponent is controlled by the
//! AZ::Entity it is attached to, adhering to the same rules as any other AZ::Component, and the
//! InputContext which it owns is created/destroyed when the component is activated/deactivated.
class InputContextComponent : public AZ::Component
, public InputContextComponentRequestBus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
// AZ::Component Setup
AZ_COMPONENT(InputContextComponent, "{321689F8-A572-47D7-9D1C-EF9E0D2CD472}");
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::ComponentDescriptor::GetProvidedServices
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::ComponentDescriptor::Reflect
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Default Constructor
InputContextComponent() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~InputContextComponent() override;
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::Component::Init
void Init() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::Component::Activate
void Activate() override;
////////////////////////////////////////////////////////////////////////////////////////////
//! \ref AZ::Component::Deactivate
void Deactivate() override;
////////////////////////////////////////////////////////////////////////////////////////////
// \ref AzFramework::InputContextComponentRequests::SetLocalUserId
void SetLocalUserId(LocalUserId localUserId) override;
private:
////////////////////////////////////////////////////////////////////////////////////////////
//! Create the input context.
void CreateInputContext();
////////////////////////////////////////////////////////////////////////////////////////////
//! Reset the input context.
void ResetInputContext();
////////////////////////////////////////////////////////////////////////////////////////////
//! The list of all input mappings that will be created when the input context is activated.
//! Reflected to EditContext, then used to create and add input mapping classes in Activate.
AZStd::vector<InputMapping::ConfigBase*> m_inputMappings;
////////////////////////////////////////////////////////////////////////////////////////////
//! The name of the input context, unique among all active input contexts and input devices.
//! This will be truncated if its length exceeds that of InputDeviceId::MAX_NAME_LENGTH = 64
//! Reflected to EditContext, then used to create the unique input context class in Activate.
AZStd::string m_uniqueName;
////////////////////////////////////////////////////////////////////////////////////////////
//! Input context that is created and owned by this component. Not reflected to EditContext.
AZStd::unique_ptr<InputContext> m_inputContext;
////////////////////////////////////////////////////////////////////////////////////////////
//! Filter used to determine whether an input event should be handled by this input context.
//! Not reflected, but created inside SetLocalUserId if needed to fliter by a local user id.
AZStd::shared_ptr<InputChannelEventFilterInclusionList> m_localUserIdFilter;
////////////////////////////////////////////////////////////////////////////////////////////
//! The local player index that this component will receive input from (0 base, -1 wildcard).
//! Will only work on platforms where the local user id corresponds to the local player index.
//! For other platforms, SetLocalUserId must be called at runtime with id of a logged in user.
//! Reflected to EditContext, then used if needed to create the local user id filter in Init.
AZ::s32 m_localPlayerIndex = -1;
////////////////////////////////////////////////////////////////////////////////////////////
//! The priority used to sort the input context relative to all other input event listeners.
//! Reflected to EditContext, then used to create the unique input context class in Activate.
AZ::s32 m_inputListenerPriority = InputChannelEventListener::GetPriorityDefault();
////////////////////////////////////////////////////////////////////////////////////////////
//! Should the input context consume input that is processed by any of its input mappings?
//! Reflected to EditContext, then used to create the unique input context class in Activate.
bool m_consumesProcessedInput = false;
};
} // namespace AzFramework
@@ -28,91 +28,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == IdForIndex0.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::Button::A("gamepad_button_a");
const InputChannelId InputDeviceGamepad::Button::B("gamepad_button_b");
const InputChannelId InputDeviceGamepad::Button::X("gamepad_button_x");
const InputChannelId InputDeviceGamepad::Button::Y("gamepad_button_y");
const InputChannelId InputDeviceGamepad::Button::L1("gamepad_button_l1");
const InputChannelId InputDeviceGamepad::Button::R1("gamepad_button_r1");
const InputChannelId InputDeviceGamepad::Button::L3("gamepad_button_l3");
const InputChannelId InputDeviceGamepad::Button::R3("gamepad_button_r3");
const InputChannelId InputDeviceGamepad::Button::DU("gamepad_button_d_up");
const InputChannelId InputDeviceGamepad::Button::DD("gamepad_button_d_down");
const InputChannelId InputDeviceGamepad::Button::DL("gamepad_button_d_left");
const InputChannelId InputDeviceGamepad::Button::DR("gamepad_button_d_right");
const InputChannelId InputDeviceGamepad::Button::Start("gamepad_button_start");
const InputChannelId InputDeviceGamepad::Button::Select("gamepad_button_select");
const AZStd::array<InputChannelId, 14> InputDeviceGamepad::Button::All =
{{
A,
B,
X,
Y,
L1,
R1,
L3,
R3,
DU,
DD,
DL,
DR,
Start,
Select
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::Trigger::L2("gamepad_trigger_l2");
const InputChannelId InputDeviceGamepad::Trigger::R2("gamepad_trigger_r2");
const AZStd::array<InputChannelId, 2> InputDeviceGamepad::Trigger::All =
{{
L2,
R2
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::L("gamepad_thumbstick_l");
const InputChannelId InputDeviceGamepad::ThumbStickAxis2D::R("gamepad_thumbstick_r");
const AZStd::array<InputChannelId, 2> InputDeviceGamepad::ThumbStickAxis2D::All =
{{
L,
R
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LX("gamepad_thumbstick_l_x");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::LY("gamepad_thumbstick_l_y");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RX("gamepad_thumbstick_r_x");
const InputChannelId InputDeviceGamepad::ThumbStickAxis1D::RY("gamepad_thumbstick_r_y");
const AZStd::array<InputChannelId, 4> InputDeviceGamepad::ThumbStickAxis1D::All =
{{
LX,
LY,
RX,
RY
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LU("gamepad_thumbstick_l_up");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LD("gamepad_thumbstick_l_down");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LL("gamepad_thumbstick_l_left");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::LR("gamepad_thumbstick_l_right");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RU("gamepad_thumbstick_r_up");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RD("gamepad_thumbstick_r_down");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RL("gamepad_thumbstick_r_left");
const InputChannelId InputDeviceGamepad::ThumbStickDirection::RR("gamepad_thumbstick_r_right");
const AZStd::array<InputChannelId, 8> InputDeviceGamepad::ThumbStickDirection::All =
{{
LU,
LD,
LL,
LR,
RU,
RD,
RL,
RR
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceGamepad::Reflect(AZ::ReflectContext* context)
{
@@ -59,75 +59,115 @@ namespace AzFramework
//! All the input channel ids that identify game-pad digital button input
struct Button
{
static const InputChannelId A; //!< The bottom diamond face button
static const InputChannelId B; //!< The right diamond face button
static const InputChannelId X; //!< The left diamond face button
static const InputChannelId Y; //!< The top diamond face button
static const InputChannelId L1; //!< The top-left shoulder bumper button
static const InputChannelId R1; //!< The top-right shoulder bumper button
static const InputChannelId L3; //!< The left thumb-stick click button
static const InputChannelId R3; //!< The right thumb-stick click button
static const InputChannelId DU; //!< The up directional pad button
static const InputChannelId DD; //!< The down directional pad button
static const InputChannelId DL; //!< The left directional pad button
static const InputChannelId DR; //!< The right directional pad button
static const InputChannelId Start; //!< The start/pause/options button
static const InputChannelId Select; //!< The select/back button
static constexpr inline InputChannelId A{"gamepad_button_a"}; //!< The bottom diamond face button
static constexpr inline InputChannelId B{"gamepad_button_b"}; //!< The right diamond face button
static constexpr inline InputChannelId X{"gamepad_button_x"}; //!< The left diamond face button
static constexpr inline InputChannelId Y{"gamepad_button_y"}; //!< The top diamond face button
static constexpr inline InputChannelId L1{"gamepad_button_l1"}; //!< The top-left shoulder bumper button
static constexpr inline InputChannelId R1{"gamepad_button_r1"}; //!< The top-right shoulder bumper button
static constexpr inline InputChannelId L3{"gamepad_button_l3"}; //!< The left thumb-stick click button
static constexpr inline InputChannelId R3{"gamepad_button_r3"}; //!< The right thumb-stick click button
static constexpr inline InputChannelId DU{"gamepad_button_d_up"}; //!< The up directional pad button
static constexpr inline InputChannelId DD{"gamepad_button_d_down"}; //!< The down directional pad button
static constexpr inline InputChannelId DL{"gamepad_button_d_left"}; //!< The left directional pad button
static constexpr inline InputChannelId DR{"gamepad_button_d_right"}; //!< The right directional pad button
static constexpr inline InputChannelId Start{"gamepad_button_start"}; //!< The start/pause/options button
static constexpr inline InputChannelId Select{"gamepad_button_select"}; //!< The select/back button
//!< All digital game-pad button ids
static const AZStd::array<InputChannelId, 14> All;
static constexpr inline AZStd::array All
{
A,
B,
X,
Y,
L1,
R1,
L3,
R3,
DU,
DD,
DL,
DR,
Start,
Select
};
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad analog trigger input
struct Trigger
{
static const InputChannelId L2; //!< The bottom-left shoulder trigger
static const InputChannelId R2; //!< The bottom-right shoulder trigger
static constexpr inline InputChannelId L2{"gamepad_trigger_l2"}; //!< The bottom-left shoulder trigger
static constexpr inline InputChannelId R2{"gamepad_trigger_r2"}; //!< The bottom-right shoulder trigger
//!< All analog game-pad trigger ids
static const AZStd::array<InputChannelId, 2> All;
static constexpr inline AZStd::array All
{
L2,
R2
};
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick 2D axis input
struct ThumbStickAxis2D
{
static const InputChannelId L; //!< The left-hand thumb-stick
static const InputChannelId R; //!< The right-hand thumb-stick
static constexpr inline InputChannelId L{"gamepad_thumbstick_l"}; //!< The left-hand thumb-stick
static constexpr inline InputChannelId R{"gamepad_thumbstick_r"}; //!< The right-hand thumb-stick
//!< All game-pad thumb-stick 2D axis input channel ids
static const AZStd::array<InputChannelId, 2> All;
static constexpr inline AZStd::array All
{
L,
R
};
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick 1D axis input
struct ThumbStickAxis1D
{
static const InputChannelId LX; //!< X-axis of the left-hand thumb-stick
static const InputChannelId LY; //!< Y-axis of the left-hand thumb-stick
static const InputChannelId RX; //!< X-axis of the right-hand thumb-stick
static const InputChannelId RY; //!< Y-axis of the right-hand thumb-stick
static constexpr inline InputChannelId LX{"gamepad_thumbstick_l_x"}; //!< X-axis of the left-hand thumb-stick
static constexpr inline InputChannelId LY{"gamepad_thumbstick_l_y"}; //!< Y-axis of the left-hand thumb-stick
static constexpr inline InputChannelId RX{"gamepad_thumbstick_r_x"}; //!< X-axis of the right-hand thumb-stick
static constexpr inline InputChannelId RY{"gamepad_thumbstick_r_y"}; //!< Y-axis of the right-hand thumb-stick
//!< All game-pad thumb-stick 1D axis input channel ids
static const AZStd::array<InputChannelId, 4> All;
static constexpr inline AZStd::array All
{
LX,
LY,
RX,
RY
};
};
////////////////////////////////////////////////////////////////////////////////////////////
//! All the input channel ids that identify game-pad thumb-stick directional input
struct ThumbStickDirection
{
static const InputChannelId LU; //!< Up on the left-hand thumb-stick
static const InputChannelId LD; //!< Down on the left-hand thumb-stick
static const InputChannelId LL; //!< Left on the left-hand thumb-stick
static const InputChannelId LR; //!< Right on the left-hand thumb-stick
static const InputChannelId RU; //!< Up on the left-hand thumb-stick
static const InputChannelId RD; //!< Down on the left-hand thumb-stick
static const InputChannelId RL; //!< Left on the left-hand thumb-stick
static const InputChannelId RR; //!< Right on the left-hand thumb-stick
static constexpr inline InputChannelId LU{"gamepad_thumbstick_l_up"}; //!< Up on the left-hand thumb-stick
static constexpr inline InputChannelId LD{"gamepad_thumbstick_l_down"}; //!< Down on the left-hand thumb-stick
static constexpr inline InputChannelId LL{"gamepad_thumbstick_l_left"}; //!< Left on the left-hand thumb-stick
static constexpr inline InputChannelId LR{"gamepad_thumbstick_l_right"}; //!< Right on the left-hand thumb-stick
static constexpr inline InputChannelId RU{"gamepad_thumbstick_r_up"}; //!< Up on the left-hand thumb-stick
static constexpr inline InputChannelId RD{"gamepad_thumbstick_r_down"}; //!< Down on the left-hand thumb-stick
static constexpr inline InputChannelId RL{"gamepad_thumbstick_r_left"}; //!< Left on the left-hand thumb-stick
static constexpr inline InputChannelId RR{"gamepad_thumbstick_r_right"}; //!< Right on the left-hand thumb-stick
//!< All game-pad thumb-stick directional input channel ids
static const AZStd::array<InputChannelId, 8> All;
static constexpr inline AZStd::array All
{
LU,
LD,
LL,
LR,
RU,
RD,
RL,
RR
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -24,279 +24,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Alphanumeric Keys
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric0("keyboard_key_alphanumeric_0");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric1("keyboard_key_alphanumeric_1");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric2("keyboard_key_alphanumeric_2");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric3("keyboard_key_alphanumeric_3");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric4("keyboard_key_alphanumeric_4");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric5("keyboard_key_alphanumeric_5");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric6("keyboard_key_alphanumeric_6");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric7("keyboard_key_alphanumeric_7");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric8("keyboard_key_alphanumeric_8");
const InputChannelId InputDeviceKeyboard::Key::Alphanumeric9("keyboard_key_alphanumeric_9");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericA("keyboard_key_alphanumeric_A");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericB("keyboard_key_alphanumeric_B");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericC("keyboard_key_alphanumeric_C");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericD("keyboard_key_alphanumeric_D");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericE("keyboard_key_alphanumeric_E");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericF("keyboard_key_alphanumeric_F");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericG("keyboard_key_alphanumeric_G");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericH("keyboard_key_alphanumeric_H");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericI("keyboard_key_alphanumeric_I");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericJ("keyboard_key_alphanumeric_J");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericK("keyboard_key_alphanumeric_K");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericL("keyboard_key_alphanumeric_L");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericM("keyboard_key_alphanumeric_M");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericN("keyboard_key_alphanumeric_N");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericO("keyboard_key_alphanumeric_O");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericP("keyboard_key_alphanumeric_P");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericQ("keyboard_key_alphanumeric_Q");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericR("keyboard_key_alphanumeric_R");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericS("keyboard_key_alphanumeric_S");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericT("keyboard_key_alphanumeric_T");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericU("keyboard_key_alphanumeric_U");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericV("keyboard_key_alphanumeric_V");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericW("keyboard_key_alphanumeric_W");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericX("keyboard_key_alphanumeric_X");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericY("keyboard_key_alphanumeric_Y");
const InputChannelId InputDeviceKeyboard::Key::AlphanumericZ("keyboard_key_alphanumeric_Z");
////////////////////////////////////////////////////////////////////////////////////////////////
// Edit (and escape) Keys
const InputChannelId InputDeviceKeyboard::Key::EditBackspace("keyboard_key_edit_backspace");
const InputChannelId InputDeviceKeyboard::Key::EditCapsLock("keyboard_key_edit_capslock");
const InputChannelId InputDeviceKeyboard::Key::EditEnter("keyboard_key_edit_enter");
const InputChannelId InputDeviceKeyboard::Key::EditSpace("keyboard_key_edit_space");
const InputChannelId InputDeviceKeyboard::Key::EditTab("keyboard_key_edit_tab");
const InputChannelId InputDeviceKeyboard::Key::Escape("keyboard_key_escape");
////////////////////////////////////////////////////////////////////////////////////////////////
// Function Keys
const InputChannelId InputDeviceKeyboard::Key::Function01("keyboard_key_function_F01");
const InputChannelId InputDeviceKeyboard::Key::Function02("keyboard_key_function_F02");
const InputChannelId InputDeviceKeyboard::Key::Function03("keyboard_key_function_F03");
const InputChannelId InputDeviceKeyboard::Key::Function04("keyboard_key_function_F04");
const InputChannelId InputDeviceKeyboard::Key::Function05("keyboard_key_function_F05");
const InputChannelId InputDeviceKeyboard::Key::Function06("keyboard_key_function_F06");
const InputChannelId InputDeviceKeyboard::Key::Function07("keyboard_key_function_F07");
const InputChannelId InputDeviceKeyboard::Key::Function08("keyboard_key_function_F08");
const InputChannelId InputDeviceKeyboard::Key::Function09("keyboard_key_function_F09");
const InputChannelId InputDeviceKeyboard::Key::Function10("keyboard_key_function_F10");
const InputChannelId InputDeviceKeyboard::Key::Function11("keyboard_key_function_F11");
const InputChannelId InputDeviceKeyboard::Key::Function12("keyboard_key_function_F12");
const InputChannelId InputDeviceKeyboard::Key::Function13("keyboard_key_function_F13");
const InputChannelId InputDeviceKeyboard::Key::Function14("keyboard_key_function_F14");
const InputChannelId InputDeviceKeyboard::Key::Function15("keyboard_key_function_F15");
const InputChannelId InputDeviceKeyboard::Key::Function16("keyboard_key_function_F16");
const InputChannelId InputDeviceKeyboard::Key::Function17("keyboard_key_function_F17");
const InputChannelId InputDeviceKeyboard::Key::Function18("keyboard_key_function_F18");
const InputChannelId InputDeviceKeyboard::Key::Function19("keyboard_key_function_F19");
const InputChannelId InputDeviceKeyboard::Key::Function20("keyboard_key_function_F20");
////////////////////////////////////////////////////////////////////////////////////////////////
// Modifier Keys
const InputChannelId InputDeviceKeyboard::Key::ModifierAltL("keyboard_key_modifier_alt_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierAltR("keyboard_key_modifier_alt_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlL("keyboard_key_modifier_ctrl_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierCtrlR("keyboard_key_modifier_ctrl_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierShiftL("keyboard_key_modifier_shift_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierShiftR("keyboard_key_modifier_shift_r");
const InputChannelId InputDeviceKeyboard::Key::ModifierSuperL("keyboard_key_modifier_super_l");
const InputChannelId InputDeviceKeyboard::Key::ModifierSuperR("keyboard_key_modifier_super_r");
////////////////////////////////////////////////////////////////////////////////////////////////
// Navigation Keys
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowDown("keyboard_key_navigation_arrow_down");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowLeft("keyboard_key_navigation_arrow_left");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowRight("keyboard_key_navigation_arrow_right");
const InputChannelId InputDeviceKeyboard::Key::NavigationArrowUp("keyboard_key_navigation_arrow_up");
const InputChannelId InputDeviceKeyboard::Key::NavigationDelete("keyboard_key_navigation_delete");
const InputChannelId InputDeviceKeyboard::Key::NavigationEnd("keyboard_key_navigation_end");
const InputChannelId InputDeviceKeyboard::Key::NavigationHome("keyboard_key_navigation_home");
const InputChannelId InputDeviceKeyboard::Key::NavigationInsert("keyboard_key_navigation_insert");
const InputChannelId InputDeviceKeyboard::Key::NavigationPageDown("keyboard_key_navigation_page_down");
const InputChannelId InputDeviceKeyboard::Key::NavigationPageUp("keyboard_key_navigation_page_up");
////////////////////////////////////////////////////////////////////////////////////////////////
// Numpad Keys
const InputChannelId InputDeviceKeyboard::Key::NumLock("keyboard_key_num_lock");
const InputChannelId InputDeviceKeyboard::Key::NumPad0("keyboard_key_numpad_0");
const InputChannelId InputDeviceKeyboard::Key::NumPad1("keyboard_key_numpad_1");
const InputChannelId InputDeviceKeyboard::Key::NumPad2("keyboard_key_numpad_2");
const InputChannelId InputDeviceKeyboard::Key::NumPad3("keyboard_key_numpad_3");
const InputChannelId InputDeviceKeyboard::Key::NumPad4("keyboard_key_numpad_4");
const InputChannelId InputDeviceKeyboard::Key::NumPad5("keyboard_key_numpad_5");
const InputChannelId InputDeviceKeyboard::Key::NumPad6("keyboard_key_numpad_6");
const InputChannelId InputDeviceKeyboard::Key::NumPad7("keyboard_key_numpad_7");
const InputChannelId InputDeviceKeyboard::Key::NumPad8("keyboard_key_numpad_8");
const InputChannelId InputDeviceKeyboard::Key::NumPad9("keyboard_key_numpad_9");
const InputChannelId InputDeviceKeyboard::Key::NumPadAdd("keyboard_key_numpad_add");
const InputChannelId InputDeviceKeyboard::Key::NumPadDecimal("keyboard_key_numpad_decimal");
const InputChannelId InputDeviceKeyboard::Key::NumPadDivide("keyboard_key_numpad_divide");
const InputChannelId InputDeviceKeyboard::Key::NumPadEnter("keyboard_key_numpad_enter");
const InputChannelId InputDeviceKeyboard::Key::NumPadMultiply("keyboard_key_numpad_multiply");
const InputChannelId InputDeviceKeyboard::Key::NumPadSubtract("keyboard_key_numpad_subtract");
////////////////////////////////////////////////////////////////////////////////////////////////
// Punctuation Keys
const InputChannelId InputDeviceKeyboard::Key::PunctuationApostrophe("keyboard_key_punctuation_apostrophe");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBackslash("keyboard_key_punctuation_backslash");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketL("keyboard_key_punctuation_bracket_l");
const InputChannelId InputDeviceKeyboard::Key::PunctuationBracketR("keyboard_key_punctuation_bracket_r");
const InputChannelId InputDeviceKeyboard::Key::PunctuationComma("keyboard_key_punctuation_comma");
const InputChannelId InputDeviceKeyboard::Key::PunctuationEquals("keyboard_key_punctuation_equals");
const InputChannelId InputDeviceKeyboard::Key::PunctuationHyphen("keyboard_key_punctuation_hyphen");
const InputChannelId InputDeviceKeyboard::Key::PunctuationPeriod("keyboard_key_punctuation_period");
const InputChannelId InputDeviceKeyboard::Key::PunctuationSemicolon("keyboard_key_punctuation_semicolon");
const InputChannelId InputDeviceKeyboard::Key::PunctuationSlash("keyboard_key_punctuation_slash");
const InputChannelId InputDeviceKeyboard::Key::PunctuationTilde("keyboard_key_punctuation_tilde");
////////////////////////////////////////////////////////////////////////////////////////////////
// Supplementary ISO Key
const InputChannelId InputDeviceKeyboard::Key::SupplementaryISO("keyboard_key_supplementary_iso");
////////////////////////////////////////////////////////////////////////////////////////////////
// Windows System Keys
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPause("keyboard_key_windows_system_pause");
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemPrint("keyboard_key_windows_system_print");
const InputChannelId InputDeviceKeyboard::Key::WindowsSystemScrollLock("keyboard_key_windows_system_scroll_lock");
////////////////////////////////////////////////////////////////////////////////////////////////
const AZStd::array<InputChannelId, 112> InputDeviceKeyboard::Key::All =
{{
// Alphanumeric Keys
Alphanumeric0,
Alphanumeric1,
Alphanumeric2,
Alphanumeric3,
Alphanumeric4,
Alphanumeric5,
Alphanumeric6,
Alphanumeric7,
Alphanumeric8,
Alphanumeric9,
AlphanumericA,
AlphanumericB,
AlphanumericC,
AlphanumericD,
AlphanumericE,
AlphanumericF,
AlphanumericG,
AlphanumericH,
AlphanumericI,
AlphanumericJ,
AlphanumericK,
AlphanumericL,
AlphanumericM,
AlphanumericN,
AlphanumericO,
AlphanumericP,
AlphanumericQ,
AlphanumericR,
AlphanumericS,
AlphanumericT,
AlphanumericU,
AlphanumericV,
AlphanumericW,
AlphanumericX,
AlphanumericY,
AlphanumericZ,
// Edit (and escape) Keys
EditBackspace,
EditCapsLock,
EditEnter,
EditSpace,
EditTab,
Escape,
// Function Keys
Function01,
Function02,
Function03,
Function04,
Function05,
Function06,
Function07,
Function08,
Function09,
Function10,
Function11,
Function12,
Function13,
Function14,
Function15,
Function16,
Function17,
Function18,
Function19,
Function20,
// Modifier Keys
ModifierAltL,
ModifierAltR,
ModifierCtrlL,
ModifierCtrlR,
ModifierShiftL,
ModifierShiftR,
ModifierSuperL,
ModifierSuperR,
// Navigation Keys
NavigationArrowDown,
NavigationArrowLeft,
NavigationArrowRight,
NavigationArrowUp,
NavigationDelete,
NavigationEnd,
NavigationHome,
NavigationInsert,
NavigationPageDown,
NavigationPageUp,
// Numpad Keys
NumLock,
NumPad0,
NumPad1,
NumPad2,
NumPad3,
NumPad4,
NumPad5,
NumPad6,
NumPad7,
NumPad8,
NumPad9,
NumPadAdd,
NumPadDecimal,
NumPadDivide,
NumPadEnter,
NumPadMultiply,
NumPadSubtract,
// Punctuation Keys
PunctuationApostrophe,
PunctuationBackslash,
PunctuationBracketL,
PunctuationBracketR,
PunctuationComma,
PunctuationEquals,
PunctuationHyphen,
PunctuationPeriod,
PunctuationSemicolon,
PunctuationSlash,
PunctuationTilde,
// Supplementary ISO Key
SupplementaryISO,
// Windows System Keys
WindowsSystemPause,
WindowsSystemPrint,
WindowsSystemScrollLock
}};
////////////////////////////////////////////////////////////////////////////////////////////////
ModifierKeyMask GetCorrespondingModifierKeyMask(const InputChannelId& channelId)
{
@@ -94,137 +94,268 @@ namespace AzFramework
struct Key
{
// Alphanumeric Keys
static const InputChannelId Alphanumeric0; //!< The 0 key
static const InputChannelId Alphanumeric1; //!< The 1 key
static const InputChannelId Alphanumeric2; //!< The 2 key
static const InputChannelId Alphanumeric3; //!< The 3 key
static const InputChannelId Alphanumeric4; //!< The 4 key
static const InputChannelId Alphanumeric5; //!< The 5 key
static const InputChannelId Alphanumeric6; //!< The 6 key
static const InputChannelId Alphanumeric7; //!< The 7 key
static const InputChannelId Alphanumeric8; //!< The 8 key
static const InputChannelId Alphanumeric9; //!< The 9 key
static const InputChannelId AlphanumericA; //!< The A key
static const InputChannelId AlphanumericB; //!< The B key
static const InputChannelId AlphanumericC; //!< The C key
static const InputChannelId AlphanumericD; //!< The D key
static const InputChannelId AlphanumericE; //!< The E key
static const InputChannelId AlphanumericF; //!< The F key
static const InputChannelId AlphanumericG; //!< The G key
static const InputChannelId AlphanumericH; //!< The H key
static const InputChannelId AlphanumericI; //!< The I key
static const InputChannelId AlphanumericJ; //!< The J key
static const InputChannelId AlphanumericK; //!< The K key
static const InputChannelId AlphanumericL; //!< The L key
static const InputChannelId AlphanumericM; //!< The M key
static const InputChannelId AlphanumericN; //!< The N key
static const InputChannelId AlphanumericO; //!< The O key
static const InputChannelId AlphanumericP; //!< The P key
static const InputChannelId AlphanumericQ; //!< The Q key
static const InputChannelId AlphanumericR; //!< The R key
static const InputChannelId AlphanumericS; //!< The S key
static const InputChannelId AlphanumericT; //!< The T key
static const InputChannelId AlphanumericU; //!< The U key
static const InputChannelId AlphanumericV; //!< The V key
static const InputChannelId AlphanumericW; //!< The W key
static const InputChannelId AlphanumericX; //!< The X key
static const InputChannelId AlphanumericY; //!< The Y key
static const InputChannelId AlphanumericZ; //!< The Z key
static constexpr inline InputChannelId Alphanumeric0{"keyboard_key_alphanumeric_0"}; //!< The 0 key
static constexpr inline InputChannelId Alphanumeric1{"keyboard_key_alphanumeric_1"}; //!< The 1 key
static constexpr inline InputChannelId Alphanumeric2{"keyboard_key_alphanumeric_2"}; //!< The 2 key
static constexpr inline InputChannelId Alphanumeric3{"keyboard_key_alphanumeric_3"}; //!< The 3 key
static constexpr inline InputChannelId Alphanumeric4{"keyboard_key_alphanumeric_4"}; //!< The 4 key
static constexpr inline InputChannelId Alphanumeric5{"keyboard_key_alphanumeric_5"}; //!< The 5 key
static constexpr inline InputChannelId Alphanumeric6{"keyboard_key_alphanumeric_6"}; //!< The 6 key
static constexpr inline InputChannelId Alphanumeric7{"keyboard_key_alphanumeric_7"}; //!< The 7 key
static constexpr inline InputChannelId Alphanumeric8{"keyboard_key_alphanumeric_8"}; //!< The 8 key
static constexpr inline InputChannelId Alphanumeric9{"keyboard_key_alphanumeric_9"}; //!< The 9 key
static constexpr inline InputChannelId AlphanumericA{"keyboard_key_alphanumeric_A"}; //!< The A key
static constexpr inline InputChannelId AlphanumericB{"keyboard_key_alphanumeric_B"}; //!< The B key
static constexpr inline InputChannelId AlphanumericC{"keyboard_key_alphanumeric_C"}; //!< The C key
static constexpr inline InputChannelId AlphanumericD{"keyboard_key_alphanumeric_D"}; //!< The D key
static constexpr inline InputChannelId AlphanumericE{"keyboard_key_alphanumeric_E"}; //!< The E key
static constexpr inline InputChannelId AlphanumericF{"keyboard_key_alphanumeric_F"}; //!< The F key
static constexpr inline InputChannelId AlphanumericG{"keyboard_key_alphanumeric_G"}; //!< The G key
static constexpr inline InputChannelId AlphanumericH{"keyboard_key_alphanumeric_H"}; //!< The H key
static constexpr inline InputChannelId AlphanumericI{"keyboard_key_alphanumeric_I"}; //!< The I key
static constexpr inline InputChannelId AlphanumericJ{"keyboard_key_alphanumeric_J"}; //!< The J key
static constexpr inline InputChannelId AlphanumericK{"keyboard_key_alphanumeric_K"}; //!< The K key
static constexpr inline InputChannelId AlphanumericL{"keyboard_key_alphanumeric_L"}; //!< The L key
static constexpr inline InputChannelId AlphanumericM{"keyboard_key_alphanumeric_M"}; //!< The M key
static constexpr inline InputChannelId AlphanumericN{"keyboard_key_alphanumeric_N"}; //!< The N key
static constexpr inline InputChannelId AlphanumericO{"keyboard_key_alphanumeric_O"}; //!< The O key
static constexpr inline InputChannelId AlphanumericP{"keyboard_key_alphanumeric_P"}; //!< The P key
static constexpr inline InputChannelId AlphanumericQ{"keyboard_key_alphanumeric_Q"}; //!< The Q key
static constexpr inline InputChannelId AlphanumericR{"keyboard_key_alphanumeric_R"}; //!< The R key
static constexpr inline InputChannelId AlphanumericS{"keyboard_key_alphanumeric_S"}; //!< The S key
static constexpr inline InputChannelId AlphanumericT{"keyboard_key_alphanumeric_T"}; //!< The T key
static constexpr inline InputChannelId AlphanumericU{"keyboard_key_alphanumeric_U"}; //!< The U key
static constexpr inline InputChannelId AlphanumericV{"keyboard_key_alphanumeric_V"}; //!< The V key
static constexpr inline InputChannelId AlphanumericW{"keyboard_key_alphanumeric_W"}; //!< The W key
static constexpr inline InputChannelId AlphanumericX{"keyboard_key_alphanumeric_X"}; //!< The X key
static constexpr inline InputChannelId AlphanumericY{"keyboard_key_alphanumeric_Y"}; //!< The Y key
static constexpr inline InputChannelId AlphanumericZ{"keyboard_key_alphanumeric_Z"}; //!< The Z key
// Edit (and escape) Keys
static const InputChannelId EditBackspace; //!< The backspace key
static const InputChannelId EditCapsLock; //!< The caps lock key
static const InputChannelId EditEnter; //!< The enter/return key
static const InputChannelId EditSpace; //!< The spacebar key
static const InputChannelId EditTab; //!< The tab key
static const InputChannelId Escape; //!< The escape key
// Edit {and escape} Keys
static constexpr inline InputChannelId EditBackspace{"keyboard_key_edit_backspace"}; //!< The backspace key
static constexpr inline InputChannelId EditCapsLock{"keyboard_key_edit_capslock"}; //!< The caps lock key
static constexpr inline InputChannelId EditEnter{"keyboard_key_edit_enter"}; //!< The enter/return key
static constexpr inline InputChannelId EditSpace{"keyboard_key_edit_space"}; //!< The spacebar key
static constexpr inline InputChannelId EditTab{"keyboard_key_edit_tab"}; //!< The tab key
static constexpr inline InputChannelId Escape{"keyboard_key_escape"}; //!< The escape key
// Function Keys
static const InputChannelId Function01; //!< The F1 key
static const InputChannelId Function02; //!< The F2 key
static const InputChannelId Function03; //!< The F3 key
static const InputChannelId Function04; //!< The F4 key
static const InputChannelId Function05; //!< The F5 key
static const InputChannelId Function06; //!< The F6 key
static const InputChannelId Function07; //!< The F7 key
static const InputChannelId Function08; //!< The F8 key
static const InputChannelId Function09; //!< The F9 key
static const InputChannelId Function10; //!< The F10 key
static const InputChannelId Function11; //!< The F11 key
static const InputChannelId Function12; //!< The F12 key
static const InputChannelId Function13; //!< The F13 key
static const InputChannelId Function14; //!< The F14 key
static const InputChannelId Function15; //!< The F15 key
static const InputChannelId Function16; //!< The F16 key
static const InputChannelId Function17; //!< The F17 key
static const InputChannelId Function18; //!< The F18 key
static const InputChannelId Function19; //!< The F19 key
static const InputChannelId Function20; //!< The F20 key
static constexpr inline InputChannelId Function01{"keyboard_key_function_F01"}; //!< The F1 key
static constexpr inline InputChannelId Function02{"keyboard_key_function_F02"}; //!< The F2 key
static constexpr inline InputChannelId Function03{"keyboard_key_function_F03"}; //!< The F3 key
static constexpr inline InputChannelId Function04{"keyboard_key_function_F04"}; //!< The F4 key
static constexpr inline InputChannelId Function05{"keyboard_key_function_F05"}; //!< The F5 key
static constexpr inline InputChannelId Function06{"keyboard_key_function_F06"}; //!< The F6 key
static constexpr inline InputChannelId Function07{"keyboard_key_function_F07"}; //!< The F7 key
static constexpr inline InputChannelId Function08{"keyboard_key_function_F08"}; //!< The F8 key
static constexpr inline InputChannelId Function09{"keyboard_key_function_F09"}; //!< The F9 key
static constexpr inline InputChannelId Function10{"keyboard_key_function_F10"}; //!< The F10 key
static constexpr inline InputChannelId Function11{"keyboard_key_function_F11"}; //!< The F11 key
static constexpr inline InputChannelId Function12{"keyboard_key_function_F12"}; //!< The F12 key
static constexpr inline InputChannelId Function13{"keyboard_key_function_F13"}; //!< The F13 key
static constexpr inline InputChannelId Function14{"keyboard_key_function_F14"}; //!< The F14 key
static constexpr inline InputChannelId Function15{"keyboard_key_function_F15"}; //!< The F15 key
static constexpr inline InputChannelId Function16{"keyboard_key_function_F16"}; //!< The F16 key
static constexpr inline InputChannelId Function17{"keyboard_key_function_F17"}; //!< The F17 key
static constexpr inline InputChannelId Function18{"keyboard_key_function_F18"}; //!< The F18 key
static constexpr inline InputChannelId Function19{"keyboard_key_function_F19"}; //!< The F19 key
static constexpr inline InputChannelId Function20{"keyboard_key_function_F20"}; //!< The F20 key
// Modifier Keys
static const InputChannelId ModifierAltL; //!< The left alt/option key
static const InputChannelId ModifierAltR; //!< The right alt/option key
static const InputChannelId ModifierCtrlL; //!< The left control key
static const InputChannelId ModifierCtrlR; //!< The right control key
static const InputChannelId ModifierShiftL; //!< The left shift key
static const InputChannelId ModifierShiftR; //!< The right shift key
static const InputChannelId ModifierSuperL; //!< The left super (windows or apple) key
static const InputChannelId ModifierSuperR; //!< The right super (windows or apple) key
static constexpr inline InputChannelId ModifierAltL{"keyboard_key_modifier_alt_l"}; //!< The left alt/option key
static constexpr inline InputChannelId ModifierAltR{"keyboard_key_modifier_alt_r"}; //!< The right alt/option key
static constexpr inline InputChannelId ModifierCtrlL{"keyboard_key_modifier_ctrl_l"}; //!< The left control key
static constexpr inline InputChannelId ModifierCtrlR{"keyboard_key_modifier_ctrl_r"}; //!< The right control key
static constexpr inline InputChannelId ModifierShiftL{"keyboard_key_modifier_shift_l"}; //!< The left shift key
static constexpr inline InputChannelId ModifierShiftR{"keyboard_key_modifier_shift_r"}; //!< The right shift key
static constexpr inline InputChannelId ModifierSuperL{"keyboard_key_modifier_super_l"}; //!< The left super {windows or apple} key
static constexpr inline InputChannelId ModifierSuperR{"keyboard_key_modifier_super_r"}; //!< The right super {windows or apple} key
// Navigation Keys
static const InputChannelId NavigationArrowDown; //!< The down arrow key
static const InputChannelId NavigationArrowLeft; //!< The left arrow key
static const InputChannelId NavigationArrowRight; //!< The right arrow key
static const InputChannelId NavigationArrowUp; //!< The up arrow key
static const InputChannelId NavigationDelete; //!< The delete key
static const InputChannelId NavigationEnd; //!< The end key
static const InputChannelId NavigationHome; //!< The home key
static const InputChannelId NavigationInsert; //!< The insert key
static const InputChannelId NavigationPageDown; //!< The page down key
static const InputChannelId NavigationPageUp; //!< The page up key
static constexpr inline InputChannelId NavigationArrowDown{"keyboard_key_navigation_arrow_down"}; //!< The down arrow key
static constexpr inline InputChannelId NavigationArrowLeft{"keyboard_key_navigation_arrow_left"}; //!< The left arrow key
static constexpr inline InputChannelId NavigationArrowRight{"keyboard_key_navigation_arrow_right"}; //!< The right arrow key
static constexpr inline InputChannelId NavigationArrowUp{"keyboard_key_navigation_arrow_up"}; //!< The up arrow key
static constexpr inline InputChannelId NavigationDelete{"keyboard_key_navigation_delete"}; //!< The delete key
static constexpr inline InputChannelId NavigationEnd{"keyboard_key_navigation_end"}; //!< The end key
static constexpr inline InputChannelId NavigationHome{"keyboard_key_navigation_home"}; //!< The home key
static constexpr inline InputChannelId NavigationInsert{"keyboard_key_navigation_insert"}; //!< The insert key
static constexpr inline InputChannelId NavigationPageDown{"keyboard_key_navigation_page_down"}; //!< The page down key
static constexpr inline InputChannelId NavigationPageUp{"keyboard_key_navigation_page_up"}; //!< The page up key
// Numpad Keys
static const InputChannelId NumLock; //!< The num lock key (the clear key on apple keyboards)
static const InputChannelId NumPad0; //!< The numpad 0 key
static const InputChannelId NumPad1; //!< The numpad 1 key
static const InputChannelId NumPad2; //!< The numpad 2 key
static const InputChannelId NumPad3; //!< The numpad 3 key
static const InputChannelId NumPad4; //!< The numpad 4 key
static const InputChannelId NumPad5; //!< The numpad 5 key
static const InputChannelId NumPad6; //!< The numpad 6 key
static const InputChannelId NumPad7; //!< The numpad 7 key
static const InputChannelId NumPad8; //!< The numpad 8 key
static const InputChannelId NumPad9; //!< The numpad 9 key
static const InputChannelId NumPadAdd; //!< The numpad add key
static const InputChannelId NumPadDecimal; //!< The numpad decimal key
static const InputChannelId NumPadDivide; //!< The numpad divide key
static const InputChannelId NumPadEnter; //!< The numpad enter key
static const InputChannelId NumPadMultiply; //!< The numpad multiply key
static const InputChannelId NumPadSubtract; //!< The numpad subtract key
static constexpr inline InputChannelId NumLock{"keyboard_key_num_lock"}; //!< The num lock key {the clear key on apple keyboards}
static constexpr inline InputChannelId NumPad0{"keyboard_key_numpad_0"}; //!< The numpad 0 key
static constexpr inline InputChannelId NumPad1{"keyboard_key_numpad_1"}; //!< The numpad 1 key
static constexpr inline InputChannelId NumPad2{"keyboard_key_numpad_2"}; //!< The numpad 2 key
static constexpr inline InputChannelId NumPad3{"keyboard_key_numpad_3"}; //!< The numpad 3 key
static constexpr inline InputChannelId NumPad4{"keyboard_key_numpad_4"}; //!< The numpad 4 key
static constexpr inline InputChannelId NumPad5{"keyboard_key_numpad_5"}; //!< The numpad 5 key
static constexpr inline InputChannelId NumPad6{"keyboard_key_numpad_6"}; //!< The numpad 6 key
static constexpr inline InputChannelId NumPad7{"keyboard_key_numpad_7"}; //!< The numpad 7 key
static constexpr inline InputChannelId NumPad8{"keyboard_key_numpad_8"}; //!< The numpad 8 key
static constexpr inline InputChannelId NumPad9{"keyboard_key_numpad_9"}; //!< The numpad 9 key
static constexpr inline InputChannelId NumPadAdd{"keyboard_key_numpad_add"}; //!< The numpad add key
static constexpr inline InputChannelId NumPadDecimal{"keyboard_key_numpad_decimal"}; //!< The numpad decimal key
static constexpr inline InputChannelId NumPadDivide{"keyboard_key_numpad_divide"}; //!< The numpad divide key
static constexpr inline InputChannelId NumPadEnter{"keyboard_key_numpad_enter"}; //!< The numpad enter key
static constexpr inline InputChannelId NumPadMultiply{"keyboard_key_numpad_multiply"}; //!< The numpad multiply key
static constexpr inline InputChannelId NumPadSubtract{"keyboard_key_numpad_subtract"}; //!< The numpad subtract key
// Punctuation Keys
static const InputChannelId PunctuationApostrophe; //!< The apostrophe key
static const InputChannelId PunctuationBackslash; //!< The backslash key
static const InputChannelId PunctuationBracketL; //!< The left bracket key
static const InputChannelId PunctuationBracketR; //!< The right bracket key
static const InputChannelId PunctuationComma; //!< The comma key
static const InputChannelId PunctuationEquals; //!< The equals key
static const InputChannelId PunctuationHyphen; //!< The hyphen/underscore key
static const InputChannelId PunctuationPeriod; //!< The period key
static const InputChannelId PunctuationSemicolon; //!< The semicolon key
static const InputChannelId PunctuationSlash; //!< The (forward) slash key
static const InputChannelId PunctuationTilde; //!< The tilde/grave key
static constexpr inline InputChannelId PunctuationApostrophe{"keyboard_key_punctuation_apostrophe"}; //!< The apostrophe key
static constexpr inline InputChannelId PunctuationBackslash{"keyboard_key_punctuation_backslash"}; //!< The backslash key
static constexpr inline InputChannelId PunctuationBracketL{"keyboard_key_punctuation_bracket_l"}; //!< The left bracket key
static constexpr inline InputChannelId PunctuationBracketR{"keyboard_key_punctuation_bracket_r"}; //!< The right bracket key
static constexpr inline InputChannelId PunctuationComma{"keyboard_key_punctuation_comma"}; //!< The comma key
static constexpr inline InputChannelId PunctuationEquals{"keyboard_key_punctuation_equals"}; //!< The equals key
static constexpr inline InputChannelId PunctuationHyphen{"keyboard_key_punctuation_hyphen"}; //!< The hyphen/underscore key
static constexpr inline InputChannelId PunctuationPeriod{"keyboard_key_punctuation_period"}; //!< The period key
static constexpr inline InputChannelId PunctuationSemicolon{"keyboard_key_punctuation_semicolon"}; //!< The semicolon key
static constexpr inline InputChannelId PunctuationSlash{"keyboard_key_punctuation_slash"}; //!< The {forward} slash key
static constexpr inline InputChannelId PunctuationTilde{"keyboard_key_punctuation_tilde"}; //!< The tilde/grave key
// Supplementary ISO Key
static const InputChannelId SupplementaryISO; //!< The supplementary ISO layout key
static constexpr inline InputChannelId SupplementaryISO{"keyboard_key_supplementary_iso"}; //!< The supplementary ISO layout key
// Windows System Keys
static const InputChannelId WindowsSystemPause; //!< The windows pause key
static const InputChannelId WindowsSystemPrint; //!< The windows print key
static const InputChannelId WindowsSystemScrollLock; //!< The windows scroll lock key
static constexpr inline InputChannelId WindowsSystemPause{"keyboard_key_windows_system_pause"}; //!< The windows pause key
static constexpr inline InputChannelId WindowsSystemPrint{"keyboard_key_windows_system_print"}; //!< The windows print key
static constexpr inline InputChannelId WindowsSystemScrollLock{"keyboard_key_windows_system_scroll_lock"}; //!< The windows scroll lock key
//!< All keyboard key ids
static const AZStd::array<InputChannelId, 112> All;
static constexpr inline AZStd::array All
{
// Alphanumeric Keys
Alphanumeric0,
Alphanumeric1,
Alphanumeric2,
Alphanumeric3,
Alphanumeric4,
Alphanumeric5,
Alphanumeric6,
Alphanumeric7,
Alphanumeric8,
Alphanumeric9,
AlphanumericA,
AlphanumericB,
AlphanumericC,
AlphanumericD,
AlphanumericE,
AlphanumericF,
AlphanumericG,
AlphanumericH,
AlphanumericI,
AlphanumericJ,
AlphanumericK,
AlphanumericL,
AlphanumericM,
AlphanumericN,
AlphanumericO,
AlphanumericP,
AlphanumericQ,
AlphanumericR,
AlphanumericS,
AlphanumericT,
AlphanumericU,
AlphanumericV,
AlphanumericW,
AlphanumericX,
AlphanumericY,
AlphanumericZ,
// Edit (and escape) Keys
EditBackspace,
EditCapsLock,
EditEnter,
EditSpace,
EditTab,
Escape,
// Function Keys
Function01,
Function02,
Function03,
Function04,
Function05,
Function06,
Function07,
Function08,
Function09,
Function10,
Function11,
Function12,
Function13,
Function14,
Function15,
Function16,
Function17,
Function18,
Function19,
Function20,
// Modifier Keys
ModifierAltL,
ModifierAltR,
ModifierCtrlL,
ModifierCtrlR,
ModifierShiftL,
ModifierShiftR,
ModifierSuperL,
ModifierSuperR,
// Navigation Keys
NavigationArrowDown,
NavigationArrowLeft,
NavigationArrowRight,
NavigationArrowUp,
NavigationDelete,
NavigationEnd,
NavigationHome,
NavigationInsert,
NavigationPageDown,
NavigationPageUp,
// Numpad Keys
NumLock,
NumPad0,
NumPad1,
NumPad2,
NumPad3,
NumPad4,
NumPad5,
NumPad6,
NumPad7,
NumPad8,
NumPad9,
NumPadAdd,
NumPadDecimal,
NumPadDivide,
NumPadEnter,
NumPadMultiply,
NumPadSubtract,
// Punctuation Keys
PunctuationApostrophe,
PunctuationBackslash,
PunctuationBracketL,
PunctuationBracketR,
PunctuationComma,
PunctuationEquals,
PunctuationHyphen,
PunctuationPeriod,
PunctuationSemicolon,
PunctuationSlash,
PunctuationTilde,
// Supplementary ISO Key
SupplementaryISO,
// Windows System Keys
WindowsSystemPause,
WindowsSystemPrint,
WindowsSystemScrollLock
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -23,44 +23,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::Acceleration::Gravity("motion_acceleration_gravity");
const InputChannelId InputDeviceMotion::Acceleration::Raw("motion_acceleration_raw");
const InputChannelId InputDeviceMotion::Acceleration::User("motion_acceleration_user");
const AZStd::array<InputChannelId, 3> InputDeviceMotion::Acceleration::All =
{{
Gravity,
Raw,
User
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::RotationRate::Raw("motion_rotation_rate_raw");
const InputChannelId InputDeviceMotion::RotationRate::Unbiased("motion_rotation_rate_unbiased");
const AZStd::array<InputChannelId, 2> InputDeviceMotion::RotationRate::All =
{{
Raw,
Unbiased
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::MagneticField::North("motion_magnetic_field_north");
const InputChannelId InputDeviceMotion::MagneticField::Raw("motion_magnetic_field_raw");
const InputChannelId InputDeviceMotion::MagneticField::Unbiased("motion_magnetic_field_unbiased");
const AZStd::array<InputChannelId, 3> InputDeviceMotion::MagneticField::All =
{{
North,
Raw,
Unbiased
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMotion::Orientation::Current("motion_orientation_current");
const AZStd::array<InputChannelId, 1> InputDeviceMotion::Orientation::All =
{{
Current
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMotion::Reflect(AZ::ReflectContext* context)
{
@@ -44,12 +44,17 @@ namespace AzFramework
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct Acceleration
{
static const InputChannelId Gravity;
static const InputChannelId Raw;
static const InputChannelId User;
static constexpr inline InputChannelId Gravity{"motion_acceleration_gravity"};
static constexpr inline InputChannelId Raw{"motion_acceleration_raw"};
static constexpr inline InputChannelId User{"motion_acceleration_user"};
//!< All acceleration input channel ids
static const AZStd::array<InputChannelId, 3> All;
static constexpr inline AZStd::array All
{
Gravity,
Raw,
User
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -60,11 +65,15 @@ namespace AzFramework
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct RotationRate
{
static const InputChannelId Raw;
static const InputChannelId Unbiased;
static constexpr inline InputChannelId Raw{"motion_rotation_rate_raw"};
static constexpr inline InputChannelId Unbiased{"motion_rotation_rate_unbiased"};
//!< All rotation rate input channel ids
static const AZStd::array<InputChannelId, 2> All;
static constexpr inline AZStd::array All
{
Raw,
Unbiased
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -75,12 +84,17 @@ namespace AzFramework
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct MagneticField
{
static const InputChannelId North;
static const InputChannelId Raw;
static const InputChannelId Unbiased;
static constexpr inline InputChannelId North{"motion_magnetic_field_north"};
static constexpr inline InputChannelId Raw{"motion_magnetic_field_raw"};
static constexpr inline InputChannelId Unbiased{"motion_magnetic_field_unbiased"};
//!< All magnetic field input channel ids
static const AZStd::array<InputChannelId, 3> All;
static constexpr inline AZStd::array All
{
North,
Raw,
Unbiased
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -91,10 +105,13 @@ namespace AzFramework
//! - InputMotionSensorRequests::SetInputChannelEnabled
struct Orientation
{
static const InputChannelId Current;
static constexpr inline InputChannelId Current{"motion_orientation_current"};
//!< All orientation input channel ids
static const AZStd::array<InputChannelId, 1> All;
static constexpr inline AZStd::array All
{
Current
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -33,35 +33,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::Button::Left("mouse_button_left");
const InputChannelId InputDeviceMouse::Button::Right("mouse_button_right");
const InputChannelId InputDeviceMouse::Button::Middle("mouse_button_middle");
const InputChannelId InputDeviceMouse::Button::Other1("mouse_button_other1");
const InputChannelId InputDeviceMouse::Button::Other2("mouse_button_other2");
const AZStd::array<InputChannelId, 5> InputDeviceMouse::Button::All =
{{
Left,
Right,
Middle,
Other1,
Other2
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::Movement::X("mouse_delta_x");
const InputChannelId InputDeviceMouse::Movement::Y("mouse_delta_y");
const InputChannelId InputDeviceMouse::Movement::Z("mouse_delta_z");
const AZStd::array<InputChannelId, 3> InputDeviceMouse::Movement::All =
{{
X,
Y,
Z
}};
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceMouse::SystemCursorPosition("mouse_system_cursor_position");
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceMouse::Reflect(AZ::ReflectContext* context)
{
@@ -66,14 +66,21 @@ namespace AzFramework
//! been implemented for windows simply to provide for backwards compatibility with CryInput.
struct Button
{
static const InputChannelId Left; //!< The left mouse button
static const InputChannelId Right; //!< The right mouse button
static const InputChannelId Middle; //!< The middle mouse button
static const InputChannelId Other1; //!< DEPRECATED: the x1 mouse button
static const InputChannelId Other2; //!< DEPRECATED: the x2 mouse button
static constexpr inline InputChannelId Left{"mouse_button_left"}; //!< The left mouse button
static constexpr inline InputChannelId Right{"mouse_button_right"}; //!< The right mouse button
static constexpr inline InputChannelId Middle{"mouse_button_middle"}; //!< The middle mouse button
static constexpr inline InputChannelId Other1{"mouse_button_other1"}; //!< DEPRECATED: the x1 mouse button
static constexpr inline InputChannelId Other2{"mouse_button_other2"}; //!< DEPRECATED: the x2 mouse button
//!< All mouse button ids
static const AZStd::array<InputChannelId, 5> All;
static constexpr inline AZStd::array All
{
Left,
Right,
Middle,
Other1,
Other2
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -82,12 +89,17 @@ namespace AzFramework
//! directly correlate to the mouse position (which is queried directly from the system).
struct Movement
{
static const InputChannelId X; //!< Raw horizontal mouse movement over the last frame
static const InputChannelId Y; //!< Raw vertical mouse movement over the last frame
static const InputChannelId Z; //!< Raw mouse wheel movement over the last frame
static constexpr inline InputChannelId X{"mouse_delta_x"}; //!< Raw horizontal mouse movement over the last frame
static constexpr inline InputChannelId Y{"mouse_delta_y"}; //!< Raw vertical mouse movement over the last frame
static constexpr inline InputChannelId Z{"mouse_delta_z"}; //!< Raw mouse wheel movement over the last frame
//!< All mouse movement ids
static const AZStd::array<InputChannelId, 3> All;
static constexpr inline AZStd::array All
{
X,
Y,
Z
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -96,7 +108,7 @@ namespace AzFramework
//! the system cursor is hidden or visible. When the system cursor has been constrained to
//! the active window values will be in the [0.0, 1.0] range, but not when unconstrained.
//! See also InputSystemCursorRequests::SetSystemCursorState and GetSystemCursorState.
static const InputChannelId SystemCursorPosition;
static constexpr inline InputChannelId SystemCursorPosition{"mouse_system_cursor_position"};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
@@ -24,31 +24,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceTouch::Touch::Index0("touch_index_0");
const InputChannelId InputDeviceTouch::Touch::Index1("touch_index_1");
const InputChannelId InputDeviceTouch::Touch::Index2("touch_index_2");
const InputChannelId InputDeviceTouch::Touch::Index3("touch_index_3");
const InputChannelId InputDeviceTouch::Touch::Index4("touch_index_4");
const InputChannelId InputDeviceTouch::Touch::Index5("touch_index_5");
const InputChannelId InputDeviceTouch::Touch::Index6("touch_index_6");
const InputChannelId InputDeviceTouch::Touch::Index7("touch_index_7");
const InputChannelId InputDeviceTouch::Touch::Index8("touch_index_8");
const InputChannelId InputDeviceTouch::Touch::Index9("touch_index_9");
const AZStd::array<InputChannelId, 10> InputDeviceTouch::Touch::All =
{{
Index0,
Index1,
Index2,
Index3,
Index4,
Index5,
Index6,
Index7,
Index8,
Index9
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceTouch::Reflect(AZ::ReflectContext* context)
{
@@ -38,19 +38,31 @@ namespace AzFramework
//! track is arbitrary, but ten seems to be more than sufficient for most game applications.
struct Touch
{
static const InputChannelId Index0; //!< Touch index 0
static const InputChannelId Index1; //!< Touch index 1
static const InputChannelId Index2; //!< Touch index 2
static const InputChannelId Index3; //!< Touch index 3
static const InputChannelId Index4; //!< Touch index 4
static const InputChannelId Index5; //!< Touch index 5
static const InputChannelId Index6; //!< Touch index 6
static const InputChannelId Index7; //!< Touch index 7
static const InputChannelId Index8; //!< Touch index 8
static const InputChannelId Index9; //!< Touch index 9
static constexpr inline InputChannelId Index0{"touch_index_0"}; //!< Touch index 0
static constexpr inline InputChannelId Index1{"touch_index_1"}; //!< Touch index 1
static constexpr inline InputChannelId Index2{"touch_index_2"}; //!< Touch index 2
static constexpr inline InputChannelId Index3{"touch_index_3"}; //!< Touch index 3
static constexpr inline InputChannelId Index4{"touch_index_4"}; //!< Touch index 4
static constexpr inline InputChannelId Index5{"touch_index_5"}; //!< Touch index 5
static constexpr inline InputChannelId Index6{"touch_index_6"}; //!< Touch index 6
static constexpr inline InputChannelId Index7{"touch_index_7"}; //!< Touch index 7
static constexpr inline InputChannelId Index8{"touch_index_8"}; //!< Touch index 8
static constexpr inline InputChannelId Index9{"touch_index_9"}; //!< Touch index 9
//!< All touch input channel ids
static const AZStd::array<InputChannelId, 10> All;
static constexpr inline AZStd::array All
{
Index0,
Index1,
Index2,
Index3,
Index4,
Index5,
Index6,
Index7,
Index8,
Index9
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -23,17 +23,6 @@ namespace AzFramework
return (inputDeviceId.GetNameCrc32() == Id.GetNameCrc32());
}
////////////////////////////////////////////////////////////////////////////////////////////////
const InputChannelId InputDeviceVirtualKeyboard::Command::EditEnter("virtual_keyboard_edit_enter");
const InputChannelId InputDeviceVirtualKeyboard::Command::EditClear("virtual_keyboard_edit_clear");
const InputChannelId InputDeviceVirtualKeyboard::Command::NavigationBack("virtual_keyboard_navigation_back");
const AZStd::array<InputChannelId, 3> InputDeviceVirtualKeyboard::Command::All =
{{
EditClear,
EditEnter,
NavigationBack
}};
////////////////////////////////////////////////////////////////////////////////////////////////
void InputDeviceVirtualKeyboard::Reflect(AZ::ReflectContext* context)
{
@@ -39,17 +39,22 @@ namespace AzFramework
struct Command
{
//!< The clear command used to indicate the user wants to clear the active text field
static const InputChannelId EditClear;
static constexpr inline InputChannelId EditClear{"virtual_keyboard_edit_enter"};
//!< The enter/return/close command used to indicate the user has finished text editing
static const InputChannelId EditEnter;
static constexpr inline InputChannelId EditEnter{"virtual_keyboard_edit_clear"};
//!< The back command used to indicate the user wants to navigate 'backwards'.
//!< This is specific to android devices, and does not have an ios equivalent.
static const InputChannelId NavigationBack;
static constexpr inline InputChannelId NavigationBack{"virtual_keyboard_navigation_back"};
//!< All virtual keyboard command ids
static const AZStd::array<InputChannelId, 3> All;
static constexpr inline AZStd::array All
{
EditClear,
EditEnter,
NavigationBack
};
};
////////////////////////////////////////////////////////////////////////////////////////////
@@ -9,9 +9,156 @@
#include <AzFramework/Input/Mappings/InputMapping.h>
#include <AzFramework/Input/Contexts/InputContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/sort.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
void InputMapping::InputChannelNameFilteredByDeviceType::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<InputMapping::InputChannelNameFilteredByDeviceType>()
->Version(0)
->Field("Input Device Type", &InputChannelNameFilteredByDeviceType::m_inputDeviceType)
->Field("Input Channel Name", &InputChannelNameFilteredByDeviceType::m_inputChannelName)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<InputChannelNameFilteredByDeviceType>("InputChannelNameFilteredByDeviceType",
"An input channel name (filtered by an input device type) to add to the input mapping.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputChannelNameFilteredByDeviceType::GetNameLabelOverride)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &InputChannelNameFilteredByDeviceType::m_inputDeviceType, "Input Device Type",
"The type of input device by which to filter input channel names.")
->Attribute(AZ::Edit::Attributes::StringList, &InputChannelNameFilteredByDeviceType::GetValidInputDeviceTypes)
->Attribute(AZ::Edit::Attributes::ChangeNotify, &InputChannelNameFilteredByDeviceType::OnInputDeviceTypeSelected)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &InputChannelNameFilteredByDeviceType::m_inputChannelName, "Input Channel Name",
"The input channel name to add to the input mapping.")
->Attribute(AZ::Edit::Attributes::StringList, &InputChannelNameFilteredByDeviceType::GetValidInputChannelNamesBySelectedDevice)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputMapping::InputChannelNameFilteredByDeviceType::InputChannelNameFilteredByDeviceType()
{
// Try initialize the selected input device type and input channel name to something valid.
if (m_inputDeviceType.empty())
{
const AZStd::vector<AZStd::string> validInputDeviceTypes = GetValidInputDeviceTypes();
if (!validInputDeviceTypes.empty())
{
m_inputDeviceType = validInputDeviceTypes[0];
OnInputDeviceTypeSelected();
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZ::Crc32 InputMapping::InputChannelNameFilteredByDeviceType::OnInputDeviceTypeSelected()
{
const AZStd::vector<AZStd::string> validInputNames = GetValidInputChannelNamesBySelectedDevice();
if (!validInputNames.empty())
{
m_inputChannelName = validInputNames[0];
}
return AZ::Edit::PropertyRefreshLevels::AttributesAndValues;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::string InputMapping::InputChannelNameFilteredByDeviceType::GetNameLabelOverride() const
{
return m_inputChannelName.empty() ? "<Select>" : m_inputChannelName;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::vector<AZStd::string> InputMapping::InputChannelNameFilteredByDeviceType::GetValidInputDeviceTypes() const
{
AZStd::set<AZStd::string> uniqueInputDeviceTypes;
InputDeviceRequests::InputDeviceByIdMap availableInputDevicesById;
InputDeviceRequestBus::Broadcast(&InputDeviceRequests::GetInputDevicesById,
availableInputDevicesById);
for (const auto& inputDeviceById : availableInputDevicesById)
{
// Filter out input contexts so that mappings can only be created from 'raw' input events.
if (!azrtti_istypeof<InputContext*>(inputDeviceById.second))
{
uniqueInputDeviceTypes.insert(inputDeviceById.first.GetName());
}
}
return AZStd::vector<AZStd::string>(uniqueInputDeviceTypes.begin(), uniqueInputDeviceTypes.end());
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::vector<AZStd::string> InputMapping::InputChannelNameFilteredByDeviceType::GetValidInputChannelNamesBySelectedDevice() const
{
AZStd::vector<AZStd::string> validInputChannelNames;
InputDeviceId selectedDeviceId(m_inputDeviceType.c_str());
InputDeviceRequests::InputChannelIdSet validInputChannelIds;
InputDeviceRequestBus::Event(selectedDeviceId,
&InputDeviceRequests::GetInputChannelIds,
validInputChannelIds);
for (const InputChannelId& inputChannelId : validInputChannelIds)
{
validInputChannelNames.push_back(inputChannelId.GetName());
}
AZStd::sort(validInputChannelNames.begin(), validInputChannelNames.end());
return validInputChannelNames;
}
////////////////////////////////////////////////////////////////////////////////////////////////
void InputMapping::ConfigBase::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<InputMapping::ConfigBase>()
->Version(0)
->Field("Output Input Channel Name", &InputMapping::ConfigBase::m_outputInputChannelName)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<InputMapping::ConfigBase>("Input Mapping: Base",
"Maps multiple different input sources to a single output input channel.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &ConfigBase::m_outputInputChannelName, "Output Input Channel Name",
"The unique input channel name (ie. input event name) output by the input mapping.\n"
"This will be truncated if its length exceeds that of InputChannelId::MAX_NAME_LENGTH = 64")
->Attribute(AZ::Edit::Attributes::Max, InputChannelId::MAX_NAME_LENGTH)
;
}
}
InputChannelNameFilteredByDeviceType::Reflect(context);
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::shared_ptr<InputMapping> InputMapping::ConfigBase::CreateInputMappingAndAddToContext(InputContext& inputContext) const
{
AZStd::shared_ptr<InputMapping> inputMapping = CreateInputMapping(inputContext);
if (inputMapping)
{
inputContext.AddInputMapping(inputMapping);
}
return inputMapping;
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::string InputMapping::ConfigBase::GetNameLabelOverride() const
{
return m_outputInputChannelName.empty() ? "<Input Mapping>" : m_outputInputChannelName;
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputMapping::InputMapping(const InputChannelId& inputChannelId, const InputContext& inputContext)
: InputChannel(inputChannelId, inputContext)
@@ -12,6 +12,7 @@
#include <AzFramework/Input/Devices/InputDevice.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
@@ -26,6 +27,111 @@ namespace AzFramework
class InputMapping : public InputChannel
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! Convenience class that allows for selection of an input channel name filtered by device.
struct InputChannelNameFilteredByDeviceType
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputChannelNameFilteredByDeviceType, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(InputChannelNameFilteredByDeviceType, "{68CC4865-1C0E-4E2E-BDAE-AF42EA30DBE8}");
////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Constructor
InputChannelNameFilteredByDeviceType();
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~InputChannelNameFilteredByDeviceType() = default;
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the currently selected input device type.
//! \return Currently selected input device type.
inline const AZStd::string& GetInputDeviceType() const { return m_inputDeviceType; }
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the currently selected input channel name.
//! \return Currently selected input channel name.
inline const AZStd::string& GetInputChannelName() const { return m_inputChannelName; }
protected:
////////////////////////////////////////////////////////////////////////////////////////////
//! Called when an input device type is selected.
//! \return The AZ::Edit::PropertyRefreshLevels to apply to the property tree view.
virtual AZ::Crc32 OnInputDeviceTypeSelected();
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the name label override to display.
//! \return Name label override to display.
virtual AZStd::string GetNameLabelOverride() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the valid input device types for this input mapping.
//! \return Valid input device types for this input mapping.
virtual AZStd::vector<AZStd::string> GetValidInputDeviceTypes() const;
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the valid input channel names for this input mapping given the selected device type.
//! \return Valid input channel names for this input mapping given the selected device type.
virtual AZStd::vector<AZStd::string> GetValidInputChannelNamesBySelectedDevice() const;
private:
////////////////////////////////////////////////////////////////////////////////////////////
// Variables
AZStd::string m_inputDeviceType; //!< The currently selected input device type.
AZStd::string m_inputChannelName; //!< The currently selected input channel name.
};
////////////////////////////////////////////////////////////////////////////////////////////
//! Base class for input mapping configuration values that are exposed to the editor.
class ConfigBase
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(ConfigBase, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(ConfigBase, "{72EBBBCC-D57E-4085-AFD9-4910506010B6}");
////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
virtual ~ConfigBase() = default;
////////////////////////////////////////////////////////////////////////////////////////
//! Create an input mapping and add it to the input context.
//! \param[in] inputContext Input context that the input mapping will be added to.
AZStd::shared_ptr<InputMapping> CreateInputMappingAndAddToContext(InputContext& inputContext) const;
////////////////////////////////////////////////////////////////////////////////////////
//! Override to create the relevant input mapping.
//! \param[in] inputContext Input context that owns the input mapping.
virtual AZStd::shared_ptr<InputMapping> CreateInputMapping(const InputContext& inputContext) const = 0;
////////////////////////////////////////////////////////////////////////////////////////////
//! Get the name label override to display.
//! \return Name label override to display.
virtual AZStd::string GetNameLabelOverride() const;
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! The unique input channel name (event) output by the input mapping.
AZStd::string m_outputInputChannelName;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputMapping, AZ::SystemAllocator, 0);
@@ -8,9 +8,72 @@
#include <AzFramework/Input/Mappings/InputMappingAnd.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
void InputMappingAnd::Config::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<InputMappingAnd::Config, InputMapping::ConfigBase>()
->Version(0)
->Field("Source Input Channel Names", &Config::m_sourceInputChannelNames)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<InputMappingAnd::Config>("Input Mapping: And",
"Maps multiple different input sources to a single output using 'AND' logic.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingAnd::Config::GetNameLabelOverride)
->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names",
"The source input channel names that will be mapped to the output input channel name.")
;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::shared_ptr<InputMapping> InputMappingAnd::Config::CreateInputMapping(const InputContext& inputContext) const
{
if (m_outputInputChannelName.empty())
{
AZ_Error("InputMappingAnd::Config", false, "Cannot create input mapping with empty name.");
return nullptr;
}
if (InputChannelRequests::FindInputChannel(InputChannelId(m_outputInputChannelName.c_str())))
{
AZ_Error("InputMappingAnd::Config", false,
"Cannot create input mapping '%s' with non-unique name.", m_outputInputChannelName.c_str());
return nullptr;
}
if (m_sourceInputChannelNames.empty())
{
AZ_Error("InputMappingAnd::Config", false,
"Cannot create input mapping '%s' with no source inputs.", m_outputInputChannelName.c_str());
return nullptr;
}
const InputChannelId outputInputChannelId(m_outputInputChannelName.c_str());
AZStd::shared_ptr<InputMappingAnd> inputMapping = AZStd::make_shared<InputMappingAnd>(outputInputChannelId,
inputContext);
for (const InputChannelNameFilteredByDeviceType& sourceInputChannelName : m_sourceInputChannelNames)
{
const InputChannelId sourceInputChannelId(sourceInputChannelName.GetInputChannelName().c_str());
inputMapping->AddSourceInput(sourceInputChannelId);
}
return inputMapping;
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputMappingAnd::InputMappingAnd(const InputChannelId& inputChannelId, const InputContext& inputContext)
: InputMapping(inputChannelId, inputContext)
@@ -19,6 +19,38 @@ namespace AzFramework
class InputMappingAnd : public InputMapping
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! The input mapping configuration values that are exposed to the editor.
class Config : public InputMapping::ConfigBase
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(Config, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(Config, "{54E972F3-0477-4E2E-93F5-4E06ED755DF6}", InputMapping::ConfigBase);
////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~Config() override = default;
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputMapping::Type::CreateInputMapping
AZStd::shared_ptr<InputMapping> CreateInputMapping(const InputContext& inputContext) const override;
private:
////////////////////////////////////////////////////////////////////////////////////////
//! The source input channel names that will be mapped to the output input channel name.
AZStd::vector<InputChannelNameFilteredByDeviceType> m_sourceInputChannelNames;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputMappingAnd, AZ::SystemAllocator, 0);
@@ -8,9 +8,72 @@
#include <AzFramework/Input/Mappings/InputMappingOr.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/std/smart_ptr/make_shared.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
////////////////////////////////////////////////////////////////////////////////////////////////
void InputMappingOr::Config::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<InputMappingOr::Config, InputMapping::ConfigBase>()
->Version(0)
->Field("Source Input Channel Names", &Config::m_sourceInputChannelNames)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<InputMappingOr::Config>("Input Mapping: Or",
"Maps multiple different input sources to a single output using 'OR' logic.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
->Attribute(AZ::Edit::Attributes::NameLabelOverride, &InputMappingOr::Config::GetNameLabelOverride)
->DataElement(AZ::Edit::UIHandlers::Default, &Config::m_sourceInputChannelNames, "Source Input Channel Names",
"The source input channel names that will be mapped to the output input channel name.")
;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::shared_ptr<InputMapping> InputMappingOr::Config::CreateInputMapping(const InputContext& inputContext) const
{
if (m_outputInputChannelName.empty())
{
AZ_Error("InputMappingOr::Config", false, "Cannot create input mapping with empty name.");
return nullptr;
}
if (InputChannelRequests::FindInputChannel(InputChannelId(m_outputInputChannelName.c_str())))
{
AZ_Error("InputMappingOr::Config", false,
"Cannot create input mapping '%s' with non-unique name.", m_outputInputChannelName.c_str());
return nullptr;
}
if (m_sourceInputChannelNames.empty())
{
AZ_Error("InputMappingOr::Config", false,
"Cannot create input mapping '%s' with no source inputs.", m_outputInputChannelName.c_str());
return nullptr;
}
const InputChannelId outputInputChannelId(m_outputInputChannelName.c_str());
AZStd::shared_ptr<InputMappingOr> inputMapping = AZStd::make_shared<InputMappingOr>(outputInputChannelId,
inputContext);
for (const InputChannelNameFilteredByDeviceType& sourceInputChannelName : m_sourceInputChannelNames)
{
const InputChannelId sourceInputChannelId(sourceInputChannelName.GetInputChannelName().c_str());
inputMapping->AddSourceInput(sourceInputChannelId);
}
return inputMapping;
}
////////////////////////////////////////////////////////////////////////////////////////////////
InputMappingOr::InputMappingOr(const InputChannelId& inputChannelId, const InputContext& inputContext)
: InputMapping(inputChannelId, inputContext)
@@ -19,6 +19,38 @@ namespace AzFramework
class InputMappingOr : public InputMapping
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
//! The input mapping configuration values that are exposed to the editor.
class Config : public InputMapping::ConfigBase
{
public:
////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(Config, AZ::SystemAllocator, 0);
////////////////////////////////////////////////////////////////////////////////////////
// Type Info
AZ_RTTI(Config, "{428AFDD4-D353-494A-BBAC-37E00F82CFFD}", InputMapping::ConfigBase);
////////////////////////////////////////////////////////////////////////////////////////
// Reflection
static void Reflect(AZ::ReflectContext* context);
////////////////////////////////////////////////////////////////////////////////////////////
//! Destructor
~Config() override = default;
protected:
////////////////////////////////////////////////////////////////////////////////////////
//! \ref AzFramework::InputMapping::Type::CreateInputMapping
AZStd::shared_ptr<InputMapping> CreateInputMapping(const InputContext& inputContext) const override;
private:
////////////////////////////////////////////////////////////////////////////////////////
//! The source input channel names that will be mapped to the output input channel name.
AZStd::vector<InputChannelNameFilteredByDeviceType> m_sourceInputChannelNames;
};
////////////////////////////////////////////////////////////////////////////////////////////
// Allocator
AZ_CLASS_ALLOCATOR(InputMappingOr, AZ::SystemAllocator, 0);
@@ -31,9 +31,9 @@ namespace AzFramework
//////////////////////////////////////////////////////////////////////////
// AZ::Component
virtual void Init();
virtual void Activate();
virtual void Deactivate();
void Init() override;
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
@@ -36,6 +36,16 @@ namespace AzFramework
return m_end;
}
const AZ::Entity* const* SpawnableEntityContainerView::begin() const
{
return m_begin;
}
const AZ::Entity* const* SpawnableEntityContainerView::end() const
{
return m_end;
}
const AZ::Entity* const* SpawnableEntityContainerView::cbegin()
{
return m_begin;
@@ -46,11 +56,28 @@ namespace AzFramework
return m_end;
}
size_t SpawnableEntityContainerView::size()
AZ::Entity* SpawnableEntityContainerView::operator[](size_t n)
{
AZ_Assert(n < size(), "Index %zu is out of bounds (size: %llu) for Spawnable Entity Container View", n, size());
return *(m_begin + n);
}
const AZ::Entity* SpawnableEntityContainerView::operator[](size_t n) const
{
AZ_Assert(n < size(), "Index %zu is out of bounds (size: %llu) for Spawnable Entity Container View", n, size());
return *(m_begin + n);
}
size_t SpawnableEntityContainerView::size() const
{
return AZStd::distance(m_begin, m_end);
}
bool SpawnableEntityContainerView::empty() const
{
return m_begin == m_end;
}
//
// SpawnableConstEntityContainerView
@@ -78,6 +105,16 @@ namespace AzFramework
return m_end;
}
const AZ::Entity* const* SpawnableConstEntityContainerView::begin() const
{
return m_begin;
}
const AZ::Entity* const* SpawnableConstEntityContainerView::end() const
{
return m_end;
}
const AZ::Entity* const* SpawnableConstEntityContainerView::cbegin()
{
return m_begin;
@@ -88,11 +125,28 @@ namespace AzFramework
return m_end;
}
size_t SpawnableConstEntityContainerView::size()
const AZ::Entity* SpawnableConstEntityContainerView::operator[](size_t n)
{
AZ_Assert(n < size(), "Index %zu is out of bounds (size: %llu) for Spawnable Const Entity Container View", n, size());
return *(m_begin + n);
}
const AZ::Entity* SpawnableConstEntityContainerView::operator[](size_t n) const
{
AZ_Assert(n < size(), "Index %zu is out of bounds (size: %llu) for Spawnable Entity Container View", n, size());
return *(m_begin + n);
}
size_t SpawnableConstEntityContainerView::size() const
{
return AZStd::distance(m_begin, m_end);
}
bool SpawnableConstEntityContainerView::empty() const
{
return m_begin == m_end;
}
//
// SpawnableIndexEntityPair
@@ -36,11 +36,18 @@ namespace AzFramework
SpawnableEntityContainerView(AZ::Entity** begin, size_t length);
SpawnableEntityContainerView(AZ::Entity** begin, AZ::Entity** end);
AZ::Entity** begin();
AZ::Entity** end();
const AZ::Entity* const* cbegin();
const AZ::Entity* const* cend();
size_t size();
[[nodiscard]] AZ::Entity** begin();
[[nodiscard]] AZ::Entity** end();
[[nodiscard]] const AZ::Entity* const* begin() const;
[[nodiscard]] const AZ::Entity* const* end() const;
[[nodiscard]] const AZ::Entity* const* cbegin();
[[nodiscard]] const AZ::Entity* const* cend();
[[nodiscard]] AZ::Entity* operator[](size_t n);
[[nodiscard]] const AZ::Entity* operator[](size_t n) const;
[[nodiscard]] size_t size() const;
[[nodiscard]] bool empty() const;
private:
AZ::Entity** m_begin;
@@ -53,11 +60,18 @@ namespace AzFramework
SpawnableConstEntityContainerView(AZ::Entity** begin, size_t length);
SpawnableConstEntityContainerView(AZ::Entity** begin, AZ::Entity** end);
const AZ::Entity* const* begin();
const AZ::Entity* const* end();
const AZ::Entity* const* cbegin();
const AZ::Entity* const* cend();
size_t size();
[[nodiscard]] const AZ::Entity* const* begin();
[[nodiscard]] const AZ::Entity* const* end();
[[nodiscard]] const AZ::Entity* const* begin() const;
[[nodiscard]] const AZ::Entity* const* end() const;
[[nodiscard]] const AZ::Entity* const* cbegin();
[[nodiscard]] const AZ::Entity* const* cend();
[[nodiscard]] const AZ::Entity* operator[](size_t n);
[[nodiscard]] const AZ::Entity* operator[](size_t n) const;
[[nodiscard]] size_t size() const;
[[nodiscard]] bool empty() const;
private:
AZ::Entity** m_begin;
@@ -195,7 +195,7 @@ namespace AzFramework
TmMsgCallback(const MsgCB& cb = NULL)
: m_cb(cb) {}
virtual void OnReceivedMsg(TmMsgPtr msg)
void OnReceivedMsg(TmMsgPtr msg) override
{
if (m_cb)
{
@@ -19,6 +19,8 @@ namespace UnitTest
{
public:
TestDebugDisplayRequests();
~TestDebugDisplayRequests() override = default;
const AZStd::vector<AZ::Vector3>& GetPoints() const;
void ClearPoints();
//! Returns the AABB of the points generated from received draw calls.
@@ -27,7 +29,9 @@ namespace UnitTest
// DebugDisplayRequests ...
void DrawWireBox(const AZ::Vector3& min, const AZ::Vector3& max) override;
void DrawSolidBox(const AZ::Vector3& min, const AZ::Vector3& max) override;
using AzFramework::DebugDisplayRequests::DrawWireQuad;
void DrawWireQuad(float width, float height) override;
using AzFramework::DebugDisplayRequests::DrawQuad;
void DrawQuad(float width, float height) override;
void DrawTriangles(const AZStd::vector<AZ::Vector3>& vertices, const AZ::Color& color) override;
void DrawTrianglesIndexed(const AZStd::vector<AZ::Vector3>& vertices, const AZStd::vector<AZ::u32>& indices, const AZ::Color& color) override;
@@ -9,8 +9,19 @@
#include <AzFramework/Viewport/ClickDetector.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
#include <AzCore/std/chrono/clocks.h>
namespace AzFramework
{
ClickDetector::ClickDetector()
{
m_timeNowFn = []
{
const auto now = AZStd::chrono::high_resolution_clock::now();
return AZStd::chrono::time_point_cast<AZStd::chrono::milliseconds>(now).time_since_epoch();
};
}
ClickDetector::ClickOutcome ClickDetector::DetectClick(const ClickEvent clickEvent, const ScreenVector& cursorDelta)
{
const auto previousDetectionState = m_detectionState;
@@ -26,11 +37,13 @@ namespace AzFramework
if (clickEvent == ClickEvent::Down)
{
const auto now = std::chrono::steady_clock::now();
const auto now = m_timeNowFn();
if (m_tryBeginTime)
{
const std::chrono::duration<float> diff = now - m_tryBeginTime.value();
if (diff.count() < m_doubleClickInterval)
using FloatingPointSeconds = AZStd::chrono::duration<float, AZStd::chrono::seconds::period>;
const auto diff = now - m_tryBeginTime.value();
if (FloatingPointSeconds(diff).count() < m_doubleClickInterval)
{
return ClickOutcome::Nil;
}
@@ -43,7 +56,8 @@ namespace AzFramework
}
else if (clickEvent == ClickEvent::Up)
{
const auto clickOutcome = [detectionState = m_detectionState] {
const auto clickOutcome = [detectionState = m_detectionState]
{
if (detectionState == DetectionState::WaitingForMove)
{
return ClickOutcome::Click;
@@ -66,4 +80,9 @@ namespace AzFramework
return ClickOutcome::Nil;
}
void ClickDetector::OverrideTimeNowFn(AZStd::function<AZStd::chrono::milliseconds()> timeNowFn)
{
m_timeNowFn = AZStd::move(timeNowFn);
}
} // namespace AzFramework
@@ -8,6 +8,7 @@
#pragma once
#include <AzCore/std/functional.h>
#include <AzCore/std/optional.h>
#include <chrono>
@@ -21,10 +22,9 @@ namespace AzFramework
//! (mouse down with movement and then mouse up).
class ClickDetector
{
//! Alias for recording time of mouse down events
using Time = std::chrono::time_point<std::chrono::steady_clock>;
public:
ClickDetector();
//! Internal representation of click event (map from external event for this when
//! calling DetectClick).
enum class ClickEvent
@@ -51,6 +51,10 @@ namespace AzFramework
void SetDoubleClickInterval(float doubleClickInterval);
//! Override the dead zone before a 'move' outcome will be triggered.
void SetDeadZone(float deadZone);
//! Override how the current time is retrieved.
//! This is helpful to override when it comes to simulating different passages of
//! time to avoid double click issues in tests for example.
void OverrideTimeNowFn(AZStd::function<AZStd::chrono::milliseconds()> timeNowFn);
private:
//! Internal state of ClickDetector based on incoming events.
@@ -65,7 +69,9 @@ namespace AzFramework
float m_deadZone = 2.0f; //!< How far to move before a click is cancelled (when Move will fire).
float m_doubleClickInterval = 0.4f; //!< Default double click interval, can be overridden.
DetectionState m_detectionState; //!< Internal state of ClickDetector.
AZStd::optional<Time> m_tryBeginTime; //!< Mouse down time (happens each mouse down, helps with double click handling).
//! Mouse down time (happens each mouse down, helps with double click handling).
AZStd::optional<AZStd::chrono::milliseconds> m_tryBeginTime;
AZStd::function<AZStd::chrono::milliseconds()> m_timeNowFn; //!< Interface to query the current time.
};
inline void ClickDetector::SetDoubleClickInterval(const float doubleClickInterval)
@@ -8,11 +8,10 @@
#pragma once
#include <AzCore/base.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzCore/RTTI/TypeInfoSimple.h>
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/RTTI/TypeInfoSimple.h>
#include <AzCore/base.h>
namespace AZ
{
@@ -27,7 +26,11 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenPoint, "{8472B6C2-527F-44FC-87F8-C226B1A57A97}");
ScreenPoint() = default;
ScreenPoint(int x, int y) : m_x(x), m_y(y) {}
ScreenPoint(int x, int y)
: m_x(x)
, m_y(y)
{
}
int m_x; //!< X screen position.
int m_y; //!< Y screen position.
@@ -42,7 +45,11 @@ namespace AzFramework
AZ_TYPE_INFO(ScreenVector, "{1EAA2C62-8FDB-4A28-9FE3-1FA4F1418894}");
ScreenVector() = default;
ScreenVector(int x, int y) : m_x(x), m_y(y) {}
ScreenVector(int x, int y)
: m_x(x)
, m_y(y)
{
}
int m_x; //!< X screen delta.
int m_y; //!< Y screen delta.
@@ -71,14 +78,14 @@ namespace AzFramework
inline const ScreenPoint operator+(const ScreenPoint& lhs, const ScreenVector& rhs)
{
ScreenPoint result{lhs};
ScreenPoint result{ lhs };
result += rhs;
return result;
}
inline const ScreenPoint operator-(const ScreenPoint& lhs, const ScreenVector& rhs)
{
ScreenPoint result{lhs};
ScreenPoint result{ lhs };
result -= rhs;
return result;
}
@@ -99,14 +106,14 @@ namespace AzFramework
inline const ScreenVector operator+(const ScreenVector& lhs, const ScreenVector& rhs)
{
ScreenVector result{lhs};
ScreenVector result{ lhs };
result += rhs;
return result;
}
inline const ScreenVector operator-(const ScreenVector& lhs, const ScreenVector& rhs)
{
ScreenVector result{lhs};
ScreenVector result{ lhs };
result -= rhs;
return result;
}
@@ -131,23 +138,25 @@ namespace AzFramework
return !operator==(lhs, rhs);
}
inline ScreenVector& operator*=(ScreenVector& lhs, const float rhs)
{
lhs.m_x = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_x) * rhs));
lhs.m_y = aznumeric_cast<int>(AZStd::lround(aznumeric_cast<float>(lhs.m_y) * rhs));
return lhs;
}
inline const ScreenVector operator*(const ScreenVector& lhs, const float rhs)
{
ScreenVector result{ lhs };
result *= rhs;
return result;
}
inline float ScreenVectorLength(const ScreenVector& screenVector)
{
return aznumeric_cast<float>(AZStd::sqrt(screenVector.m_x * screenVector.m_x + screenVector.m_y * screenVector.m_y));
}
inline ScreenPoint ScreenPointFromNDC(const AZ::Vector3& screenNDC, const AZ::Vector2& viewportSize)
{
return ScreenPoint(
aznumeric_caster(AZStd::round(screenNDC.GetX() * viewportSize.GetX())),
aznumeric_caster(AZStd::round((1.0f - screenNDC.GetY()) * viewportSize.GetY())));
}
inline AZ::Vector2 NDCFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize)
{
return AZ::Vector2(aznumeric_cast<float>(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast<float>(screenPoint.m_y)) / viewportSize;
}
//! Return an AZ::Vector2 from a ScreenPoint.
inline AZ::Vector2 Vector2FromScreenPoint(const ScreenPoint& screenPoint)
{
@@ -45,10 +45,10 @@ namespace AzFramework
//! All current and added controllers will be registered with this viewport.
void RegisterViewportContext(ViewportId viewport) override;
//! Unregisters a Viewport from this list and all associated controllers.
void UnregisterViewportContext(ViewportId viewport);
void UnregisterViewportContext(ViewportId viewport) override;
//! All ViewportControllerLists have a priority of Custom to ensure
//! that they receive events at all priorities from any parent controllers.
AzFramework::ViewportControllerPriority GetPriority() const { return ViewportControllerPriority::DispatchToAllPriorities; }
AzFramework::ViewportControllerPriority GetPriority() const override { return ViewportControllerPriority::DispatchToAllPriorities; }
//! Returns true if this controller list is enabled, i.e.
//! it is accepting and forwarding input and update events to its children.
bool IsEnabled() const;
@@ -23,7 +23,7 @@ namespace AzFramework
// y-up, z into the screen, x-left
//
// x -> -x
// y -> z
// y -> z
// z -> y
//
// the same transform can be used to go to/from z-up - the only difference is the order of
@@ -37,15 +37,15 @@ namespace AzFramework
// yaw = AZ::Matrix4x4::CreateRotationZ(AZ::DegToRad(180.0f));
// conversion = pitch * yaw
return AZ::Matrix4x4::CreateFromColumns(
AZ::Vector4(-1.0f, 0.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 1.0f, .0f),
AZ::Vector4(0.0f, 1.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 0.0f, 1.0f));
AZ::Vector4(-1.0f, 0.0f, 0.0f, 0.0f), AZ::Vector4(0.0f, 0.0f, 1.0f, 0.0f), AZ::Vector4(0.0f, 1.0f, 0.0f, 0.0f),
AZ::Vector4(0.0f, 0.0f, 0.0f, 1.0f));
}
AZ::Matrix4x4 CameraTransform(const CameraState& cameraState)
{
return AZ::Matrix4x4::CreateFromColumns(
AZ::Vector3ToVector4(cameraState.m_side), AZ::Vector3ToVector4(cameraState.m_forward),
AZ::Vector3ToVector4(cameraState.m_up), AZ::Vector3ToVector4(cameraState.m_position, 1.0f));
AZ::Vector3ToVector4(cameraState.m_side), AZ::Vector3ToVector4(cameraState.m_forward), AZ::Vector3ToVector4(cameraState.m_up),
AZ::Vector3ToVector4(cameraState.m_position, 1.0f));
}
AZ::Matrix4x4 CameraView(const CameraState& cameraState)
@@ -63,8 +63,7 @@ namespace AzFramework
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState)
{
return AZ::Matrix4x4::CreateProjection(
cameraState.VerticalFovRadian(), AspectRatio(cameraState.m_viewportSize), cameraState.m_nearClip,
cameraState.m_farClip);
cameraState.VerticalFovRadian(), AspectRatio(cameraState.m_viewportSize), cameraState.m_nearClip, cameraState.m_farClip);
}
AZ::Matrix4x4 InverseCameraProjection(const CameraState& cameraState)
@@ -93,12 +92,11 @@ namespace AzFramework
const auto cameraWorldTransform = AZ::Transform::CreateFromMatrix3x3AndTranslation(
AZ::Matrix3x3::CreateFromMatrix4x4(worldFromView), worldFromView.GetTranslation());
return AZ::ViewFrustumAttributes(
cameraWorldTransform, AspectRatio(cameraState.m_viewportSize), cameraState.m_fovOrZoom,
cameraState.m_nearClip, cameraState.m_farClip);
cameraWorldTransform, AspectRatio(cameraState.m_viewportSize), cameraState.m_fovOrZoom, cameraState.m_nearClip,
cameraState.m_farClip);
}
AZ::Vector3 WorldToScreenNDC(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection)
AZ::Vector3 WorldToScreenNdc(const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection)
{
// transform the world space position to clip space
const auto clipSpacePosition = cameraProjection * cameraView * AZ::Vector3ToVector4(worldPosition, 1.0f);
@@ -108,25 +106,24 @@ namespace AzFramework
return (AZ::Vector4ToVector3(ndcPosition) + AZ::Vector3::CreateOne()) * 0.5f;
}
ScreenPoint WorldToScreen(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection,
const AZ::Vector3& worldPosition,
const AZ::Matrix4x4& cameraView,
const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize)
{
const auto ndcNormalizedPosition = WorldToScreenNDC(worldPosition, cameraView, cameraProjection);
const auto ndcNormalizedPosition = WorldToScreenNdc(worldPosition, cameraView, cameraProjection);
// scale ndc position by screen dimensions to return screen position
return ScreenPointFromNDC(ndcNormalizedPosition, viewportSize);
return ScreenPointFromNdc(AZ::Vector3ToVector2(ndcNormalizedPosition), viewportSize);
}
ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState)
{
return WorldToScreen(
worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
return WorldToScreen(worldPosition, CameraView(cameraState), CameraProjection(cameraState), cameraState.m_viewportSize);
}
AZ::Vector3 ScreenNDCToWorld(
const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection)
AZ::Vector3 ScreenNdcToWorld(
const AZ::Vector2& normalizedScreenPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection)
{
// convert screen space coordinates from <0, 1> to <-1,1> range
const auto ndcPosition = normalizedScreenPosition * 2.0f - AZ::Vector2::CreateOne();
@@ -142,18 +139,19 @@ namespace AzFramework
}
AZ::Vector3 ScreenToWorld(
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize)
const ScreenPoint& screenPosition,
const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection,
const AZ::Vector2& viewportSize)
{
const auto normalizedScreenPosition = NDCFromScreenPoint(screenPosition, viewportSize);
const auto normalizedScreenPosition = NdcFromScreenPoint(screenPosition, viewportSize);
return ScreenNDCToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection);
return ScreenNdcToWorld(normalizedScreenPosition, inverseCameraView, inverseCameraProjection);
}
AZ::Vector3 ScreenToWorld(const ScreenPoint& screenPosition, const CameraState& cameraState)
{
return ScreenToWorld(
screenPosition, InverseCameraView(cameraState), InverseCameraProjection(cameraState),
cameraState.m_viewportSize);
screenPosition, InverseCameraView(cameraState), InverseCameraProjection(cameraState), cameraState.m_viewportSize);
}
} // namespace AzFramework
@@ -8,26 +8,42 @@
#pragma once
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Math/Vector2.h>
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Viewport/ScreenGeometry.h>
namespace AZ
{
class Frustum;
class Matrix4x4;
class Vector3;
struct ViewFrustumAttributes;
} // namespace AZ
namespace AzFramework
{
struct CameraState;
struct ScreenPoint;
struct ViewportInfo;
//! Projects a position in world space to screen space normalized device coordinates for the given camera.
AZ::Vector3 WorldToScreenNDC(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection);
//! Returns a position in screen space (in the range [0-viewportSize.x, 0-viewportSize.y]) from normalized device
//! coordinates (in the range 0.0-1.0).
inline ScreenPoint ScreenPointFromNdc(const AZ::Vector2& screenNdc, const AZ::Vector2& viewportSize)
{
return ScreenPoint(
aznumeric_cast<int>(AZStd::lround(screenNdc.GetX() * viewportSize.GetX())),
aznumeric_cast<int>(AZStd::lround((1.0f - screenNdc.GetY()) * viewportSize.GetY())));
}
//! Returns a position in normalized device coordinates (in the range [0.0-1.0, 0.0-1.0]) from a
//! screen space position (in the range [0-viewportSize.x, 0-viewportSize.y]).
inline AZ::Vector2 NdcFromScreenPoint(const ScreenPoint& screenPoint, const AZ::Vector2& viewportSize)
{
return AZ::Vector2(aznumeric_cast<float>(screenPoint.m_x), viewportSize.GetY() - aznumeric_cast<float>(screenPoint.m_y)) /
viewportSize;
}
//! Projects a position in world space to screen space normalized device coordinates for the given camera.
AZ::Vector3 WorldToScreenNdc(const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection);
//! Projects a position in world space to screen space for the given camera.
ScreenPoint WorldToScreen(const AZ::Vector3& worldPosition, const CameraState& cameraState);
@@ -35,7 +51,9 @@ namespace AzFramework
//! Overload of WorldToScreen that accepts camera values that can be precomputed if this function
//! is called many times in a loop.
ScreenPoint WorldToScreen(
const AZ::Vector3& worldPosition, const AZ::Matrix4x4& cameraView, const AZ::Matrix4x4& cameraProjection,
const AZ::Vector3& worldPosition,
const AZ::Matrix4x4& cameraView,
const AZ::Matrix4x4& cameraProjection,
const AZ::Vector2& viewportSize);
//! Unprojects a position in screen space pixel coordinates to world space.
@@ -45,14 +63,15 @@ namespace AzFramework
//! Overload of ScreenToWorld that accepts camera values that can be precomputed if this function
//! is called many times in a loop.
AZ::Vector3 ScreenToWorld(
const ScreenPoint& screenPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection, const AZ::Vector2& viewportSize);
const ScreenPoint& screenPosition,
const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection,
const AZ::Vector2& viewportSize);
//! Unprojects a position in screen space normalized device coordinates to world space.
//! Note: The position returned will be on the near clip plane of the camera in world space.
AZ::Vector3 ScreenNDCToWorld(
const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView,
const AZ::Matrix4x4& inverseCameraProjection);
AZ::Vector3 ScreenNdcToWorld(
const AZ::Vector2& ndcPosition, const AZ::Matrix4x4& inverseCameraView, const AZ::Matrix4x4& inverseCameraProjection);
//! Returns the camera projection for the current camera state.
AZ::Matrix4x4 CameraProjection(const CameraState& cameraState);
@@ -343,6 +343,8 @@ set(FILES
Input/Channels/InputChannelQuaternion.h
Input/Contexts/InputContext.cpp
Input/Contexts/InputContext.h
Input/Contexts/InputContextComponent.cpp
Input/Contexts/InputContextComponent.h
Input/Devices/InputDevice.cpp
Input/Devices/InputDevice.h
Input/Devices/InputDeviceId.cpp
+1 -2
View File
@@ -29,7 +29,6 @@ ly_add_target(
AZ::AzCore
PUBLIC
AZ::GridMate
3rdParty::md5
3rdParty::zlib
3rdParty::zstd
3rdParty::lz4
@@ -96,4 +95,4 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
endif()
endif()
endif()
@@ -6,29 +6,26 @@
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include "Application_Linux_xcb.h"
#include <AzFramework/XcbApplication.h>
#include <AzFramework/XcbEventHandler.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
////////////////////////////////////////////////////////////////////////////////////////////////
class LinuxXcbConnectionManagerImpl
: public LinuxXcbConnectionManagerBus::Handler
class XcbConnectionManagerImpl
: public XcbConnectionManagerBus::Handler
{
public:
LinuxXcbConnectionManagerImpl()
XcbConnectionManagerImpl()
{
m_xcbConnection = xcb_connect(nullptr, nullptr);
AZ_Error("ApplicationLinux", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
LinuxXcbConnectionManagerBus::Handler::BusConnect();
AZ_Error("Application", m_xcbConnection != nullptr, "Unable to connect to X11 Server.");
XcbConnectionManagerBus::Handler::BusConnect();
}
~LinuxXcbConnectionManagerImpl()
~XcbConnectionManagerImpl() override
{
LinuxXcbConnectionManagerBus::Handler::BusDisconnect();
XcbConnectionManagerBus::Handler::BusDisconnect();
xcb_disconnect(m_xcbConnection);
}
@@ -42,53 +39,51 @@ namespace AzFramework
};
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux_xcb::ApplicationLinux_xcb()
XcbApplication::XcbApplication()
{
LinuxLifecycleEvents::Bus::Handler::BusConnect();
m_xcbConnectionManager = AZStd::make_unique<LinuxXcbConnectionManagerImpl>();
if (LinuxXcbConnectionManagerInterface::Get() == nullptr)
m_xcbConnectionManager = AZStd::make_unique<XcbConnectionManagerImpl>();
if (XcbConnectionManagerInterface::Get() == nullptr)
{
LinuxXcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
XcbConnectionManagerInterface::Register(m_xcbConnectionManager.get());
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
ApplicationLinux_xcb::~ApplicationLinux_xcb()
XcbApplication::~XcbApplication()
{
if (LinuxXcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
if (XcbConnectionManagerInterface::Get() == m_xcbConnectionManager.get())
{
LinuxXcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
XcbConnectionManagerInterface::Unregister(m_xcbConnectionManager.get());
}
m_xcbConnectionManager.reset();
LinuxLifecycleEvents::Bus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ApplicationLinux_xcb::PumpSystemEventLoopOnce()
void XcbApplication::PumpSystemEventLoopOnce()
{
if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection())
{
if (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection))
{
LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event);
XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event);
free(event);
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void ApplicationLinux_xcb::PumpSystemEventLoopUntilEmpty()
void XcbApplication::PumpSystemEventLoopUntilEmpty()
{
if (xcb_connection_t* xcbConnection = m_xcbConnectionManager->GetXcbConnection())
{
while (xcb_generic_event_t* event = xcb_poll_for_event(xcbConnection))
{
LinuxXcbEventHandlerBus::Broadcast(&LinuxXcbEventHandlerBus::Events::HandleXcbEvent, event);
XcbEventHandlerBus::Broadcast(&XcbEventHandlerBus::Events::HandleXcbEvent, event);
free(event);
}
}
}
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -5,27 +5,24 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/XcbConnectionManager.h>
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
////////////////////////////////////////////////////////////////////////////////////////////////
class ApplicationLinux_xcb
class XcbApplication
: public Application::Implementation
, public LinuxLifecycleEvents::Bus::Handler
{
public:
////////////////////////////////////////////////////////////////////////////////////////////
AZ_CLASS_ALLOCATOR(ApplicationLinux_xcb, AZ::SystemAllocator, 0);
ApplicationLinux_xcb();
~ApplicationLinux_xcb() override;
AZ_CLASS_ALLOCATOR(XcbApplication, AZ::SystemAllocator, 0);
XcbApplication();
~XcbApplication() override;
////////////////////////////////////////////////////////////////////////////////////////////
// Application::Implementation
@@ -33,9 +30,6 @@ namespace AzFramework
void PumpSystemEventLoopUntilEmpty() override;
private:
AZStd::unique_ptr<LinuxXcbConnectionManager> m_xcbConnectionManager;
AZStd::unique_ptr<XcbConnectionManager> m_xcbConnectionManager;
};
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -0,0 +1,42 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/RTTI/RTTI.h>
#include <xcb/xcb.h>
namespace AzFramework
{
class XcbConnectionManager
{
public:
AZ_RTTI(XcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~XcbConnectionManager() = default;
virtual xcb_connection_t* GetXcbConnection() const = 0;
};
class XcbConnectionManagerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using XcbConnectionManagerBus = AZ::EBus<XcbConnectionManager, XcbConnectionManagerBusTraits>;
using XcbConnectionManagerInterface = AZ::Interface<XcbConnectionManager>;
} // namespace AzFramework
@@ -0,0 +1,40 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/RTTI/RTTI.h>
#include <xcb/xcb.h>
namespace AzFramework
{
class XcbEventHandler
{
public:
AZ_RTTI(XcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~XcbEventHandler() = default;
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
};
class XcbEventHandlerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using XcbEventHandlerBus = AZ::EBus<XcbEventHandler, XcbEventHandlerBusTraits>;
} // namespace AzFramework
@@ -0,0 +1,271 @@
/*
* 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/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/XcbEventHandler.h>
#include <AzFramework/XcbConnectionManager.h>
#include <AzFramework/XcbInputDeviceKeyboard.h>
#define explicit ExplicitIsACXXKeyword
#include <xcb/xkb.h>
#undef explicit
#include <xkbcommon/xkbcommon-keysyms.h>
#include <xkbcommon/xkbcommon.h>
#include <xkbcommon/xkbcommon-x11.h>
namespace AzFramework
{
XcbInputDeviceKeyboard::XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice)
: InputDeviceKeyboard::Implementation(inputDevice)
{
XcbEventHandlerBus::Handler::BusConnect();
auto* interface = AzFramework::XcbConnectionManagerInterface::Get();
if (!interface)
{
AZ_Warning("ApplicationLinux", false, "XCB interface not available");
return;
}
auto* connection = interface->GetXcbConnection();
if (!connection)
{
AZ_Warning("ApplicationLinux", false, "XCB connection not available");
return;
}
XcbStdFreePtr<xcb_xkb_use_extension_reply_t> xkbUseExtensionReply{
xcb_xkb_use_extension_reply(connection, xcb_xkb_use_extension(connection, 1, 0), nullptr)
};
if (!xkbUseExtensionReply)
{
AZ_Warning("ApplicationLinux", false, "Failed to initialize the xkb extension");
return;
}
if (!xkbUseExtensionReply->supported)
{
AZ_Warning("ApplicationLinux", false, "The X server does not support the xkb extension");
return;
}
m_coreDeviceId = xkb_x11_get_core_keyboard_device_id(connection);
m_xkbContext.reset(xkb_context_new(XKB_CONTEXT_NO_FLAGS));
m_xkbKeymap.reset(xkb_x11_keymap_new_from_device(m_xkbContext.get(), connection, m_coreDeviceId, XKB_KEYMAP_COMPILE_NO_FLAGS));
m_xkbState.reset(xkb_x11_state_new_from_device(m_xkbKeymap.get(), connection, m_coreDeviceId));
m_initialized = true;
}
bool XcbInputDeviceKeyboard::IsConnected() const
{
auto* connection = AzFramework::XcbConnectionManagerInterface::Get()->GetXcbConnection();
return connection && !xcb_connection_has_error(connection);
}
bool XcbInputDeviceKeyboard::HasTextEntryStarted() const
{
return false;
}
void XcbInputDeviceKeyboard::TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options)
{
}
void XcbInputDeviceKeyboard::TextEntryStop()
{
}
void XcbInputDeviceKeyboard::TickInputDevice()
{
ProcessRawEventQueues();
}
void XcbInputDeviceKeyboard::HandleXcbEvent(xcb_generic_event_t* event)
{
if (!m_initialized)
{
return;
}
switch (event->response_type & ~0x80)
{
case XCB_KEY_PRESS:
{
auto* keyPress = reinterpret_cast<xcb_key_press_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyPress->detail);
if (key)
{
QueueRawKeyEvent(*key, true);
}
break;
}
case XCB_KEY_RELEASE:
{
auto* keyRelease = reinterpret_cast<xcb_key_release_event_t*>(event);
const InputChannelId* key = InputChannelFromKeyEvent(keyRelease->detail);
if (key)
{
QueueRawKeyEvent(*key, false);
}
break;
}
}
}
[[nodiscard]] const InputChannelId* XcbInputDeviceKeyboard::InputChannelFromKeyEvent(xcb_keycode_t code) const
{
const xcb_keysym_t keysym = xkb_state_key_get_one_sym(m_xkbState.get(), code);
switch(keysym)
{
case XKB_KEY_0: return &InputDeviceKeyboard::Key::Alphanumeric0;
case XKB_KEY_1: return &InputDeviceKeyboard::Key::Alphanumeric1;
case XKB_KEY_2: return &InputDeviceKeyboard::Key::Alphanumeric2;
case XKB_KEY_3: return &InputDeviceKeyboard::Key::Alphanumeric3;
case XKB_KEY_4: return &InputDeviceKeyboard::Key::Alphanumeric4;
case XKB_KEY_5: return &InputDeviceKeyboard::Key::Alphanumeric5;
case XKB_KEY_6: return &InputDeviceKeyboard::Key::Alphanumeric6;
case XKB_KEY_7: return &InputDeviceKeyboard::Key::Alphanumeric7;
case XKB_KEY_8: return &InputDeviceKeyboard::Key::Alphanumeric8;
case XKB_KEY_9: return &InputDeviceKeyboard::Key::Alphanumeric9;
case XKB_KEY_A:
case XKB_KEY_a: return &InputDeviceKeyboard::Key::AlphanumericA;
case XKB_KEY_B:
case XKB_KEY_b: return &InputDeviceKeyboard::Key::AlphanumericB;
case XKB_KEY_C:
case XKB_KEY_c: return &InputDeviceKeyboard::Key::AlphanumericC;
case XKB_KEY_D:
case XKB_KEY_d: return &InputDeviceKeyboard::Key::AlphanumericD;
case XKB_KEY_E:
case XKB_KEY_e: return &InputDeviceKeyboard::Key::AlphanumericE;
case XKB_KEY_F:
case XKB_KEY_f: return &InputDeviceKeyboard::Key::AlphanumericF;
case XKB_KEY_G:
case XKB_KEY_g: return &InputDeviceKeyboard::Key::AlphanumericG;
case XKB_KEY_H:
case XKB_KEY_h: return &InputDeviceKeyboard::Key::AlphanumericH;
case XKB_KEY_I:
case XKB_KEY_i: return &InputDeviceKeyboard::Key::AlphanumericI;
case XKB_KEY_J:
case XKB_KEY_j: return &InputDeviceKeyboard::Key::AlphanumericJ;
case XKB_KEY_K:
case XKB_KEY_k: return &InputDeviceKeyboard::Key::AlphanumericK;
case XKB_KEY_L:
case XKB_KEY_l: return &InputDeviceKeyboard::Key::AlphanumericL;
case XKB_KEY_M:
case XKB_KEY_m: return &InputDeviceKeyboard::Key::AlphanumericM;
case XKB_KEY_N:
case XKB_KEY_n: return &InputDeviceKeyboard::Key::AlphanumericN;
case XKB_KEY_O:
case XKB_KEY_o: return &InputDeviceKeyboard::Key::AlphanumericO;
case XKB_KEY_P:
case XKB_KEY_p: return &InputDeviceKeyboard::Key::AlphanumericP;
case XKB_KEY_Q:
case XKB_KEY_q: return &InputDeviceKeyboard::Key::AlphanumericQ;
case XKB_KEY_R:
case XKB_KEY_r: return &InputDeviceKeyboard::Key::AlphanumericR;
case XKB_KEY_S:
case XKB_KEY_s: return &InputDeviceKeyboard::Key::AlphanumericS;
case XKB_KEY_T:
case XKB_KEY_t: return &InputDeviceKeyboard::Key::AlphanumericT;
case XKB_KEY_U:
case XKB_KEY_u: return &InputDeviceKeyboard::Key::AlphanumericU;
case XKB_KEY_V:
case XKB_KEY_v: return &InputDeviceKeyboard::Key::AlphanumericV;
case XKB_KEY_W:
case XKB_KEY_w: return &InputDeviceKeyboard::Key::AlphanumericW;
case XKB_KEY_X:
case XKB_KEY_x: return &InputDeviceKeyboard::Key::AlphanumericX;
case XKB_KEY_Y:
case XKB_KEY_y: return &InputDeviceKeyboard::Key::AlphanumericY;
case XKB_KEY_Z:
case XKB_KEY_z: return &InputDeviceKeyboard::Key::AlphanumericZ;
case XKB_KEY_BackSpace: return &InputDeviceKeyboard::Key::EditBackspace;
case XKB_KEY_Caps_Lock: return &InputDeviceKeyboard::Key::EditCapsLock;
case XKB_KEY_Return: return &InputDeviceKeyboard::Key::EditEnter;
case XKB_KEY_space: return &InputDeviceKeyboard::Key::EditSpace;
case XKB_KEY_Tab: return &InputDeviceKeyboard::Key::EditTab;
case XKB_KEY_Escape: return &InputDeviceKeyboard::Key::Escape;
case XKB_KEY_F1: return &InputDeviceKeyboard::Key::Function01;
case XKB_KEY_F2: return &InputDeviceKeyboard::Key::Function02;
case XKB_KEY_F3: return &InputDeviceKeyboard::Key::Function03;
case XKB_KEY_F4: return &InputDeviceKeyboard::Key::Function04;
case XKB_KEY_F5: return &InputDeviceKeyboard::Key::Function05;
case XKB_KEY_F6: return &InputDeviceKeyboard::Key::Function06;
case XKB_KEY_F7: return &InputDeviceKeyboard::Key::Function07;
case XKB_KEY_F8: return &InputDeviceKeyboard::Key::Function08;
case XKB_KEY_F9: return &InputDeviceKeyboard::Key::Function09;
case XKB_KEY_F10: return &InputDeviceKeyboard::Key::Function10;
case XKB_KEY_F11: return &InputDeviceKeyboard::Key::Function11;
case XKB_KEY_F12: return &InputDeviceKeyboard::Key::Function12;
case XKB_KEY_F13: return &InputDeviceKeyboard::Key::Function13;
case XKB_KEY_F14: return &InputDeviceKeyboard::Key::Function14;
case XKB_KEY_F15: return &InputDeviceKeyboard::Key::Function15;
case XKB_KEY_F16: return &InputDeviceKeyboard::Key::Function16;
case XKB_KEY_F17: return &InputDeviceKeyboard::Key::Function17;
case XKB_KEY_F18: return &InputDeviceKeyboard::Key::Function18;
case XKB_KEY_F19: return &InputDeviceKeyboard::Key::Function19;
case XKB_KEY_F20: return &InputDeviceKeyboard::Key::Function20;
case XKB_KEY_Alt_L: return &InputDeviceKeyboard::Key::ModifierAltL;
case XKB_KEY_Alt_R: return &InputDeviceKeyboard::Key::ModifierAltR;
case XKB_KEY_Control_L: return &InputDeviceKeyboard::Key::ModifierCtrlL;
case XKB_KEY_Control_R: return &InputDeviceKeyboard::Key::ModifierCtrlR;
case XKB_KEY_Shift_L: return &InputDeviceKeyboard::Key::ModifierShiftL;
case XKB_KEY_Shift_R: return &InputDeviceKeyboard::Key::ModifierShiftR;
case XKB_KEY_Super_L: return &InputDeviceKeyboard::Key::ModifierSuperL;
case XKB_KEY_Super_R: return &InputDeviceKeyboard::Key::ModifierSuperR;
case XKB_KEY_Down: return &InputDeviceKeyboard::Key::NavigationArrowDown;
case XKB_KEY_Left: return &InputDeviceKeyboard::Key::NavigationArrowLeft;
case XKB_KEY_Right: return &InputDeviceKeyboard::Key::NavigationArrowRight;
case XKB_KEY_Up: return &InputDeviceKeyboard::Key::NavigationArrowUp;
case XKB_KEY_Delete: return &InputDeviceKeyboard::Key::NavigationDelete;
case XKB_KEY_End: return &InputDeviceKeyboard::Key::NavigationEnd;
case XKB_KEY_Home: return &InputDeviceKeyboard::Key::NavigationHome;
case XKB_KEY_Insert: return &InputDeviceKeyboard::Key::NavigationInsert;
case XKB_KEY_Page_Down: return &InputDeviceKeyboard::Key::NavigationPageDown;
case XKB_KEY_Page_Up: return &InputDeviceKeyboard::Key::NavigationPageUp;
case XKB_KEY_Num_Lock: return &InputDeviceKeyboard::Key::NumLock;
case XKB_KEY_KP_0: return &InputDeviceKeyboard::Key::NumPad0;
case XKB_KEY_KP_1: return &InputDeviceKeyboard::Key::NumPad1;
case XKB_KEY_KP_2: return &InputDeviceKeyboard::Key::NumPad2;
case XKB_KEY_KP_3: return &InputDeviceKeyboard::Key::NumPad3;
case XKB_KEY_KP_4: return &InputDeviceKeyboard::Key::NumPad4;
case XKB_KEY_KP_5: return &InputDeviceKeyboard::Key::NumPad5;
case XKB_KEY_KP_6: return &InputDeviceKeyboard::Key::NumPad6;
case XKB_KEY_KP_7: return &InputDeviceKeyboard::Key::NumPad7;
case XKB_KEY_KP_8: return &InputDeviceKeyboard::Key::NumPad8;
case XKB_KEY_KP_9: return &InputDeviceKeyboard::Key::NumPad9;
case XKB_KEY_KP_Add: return &InputDeviceKeyboard::Key::NumPadAdd;
case XKB_KEY_KP_Decimal: return &InputDeviceKeyboard::Key::NumPadDecimal;
case XKB_KEY_KP_Divide: return &InputDeviceKeyboard::Key::NumPadDivide;
case XKB_KEY_KP_Enter: return &InputDeviceKeyboard::Key::NumPadEnter;
case XKB_KEY_KP_Multiply: return &InputDeviceKeyboard::Key::NumPadMultiply;
case XKB_KEY_KP_Subtract: return &InputDeviceKeyboard::Key::NumPadSubtract;
case XKB_KEY_apostrophe: return &InputDeviceKeyboard::Key::PunctuationApostrophe;
case XKB_KEY_backslash: return &InputDeviceKeyboard::Key::PunctuationBackslash;
case XKB_KEY_bracketleft: return &InputDeviceKeyboard::Key::PunctuationBracketL;
case XKB_KEY_bracketright: return &InputDeviceKeyboard::Key::PunctuationBracketR;
case XKB_KEY_comma: return &InputDeviceKeyboard::Key::PunctuationComma;
case XKB_KEY_equal: return &InputDeviceKeyboard::Key::PunctuationEquals;
case XKB_KEY_hyphen: return &InputDeviceKeyboard::Key::PunctuationHyphen;
case XKB_KEY_period: return &InputDeviceKeyboard::Key::PunctuationPeriod;
case XKB_KEY_semicolon: return &InputDeviceKeyboard::Key::PunctuationSemicolon;
case XKB_KEY_slash: return &InputDeviceKeyboard::Key::PunctuationSlash;
case XKB_KEY_grave:
case XKB_KEY_asciitilde: return &InputDeviceKeyboard::Key::PunctuationTilde;
case XKB_KEY_ISO_Group_Shift: return &InputDeviceKeyboard::Key::SupplementaryISO;
case XKB_KEY_Pause: return &InputDeviceKeyboard::Key::WindowsSystemPause;
case XKB_KEY_Print: return &InputDeviceKeyboard::Key::WindowsSystemPrint;
case XKB_KEY_Scroll_Lock: return &InputDeviceKeyboard::Key::WindowsSystemScrollLock;
default: return nullptr;
}
}
} // namespace AzFramework
@@ -0,0 +1,46 @@
/*
* 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/Input/Devices/Keyboard/InputDeviceKeyboard.h>
#include <AzFramework/XcbEventHandler.h>
#include <AzFramework/XcbInterface.h>
#include <xcb/xcb.h>
#include <xkbcommon/xkbcommon.h>
namespace AzFramework
{
class XcbInputDeviceKeyboard
: public InputDeviceKeyboard::Implementation
, public XcbEventHandlerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(XcbInputDeviceKeyboard, AZ::SystemAllocator, 0);
using InputDeviceKeyboard::Implementation::Implementation;
XcbInputDeviceKeyboard(InputDeviceKeyboard& inputDevice);
bool IsConnected() const override;
bool HasTextEntryStarted() const override;
void TextEntryStart(const InputDeviceKeyboard::VirtualKeyboardOptions& options) override;
void TextEntryStop() override;
void TickInputDevice() override;
void HandleXcbEvent(xcb_generic_event_t* event) override;
private:
[[nodiscard]] const InputChannelId* InputChannelFromKeyEvent(xcb_keycode_t code) const;
XcbUniquePtr<xkb_context, xkb_context_unref> m_xkbContext;
XcbUniquePtr<xkb_keymap, xkb_keymap_unref> m_xkbKeymap;
XcbUniquePtr<xkb_state, xkb_state_unref> m_xkbState;
int m_coreDeviceId{-1};
bool m_initialized{false};
};
} // namespace AzFramework
@@ -0,0 +1,38 @@
/*
* 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
*
*/
#pragma once
#include <xcb/xcb.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace AzFramework
{
// @brief Wrap a function pointer in a type
// This serves as a convenient way to wrap a function pointer in a given
// type. That type can then be used in a `unique_ptr` or `shared_ptr`.
// Using a type instead of a function pointer by value prevents the need to
// copy the pointer when copying the smart poiner.
template<auto Callable>
struct XcbDeleterFreeFunctionWrapper
{
using value_type = decltype(Callable);
static constexpr value_type s_value = Callable;
constexpr operator value_type() const noexcept
{
return s_value;
}
};
template<typename T, auto fn>
using XcbUniquePtr = AZStd::unique_ptr<T, XcbDeleterFreeFunctionWrapper<fn>>;
template<typename T>
using XcbStdFreePtr = XcbUniquePtr<T, ::free>;
} // namespace AzFramework
@@ -6,41 +6,37 @@
*
*/
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <xcb/xcb.h>
#include <AzFramework/XcbNativeWindow.h>
#include <AzFramework/XcbConnectionManager.h>
#include "NativeWindow_Linux_xcb.h"
#include <xcb/xcb.h>
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
[[maybe_unused]] const char LinuxXcbErrorWindow[] = "NativeWindow_Linux_xcb";
[[maybe_unused]] const char XcbErrorWindow[] = "XcbNativeWindow";
static constexpr uint8_t s_XcbFormatDataSize = 32; // Format indicator for xcb for client messages
static constexpr uint16_t s_DefaultXcbWindowBorderWidth = 4; // The default border with in pixels if a border was specified
static constexpr uint8_t s_XcbResponseTypeMask = 0x7f; // Mask to extract the specific event type from an xcb event
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowImpl_Linux_xcb::NativeWindowImpl_Linux_xcb()
XcbNativeWindow::XcbNativeWindow()
: NativeWindow::Implementation()
{
if (auto xcbConnectionManager = AzFramework::LinuxXcbConnectionManagerInterface::Get();
if (auto xcbConnectionManager = AzFramework::XcbConnectionManagerInterface::Get();
xcbConnectionManager != nullptr)
{
m_xcbConnection = xcbConnectionManager->GetXcbConnection();
}
AZ_Error(LinuxXcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection");
AZ_Error(XcbErrorWindow, m_xcbConnection != nullptr, "Unable to get XCB Connection");
}
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowImpl_Linux_xcb::~NativeWindowImpl_Linux_xcb()
{
}
XcbNativeWindow::~XcbNativeWindow() = default;
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::InitWindow(const AZStd::string& title,
void XcbNativeWindow::InitWindow(const AZStd::string& title,
const WindowGeometry& geometry,
const WindowStyleMasks& styleMasks)
{
@@ -62,8 +58,16 @@ namespace AzFramework
uint32_t eventMask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
const uint32_t interestedEvents =
XCB_EVENT_MASK_STRUCTURE_NOTIFY
| XCB_EVENT_MASK_BUTTON_PRESS
| XCB_EVENT_MASK_BUTTON_RELEASE
| XCB_EVENT_MASK_KEY_PRESS
| XCB_EVENT_MASK_KEY_RELEASE
| XCB_EVENT_MASK_POINTER_MOTION
;
uint32_t valueList[] = { xcbRootScreen->black_pixel,
XCB_EVENT_MASK_STRUCTURE_NOTIFY };
interestedEvents };
xcb_void_cookie_t xcbCheckResult;
@@ -90,13 +94,13 @@ namespace AzFramework
xcb_intern_atom_cookie_t cookieProtocol = xcb_intern_atom(m_xcbConnection, 1, strlen(wmProtocolString), wmProtocolString);
xcb_intern_atom_reply_t* replyProtocol = xcb_intern_atom_reply(m_xcbConnection, cookieProtocol, nullptr);
AZ_Error(LinuxXcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString);
AZ_Error(XcbErrorWindow, replyProtocol != nullptr, "Unable to query xcb '%s' atom", wmProtocolString);
m_xcbAtomProtocols = replyProtocol->atom;
const static char* wmDeleteWindowString = "WM_DELETE_WINDOW";
xcb_intern_atom_cookie_t cookieDeleteWindow = xcb_intern_atom(m_xcbConnection, 0, strlen(wmDeleteWindowString), wmDeleteWindowString);
xcb_intern_atom_reply_t* replyDeleteWindow = xcb_intern_atom_reply(m_xcbConnection, cookieDeleteWindow, nullptr);
AZ_Error(LinuxXcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString);
AZ_Error(XcbErrorWindow, replyDeleteWindow != nullptr, "Unable to query xcb '%s' atom", wmDeleteWindowString);
m_xcbAtomDeleteWindow = replyDeleteWindow->atom;
xcbCheckResult = xcb_change_property_checked(m_xcbConnection,
@@ -115,9 +119,9 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::Activate()
void XcbNativeWindow::Activate()
{
LinuxXcbEventHandlerBus::Handler::BusConnect();
XcbEventHandlerBus::Handler::BusConnect();
if (!m_activated) // nothing to do if window was already activated
{
@@ -129,7 +133,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::Deactivate()
void XcbNativeWindow::Deactivate()
{
if (m_activated) // nothing to do if window was already deactivated
{
@@ -140,17 +144,17 @@ namespace AzFramework
xcb_unmap_window(m_xcbConnection, m_xcbWindow);
xcb_flush(m_xcbConnection);
}
LinuxXcbEventHandlerBus::Handler::BusDisconnect();
XcbEventHandlerBus::Handler::BusDisconnect();
}
////////////////////////////////////////////////////////////////////////////////////////////////
NativeWindowHandle NativeWindowImpl_Linux_xcb::GetWindowHandle() const
NativeWindowHandle XcbNativeWindow::GetWindowHandle() const
{
return reinterpret_cast<NativeWindowHandle>(m_xcbWindow);
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::SetWindowTitle(const AZStd::string& title)
void XcbNativeWindow::SetWindowTitle(const AZStd::string& title)
{
xcb_void_cookie_t xcbCheckResult;
xcbCheckResult = xcb_change_property(m_xcbConnection,
@@ -165,7 +169,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::ResizeClientArea(WindowSize clientAreaSize)
void XcbNativeWindow::ResizeClientArea(WindowSize clientAreaSize)
{
const uint32_t values[] = { clientAreaSize.m_width, clientAreaSize.m_height };
@@ -176,7 +180,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
uint32_t NativeWindowImpl_Linux_xcb::GetDisplayRefreshRate() const
uint32_t XcbNativeWindow::GetDisplayRefreshRate() const
{
// [GFX TODO][GHI - 2678]
// Using 60 for now until proper support is added
@@ -184,7 +188,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
bool NativeWindowImpl_Linux_xcb::ValidateXcbResult(xcb_void_cookie_t cookie)
bool XcbNativeWindow::ValidateXcbResult(xcb_void_cookie_t cookie)
{
bool result = true;
if (xcb_generic_error_t* error = xcb_request_check(m_xcbConnection, cookie))
@@ -196,7 +200,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::HandleXcbEvent(xcb_generic_event_t* event)
void XcbNativeWindow::HandleXcbEvent(xcb_generic_event_t* event)
{
switch (event->response_type & s_XcbResponseTypeMask)
{
@@ -225,7 +229,7 @@ namespace AzFramework
}
////////////////////////////////////////////////////////////////////////////////////////////////
void NativeWindowImpl_Linux_xcb::WindowSizeChanged(const uint32_t width, const uint32_t height)
void XcbNativeWindow::WindowSizeChanged(const uint32_t width, const uint32_t height)
{
if (m_width != width || m_height != height)
{
@@ -238,7 +242,4 @@ namespace AzFramework
}
}
}
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -5,24 +5,25 @@
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzFramework/API/ApplicationAPI_Platform.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Windowing/NativeWindow.h>
#include <AzFramework/XcbEventHandler.h>
#include <xcb/xcb.h>
namespace AzFramework
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
class NativeWindowImpl_Linux_xcb final
class XcbNativeWindow final
: public NativeWindow::Implementation
, public LinuxXcbEventHandlerBus::Handler
, public XcbEventHandlerBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(NativeWindowImpl_Linux_xcb, AZ::SystemAllocator, 0);
NativeWindowImpl_Linux_xcb();
~NativeWindowImpl_Linux_xcb() override;
AZ_CLASS_ALLOCATOR(XcbNativeWindow, AZ::SystemAllocator, 0);
XcbNativeWindow();
~XcbNativeWindow() override;
////////////////////////////////////////////////////////////////////////////////////////////
// NativeWindow::Implementation
@@ -37,7 +38,7 @@ namespace AzFramework
uint32_t GetDisplayRefreshRate() const override;
////////////////////////////////////////////////////////////////////////////////////////////
// LinuxXcbEventHandlerBus::Handler
// XcbEventHandlerBus::Handler
void HandleXcbEvent(xcb_generic_event_t* event) override;
private:
@@ -49,6 +50,4 @@ namespace AzFramework
xcb_atom_t m_xcbAtomProtocols;
xcb_atom_t m_xcbAtomDeleteWindow;
};
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -0,0 +1,18 @@
#
# 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
#
#
set(FILES
AzFramework/XcbApplication.cpp
AzFramework/XcbApplication.h
AzFramework/XcbConnectionManager.h
AzFramework/XcbInputDeviceKeyboard.cpp
AzFramework/XcbInputDeviceKeyboard.h
AzFramework/XcbInterface.h
AzFramework/XcbNativeWindow.cpp
AzFramework/XcbNativeWindow.h
)
@@ -12,10 +12,6 @@
#include <AzCore/Interface/Interface.h>
#include <AzCore/EBus/EBus.h>
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <xcb/xcb.h>
#endif // LY_COMPILE_DEFINITIONS
namespace AzFramework
{
class LinuxLifecycleEvents
@@ -30,54 +26,4 @@ namespace AzFramework
using Bus = AZ::EBus<LinuxLifecycleEvents>;
};
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
class LinuxXcbConnectionManager
{
public:
AZ_RTTI(LinuxXcbConnectionManager, "{1F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~LinuxXcbConnectionManager() = default;
virtual xcb_connection_t* GetXcbConnection() const = 0;
};
class LinuxXcbConnectionManagerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using LinuxXcbConnectionManagerBus = AZ::EBus<LinuxXcbConnectionManager, LinuxXcbConnectionManagerBusTraits>;
using LinuxXcbConnectionManagerInterface = AZ::Interface<LinuxXcbConnectionManager>;
class LinuxXcbEventHandler
{
public:
AZ_RTTI(LinuxXcbEventHandler, "{3F756E14-8D74-42FD-843C-4863307710DB}");
virtual ~LinuxXcbEventHandler() = default;
virtual void HandleXcbEvent(xcb_generic_event_t* event) = 0;
};
class LinuxXcbEventHandlerBusTraits
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
};
using LinuxXcbEventHandlerBus = AZ::EBus<LinuxXcbEventHandler, LinuxXcbEventHandlerBusTraits>;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
} // namespace AzFramework
@@ -8,7 +8,9 @@
#include <AzFramework/Application/Application.h>
#include "Application_Linux_xcb.h"
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/XcbApplication.h>
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////
namespace AzFramework
@@ -17,7 +19,7 @@ namespace AzFramework
Application::Implementation* Application::Implementation::Create()
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
return aznew ApplicationLinux_xcb();
return aznew XcbApplication();
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
@@ -0,0 +1,27 @@
/*
* 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
*
*/
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/XcbInputDeviceKeyboard.h>
#endif
namespace AzFramework
{
InputDeviceKeyboard::Implementation* InputDeviceKeyboard::Implementation::Create(InputDeviceKeyboard& inputDevice)
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
return aznew XcbInputDeviceKeyboard(inputDevice);
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
#else
#error "Linux Window Manager not recognized."
return nullptr;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
} // namespace AzFramework
@@ -6,14 +6,16 @@
*
*/
#include "NativeWindow_Linux_xcb.h"
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
#include <AzFramework/XcbNativeWindow.h>
#endif
namespace AzFramework
{
NativeWindow::Implementation* NativeWindow::Implementation::Create()
{
#if PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
return aznew NativeWindowImpl_Linux_xcb();
return aznew XcbNativeWindow();
#elif PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND
#error "Linux Window Manager Wayland not supported."
return nullptr;
@@ -22,5 +24,4 @@ namespace AzFramework
return nullptr;
#endif // PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB
}
} // namespace AzFramework
@@ -10,15 +10,22 @@
# Only 'xcb' and 'wayland' are recognized
if (${PAL_TRAIT_LINUX_WINDOW_MANAGER} STREQUAL "xcb")
find_library(XCB_LIBRARY xcb)
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
set(LY_INCLUDE_DIRECTORIES
PUBLIC
Platform/Common/Xcb
)
set(LY_FILES_CMAKE
Platform/Common/Xcb/azframework_xcb_files.cmake
)
set(LY_BUILD_DEPENDENCIES
PRIVATE
${XCB_LIBRARY}
3rdParty::X11::xcb
3rdParty::X11::xcb_xkb
3rdParty::X11::xkbcommon
3rdParty::X11::xkbcommon_X11
)
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_XCB)
elseif(PAL_TRAIT_LINUX_WINDOW_MANAGER STREQUAL "wayland")
set(LY_COMPILE_DEFINITIONS PUBLIC PAL_TRAIT_LINUX_WINDOW_MANAGER_WAYLAND)
@@ -12,8 +12,6 @@ set(FILES
AzFramework/API/ApplicationAPI_Platform.h
AzFramework/API/ApplicationAPI_Linux.h
AzFramework/Application/Application_Linux.cpp
AzFramework/Application/Application_Linux_xcb.h
AzFramework/Application/Application_Linux_xcb.cpp
AzFramework/Asset/AssetSystemComponentHelper_Linux.cpp
AzFramework/Process/ProcessWatcher_Linux.cpp
AzFramework/Process/ProcessCommon.h
@@ -22,10 +20,8 @@ set(FILES
../Common/Unimplemented/AzFramework/StreamingInstall/StreamingInstall_Unimplemented.cpp
../Common/Default/AzFramework/TargetManagement/TargetManagementComponent_Default.cpp
AzFramework/Windowing/NativeWindow_Linux.cpp
AzFramework/Windowing/NativeWindow_Linux_xcb.h
AzFramework/Windowing/NativeWindow_Linux_xcb.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Gamepad/InputDeviceGamepad_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Unimplemented.cpp
AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard_Linux.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Motion/InputDeviceMotion_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Mouse/InputDeviceMouse_Unimplemented.cpp
../Common/Unimplemented/AzFramework/Input/Devices/Touch/InputDeviceTouch_Unimplemented.cpp
@@ -68,7 +68,7 @@ namespace UnitTest
return false;
}
if (!archive->OpenPack(path, AZ::IO::IArchive::FLAGS_PATH_REAL))
if (!archive->OpenPack(path))
{
return false;
}
@@ -94,7 +94,7 @@ namespace UnitTest
fileIo->Remove(testArchivePath.c_str());
// ------------ BASIC TEST: Create and read Empty Archive ------------
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath.c_str(), nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath.c_str(), {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
EXPECT_NE(nullptr, pArchive);
pArchive.reset();
EXPECT_TRUE(IsPackValid(testArchivePath.c_str()));
@@ -122,7 +122,7 @@ namespace UnitTest
checkSums[pos] = static_cast<uint8_t>(pos % 256);
}
auto pArchive = archive->OpenArchive(testArchivePath.c_str(), nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
auto pArchive = archive->OpenArchive(testArchivePath.c_str(), {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
EXPECT_NE(nullptr, pArchive);
// the strategy here is to find errors related to file sizes, alignment, overwrites
@@ -143,7 +143,7 @@ namespace UnitTest
// --------------------------------------------- read it back and verify
pArchive = archive->OpenArchive(testArchivePath.c_str(), nullptr, openFlags);
pArchive = archive->OpenArchive(testArchivePath.c_str(), {}, openFlags);
EXPECT_NE(nullptr, pArchive);
for (int j = 0; j < iterations; ++j)
@@ -241,7 +241,7 @@ namespace UnitTest
// -------------------------------------------------------------------------------------------
// read it back and verify
pArchive = archive->OpenArchive(testArchivePath.c_str(), nullptr, openFlags);
pArchive = archive->OpenArchive(testArchivePath.c_str(), {}, openFlags);
EXPECT_NE(nullptr, pArchive);
for (int j = 0; j < iterations; ++j)
@@ -298,7 +298,7 @@ namespace UnitTest
}
// first, reset the pack to the original state:
auto pArchive = archive->OpenArchive(testArchivePath.c_str(), nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
auto pArchive = archive->OpenArchive(testArchivePath.c_str(), {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
EXPECT_NE(nullptr, pArchive);
for (int j = 0; j < iterations; ++j)
@@ -382,7 +382,7 @@ namespace UnitTest
// -------------------------------------------------------------------------------------------
// read it back and verify
pArchive = archive->OpenArchive(testArchivePath.c_str(), nullptr, openFlags);
pArchive = archive->OpenArchive(testArchivePath.c_str(), {}, openFlags);
EXPECT_NE(nullptr, pArchive);
writeCount = 0;
+44 -222
View File
@@ -30,16 +30,17 @@ namespace UnitTest
: public ScopedAllocatorSetupFixture
{
public:
// Use an Immediately invoked function to initlaize the m_stackRecordLevels value of the AZ::SystemAllocator::Descriptor class
ArchiveTestFixture()
: m_application{ AZStd::make_unique<AzFramework::Application>() }
: ScopedAllocatorSetupFixture(
[]() { AZ::SystemAllocator::Descriptor desc; desc.m_stackRecordLevels = 30; return desc; }()
)
, m_application{ AZStd::make_unique<AzFramework::Application>() }
{
}
void SetUp() override
{
AZ::ComponentApplication::Descriptor descriptor;
descriptor.m_stackRecordLevels = 30;
AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
auto projectPathKey =
@@ -47,7 +48,7 @@ namespace UnitTest
registry->Set(projectPathKey, "AutomatedTesting");
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
m_application->Start(descriptor);
m_application->Start({});
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
@@ -68,7 +69,7 @@ namespace UnitTest
return false;
}
return archive->OpenPack(path, AZ::IO::IArchive::FLAGS_PATH_REAL) && archive->ClosePack(path);
return archive->OpenPack(path) && archive->ClosePack(path);
}
template <class Function>
@@ -116,7 +117,7 @@ namespace UnitTest
{
// Canary tests first
AZ::IO::HandleType fileHandle = archive->FOpen(testFilePath, "rb", 0);
AZ::IO::HandleType fileHandle = archive->FOpen(testFilePath, "rb");
ASSERT_NE(AZ::IO::InvalidHandle, fileHandle);
@@ -136,9 +137,9 @@ namespace UnitTest
// open already open file and call FGetCachedFileData
fileSize = 0;
{
AZ::IO::HandleType fileHandle2 = archive->FOpen(testFilePath, "rb", 0);
AZ::IO::HandleType fileHandle2 = archive->FOpen(testFilePath, "rb");
char* pFileBuffer3 = (char*)archive->FGetCachedFileData(fileHandle2, fileSize);
ASSERT_NE(nullptr,pFileBuffer3);
ASSERT_NE(nullptr, pFileBuffer3);
EXPECT_EQ(dataLen, fileSize);
EXPECT_EQ(0, memcmp(pFileBuffer3, testData, dataLen));
archive->FClose(fileHandle2);
@@ -174,7 +175,7 @@ namespace UnitTest
// Multithreaded Test #2 reading from the same file concurrently
auto concurrentArchiveFileReadFunc = [archive, testFilePath, dataLen, testData]()
{
AZ::IO::HandleType threadFileHandle = archive->FOpen(testFilePath, "rb", 0);
AZ::IO::HandleType threadFileHandle = archive->FOpen(testFilePath, "rb");
if (threadFileHandle == AZ::IO::InvalidHandle)
{
@@ -255,7 +256,7 @@ namespace UnitTest
fileIo->CreatePath("@usercache@/levels/test");
// setup test archive and file
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath_withSubfolders.c_str(), nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath_withSubfolders.c_str(), {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
EXPECT_NE(nullptr, pArchive);
EXPECT_EQ(0, pArchive->UpdateFile(fileInArchiveFile, dataString.data(), dataString.size(), AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_FASTEST));
pArchive.reset();
@@ -290,7 +291,7 @@ namespace UnitTest
archive->ClosePack(filePath.c_str());
fileIo->Remove(filePath.c_str());
auto pArchive = archive->OpenArchive(filePath.c_str(), nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
auto pArchive = archive->OpenArchive(filePath.c_str(), {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
EXPECT_NE(nullptr, pArchive);
pArchive.reset();
archive->ClosePack(filePath.c_str());
@@ -305,7 +306,7 @@ namespace UnitTest
// open and fetch the opened pak file using a *.pak
AZStd::vector<AZ::IO::FixedMaxPathString> fullPaths;
archive->OpenPacks("@usercache@/*.pak", AZ::IO::IArchive::EPathResolutionRules::FLAGS_PATH_REAL, &fullPaths);
archive->OpenPacks("@usercache@/*.pak", &fullPaths);
EXPECT_TRUE(AZStd::any_of(fullPaths.cbegin(), fullPaths.cend(), [](auto& path) { return path.ends_with("one.pak"); }));
EXPECT_TRUE(AZStd::any_of(fullPaths.cbegin(), fullPaths.cend(), [](auto& path) { return path.ends_with("two.pak"); }));
}
@@ -477,7 +478,7 @@ namespace UnitTest
bool found_mylevel_file{};
bool found_mylevel_folder{};
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath_withMountPoint, nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath_withMountPoint, {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
EXPECT_NE(nullptr, pArchive);
EXPECT_EQ(0, pArchive->UpdateFile("levelinfo.xml", dataString.data(), dataString.size(), AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_FASTEST));
pArchive.reset();
@@ -628,7 +629,7 @@ namespace UnitTest
normalFileHandle = InvalidHandle;
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/realfileforunittest.xml"));
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(genericArchiveFileName, nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(genericArchiveFileName, {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
EXPECT_NE(nullptr, pArchive);
EXPECT_EQ(0, pArchive->UpdateFile("testfile.xml", dataString, aznumeric_cast<uint32_t>(dataLen), AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_FASTEST));
pArchive.reset();
@@ -712,13 +713,16 @@ namespace UnitTest
EXPECT_TRUE(cpfio.Exists("@log@/unittesttemp/copiedfile2.xml"));
// find files test.
AZ::IO::FixedMaxPath resolvedTestFilePath;
EXPECT_TRUE(cpfio.ResolvePath(resolvedTestFilePath, AZ::IO::PathView("@assets@/testfile.xml")));
bool foundIt = false;
// note that this file exists only in the archive.
cpfio.FindFiles("@assets@", "*.xml", [&foundIt](const char* foundName)
cpfio.FindFiles("@assets@", "*.xml", [&foundIt, &cpfio, &resolvedTestFilePath](const char* foundName)
{
AZ::IO::FixedMaxPath resolvedFoundPath;
EXPECT_TRUE(cpfio.ResolvePath(resolvedFoundPath, AZ::IO::PathView(foundName)));
// according to the contract stated in the FileIO.h file, we expect full paths. (Aliases are full paths)
if (azstricmp(foundName, "@assets@/testfile.xml") == 0)
if (resolvedTestFilePath == resolvedFoundPath)
{
foundIt = true;
return false;
@@ -769,7 +773,7 @@ namespace UnitTest
fileIo->Remove(testArchivePath);
// ------------ BASIC TEST: Create and read Empty Archive ------------
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath, nullptr, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
AZStd::intrusive_ptr<AZ::IO::INestedArchive> pArchive = archive->OpenArchive(testArchivePath, {}, AZ::IO::INestedArchive::FLAGS_CREATE_NEW);
EXPECT_NE(nullptr, pArchive);
EXPECT_EQ(0, pArchive->UpdateFile("foundit.dat", const_cast<char*>("test"), 4, AZ::IO::INestedArchive::METHOD_COMPRESS, AZ::IO::INestedArchive::LEVEL_BEST));
@@ -803,40 +807,49 @@ namespace UnitTest
EXPECT_TRUE(archive->ClosePack(realNameBuf));
}
TEST_F(ArchiveTestFixture, IResourceList_Add_EmptyFileName_DoesNotCrash)
TEST_F(ArchiveTestFixture, IResourceList_Add_EmptyFileName_DoesNotInsert)
{
AZ::IO::IResourceList* reslist = AZ::Interface<AZ::IO::IArchive>::Get()->GetResourceList(AZ::IO::IArchive::RFOM_EngineStartup);
ASSERT_NE(nullptr, reslist);
reslist->Clear();
reslist->Add("");
EXPECT_STREQ(reslist->GetFirst(), "");
reslist->Clear();
EXPECT_EQ(nullptr, reslist->GetFirst());
}
TEST_F(ArchiveTestFixture, IResourceList_Add_RegularFileName_NormalizesAppropriately)
TEST_F(ArchiveTestFixture, IResourceList_Add_RegularFileName_ResolvesAppropriately)
{
AZ::IO::IResourceList* reslist = AZ::Interface<AZ::IO::IArchive>::Get()->GetResourceList(AZ::IO::IArchive::RFOM_EngineStartup);
ASSERT_NE(nullptr, reslist);
AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
ASSERT_NE(nullptr, ioBase);
AZ::IO::FixedMaxPath resolvedTestPath;
EXPECT_TRUE(ioBase->ResolvePath(resolvedTestPath, "blah/blah/abcde"));
reslist->Clear();
reslist->Add("blah\\blah/AbCDE");
// it normalizes the string, so the slashes flip and everything is lowercased.
EXPECT_STREQ(reslist->GetFirst(), "blah/blah/abcde");
AZ::IO::FixedMaxPath resolvedAddedPath;
EXPECT_TRUE(ioBase->ResolvePath(resolvedAddedPath, reslist->GetFirst()));
EXPECT_EQ(resolvedTestPath, resolvedAddedPath);
reslist->Clear();
}
TEST_F(ArchiveTestFixture, IResourceList_Add_ReallyShortFileName_NormalizesAppropriately)
TEST_F(ArchiveTestFixture, IResourceList_Add_ReallyShortFileName_ResolvesAppropriately)
{
AZ::IO::IResourceList* reslist = AZ::Interface<AZ::IO::IArchive>::Get()->GetResourceList(AZ::IO::IArchive::RFOM_EngineStartup);
ASSERT_NE(nullptr, reslist);
AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
ASSERT_NE(nullptr, ioBase);
AZ::IO::FixedMaxPath resolvedTestPath;
EXPECT_TRUE(ioBase->ResolvePath(resolvedTestPath, "a"));
reslist->Clear();
reslist->Add("A");
// it normalizes the string, so the slashes flip and everything is lowercased.
EXPECT_STREQ(reslist->GetFirst(), "a");
AZ::IO::FixedMaxPath resolvedAddedPath;
EXPECT_TRUE(ioBase->ResolvePath(resolvedAddedPath, reslist->GetFirst()));
EXPECT_EQ(resolvedTestPath, resolvedAddedPath);
reslist->Clear();
}
@@ -848,7 +861,7 @@ namespace UnitTest
AZ::IO::FileIOBase* ioBase = AZ::IO::FileIOBase::GetInstance();
ASSERT_NE(nullptr, ioBase);
const char *assetsPath = ioBase->GetAlias("@assets@");
const char* assetsPath = ioBase->GetAlias("@assets@");
ASSERT_NE(nullptr, assetsPath);
auto stringToAdd = AZ::IO::Path(assetsPath) / "textures" / "test.dds";
@@ -864,195 +877,4 @@ namespace UnitTest
EXPECT_EQ(resolvedAddedPath, resolvedResourcePath);
reslist->Clear();
}
class ArchiveUnitTestsWithAllocators
: public ScopedAllocatorSetupFixture
{
protected:
void SetUp() override
{
m_localFileIO = aznew AZ::IO::LocalFileIO();
AZ::IO::FileIOBase::SetDirectInstance(m_localFileIO);
m_localFileIO->SetAlias(m_firstAlias.c_str(), m_firstAliasPath.c_str());
m_localFileIO->SetAlias(m_secondAlias.c_str(), m_secondAliasPath.c_str());
}
void TearDown() override
{
AZ::IO::FileIOBase::SetDirectInstance(nullptr);
delete m_localFileIO;
m_localFileIO = nullptr;
}
AZ::IO::FileIOBase* m_localFileIO = nullptr;
AZStd::string m_firstAlias = "@devassets@";
AZStd::string m_firstAliasPath = "devassets_absolutepath";
AZStd::string m_secondAlias = "@assets@";
AZStd::string m_secondAliasPath = "assets_absolutepath";
};
// ConvertAbsolutePathToAliasedPath tests are built to verify existing behavior doesn't change.
// It's a legacy function and the actual intended behavior is unknown, so these are black box unit tests.
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_NullString_ReturnsSuccess)
{
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(nullptr);
EXPECT_TRUE(conversionResult);
EXPECT_TRUE(conversionResult->empty());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_NoAliasInSource_ReturnsSource)
{
AZStd::string sourceString("NoAlias");
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(sourceString.c_str());
EXPECT_TRUE(conversionResult);
// ConvertAbsolutePathToAliasedPath returns sourceString if there is no alias in the source.
EXPECT_STREQ(sourceString.c_str(), conversionResult->c_str());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_NullAliasToLookFor_ReturnsSource)
{
AZStd::string sourceString("NoAlias");
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(sourceString.c_str(), nullptr);
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(sourceString.c_str(), conversionResult->c_str());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_NullAliasToReplaceWith_ReturnsSource)
{
AZStd::string sourceString("NoAlias");
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(sourceString.c_str(), "@SomeAlias", nullptr);
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(sourceString.c_str(), conversionResult->c_str());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_NullAliases_ReturnsSource)
{
AZStd::string sourceString("NoAlias");
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(sourceString.c_str(), nullptr, nullptr);
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(sourceString.c_str(), conversionResult->c_str());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_AbsPathInSource_ReturnsReplacedAlias)
{
// ConvertAbsolutePathToAliasedPath only replaces data if GetDirectInstance is valid.
EXPECT_TRUE(AZ::IO::FileIOBase::GetDirectInstance() != nullptr);
const char* fullPath = AZ::IO::FileIOBase::GetDirectInstance()->GetAlias(m_firstAlias.c_str());
AZStd::string sourceString = AZStd::string::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "SomeStringWithAlias", fullPath);
AZStd::string expectedResult = AZStd::string::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "somestringwithalias", m_secondAlias.c_str());
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(
sourceString.c_str(),
m_firstAlias.c_str(), // find any instance of FirstAlias in sourceString
m_secondAlias.c_str()); // replace it with SecondAlias
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(conversionResult->c_str(), expectedResult.c_str());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_AliasInSource_ReturnsReplacedAlias)
{
// ConvertAbsolutePathToAliasedPath only replaces data if GetDirectInstance is valid.
EXPECT_TRUE(AZ::IO::FileIOBase::GetDirectInstance() != nullptr);
AZStd::string sourceString = AZStd::string::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "SomeStringWithAlias", m_firstAlias.c_str());
AZStd::string expectedResult = AZStd::string::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "somestringwithalias", m_secondAlias.c_str());
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(
sourceString.c_str(),
m_firstAlias.c_str(), // find any instance of FirstAlias in sourceString
m_secondAlias.c_str()); // replace it with SecondAlias
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(conversionResult->c_str(), expectedResult.c_str());
}
#if AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_AbsPathInSource_DOSSlashInSource_ReturnsReplacedAlias)
{
// ConvertAbsolutePathToAliasedPath only replaces data if GetDirectInstance is valid.
EXPECT_TRUE(AZ::IO::FileIOBase::GetDirectInstance() != nullptr);
const char* fullPath = AZ::IO::FileIOBase::GetDirectInstance()->GetAlias(m_firstAlias.c_str());
AZStd::string sourceString = AZStd::string::format("%s" AZ_WRONG_DATABASE_SEPARATOR_STRING "SomeStringWithAlias", fullPath);
AZStd::string expectedResult = AZStd::string::format("%s" AZ_CORRECT_FILESYSTEM_SEPARATOR_STRING "somestringwithalias", m_secondAlias.c_str());
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(
sourceString.c_str(),
m_firstAlias.c_str(), // find any instance of FirstAlias in sourceString
m_secondAlias.c_str()); // replace it with SecondAlias
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(conversionResult->c_str(), expectedResult.c_str());
}
#endif // AZ_TRAIT_OS_USE_WINDOWS_FILE_PATHS
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_AbsPathInSource_UNIXSlashInSource_ReturnsReplacedAlias)
{
// ConvertAbsolutePathToAliasedPath only replaces data if GetDirectInstance is valid.
EXPECT_TRUE(AZ::IO::FileIOBase::GetDirectInstance() != nullptr);
const char* fullPath = AZ::IO::FileIOBase::GetDirectInstance()->GetAlias(m_firstAlias.c_str());
AZStd::string sourceString = AZStd::string::format("%s" AZ_CORRECT_DATABASE_SEPARATOR_STRING "SomeStringWithAlias", fullPath);
AZStd::string expectedResult = AZStd::string::format("%s" AZ_CORRECT_DATABASE_SEPARATOR_STRING "somestringwithalias", m_secondAlias.c_str());
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(
sourceString.c_str(),
m_firstAlias.c_str(), // find any instance of FirstAlias in sourceString
m_secondAlias.c_str()); // replace it with SecondAlias
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(conversionResult->c_str(), expectedResult.c_str());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_AliasInSource_DOSSlashInSource_ReturnsReplacedAlias)
{
// ConvertAbsolutePathToAliasedPath only replaces data if GetDirectInstance is valid.
EXPECT_TRUE(AZ::IO::FileIOBase::GetDirectInstance() != nullptr);
AZStd::string sourceString = AZStd::string::format("%s" AZ_WRONG_DATABASE_SEPARATOR_STRING "SomeStringWithAlias", m_firstAlias.c_str());
AZStd::string expectedResult = AZStd::string::format("%s" AZ_WRONG_DATABASE_SEPARATOR_STRING "somestringwithalias", m_secondAlias.c_str());
// sourceString is now (firstAlias)SomeStringWithAlias
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(
sourceString.c_str(),
m_firstAlias.c_str(), // find any instance of FirstAlias in sourceString
m_secondAlias.c_str()); // replace it with SecondAlias
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(conversionResult->c_str(), expectedResult.c_str());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_AliasInSource_UNIXSlashInSource_ReturnsReplacedAlias)
{
// ConvertAbsolutePathToAliasedPath only replaces data if GetDirectInstance is valid.
EXPECT_TRUE(AZ::IO::FileIOBase::GetDirectInstance() != nullptr);
AZStd::string sourceString = AZStd::string::format("%s" AZ_CORRECT_DATABASE_SEPARATOR_STRING "SomeStringWithAlias", m_firstAlias.c_str());
AZStd::string expectedResult = AZStd::string::format("%s" AZ_CORRECT_DATABASE_SEPARATOR_STRING "somestringwithalias", m_secondAlias.c_str());
// sourceString is now (firstAlias)SomeStringWithAlias
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(
sourceString.c_str(),
m_firstAlias.c_str(), // find any instance of FirstAlias in sourceString
m_secondAlias.c_str()); // replace it with SecondAlias
EXPECT_TRUE(conversionResult);
EXPECT_STREQ(conversionResult->c_str(), expectedResult.c_str());
}
TEST_F(ArchiveUnitTestsWithAllocators, ConvertAbsolutePathToAliasedPath_SourceLongerThanMaxPath_ReturnsFailure)
{
const int longPathArraySize = AZ::IO::MaxPathLength + 2;
char longPath[longPathArraySize];
memset(longPath, 'a', sizeof(char) * longPathArraySize);
longPath[longPathArraySize - 1] = '\0';
AZ_TEST_START_TRACE_SUPPRESSION;
auto conversionResult = AZ::IO::ArchiveInternal::ConvertAbsolutePathToAliasedPath(longPath);
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_FALSE(conversionResult);
}
class ArchivePathCompareTestFixture
: public ScopedAllocatorSetupFixture
, public ::testing::WithParamInterface<AZStd::tuple<AZStd::string_view, AZStd::string_view>>
{
};
}
@@ -20,8 +20,7 @@ namespace Benchmark
class BM_Octree
: public benchmark::Fixture
{
public:
void SetUp([[maybe_unused]] const ::benchmark::State& state) override
void internalSetUp()
{
// Create the SystemAllocator if not available
if (!AZ::AllocatorInstance<AZ::SystemAllocator>::IsReady())
@@ -72,7 +71,7 @@ namespace Benchmark
});
}
void TearDown([[maybe_unused]] const ::benchmark::State& state) override
void internalTearDown()
{
m_octreeSystemComponent->DestroyVisibilityScene(m_visScene);
delete m_octreeSystemComponent;
@@ -91,6 +90,25 @@ namespace Benchmark
}
}
public:
void SetUp(const benchmark::State&) override
{
internalSetUp();
}
void SetUp(benchmark::State&) override
{
internalSetUp();
}
void TearDown(const benchmark::State&) override
{
internalTearDown();
}
void TearDown(benchmark::State&) override
{
internalTearDown();
}
void InsertEntries(uint32_t entryCount)
{
for (uint32_t i = 0; i < entryCount; ++i)
@@ -0,0 +1,122 @@
/*
* 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 <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/containers/array.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
//
// SpawnableEntityContainerView
//
class SpawnableEntityContainerViewTest : public ::testing::Test
{
protected:
AZStd::array<AZ::Entity*, 4> m_values{ reinterpret_cast<AZ::Entity*>(1), reinterpret_cast<AZ::Entity*>(2),
reinterpret_cast<AZ::Entity*>(3), reinterpret_cast<AZ::Entity*>(4) };
AzFramework::SpawnableEntityContainerView m_view{ m_values.begin(), m_values.end() };
};
TEST_F(SpawnableEntityContainerViewTest, begin_Get_MatchesBeginOfArray)
{
EXPECT_EQ(m_view.begin(), m_values.begin());
}
TEST_F(SpawnableEntityContainerViewTest, end_Get_MatchesEndOfArray)
{
EXPECT_EQ(m_view.end(), m_values.end());
}
TEST_F(SpawnableEntityContainerViewTest, cbegin_Get_MatchesBeginOfArray)
{
EXPECT_EQ(m_view.cbegin(), m_values.cbegin());
}
TEST_F(SpawnableEntityContainerViewTest, cend_Get_MatchesEndOfArray)
{
EXPECT_EQ(m_view.cend(), m_values.cend());
}
TEST_F(SpawnableEntityContainerViewTest, IndexOperator_Get_MatchesThirdElement)
{
EXPECT_EQ(m_view[2], m_values[2]);
}
TEST_F(SpawnableEntityContainerViewTest, Size_Get_MatchesSizeOfArray)
{
EXPECT_EQ(m_view.size(), m_values.size());
}
TEST_F(SpawnableEntityContainerViewTest, empty_GetFromFilledArray_ReturnsFalse)
{
EXPECT_FALSE(m_view.empty());
}
TEST_F(SpawnableEntityContainerViewTest, empty_GetFromEmtpyView_ReturnsTrue)
{
AzFramework::SpawnableEntityContainerView view{ nullptr, nullptr };
EXPECT_TRUE(view.empty());
}
//
// SpawnableConstEntityContainerView
//
class SpawnableConstEntityContainerViewTest : public ::testing::Test
{
protected:
AZStd::array<AZ::Entity*, 4> m_values{ reinterpret_cast<AZ::Entity*>(1), reinterpret_cast<AZ::Entity*>(2),
reinterpret_cast<AZ::Entity*>(3), reinterpret_cast<AZ::Entity*>(4) };
AzFramework::SpawnableConstEntityContainerView m_view{ m_values.begin(), m_values.end() };
};
TEST_F(SpawnableConstEntityContainerViewTest, begin_Get_MatchesBeginOfArray)
{
EXPECT_EQ(m_view.begin(), m_values.begin());
}
TEST_F(SpawnableConstEntityContainerViewTest, end_Get_MatchesEndOfArray)
{
EXPECT_EQ(m_view.end(), m_values.end());
}
TEST_F(SpawnableConstEntityContainerViewTest, cbegin_Get_MatchesBeginOfArray)
{
EXPECT_EQ(m_view.cbegin(), m_values.cbegin());
}
TEST_F(SpawnableConstEntityContainerViewTest, cend_Get_MatchesEndOfArray)
{
EXPECT_EQ(m_view.cend(), m_values.cend());
}
TEST_F(SpawnableConstEntityContainerViewTest, IndexOperator_Get_MatchesThirdElement)
{
EXPECT_EQ(m_view[2], m_values[2]);
}
TEST_F(SpawnableConstEntityContainerViewTest, size_Get_MatchesSizeOfArray)
{
EXPECT_EQ(m_view.size(), m_values.size());
}
TEST_F(SpawnableConstEntityContainerViewTest, empty_GetFromFilledArray_ReturnsFalse)
{
EXPECT_FALSE(m_view.empty());
}
TEST_F(SpawnableConstEntityContainerViewTest, empty_GetFromEmtpyView_ReturnsTrue)
{
AzFramework::SpawnableConstEntityContainerView view{ nullptr, nullptr };
EXPECT_TRUE(view.empty());
}
} // namespace UnitTest
@@ -8,6 +8,7 @@
set(FILES
../../AzCore/Tests/Main.cpp
Spawnable/SpawnableEntitiesInterfaceTests.cpp
Spawnable/SpawnableEntitiesManagerTests.cpp
ArchiveCompressionTests.cpp
ArchiveTests.cpp