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

1655 lines
82 KiB
C++

/*
* 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/Math/Crc.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/algorithm.h> // for GetMin()
#include <AzCore/std/functional.h> // for function<> in the find files callback.
#include <AzCore/std/parallel/lock.h>
#ifdef REMOTEFILEIO_CACHE_FILETREE
#include <AzCore/std/string/wildcard.h>
#endif
#include <AzFramework/Asset/AssetProcessorMessages.h>
#include <AzFramework/Network/AssetProcessorConnection.h>
#include <AzFramework/IO/RemoteFileIO.h>
namespace AZ
{
namespace IO
{
//////////////////////////////////////////////////////////////////////////
const char* const NetworkFileIOChannel = "NetworkFileIO";
#ifndef REMOTEFILEIO_IS_NETWORKFILEIO
const char* const RemoteFileIOChannel = "RemoteFileIO";
const char* const RemoteFileCacheChannel = "RemoteFileCache";
#endif
const size_t READ_CHUNK_SIZE = 1024 * 256;
#ifdef NETWORKFILEIO_LOG
AZ::OSString s_IOLog;
AZ::u64 s_fileOperation = 0;
class LogCall
{
public:
LogCall(const char* name)
:m_name(name)
{
m_fileOperation = s_fileOperation++;
s_IOLog.append(AZStd::string::format("%u Start ", m_fileOperation));
s_IOLog.append(m_name);
s_IOLog.append("\r\n");
}
void Append(const char* line)
{
s_IOLog.append(AZStd::string::format("%u ", m_fileOperation));
s_IOLog.append(line);
s_IOLog.append("\r\n");
}
~LogCall()
{
s_IOLog.append(AZStd::string::format("%u End ", m_fileOperation));
s_IOLog.append(m_name);
s_IOLog.append("\r\n");
}
AZStd::string m_name;
AZ::u64 m_fileOperation = 0;
};
#define REMOTEFILE_LOG_CALL(x) LogCall lc(x)
#define REMOTEFILE_LOG_APPEND(x) lc.Append(x)
#else
#define REMOTEFILE_LOG_CALL(x) {}
#define REMOTEFILE_LOG_APPEND(x) {}
#endif
NetworkFileIO::NetworkFileIO()
{
REMOTEFILE_LOG_CALL("NetworkFileIO()::NetworkFileIO()");
}
NetworkFileIO::~NetworkFileIO()
{
REMOTEFILE_LOG_CALL("NetworkFileIO()::~NetworkFileIO()");
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFilesGuard);
while (!m_remoteFiles.empty())
{
HandleType fileHandle = m_remoteFiles.begin()->first;
Close(fileHandle);
}
}
Result NetworkFileIO::Open(const char* filePath, OpenMode openMode, HandleType& fileHandle)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Open(filepath=%s, openMode=%i, fileHandle=OUT)",filePath?filePath:"nullptr", openMode).c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO()::Open(filePath=nullptr) return Error");
return ResultCode::Error;
}
if (openMode == OpenMode::Invalid)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO()::Open(filepath=%s, openMode=%i, fileHandle=OUT) openMode=InvalidMode return Error", filePath, openMode).c_str());
return ResultCode::Error;
}
//build a request
uint32_t mode = static_cast<uint32_t>(openMode);
AzFramework::AssetSystem::FileOpenRequest request(filePath, mode);
AzFramework::AssetSystem::FileOpenResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO()::Open(filepath=%s, openMode=%i, fileHandle=OUT) unable to send request. return Error", filePath, openMode);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO()::Open(filepath=%s, openMode=%i, fileHandle=OUT) unable to send request. return Error", filePath, openMode).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(response.m_returnCode);
if (returnValue == ResultCode::Success)
{
fileHandle = response.m_fileHandle;
//track the open file handles
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFilesGuard);
m_remoteFiles.insert(AZStd::make_pair(fileHandle, filePath));
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO()::Open(filepath=%s, openMode=%i, fileHandle=%u) return %s", filePath, openMode, fileHandle, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
Result NetworkFileIO::Close(HandleType fileHandle)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Close(fileHandle=%u)", fileHandle).c_str());
//build a request
AzFramework::AssetSystem::FileCloseRequest request(fileHandle);
if (!SendRequest(request))
{
AZ_Assert(false, "NetworkFileIO()::Close(fileHandle=%u) Failed to send request. return Error", fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO()::Close(fileHandle=%u) Failed to send request. return Error", fileHandle).c_str());
return ResultCode::Error;
}
//clean up the handle and cache
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFilesGuard);
m_remoteFiles.erase(fileHandle);
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO()::Close(fileHandle=%u) return Success", fileHandle).c_str());
return ResultCode::Success;
}
Result NetworkFileIO::Tell(HandleType fileHandle, AZ::u64& offset)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Tell(fileHandle=%u, offset=OUT)", fileHandle).c_str());
AzFramework::AssetSystem::FileTellRequest request(fileHandle);
AzFramework::AssetSystem::FileTellResponse responce;
if(!SendRequest(request, responce))
{
AZ_Assert(false, "NetworkFileIO::Tell(fileHandle=%u) Failed to send tell request. return Error", fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Tell(fileHandle=%u) Failed to send tell request. return Error", fileHandle).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(responce.m_resultCode);
if (returnValue == ResultCode::Error)
{
AZ_TracePrintf(NetworkFileIOChannel, "NetworkFileIO::Tell(fileHandle=%u) tell request failed", fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Tell(fileHandle=%u) tell request failed", fileHandle).c_str());
return ResultCode::Error;
}
offset = responce.m_offset;
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Tell(fileHandle=%u) offset=%u return Success", fileHandle, offset).c_str());
return ResultCode::Success;
}
Result NetworkFileIO::Seek(HandleType fileHandle, AZ::s64 offset, SeekType type)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Seek(fileHandle=%u, offset=%i, type=%s)", fileHandle, offset, type == SeekType::SeekFromCurrent ? "SeekFromCurrent" : type == SeekType::SeekFromEnd ? "SeekFromEnd" : type == SeekType::SeekFromStart ? "SeekFromStart" : "Unknown").c_str());
AzFramework::AssetSystem::FileSeekRequest request(fileHandle, static_cast<AZ::u32>(type), offset);
if(!SendRequest(request))
{
AZ_Assert(false, "NetworkFileIO::Seek() Failed to send request, fileHandle=%u. return Error", fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Seek(fileHandle=%u) Failed to send request. return Error", fileHandle).c_str());
return ResultCode::Error;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Seek(fileHandle=%u) return Success", fileHandle).c_str());
return ResultCode::Success;
}
Result NetworkFileIO::Size(HandleType fileHandle, AZ::u64& size)
{
size = 0;
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Size(fileHandle=%u, size=OUT)", fileHandle).c_str());
AZStd::string fileName;
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFilesGuard);
auto remoteIter = m_remoteFiles.find(fileHandle);
if (remoteIter != m_remoteFiles.end())
{
fileName = remoteIter->second.c_str();
}
}
if (!fileName.empty())
{
return Size(fileName.c_str(), size);
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(fileHandle=%u) fileHandle not found! return Error", fileHandle).c_str());
return ResultCode::Error;
}
Result NetworkFileIO::Size(const char* filePath, AZ::u64& size)
{
size = 0;
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Size(filePath=%s, size=OUT)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::Size(filePath=nullptr) return Error");
return ResultCode::Error;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::Size(filePath=\"\") strlen(filePath)==0 return Error");
return ResultCode::Error;
}
AzFramework::AssetSystem::FileSizeRequest request(filePath);
AzFramework::AssetSystem::FileSizeResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::Size(filePath=%s) failed to send request. return Error", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) failed to send request. return Error", filePath).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(response.m_resultCode);
if (returnValue == ResultCode::Error)
{
AZ_TracePrintf(NetworkFileIOChannel, "NetworkFileIO::Size(filePath=%s) size request failed. return Error", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size request failed. return Error", filePath).c_str());
return ResultCode::Error;
}
size = response.m_size;
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Size(filePath=%s) size=%u. return Success", filePath, size).c_str());
return ResultCode::Success;
}
Result NetworkFileIO::Read(HandleType fileHandle, void* buffer, AZ::u64 size, bool failOnFewerThanSizeBytesRead, AZ::u64* bytesRead)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Read(filehandle=%i, buffer=OUT, size=%u, failOnFewerThanSizeBytesRead=%s, bytesRead=OUT)", fileHandle, size, failOnFewerThanSizeBytesRead ? "True" : "False").c_str());
size_t remainingBytesToRead = size;
AZ::u64 actualRead = 0;
while(remainingBytesToRead)
{
AZ::u64 readSize = GetMin<AZ::u64>(remainingBytesToRead, READ_CHUNK_SIZE);
AzFramework::AssetSystem::FileReadRequest request(fileHandle, readSize, false);
AzFramework::AssetSystem::FileReadResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::Read(filehandle=%i, size=%u) request failed. return Error", fileHandle, size);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Read(filehandle=%i, size=%u) request failed. return Error", fileHandle, size).c_str());
return ResultCode::Error;
}
//note the response could be ANY size, could be less so be careful
AZ::u64 responseDataSize = response.m_data.size();
if (responseDataSize <= remainingBytesToRead == false)
{
AZ_TracePrintf(NetworkFileIOChannel, "NetworkFileIO::Read(filehandle=%i, size=%u) responseDataSize too large!!! responseDataSize=%u <= remainingBytesToRead=%u", fileHandle, size, responseDataSize, remainingBytesToRead);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Read(filehandle=%i, size=%u) responseDataSize too large!!! responseDataSize=%u <= remainingBytesToRead=%u", fileHandle, size, responseDataSize, remainingBytesToRead).c_str());
}
ResultCode returnValue = static_cast<ResultCode>(response.m_resultCode);
//only copy as much as we can
memcpy(buffer, response.m_data.data(), responseDataSize);
buffer = reinterpret_cast<char*>(buffer) + responseDataSize;
//only reduce by what should have come back
remainingBytesToRead -= responseDataSize;
//only record read bytes
actualRead += responseDataSize;
if(bytesRead)
{
*bytesRead = actualRead;
}
//if we get an error, we only return an error if failOnFewerThanSizeBytesRead
if(returnValue == ResultCode::Error)
{
AZ_TracePrintf(NetworkFileIOChannel, "NetworkFileIO::Read: request failed, fileHandle=%u", fileHandle);
if(failOnFewerThanSizeBytesRead && remainingBytesToRead)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Read(fileHandle=%u, size=%u) actualRead=%u failed On Fewer Than Size Bytes Read. return Error", fileHandle, size, actualRead).c_str());
return ResultCode::Error;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Read(fileHandle=%u, size=%u) actualRead=%u return Success", fileHandle, size, actualRead).c_str());
return ResultCode::Success;
}
else if(!responseDataSize)
{
break;
}
}
if(failOnFewerThanSizeBytesRead && remainingBytesToRead)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Read(fileHandle=%u, size=%u) actualRead=%u failed On Fewer Than Size Bytes Read. return Error", fileHandle, size, actualRead).c_str());
return ResultCode::Error;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Read(fileHandle=%u, size=%u) actualRead=%u return Success", fileHandle, size, actualRead).c_str());
return ResultCode::Success;
}
Result NetworkFileIO::Write(HandleType fileHandle, const void* buffer, AZ::u64 size, AZ::u64* bytesWritten)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Write(fileHandle=%u, buffer=OUT, size=%u, bytesWritten=OUT)", fileHandle, size).c_str());
AzFramework::AssetSystem::FileWriteRequest request(fileHandle, buffer, size);
//always async and just return success unless bytesWritten is set then synchronous
if (bytesWritten)
{
AzFramework::AssetSystem::FileWriteResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::Write(fileHandle=%u, size=%u) failed to send sync write request. return Error", fileHandle, size);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Write(fileHandle=%u, size=%u) failed to send sync write request. return Error", fileHandle, size).c_str());
return ResultCode::Error;
}
if (static_cast<ResultCode>(response.m_resultCode) == ResultCode::Error)
{
AZ_TracePrintf(NetworkFileIOChannel, "NetworkFileIO::Write(fileHandle=%u, size=%u) sync write request failed. return Error", fileHandle, size);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Write(fileHandle=%u, size=%u) sync write request failed. return Error", fileHandle, size).c_str());
return ResultCode::Error;
}
*bytesWritten = response.m_bytesWritten;
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Write(fileHandle=%u, size=%u) actualWrite=%u return Success", fileHandle, size, response.m_bytesWritten).c_str());
return ResultCode::Success;
}
else
{
// just send the message and assume we wrote it all successfully.
if (!SendRequest(request))
{
AZ_Assert(false, "NetworkFileIO::Write(fileHandle=%u, size=%u) failed to send async write request. return Error", fileHandle, size);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Write(fileHandle=%u, size=%u) failed to send async write request. return Error", fileHandle, size).c_str());
return ResultCode::Error;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Write(fileHandle=%u, size=%u) return Success", fileHandle).c_str());
return ResultCode::Success;
}
}
Result NetworkFileIO::Flush(HandleType fileHandle)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Flush(fileHandle=%u)", fileHandle).c_str());
// just send the message, no need to wait for flush response.
AzFramework::AssetSystem::FileFlushRequest request(fileHandle);
if (!SendRequest(request))
{
AZ_Assert(false, "NetworkFileIO::Flush(fileHandle=%u) failed to send request. return Error", fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Flush(fileHandle=%u) failed to send request. return Error", fileHandle).c_str());
return ResultCode::Error;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Flush(fileHandle=%u) return Success", fileHandle).c_str());
return ResultCode::Success;
}
bool NetworkFileIO::Eof(HandleType fileHandle)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Eof(fileHandle=%u)", fileHandle).c_str());
AZ::u64 sizeValue = 0;
Result res = Size(fileHandle, sizeValue);
AZ::u64 offset = 0;
res = Tell(fileHandle, offset);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Eof(fileHandle=%u) return %s", fileHandle, offset >= sizeValue ? "True" : "False").c_str());
return offset >= sizeValue;
}
bool NetworkFileIO::Exists(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Exists(filePath=%s)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::Exists(filePath=nullptr) return False");
return false;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::Exists(filePath=\"\") strlen(filePath)==0. return False");
return false;
}
AzFramework::AssetSystem::FileExistsRequest request(filePath);
AzFramework::AssetSystem::FileExistsResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::Exists(filePath=%s) failed to send request. return False", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Exists(filePath=%s) failed to send request. return False", filePath).c_str());
return false;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Exists(filePath=%s) return %s", filePath, response.m_exists ? "True" : "False").c_str());
return response.m_exists;
}
AZ::u64 NetworkFileIO::ModificationTime(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ModificationTime(filePath=%s)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::ModificationTime(filePath=nullptr) return 0");
return 0;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::ModificationTime(filePath=\"\") strlen(filePath)==0. return 0");
return 0;
}
AzFramework::AssetSystem::FileModTimeRequest request(filePath);
AzFramework::AssetSystem::FileModTimeResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::ModificationTime(filePath=%s) failed to send request. return 0", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::ModificationTime(filePath=%s) failed to send request. return 0", filePath).c_str());
return 0;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::ModificationTime(filePath=%s) return %i", filePath, response.m_modTime).c_str());
return response.m_modTime;
}
AZ::u64 NetworkFileIO::ModificationTime(HandleType fileHandle)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ModificationTime(fileHandle=%u)", fileHandle).c_str());
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFilesGuard);
auto remoteIter = m_remoteFiles.find(fileHandle);
if (remoteIter != m_remoteFiles.end())
{
return ModificationTime(remoteIter->second.c_str());
}
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::ModificationTime(fileHandle=%u) return 0!", fileHandle).c_str());
return 0;
}
bool NetworkFileIO::IsDirectory(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::IsDirectory(filePath=%s)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::IsDirectory(filePath=nullptr) filePath=nullptr. return False");
return false;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::IsDirectory(filePath=\"\") strlen(filePath)=0. return False");
return false;
}
AzFramework::AssetSystem::PathIsDirectoryRequest request(filePath);
AzFramework::AssetSystem::PathIsDirectoryResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::IsDirectory(filePath=%s) failed to send request. return False", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::IsDirectory(filePath=%s) failed to send request. return False", filePath).c_str());
return false;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::IsDirectory(filePath=%s) return %s", filePath, response.m_isDir ? "True": "False").c_str());
return response.m_isDir;
}
bool NetworkFileIO::IsReadOnly(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::IsReadOnly(filePath=%s)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::IsReadOnly(filePath=nullptr) return False");
return false;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::IsReadOnly(filePath=\"\") strlen(filePath)==0. return False");
return false;
}
AzFramework::AssetSystem::FileIsReadOnlyRequest request(filePath);
AzFramework::AssetSystem::FileIsReadOnlyResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::IsReadOnly(filePath=%s) failed to send request. return False", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::IsReadOnly(filePath=%s) failed to send request. return False", filePath).c_str());
return false;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::IsReadOnly(filePath=%s) return %s", filePath, response.m_isReadOnly ? "True" : "False").c_str());
return response.m_isReadOnly;
}
Result NetworkFileIO::CreatePath(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::CreatePath(filePath=%s)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::CreatePath(filePath=nullptr) filePath=nullptr. return Error");
return ResultCode::Error;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::CreatePath(filePath=\"\") strlen(filePath)==0. return Error");
return ResultCode::Error;
}
AzFramework::AssetSystem::PathCreateRequest request(filePath);
AzFramework::AssetSystem::PathCreateResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::CreatePath(filePath=%s) failed to send request. return Error", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::CreatePath(filePath=%s) failed to send request. return Error", filePath).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(response.m_resultCode);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::CreatePath(filePath=%s) return %s", filePath, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
Result NetworkFileIO::DestroyPath(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::DestroyPath(filePath=%s)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::DestroyPath(filePath=nullptr) filePth=nullptr. return Error");
return ResultCode::Error;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::DestroyPath(filePath=\"\") strlen(filePath)==0. return Error");
return ResultCode::Error;
}
AzFramework::AssetSystem::PathDestroyRequest request(filePath);
AzFramework::AssetSystem::PathDestroyResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::DestroyPath(filePath=%s) failed to send request. return Error", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::DestroyPath(filePath=%s) failed to send request. return Error", filePath).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(response.m_resultCode);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::DestroyPath(filePath=%s) return %s", filePath, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
Result NetworkFileIO::Remove(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Remove(filePath=%s)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::Remove(filePath=nullptr) filePth=nullptr. return Error");
return ResultCode::Error;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("NetworkFileIO::Remove(filePath=\"\") strlen(filePath)==0. return Error");
return ResultCode::Error;
}
AzFramework::AssetSystem::FileRemoveRequest request(filePath);
AzFramework::AssetSystem::FileRemoveResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::Remove(filePath=%s) failed to send request. return Error", filePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Remove(filePath=%s) failed to send request. return Error", filePath).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(response.m_resultCode);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Remove(filePath=%s) return %s", filePath, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
Result NetworkFileIO::Copy(const char* sourceFilePath, const char* destinationFilePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Copy(sourceFilePath=%s, destinationFilePath=%s)", sourceFilePath?sourceFilePath:"nullptr", destinationFilePath?destinationFilePath:"nullptr").c_str());
//error checks
if (!sourceFilePath)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Copy(sourceFilePath=nullptr, destinationFilePath=%s) return Error", destinationFilePath?destinationFilePath:"nullptr").c_str());
return ResultCode::Error;
}
if (!destinationFilePath)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Copy(sourceFilePath=%s, destinationFilePath=nullptr) return Error", sourceFilePath?sourceFilePath:"nullptr").c_str());
return ResultCode::Error;
}
if (!strlen(sourceFilePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Copy(sourceFilePath=\"\", destinationFilePath=%s) strlen(sourceFilePath)==0. return Error", destinationFilePath?destinationFilePath:"nullptr").c_str());
return ResultCode::Error;
}
//fail if the source doesn't exist
if (!Exists(sourceFilePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Copy(sourceFilePath=%s, destinationFilePath=%s) Exists(sourceFilePath=%s)==False. return Error", sourceFilePath, destinationFilePath, sourceFilePath).c_str());
return ResultCode::Error;
}
//else both are remote so just issue the remote copy command
AzFramework::AssetSystem::FileCopyRequest request(sourceFilePath, destinationFilePath);
AzFramework::AssetSystem::FileCopyResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::Copy(sourceFilePath=%s, destinationFilePath=%s) failed to send request. return Error", sourceFilePath, destinationFilePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Copy() failed to send request for %s -> %s. return Error", sourceFilePath, destinationFilePath).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(response.m_resultCode);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Copy(sourceFilePath=%s, destinationFilePath=%s) return %s", sourceFilePath, destinationFilePath, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
Result NetworkFileIO::Rename(const char* sourceFilePath, const char* destinationFilePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::Rename(sourceFilePath=%s, destinationFilePath=%s)", sourceFilePath?sourceFilePath:"nullptr", destinationFilePath?destinationFilePath:"nullptr").c_str());
//error checks
if (!sourceFilePath)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=nullptr, destinationFilePath=%s) sourceFilePath=nullptr. return Error", destinationFilePath?destinationFilePath:"nullptr").c_str());
return ResultCode::Error;
}
if (!destinationFilePath)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=%s, destinationFilePath=nullptr) destinationFilePath=nullptr. return Error", sourceFilePath).c_str());
return ResultCode::Error;
}
if (!strlen(sourceFilePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=\"\", destinationFilePath=%s) strlen(sourceFilePath)==0. return Error", destinationFilePath).c_str());
return ResultCode::Error;
}
//fail if the source doesn't exist
if (!Exists(sourceFilePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=%s, destinationFilePath=%s) Exists(sourceFilePath)=False. return Error", sourceFilePath, destinationFilePath).c_str());
return ResultCode::Error;
}
if (!strlen(destinationFilePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=%s, destinationFilePath=\"\") strlen(destinationFilePath)==0. return Error", sourceFilePath).c_str());
return ResultCode::Error;
}
//we are going to access shared memory so lock and copy the results into our memory
//if the source and destination are the same, shortcut
if (!strcmp(sourceFilePath, destinationFilePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=%s, destinationFilePath=%s) sourceFilePath=destinationFilePath. return Error", sourceFilePath, destinationFilePath).c_str());
return ResultCode::Error;
}
//if the destination exists
if (Exists(destinationFilePath))
{
//if its read only fail
if (IsReadOnly(destinationFilePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=%s, destinationFilePath=%s) IsReadOnly(destinationFilePath)=True. return Error", sourceFilePath, destinationFilePath).c_str());
return ResultCode::Error;
}
}
//else both are remote so just issue the remote command
AzFramework::AssetSystem::FileRenameRequest request(sourceFilePath, destinationFilePath);
AzFramework::AssetSystem::FileRenameResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::Rename(sourceFilePath=%s, destinationFilePath=%s) failed to send request. return Error", sourceFilePath, destinationFilePath);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=%s, destinationFilePath=%s) failed to send request. return Error", sourceFilePath, destinationFilePath).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(response.m_resultCode);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::Rename(sourceFilePath=%s, destinationFilePath=%s) return %s", sourceFilePath, destinationFilePath, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
Result NetworkFileIO::FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::FindFiles(filePath=%s, filter=%s, callback=OUT)", filePath?filePath:"nullptr", filter?filter:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::FindFiles(filePath=nullptr, filter=%s) filePath=nullptr. return Error", filter?filter:"nulltpr").c_str());
return ResultCode::Error;
}
if (!filter)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::FindFiles(filePath=%s, filter=nullptr) filter=nullptr. return Error", filePath).c_str());
return ResultCode::Error;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::FindFiles(filePath=\"\", filter=%s) strlen(filePath)==0. return Error", filter).c_str());
return ResultCode::Error;
}
AzFramework::AssetSystem::FindFilesRequest request(filePath, filter);
AzFramework::AssetSystem::FindFilesResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "NetworkFileIO::FindFiles(filePath=%s, filter=%s) could not send request. return Error", filePath, filter);
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::FindFiles(filePath=%s, filter=%s) could not send request. return Error", filePath, filter).c_str());
return ResultCode::Error;
}
ResultCode returnValue = static_cast<ResultCode>(response.m_resultCode);
if (returnValue == ResultCode::Success)
{
// callbacks
const uint64_t numFiles = response.m_files.size();
for (uint64_t fileIdx = 0; fileIdx < numFiles; ++fileIdx)
{
const char* fileName = response.m_files[fileIdx].c_str();
if (!callback(fileName))
{
fileIdx = numFiles;//we are done
}
}
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::FindFiles(filePath=%s, filter=%s) return %s", filePath, filter, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
void NetworkFileIO::SetAlias([[maybe_unused]] const char* alias, [[maybe_unused]] const char* path)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::SetAlias(alias=%s, path=%s)", alias?alias:"nullptr", path?path:"nullptr").c_str());
}
const char* NetworkFileIO::GetAlias([[maybe_unused]] const char* alias) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::GetAlias(alias=%s)", alias?alias:"nullptr").c_str());
REMOTEFILE_LOG_APPEND("NetworkFileIO::GetAlias() return nullptr");
return nullptr;
}
void NetworkFileIO::ClearAlias([[maybe_unused]] const char* alias)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ClearAlias(alias=%s)", alias?alias:"nullptr").c_str());
}
AZStd::optional<AZ::u64> NetworkFileIO::ConvertToAlias(char* inOutBuffer, [[maybe_unused]] AZ::u64 bufferLength) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ConvertToAlias(inOutBuffer=%s, bufferLength=%u)", inOutBuffer?inOutBuffer:"nullptr", bufferLength).c_str());
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::ConvertToAlias() return %u", strlen(inOutBuffer)).c_str());
return strlen(inOutBuffer);
}
bool NetworkFileIO::ConvertToAlias([[maybe_unused]] AZ::IO::FixedMaxPath& convertedPath, [[maybe_unused]] const AZ::IO::PathView& path) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ConvertToAlias(path=%.*s)",
aznumeric_cast<int>(path.Native().size()), path.Native().data()).c_str());
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::ConvertToAlias() return false", convertedPath.Native().size()).c_str());
return false;
}
bool NetworkFileIO::ResolvePath([[maybe_unused]] const char* path, [[maybe_unused]] char* resolvedPath, [[maybe_unused]] AZ::u64 resolvedPathSize) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ResolvePath(path=%s, resolvedPath=%s, resolvedPathsize=%u)", path?path:"nullptr", resolvedPath?resolvedPath:"nullptr", resolvedPathSize).c_str());
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::ResolvePath(path=%s, resolvedPath=%s, resolvedPathsize=%u) return False", path?path:"nullptr", resolvedPath?resolvedPath:"nullptr", resolvedPathSize).c_str());
return false;
}
bool NetworkFileIO::ResolvePath([[maybe_unused]] AZ::IO::FixedMaxPath& resolvedPath, [[maybe_unused]] const AZ::IO::PathView& path) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ResolvePath(path=%.*s)",
aznumeric_cast<int>(path.Native().size()), path.Native.data()).c_str());
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::ResolvePath(path=%.*s) return false",
aznumeric_cast<int>(path.Native().size()), path.Native().data()).c_str());
return false;
}
bool NetworkFileIO::ReplaceAlias([[maybe_unused]] AZ::IO::FixedMaxPath& replaceAliasPath, [[maybe_unused]] const AZ::IO::PathView& path) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::ReplaceAlias(path=%.*s)",
aznumeric_cast<int>(path.Native().size()), path.Native.data()).c_str());
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::ReplaceAlias(path=%.*s) return false",
aznumeric_cast<int>(path.Native().size()), path.Native().data()).c_str());
return false;
}
bool NetworkFileIO::GetFilename(HandleType fileHandle, char* filename, AZ::u64 filenameSize) const
{
REMOTEFILE_LOG_CALL(AZStd::string::format("NetworkFileIO()::GetFilename(fileHandle=%u, filename=%s, filenamesize=%u)", fileHandle, filename?filename:"nullptr", filenameSize).c_str());
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFilesGuard);
const auto fileIt = m_remoteFiles.find(fileHandle);
if (fileIt != m_remoteFiles.end())
{
if (filenameSize >= fileIt->second.length())
{
azstrncpy(filename, filenameSize, fileIt->second.c_str(), fileIt->second.length());
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO()::GetFilename(fileHandle=%u, filename=%s, filenamesize=%u) return True", fileHandle, filename?filename:"nullptr", filenameSize).c_str());
return true;
}
else
{
AZ_TracePrintf(NetworkFileIOChannel, "NetworkFileIO::GetFilename(fileHandle=%u, filename=%s, filenamesize=%u) Result buffer is too small %u", fileHandle, filename?filename:"nullptr", filenameSize, fileIt->second.length());
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO::GetFilename(fileHandle=%u, filename=%s, filenamesize=%u) Result buffer is too small %u", fileHandle, filename?filename:"nullptr", filenameSize, fileIt->second.length()).c_str());
}
}
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("NetworkFileIO()::GetFilename(fileHandle=%u, filename=%s, filenamesize=%u) return False", fileHandle, filename?filename:"nullptr", filenameSize).c_str());
return false;
}
bool NetworkFileIO::IsRemoteIOEnabled()
{
REMOTEFILE_LOG_CALL("NetworkFileIO()::IsRemoteIOEnabled()");
REMOTEFILE_LOG_APPEND("NetworkFileIO()::IsRemoteIOEnabled() return True");
return true;
}
#ifndef REMOTEFILEIO_IS_NETWORKFILEIO
//////////////////////////////////////////////////////////////////////////
const int REFRESH_FILESIZE_TIME = 500;// ms
const size_t CACHE_LOOKAHEAD_SIZE = 1024 * 256;
RemoteFileCache::RemoteFileCache(RemoteFileCache&& other)
{
REMOTEFILE_LOG_CALL("RemoteFileCache()::RemoteFileCache(other)");
*this = AZStd::move(other);
}
AZ::IO::RemoteFileCache& RemoteFileCache::operator=(RemoteFileCache&& other)
{
REMOTEFILE_LOG_CALL("RemoteFileCache()::operator=(other)");
if (this != &other)
{
m_cacheLookaheadBuffer = AZStd::move(other.m_cacheLookaheadBuffer);
m_cacheLookaheadPos = other.m_cacheLookaheadPos;
m_fileSize = other.m_fileSize;
m_fileSizeTime = other.m_fileSizeTime;
m_filePosition = other.m_filePosition;
m_fileHandle = other.m_fileHandle;
m_openMode = other.m_openMode;
}
return *this;
}
void RemoteFileCache::Invalidate()
{
m_cacheLookaheadPos = 0;
m_cacheLookaheadBuffer.clear();
}
AZ::u64 RemoteFileCache::RemainingBytes()
{
return m_cacheLookaheadBuffer.size() - m_cacheLookaheadPos;
}
AZ::u64 RemoteFileCache::CacheFilePosition()
{
return m_filePosition - RemainingBytes();
}
AZ::u64 RemoteFileCache::CacheStartFilePosition()
{
return m_filePosition - m_cacheLookaheadBuffer.size();
}
AZ::u64 RemoteFileCache::CacheEndFilePosition()
{
return m_filePosition;
}
bool RemoteFileCache::IsFilePositionInCache(AZ::u64 filePosition)
{
return filePosition >= CacheStartFilePosition() && filePosition < CacheEndFilePosition();
}
void RemoteFileCache::SetCachePositionFromFilePosition(AZ::u64 filePosition)
{
m_cacheLookaheadPos = filePosition - CacheStartFilePosition();
}
void RemoteFileCache::SyncCheck()
{
#ifdef REMOTEFILEIO_SYNC_CHECK
//don't sync check files open for write
//they can be written to asynchronously so tell may return a different position than is cached
//because we dont wait for the network request to finish before we immediately ask for tell
if (AnyFlag(m_openMode & OpenMode::ModeWrite) ||
AnyFlag(m_openMode & OpenMode::ModeAppend) ||
AnyFlag(m_openMode & OpenMode::ModeUpdate))
{
return;
}
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileCache()::SyncCheck(m_fileHandle=%u)", m_fileHandle).c_str());
FileTellRequest request(m_fileHandle);
FileTellResponse responce;
if (!SendRequest(request, responce))
{
AZ_Assert(false, "RemoteFileCache::SyncCheck(m_fileHandle=%u) Failed to send tell request.", m_fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) Failed to send tell request.", m_fileHandle).c_str());
}
ResultCode returnValue = static_cast<ResultCode>(responce.m_resultCode);
if (returnValue == ResultCode::Error)
{
AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) tell request failed.", m_fileHandle).c_str());
}
if (responce.m_offset != m_filePosition)
{
AZ_TracePrintf(RemoteFileCacheChannel, "RemoteFileCache::SyncCheck(m_fileHandle=%u) failed!!! m_filePosition=%u tell=%u", m_fileHandle, m_filePosition, responce.m_offset);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) failed!!! m_filePosition=%u tell=%u", m_fileHandle, m_filePosition, responce.m_offset).c_str());
}
else
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileCache::SyncCheck(m_fileHandle=%u) Success m_filePosition=%u tell=%u", m_fileHandle, m_filePosition, responce.m_offset).c_str());
}
#endif
}
void RemoteFileCache::SetFilePosition(AZ::u64 filePosition)
{
m_filePosition = filePosition;
SyncCheck();
}
void RemoteFileCache::OffsetFilePosition(AZ::s64 offset)
{
m_filePosition += offset;
SyncCheck();
}
bool RemoteFileCache::Eof()
{
return CacheFilePosition() == m_fileSize;
}
//////////////////////////////////////////////////////////////////////////
RemoteFileIO::RemoteFileIO(FileIOBase* excludedFileIO)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::RemoteFileIO()").c_str());
m_excludedFileIO = excludedFileIO;
#ifdef REMOTEFILEIO_CACHE_FILETREE
CacheFileTree();
#endif
}
RemoteFileIO::~RemoteFileIO()
{
delete m_excludedFileIO; //for now delete it, when We change to always create local file io We won't
#ifdef REMOTEFILEIO_CACHE_FILETREE
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
#endif
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::RemoteFileIO()").c_str());
}
Result RemoteFileIO::Open(const char* filePath, OpenMode openMode, HandleType& fileHandle)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Open(filePath=%s, openMode=%i, fileHandle=OUT)", filePath?filePath:"nullptr", openMode).c_str());
Result returnValue = NetworkFileIO::Open(filePath, openMode, fileHandle);
if (returnValue == ResultCode::Success)
{
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
m_remoteFileCache.insert(fileHandle);
RemoteFileCache& cache = GetCache(fileHandle);
cache.m_fileHandle = fileHandle;
cache.m_openMode = openMode;
}
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Open(filePath=%s, fileHandle=%u) return %s", filePath?filePath:"nullptr", fileHandle, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
Result RemoteFileIO::Close(HandleType fileHandle)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Close(fileHandle=%u)", fileHandle).c_str());
Result returnValue = NetworkFileIO::Close(fileHandle);
if (returnValue == ResultCode::Success)
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
m_remoteFileCache.erase(fileHandle);
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Open(fileHandle=%u) return %s", fileHandle, returnValue == ResultCode::Success ? "Success" : "Error").c_str());
return returnValue;
}
Result RemoteFileIO::Tell(HandleType fileHandle, AZ::u64& offset)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Tell(fileHandle=%u, offset=OUT)", fileHandle).c_str());
{
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
offset = cache.CacheFilePosition();
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Tell(fileHandle=%u, offset=%i) return Success", fileHandle, offset).c_str());
return ResultCode::Success;
}
Result RemoteFileIO::Seek(HandleType fileHandle, AZ::s64 offset, SeekType type)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Seek(fileHandle=%u, offset=%i, type=%s)", fileHandle, offset, type==SeekType::SeekFromCurrent ? "SeekFromCurrent" : type==SeekType::SeekFromEnd ? "SeekFromEnd" : type==SeekType::SeekFromStart ? "SeekFromStart" : "Unknown").c_str());
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
//if we land in the cache all we need to do is adjust the cache position and return
//calculate the new position in the file
AZ::u64 newFilePosition = 0;
if (type == AZ::IO::SeekType::SeekFromCurrent)
{
newFilePosition = cache.CacheFilePosition() + offset;
}
else if (type == AZ::IO::SeekType::SeekFromStart)
{
newFilePosition = offset;
}
else if (type == AZ::IO::SeekType::SeekFromEnd)
{
AZ::u64 fileSize = 0;
if(Size(fileHandle, fileSize)== ResultCode::Error)
{
return ResultCode::Error;
}
newFilePosition = fileSize + offset;
}
else
{
AZ_Assert(false, "RemoteFileIO::Seek(fileHandle=%u, offset=%i, type=%s) unknown seektype. return Error", fileHandle, offset, type == SeekType::SeekFromCurrent ? "SeekFromCurrent" : type == SeekType::SeekFromEnd ? "SeekFromEnd" : type == SeekType::SeekFromStart ? "SeekFromStart" : "Unknown");
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Seek(fileHandle=%u, offset=%i, type=%s) unknown seektype. return Error", fileHandle, offset, type == SeekType::SeekFromCurrent ? "SeekFromCurrent" : type == SeekType::SeekFromEnd ? "SeekFromEnd" : type == SeekType::SeekFromStart ? "SeekFromStart" : "Unknown").c_str());
return ResultCode::Error;
}
{
AZ::u64 fileSize = 0;
Size(fileHandle, fileSize);
if (newFilePosition > fileSize)
{
AZ_TracePrintf(RemoteFileIOChannel, "RemoteFileIO::Seek(fileHandle=%u, offset=%i, type=%s) seek to a position after the end of a file!", fileHandle, offset, type == SeekType::SeekFromCurrent ? "SeekFromCurrent" : type == SeekType::SeekFromEnd ? "SeekFromEnd" : type == SeekType::SeekFromStart ? "SeekFromStart" : "Unknown");
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Seek(fileHandle=%u, offset=%i, type=%s) seek to a position after the end of a file!", fileHandle, offset, type == SeekType::SeekFromCurrent ? "SeekFromCurrent" : type == SeekType::SeekFromEnd ? "SeekFromEnd" : type == SeekType::SeekFromStart ? "SeekFromStart" : "Unknown").c_str());
newFilePosition = fileSize;
}
}
//see if the new calculated position is in the cache
if (cache.IsFilePositionInCache(newFilePosition))
{
//it is, so calculate what the cache position should be given the new file position and
//set it and return success
cache.SetCachePositionFromFilePosition(newFilePosition);
cache.SyncCheck();
return ResultCode::Success;
}
else if (newFilePosition == cache.m_filePosition)
{
cache.SyncCheck();
return ResultCode::Success;
}
//we didn't land in the cache
//perform the seek for real, invalidate and set new file position
//note when setting a new absolute position we always use SeekFromStart, not the passed in seek type
AzFramework::AssetSystem::FileSeekRequest request(fileHandle, static_cast<AZ::u32>(AZ::IO::SeekType::SeekFromStart), newFilePosition);
if (!SendRequest(request))
{
AZ_Assert(false, "RemoteFileIO::Seek(fileHandle=%u, offset=%i, type=%s) Failed to send request. return Error", fileHandle, offset, type == SeekType::SeekFromCurrent ? "SeekFromCurrent" : type == SeekType::SeekFromEnd ? "SeekFromEnd" : type == SeekType::SeekFromStart ? "SeekFromStart" : "Unknown");
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Seek(fileHandle=%u, offset=%i, type=%s) Failed to send request. return Error", fileHandle, offset, type == SeekType::SeekFromCurrent ? "SeekFromCurrent" : type == SeekType::SeekFromEnd ? "SeekFromEnd" : type == SeekType::SeekFromStart ? "SeekFromStart" : "Unknown").c_str());
return ResultCode::Error;
}
cache.Invalidate();
cache.SetFilePosition(newFilePosition);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Seek(fileHandle=%u, offset=%i, type=%s) return Success", fileHandle, offset, type == SeekType::SeekFromCurrent ? "SeekFromCurrent" : type == SeekType::SeekFromEnd ? "SeekFromEnd" : type == SeekType::SeekFromStart ? "SeekFromStart" : "Unknown").c_str());
return ResultCode::Success;
}
Result RemoteFileIO::Size(HandleType fileHandle, AZ::u64& size)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO::Size(fileHandle=%u, size=OUT)", fileHandle).c_str());
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
// do we even have to check?
AZ::u64 msNow = AZStd::GetTimeUTCMilliSecond();
if (msNow - cache.m_fileSizeTime > REFRESH_FILESIZE_TIME)
{
if (NetworkFileIO::Size(fileHandle, cache.m_fileSize))
{
cache.m_fileSizeTime = msNow;
size = cache.m_fileSize;
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Size(fileHandle=%u) size=%u. return Success", fileHandle, size).c_str());
return ResultCode::Success;
}
}
else
{
size = cache.m_fileSize;
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Size(fileHandle=%u) size=%u return Success", fileHandle, size).c_str());
return ResultCode::Success;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Size(fileHandle=%u) return Error", fileHandle).c_str());
return ResultCode::Error;
}
Result RemoteFileIO::Read(HandleType fileHandle, void* buffer, AZ::u64 size, bool failOnFewerThanSizeBytesRead, AZ::u64* bytesRead)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Read(fileHandle=%u, buffer=OUT, size=%u, failOnFewerThanSizeBytesRead=%s, bytesRead=OUT)", fileHandle, size, failOnFewerThanSizeBytesRead ? "True" : "False").c_str());
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
AZ::u64 remainingBytesToRead = size;
AZ::u64 bytesReadFromCache = 0;
AZ::u64 remainingBytesInCache = cache.RemainingBytes();
if (remainingBytesInCache)
{
AZ::u64 bytesToReadFromCache = AZStd::GetMin<AZ::u64>(remainingBytesInCache, remainingBytesToRead);
memcpy(buffer, cache.m_cacheLookaheadBuffer.data() + cache.m_cacheLookaheadPos, bytesToReadFromCache);
bytesReadFromCache = bytesToReadFromCache;
remainingBytesToRead -= bytesToReadFromCache;
cache.m_cacheLookaheadPos += bytesToReadFromCache;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Read(fileHandle=%u) bytesReadFromCache=%u remainingBytesToRead=%u", fileHandle, bytesReadFromCache, remainingBytesToRead).c_str());
AZ::u64 bytesThatHaveBeenRead = bytesReadFromCache;
if (bytesRead)
{
*bytesRead = bytesThatHaveBeenRead;
}
buffer = reinterpret_cast<char*>(buffer) + bytesReadFromCache;
if (remainingBytesToRead)
{
AZ::u64 actualRead = 0;
Result returnValue = NetworkFileIO::Read(fileHandle, buffer, remainingBytesToRead, true, &actualRead);
remainingBytesToRead -= actualRead;
if (actualRead)
{
cache.OffsetFilePosition(actualRead);
}
bytesThatHaveBeenRead += actualRead;
if (bytesRead)
{
*bytesRead = bytesThatHaveBeenRead;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Read(fileHandle=%u, size=%u) %s remainingBytesToRead=%u. actualRead=%u", fileHandle, remainingBytesToRead, returnValue==ResultCode::Success ? "Success" : "Fail", remainingBytesToRead, actualRead).c_str());
//if we get an error, we only return an error if failOnFewerThanSizeBytesRead
if (returnValue == ResultCode::Error)
{
if (failOnFewerThanSizeBytesRead && remainingBytesToRead)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Read(fileHandle=%u, bytesThatHaveBeenRead=%u) failOnFewerThanSizeBytesRead. return Error", fileHandle, bytesThatHaveBeenRead).c_str());
return ResultCode::Error;
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Read(fileHandle=%u, bytesThatHaveBeenRead=%u) return Success", fileHandle, bytesThatHaveBeenRead).c_str());
return ResultCode::Success;
}
}
//they could have asked for more bytes than there is in the file
if (failOnFewerThanSizeBytesRead && remainingBytesToRead && Eof(fileHandle))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Read(fileHandle=%u, bytesThatHaveBeenRead=%u) failOnFewerThanSizeBytesRead. return Error", fileHandle, bytesThatHaveBeenRead).c_str());
return ResultCode::Error;
}
//if we get here we have satisfied the read request.
//if the cache is empty try to refill it if not eof.
if (!cache.RemainingBytes() && !Eof(fileHandle))
{
//make sure the cache file size is up to date
AZ::u64 fsize = 0;
Size(fileHandle, fsize);
AZ::u64 remainingFileBytes = cache.m_fileSize - cache.m_filePosition;
AZ::u64 readSize = AZStd::GetMin<AZ::u64>(remainingFileBytes, CACHE_LOOKAHEAD_SIZE);
cache.m_cacheLookaheadBuffer.clear();
cache.m_cacheLookaheadBuffer.resize_no_construct(readSize);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Read(fileHandle=%u, size=%u) -=CACHE READ=-", fileHandle, readSize).c_str());
AZ::u64 actualRead = 0;
Result returnValue = NetworkFileIO::Read(fileHandle, cache.m_cacheLookaheadBuffer.data(), readSize, false, &actualRead);
if (actualRead)
{
cache.m_cacheLookaheadBuffer.resize(actualRead);
cache.OffsetFilePosition(actualRead);
cache.m_cacheLookaheadPos = 0;
}
if (returnValue == ResultCode::Error)
{
AZ_TracePrintf(RemoteFileIOChannel, "RemoteFileIO::Read(fileHandle=%u, size=%u) -=CACHE READ=- actualRead=%i Failed", fileHandle, readSize, actualRead);
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Read(fileHandle=%u, size=%u) -=CACHE READ=- actualRead=%i %s", fileHandle, readSize, actualRead, returnValue == ResultCode::Success ? "Success" : "Fail").c_str());
}
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Read(fileHandle=%u, bytesThatHaveBeenRead=%u) return Success", fileHandle, bytesThatHaveBeenRead).c_str());
return ResultCode::Success;
}
Result RemoteFileIO::Write(HandleType fileHandle, const void* buffer, AZ::u64 size, AZ::u64* bytesWritten)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Write(fileHandle=%u, buffer=OUT, size=%u, bytesWritten=OUT)", fileHandle, size).c_str());
// We need to seek back to where we should be in the file before we commit a write.
// This is unnecessary if the cache is empty, or we're at the end of the cache.
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
if (cache.m_cacheLookaheadBuffer.size() && cache.RemainingBytes())
{
// find out where we are
AZ::u64 seekPosition = cache.CacheFilePosition();
// note, seeks are predicted, and do not ask for a response.
AzFramework::AssetSystem::FileSeekRequest request(fileHandle, static_cast<AZ::u32>(AZ::IO::SeekType::SeekFromStart), seekPosition);
if (!SendRequest(request))
{
AZ_Assert(false, "RemoteFileIO::Write(fileHandle=%u, size=%u) Seek Failed to send request. return Error", fileHandle, size);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Write(fileHandle=%u, size=%u) Seek Failed to send request. return Error", fileHandle, size).c_str());
return ResultCode::Error;
}
cache.SetFilePosition(seekPosition);
}
cache.Invalidate();
REMOTEFILE_LOG_APPEND("RemoteFileIO::Write() cache.Invalidate()");
cache.m_fileSizeTime = 0; // invalidate file size after write.
AzFramework::AssetSystem::FileWriteRequest request(fileHandle, buffer, size);
//always async and just return success unless bytesWritten is set then synchronous
if (bytesWritten)
{
AzFramework::AssetSystem::FileWriteResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "RemoteFileIO::Write(fileHandle=%u, size=%u) failed to send sync write request. return Error", fileHandle, size);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Write(fileHandle=%u, size=%u) failed to send sync write request. return Error", fileHandle, size).c_str());
return ResultCode::Error;
}
ResultCode res = static_cast<ResultCode>(response.m_resultCode);
if (res == ResultCode::Error)
{
AZ_TracePrintf(RemoteFileIOChannel, "RemoteFileIO::Write(fileHandle=%u, size=%u) sync write request failed", fileHandle, size);
}
*bytesWritten = response.m_bytesWritten;
cache.OffsetFilePosition(response.m_bytesWritten);
}
else
{
// just send the message and assume we wrote it all successfully.
if (!SendRequest(request))
{
AZ_Assert(false, "RemoteFileIO::Write(fileHandle=%u, size=%u) failed to send async write request. return Error", fileHandle, size);
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Write(fileHandle=%u, size=%u) failed to send async write request. return Error", fileHandle, size).c_str());
return ResultCode::Error;
}
cache.OffsetFilePosition(size);
}
REMOTEFILE_LOG_APPEND("RemoteFileIO::Write() Success");
return ResultCode::Success;
}
bool RemoteFileIO::Eof(HandleType fileHandle)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Eof(fileHandle=%u)", fileHandle).c_str());
//make sure the cache file size is up to date
AZ::u64 size = 0;
Size(fileHandle, size);
AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
RemoteFileCache& cache = GetCache(fileHandle);
bool isEof = cache.Eof();
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Eof(fileHandle=%u) return %s", fileHandle, isEof ? "True" : "False").c_str());
return isEof;
}
//get cache should only be called while AZStd::lock_guard<AZStd::recursive_mutex> lock(m_remoteFileCacheGuard);
AZ::IO::RemoteFileCache& RemoteFileIO::GetCache(HandleType fileHandle)
{
auto found = m_remoteFileCache.find(fileHandle);
// check this because it is a serious error since it may be that you're in an unguarded access to a non-existent handle.
AZ_Assert(found != m_remoteFileCache.end(), "RemoteFileIO::GetCache(fileHandle=%u) Missing! Did something go wrong with open?", fileHandle);
return found->second;
}
void RemoteFileIO::SetAlias(const char* alias, const char* path)
{
if (m_excludedFileIO)
{
m_excludedFileIO->SetAlias(alias, path);
}
}
const char* RemoteFileIO::GetAlias(const char* alias) const
{
return m_excludedFileIO ? m_excludedFileIO->GetAlias(alias) : nullptr;
}
void RemoteFileIO::ClearAlias(const char* alias)
{
if (m_excludedFileIO)
{
m_excludedFileIO->ClearAlias(alias);
}
}
AZStd::optional<AZ::u64> RemoteFileIO::ConvertToAlias(char* inOutBuffer, AZ::u64 bufferLength) const
{
return m_excludedFileIO ? m_excludedFileIO->ConvertToAlias(inOutBuffer, bufferLength) : strlen(inOutBuffer);
}
bool RemoteFileIO::ConvertToAlias(AZ::IO::FixedMaxPath& convertedPath, const AZ::IO::PathView& path) const
{
return m_excludedFileIO ? m_excludedFileIO->ConvertToAlias(convertedPath, path) : false;
}
bool RemoteFileIO::ResolvePath(const char* path, char* resolvedPath, AZ::u64 resolvedPathSize) const
{
return m_excludedFileIO ? m_excludedFileIO->ResolvePath(path, resolvedPath, resolvedPathSize) : false;
}
bool RemoteFileIO::ResolvePath(AZ::IO::FixedMaxPath& resolvedPath, const AZ::IO::PathView& path) const
{
return m_excludedFileIO ? m_excludedFileIO->ResolvePath(resolvedPath, path) : false;
}
bool RemoteFileIO::ReplaceAlias(AZ::IO::FixedMaxPath& replaceAliasPath, const AZ::IO::PathView& path) const
{
return m_excludedFileIO ? m_excludedFileIO->ReplaceAlias(replaceAliasPath, path) : false;
}
#ifdef REMOTEFILEIO_CACHE_FILETREE
bool RemoteFileIO::Exists(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::Exists(filePath=%s)", filePath?filePath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::Exists(filePath=nullptr) filePath=nullptr. return False").c_str());
return false;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("RemoteFileIO::Exists(filePath=\"\") strlen(filePath)==0. return False");
return false;
}
AZStd::string filePathName(filePath);
AZStd::replace(filePathName.begin(), filePathName.end(), '\\', '/');
AZStd::size_t lastNonSlash = filePathName.find_last_not_of('/');
AZStd::size_t lastSlash = filePathName.find_last_of('/');
if (lastSlash != AZStd::string::npos && lastSlash > lastNonSlash)
{
filePathName.erase(lastSlash);
}
filePath = filePathName.c_str();
const uint64_t numFiles = m_remoteFileTreeCache.size();
for (uint64_t fileIdx = 0; fileIdx < numFiles; ++fileIdx)
{
const char* fileName = m_remoteFileTreeCache[fileIdx].c_str();
if (!azstricmp(filePath, fileName))
{
return true;
}
}
const uint64_t numFolders = m_remoteFolderTreeCache.size();
for (uint64_t folderIdx = 0; folderIdx < numFolders; ++folderIdx)
{
const char* folderName = m_remoteFolderTreeCache[folderIdx].c_str();
if (!azstricmp(filePath, folderName))
{
return true;
}
}
#ifdef REMOTEFILEIO_CACHE_FILETREE_FALLBACK
//fall back
bool bExists = NetworkFileIO::Exists(filePath);
if (bExists)
{
//it wasn't found before, but it is now, update the cache
if (NetworkFileIO::IsDirectory(filePath))
{
m_remoteFolderTreeCache.push_back(filePath);
}
else
{
m_remoteFileTreeCache.push_back(filePath);
}
}
return bExists;
#else
return false;
#endif
}
bool RemoteFileIO::IsDirectory(const char* filePath)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::IsDirectory(filePath=%s)", filePath?filepath:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND("RemoteFileIO::IsDirectory(filePath=nullptr) filePath=nullptr. return False");
return false;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND("RemoteFileIO::IsDirectory(filePath=\"\") strlen(filePath)==0. return False");
return false;
}
AZStd::string filePathName(filePath);
AZStd::replace(filePathName.begin(), filePathName.end(), '\\', '/');
AZStd::size_t lastNonSlash = filePathName.find_last_not_of('/');
AZStd::size_t lastSlash = filePathName.find_last_of('/');
if (lastSlash != AZStd::string::npos && lastSlash > lastNonSlash)
{
filePathName.erase(lastSlash);
}
filePath = filePathName.c_str();
const uint64_t numFolders = m_remoteFolderTreeCache.size();
for (uint64_t folderIdx = 0; folderIdx < numFolders; ++folderIdx)
{
const char* folderName = m_remoteFolderTreeCache[folderIdx].c_str();
if (!azstricmp(filePath, folderName))
{
return true;
}
}
#ifdef REMOTEFILEIO_CACHE_FILETREE_FALLBACK
//fallback
bool bExists = NetworkFileIO::IsDirectory(filePath);
if (bExists)
{
//it wasn't found before, but it is now, update the cache
m_remoteFolderTreeCache.push_back(filePath);
}
return bExists;
#else
return false;
#endif
}
Result RemoteFileIO::FindFiles(const char* filePath, const char* filter, FindFilesCallbackType callback)
{
REMOTEFILE_LOG_CALL(AZStd::string::format("RemoteFileIO()::FindFiles(filePath=%s, filter=%s, callback=OUT)", filePath?filePath:"nullptr", filter?filter:"nullptr").c_str());
//error checks
if (!filePath)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::FindFiles(filePath=nullptr, filter=%s) filePath=nullptr. return Error", filter?filter:"nullptr").c_str());
return ResultCode::Error;
}
if (!filter)
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::FindFiles(filePath=%s, filter=nullptr) filter=nullptr. return Error", filePath).c_str());
return ResultCode::Error;
}
if (!strlen(filePath))
{
REMOTEFILE_LOG_APPEND(AZStd::string::format("RemoteFileIO::FindFiles(filePath=\"\", filter=%s) strlen(filePath)==0. return Error", filter).c_str());
return ResultCode::Error;
}
AZStd::string filePathName(filePath);
AZStd::replace(filePathName.begin(), filePathName.end(), '\\', '/');
AZStd::size_t lastNonSlash = filePathName.find_last_not_of('/');
AZStd::size_t lastSlash = filePathName.find_last_of('/');
if (lastSlash != AZStd::string::npos && lastSlash > lastNonSlash)
{
filePathName.erase(lastSlash);
}
filePath = filePathName.c_str();
AZ::u64 filePathLen = filePathName.length();
// files callbacks
const uint64_t numFiles = m_remoteFileTreeCache.size();
for (uint64_t fileIdx = 0; fileIdx < numFiles; ++fileIdx)
{
const char* cachedFilePath = m_remoteFileTreeCache[fileIdx].c_str();
if (!azstrnicmp(filePath, cachedFilePath, filePathLen))
{
if (strlen(cachedFilePath) > filePathLen)
{
const char* cachedFileName = cachedFilePath + filePathLen + 1;
if (!strchr(cachedFileName, '/'))
{
//no slash was found so this file is in this folder
if (AZStd::wildcard_match(filter, cachedFileName))
{
if (!callback(cachedFilePath))
{
return ResultCode::Success;
}
}
}
}
}
}
// folders
const uint64_t numFolders = m_remoteFolderTreeCache.size();
for (uint64_t folderIdx = 0; folderIdx < numFolders; ++folderIdx)
{
const char* cachedFolderPath = m_remoteFolderTreeCache[folderIdx].c_str();
if (!azstrnicmp(filePath, cachedFolderPath, filePathLen))
{
if (strlen(cachedFolderPath) > filePathLen)
{
const char* cachedFolderName = cachedFolderPath + filePathLen + 1;
if (!strchr(cachedFolderName, '/'))
{
//no slash was found so this is not a sub folder
if (AZStd::wildcard_match(filter, cachedFolderName))
{
if (!callback(cachedFolderPath))
{
return ResultCode::Success;
}
}
}
}
}
}
return ResultCode::Success;
}
Result RemoteFileIO::CacheFileTree()
{
m_remoteFileTreeCache.clear();
m_remoteFolderTreeCache.clear();
REMOTEFILE_LOG_CALL("RemoteFileIO()::CacheFileTree()");
FileTreeRequest request;
FileTreeResponse response;
if (!SendRequest(request, response))
{
AZ_Assert(false, "RemoteFileIO::CacheFileTree() could not send request. return Error");
REMOTEFILE_LOG_APPEND("RemoteFileIO::CacheFileTree() could not send request. return Error");
return ResultCode::Error;
}
Result returnValue = static_cast<ResultCode>(response.m_resultCode);
if (returnValue == ResultCode::Success)
{
for (auto& it: response.m_fileList)
{
AZStd::replace(it.begin(), it.end(), '\\', '/');
m_remoteFileTreeCache.push_back(it);
}
for (auto& it: response.m_folderList)
{
AZStd::replace(it.begin(), it.end(), '\\', '/');
AZStd::size_t lastNonSlash = it.find_last_not_of('/');
AZStd::size_t lastSlash = it.find_last_of('/');
if (lastSlash != AZStd::string::npos && lastSlash > lastNonSlash)
{
it.erase(lastSlash);
}
m_remoteFolderTreeCache.push_back(it);
}
}
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
return returnValue;
}
//=========================================================================
// AssetCatalogEventBus::OnCatalogAssetChanged
//=========================================================================
void RemoteFileIO::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
{
AZ_UNUSED(assetId);
CacheFileTree();
}
//=========================================================================
// AssetSystemBus::OnCatalogAssetRemoved
//=========================================================================
void RemoteFileIO::OnCatalogAssetRemoved(const AZ::Data::AssetId& assetId)
{
AZ_UNUSED(assetId);
CacheFileTree();
}
#endif //REMOTEFILEIO_CACHE_FILETREE
#endif //REMOTEFILEIO_IS_NETWORKFILEIO
} // namespace IO
}//namespace AZ