mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-04-29 06:49:24 -04:00
295 lines
No EOL
7.5 KiB
C++
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;
|
|
} |