Cemu/src/Cafe/Filesystem/fscDeviceHostFS.cpp

295 lines
No EOL
7.5 KiB
C++

#include "config/ActiveSettings.h"
#include "Cafe/Filesystem/fsc.h"
#include "Cafe/Filesystem/fscDeviceHostFS.h"
#include "Common/FileStream.h"
/* FSCVirtualFile implementation for HostFS */
FSCVirtualFile_Host::~FSCVirtualFile_Host()
{
if (m_type == FSC_TYPE_FILE)
delete m_fs;
}
sint32 FSCVirtualFile_Host::fscGetType()
{
return m_type;
}
uint32 FSCVirtualFile_Host::fscDeviceHostFSFile_getFileSize()
{
if (m_type == FSC_TYPE_FILE)
{
if (m_fileSize > 0xFFFFFFFFULL)
cemu_assert_suspicious(); // files larger than 4GB are not supported by Wii U filesystem
return (uint32)m_fileSize;
}
else if (m_type == FSC_TYPE_DIRECTORY)
{
// todo
return (uint32)0;
}
return 0;
}
uint64 FSCVirtualFile_Host::fscQueryValueU64(uint32 id)
{
if (m_type == FSC_TYPE_FILE)
{
if (id == FSC_QUERY_SIZE)
return fscDeviceHostFSFile_getFileSize();
else if (id == FSC_QUERY_WRITEABLE)
return m_isWritable;
else
cemu_assert_unimplemented();
}
else if (m_type == FSC_TYPE_DIRECTORY)
{
if (id == FSC_QUERY_SIZE)
return fscDeviceHostFSFile_getFileSize();
else
cemu_assert_unimplemented();
}
cemu_assert_unimplemented();
return 0;
}
uint32 FSCVirtualFile_Host::fscWriteData(void* buffer, uint32 size)
{
if (m_type != FSC_TYPE_FILE)
return 0;
if (size >= (2UL * 1024UL * 1024UL * 1024UL))
{
cemu_assert_suspicious();
return 0;
}
sint32 writtenBytes = m_fs->writeData(buffer, (sint32)size);
m_seek += (uint64)writtenBytes;
m_fileSize = std::max(m_fileSize, m_seek);
return (uint32)writtenBytes;
}
uint32 FSCVirtualFile_Host::fscReadData(void* buffer, uint32 size)
{
if (m_type != FSC_TYPE_FILE)
return 0;
if (size >= (2UL * 1024UL * 1024UL * 1024UL))
{
cemu_assert_suspicious();
return 0;
}
uint32 bytesLeft = (uint32)(m_fileSize - m_seek);
bytesLeft = std::min(bytesLeft, 0x7FFFFFFFu);
sint32 bytesToRead = std::min(bytesLeft, size);
uint32 bytesRead = m_fs->readData(buffer, bytesToRead);
m_seek += bytesRead;
return bytesRead;
}
void FSCVirtualFile_Host::fscSetSeek(uint64 seek)
{
if (m_type != FSC_TYPE_FILE)
return;
this->m_seek = seek;
cemu_assert_debug(seek <= m_fileSize);
m_fs->SetPosition(seek);
}
uint64 FSCVirtualFile_Host::fscGetSeek()
{
if (m_type != FSC_TYPE_FILE)
return 0;
return m_seek;
}
void FSCVirtualFile_Host::fscSetFileLength(uint64 endOffset)
{
if (m_type != FSC_TYPE_FILE)
return;
m_fs->SetPosition(endOffset);
bool r = m_fs->SetEndOfFile();
m_seek = std::min(m_seek, endOffset);
m_fileSize = m_seek;
m_fs->SetPosition(m_seek);
if (!r)
cemuLog_log(LogType::Force, "fscSetFileLength: Failed to set size to 0x{:x}", endOffset);
}
bool FSCVirtualFile_Host::fscDirNext(FSCDirEntry* dirEntry)
{
if (m_type != FSC_TYPE_DIRECTORY)
return false;
if (!m_dirIterator)
{
// init iterator on first iteration attempt
m_dirIterator.reset(new fs::directory_iterator(*m_path));
if (!m_dirIterator)
{
cemuLog_log(LogType::Force, "Failed to iterate directory: {}", _pathToUtf8(*m_path));
return false;
}
}
if (*m_dirIterator == fs::end(*m_dirIterator))
return false;
const fs::directory_entry& entry = **m_dirIterator;
std::string fileName = entry.path().filename().generic_string();
if (fileName.size() >= sizeof(dirEntry->path) - 1)
fileName.resize(sizeof(dirEntry->path) - 1);
strncpy(dirEntry->path, fileName.data(), sizeof(dirEntry->path));
if (entry.is_directory())
{
dirEntry->isDirectory = true;
dirEntry->isFile = false;
dirEntry->fileSize = 0;
}
else
{
dirEntry->isDirectory = false;
dirEntry->isFile = true;
dirEntry->fileSize = entry.file_size();
}
(*m_dirIterator)++;
return true;
}
FSCVirtualFile* FSCVirtualFile_Host::OpenFile(const fs::path& path, FSC_ACCESS_FLAG accessFlags, sint32& fscStatus)
{
if (!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE) && !HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
cemu_assert_debug(false); // not allowed. At least one of both flags must be set
// attempt to open as file
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
{
FileStream* fs{};
bool writeAccessRequested = HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION);
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALLOW_CREATE))
{
fs = FileStream::openFile2(path, writeAccessRequested);
if (!fs)
{
cemu_assert_debug(writeAccessRequested);
fs = FileStream::createFile2(path);
if (!fs)
cemuLog_log(LogType::Force, "FSC: File create failed for {}", _pathToUtf8(path));
}
}
else if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALWAYS_CREATE))
{
fs = FileStream::createFile2(path);
if (!fs)
cemuLog_log(LogType::Force, "FSC: File create failed for {}", _pathToUtf8(path));
}
else
{
fs = FileStream::openFile2(path, writeAccessRequested);
}
if (fs)
{
FSCVirtualFile_Host* vf = new FSCVirtualFile_Host(FSC_TYPE_FILE);
vf->m_fs = fs;
vf->m_isWritable = writeAccessRequested;
vf->m_fileSize = fs->GetSize();
fscStatus = FSC_STATUS_OK;
return vf;
}
}
// attempt to open as directory
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
{
std::error_code ec;
bool isExistingDir = fs::is_directory(path, ec);
if (isExistingDir)
{
FSCVirtualFile_Host* vf = new FSCVirtualFile_Host(FSC_TYPE_DIRECTORY);
vf->m_path.reset(new std::filesystem::path(path));
fscStatus = FSC_STATUS_OK;
return vf;
}
}
fscStatus = FSC_STATUS_FILE_NOT_FOUND;
return nullptr;
}
/* Device implementation */
class fscDeviceHostFSC : public fscDeviceC
{
public:
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
{
*fscStatus = FSC_STATUS_OK;
FSCVirtualFile* vf = FSCVirtualFile_Host::OpenFile(_utf8ToPath(path), accessFlags, *fscStatus);
cemu_assert_debug((bool)vf == (*fscStatus == FSC_STATUS_OK));
return vf;
}
bool fscDeviceCreateDir(std::string_view path, void* ctx, sint32* fscStatus) override
{
fs::path dirPath = _utf8ToPath(path);
if (fs::exists(dirPath))
{
if (!fs::is_directory(dirPath))
cemuLog_log(LogType::Force, "CreateDir: {} already exists but is not a directory", path);
*fscStatus = FSC_STATUS_ALREADY_EXISTS;
return false;
}
std::error_code ec;
bool r = fs::create_directories(dirPath, ec);
if (!r)
cemuLog_log(LogType::Force, "CreateDir: Failed to create {}", path);
*fscStatus = FSC_STATUS_OK;
return true;
}
bool fscDeviceRemoveFileOrDir(std::string_view path, void* ctx, sint32* fscStatus) override
{
*fscStatus = FSC_STATUS_OK;
fs::path _path = _utf8ToPath(path);
std::error_code ec;
if (!fs::exists(_path, ec))
{
*fscStatus = FSC_STATUS_FILE_NOT_FOUND;
return false;
}
if (!fs::remove(_path, ec))
{
cemu_assert_unimplemented(); // return correct error (e.g. if directory is non-empty)
*fscStatus = FSC_STATUS_FILE_NOT_FOUND;
}
return true;
}
bool fscDeviceRename(std::string_view srcPath, std::string_view dstPath, void* ctx, sint32* fscStatus) override
{
*fscStatus = FSC_STATUS_OK;
fs::path _srcPath = _utf8ToPath(srcPath);
fs::path _dstPath = _utf8ToPath(dstPath);
std::error_code ec;
if (!fs::exists(_srcPath, ec))
{
*fscStatus = FSC_STATUS_FILE_NOT_FOUND;
return false;
}
fs::rename(_srcPath, _dstPath, ec);
return true;
}
// singleton
public:
static fscDeviceHostFSC& instance()
{
static fscDeviceHostFSC _instance;
return _instance;
}
};
bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
{
return fsc_mount(mountPath, hostTargetPath, &fscDeviceHostFSC::instance(), nullptr, priority) == FSC_STATUS_OK;
}