Update SAF handling

This commit is contained in:
SSimco
2025-08-29 21:17:14 +03:00
parent 6a3b7f0290
commit b8903f21e8
21 changed files with 582 additions and 574 deletions
+1 -2
View File
@@ -215,14 +215,13 @@ void fscDeviceRedirect_map();
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority);
#if __ANDROID__
#include "Common/unix/FilesystemAndroid.h"
bool FSCDeviceAndroidSAF_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
#endif // __ANDROID__
inline bool FSCDeviceHost_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
{
#if __ANDROID__
if (FilesystemAndroid::isContentUri(std::string(hostTargetPath)))
if (FilesystemAndroid::IsContentUri(std::string(hostTargetPath)))
return FSCDeviceAndroidSAF_Mount(mountPath, hostTargetPath, priority);
else
#endif // __ANDROID__
+164 -166
View File
@@ -5,231 +5,229 @@
#include "Cafe/Filesystem/fsc.h"
#include "Common/FileStream.h"
#include "Common/unix/FilesystemAndroid.h"
#include "Common/android/FilesystemAndroid.h"
FSCVirtualFile_AndroidSAF::~FSCVirtualFile_AndroidSAF()
{
if (m_type == FSC_TYPE_FILE)
delete m_fs;
if (m_type == FSC_TYPE_FILE)
delete m_fs;
}
sint32 FSCVirtualFile_AndroidSAF::fscGetType()
{
return m_type;
return m_type;
}
uint32 FSCVirtualFile_AndroidSAF::fscDeviceAndroidSAFFSFile_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;
}
return 0;
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;
}
return 0;
}
uint64 FSCVirtualFile_AndroidSAF::fscQueryValueU64(uint32 id)
{
if (m_type == FSC_TYPE_FILE)
{
if (id == FSC_QUERY_SIZE)
return fscDeviceAndroidSAFFSFile_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 fscDeviceAndroidSAFFSFile_getFileSize();
else
cemu_assert_unimplemented();
}
cemu_assert_unimplemented();
return 0;
if (m_type == FSC_TYPE_FILE)
{
if (id == FSC_QUERY_SIZE)
return fscDeviceAndroidSAFFSFile_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 fscDeviceAndroidSAFFSFile_getFileSize();
else
cemu_assert_unimplemented();
}
cemu_assert_unimplemented();
return 0;
}
uint32 FSCVirtualFile_AndroidSAF::fscWriteData(void* buffer, uint32 size)
{
throw std::logic_error("write not supported with SAF");
return 0;
throw std::logic_error("write not supported with SAF");
return 0;
}
uint32 FSCVirtualFile_AndroidSAF::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;
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_AndroidSAF::fscSetSeek(uint64 seek)
{
if (m_type != FSC_TYPE_FILE)
return;
this->m_seek = seek;
cemu_assert_debug(seek <= m_fileSize);
m_fs->SetPosition(seek);
if (m_type != FSC_TYPE_FILE)
return;
this->m_seek = seek;
cemu_assert_debug(seek <= m_fileSize);
m_fs->SetPosition(seek);
}
uint64 FSCVirtualFile_AndroidSAF::fscGetSeek()
{
if (m_type != FSC_TYPE_FILE)
return 0;
return m_seek;
if (m_type != FSC_TYPE_FILE)
return 0;
return m_seek;
}
void FSCVirtualFile_AndroidSAF::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);
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_AndroidSAF::fscDirNext(FSCDirEntry* dirEntry)
{
if (m_type != FSC_TYPE_DIRECTORY)
return false;
if (m_type != FSC_TYPE_DIRECTORY)
return false;
if (!m_files)
{
// init iterator on first iteration attempt
m_files = std::make_unique<std::vector<fs::path>>(FilesystemAndroid::listFiles(*m_path));
m_filesIterator = m_files->begin();
if (!m_files)
{
cemuLog_log(LogType::Force, "Failed to iterate directory: {}", _pathToUtf8(*m_path));
return false;
}
}
if (m_filesIterator == m_files->end())
return false;
if (!m_files)
{
// init iterator on first iteration attempt
m_files = std::make_unique<std::vector<fs::path>>(FilesystemAndroid::ListFiles(*m_path));
m_filesIterator = m_files->begin();
if (!m_files)
{
cemuLog_log(LogType::Force, "Failed to iterate directory: {}", _pathToUtf8(*m_path));
return false;
}
}
if (m_filesIterator == m_files->end())
return false;
const fs::path& file = *m_filesIterator;
const fs::path& file = *m_filesIterator;
std::string fileName = file.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 (FilesystemAndroid::isDirectory(file))
{
dirEntry->isDirectory = true;
dirEntry->isFile = false;
dirEntry->fileSize = 0;
}
else
{
dirEntry->isDirectory = false;
dirEntry->isFile = true;
dirEntry->fileSize = 0;
auto fs = FileStream::openFile2(file);
if (fs)
{
dirEntry->fileSize = fs->GetSize();
delete fs;
}
}
m_filesIterator++;
return true;
std::string fileName = file.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 (FilesystemAndroid::IsDirectory(file))
{
dirEntry->isDirectory = true;
dirEntry->isFile = false;
dirEntry->fileSize = 0;
}
else
{
dirEntry->isDirectory = false;
dirEntry->isFile = true;
dirEntry->fileSize = 0;
auto fs = FileStream::openFile2(file);
if (fs)
{
dirEntry->fileSize = fs->GetSize();
delete fs;
}
}
m_filesIterator++;
return true;
}
FSCVirtualFile* FSCVirtualFile_AndroidSAF::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
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION) ||
HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALLOW_CREATE) ||
HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALWAYS_CREATE))
throw std::logic_error("writing and creating a file is not supported with SAF");
// attempt to open as file
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
{
FileStream* fs = FileStream::openFile2(path);
if (fs)
{
FSCVirtualFile_AndroidSAF* vf = new FSCVirtualFile_AndroidSAF(FSC_TYPE_FILE);
vf->m_fs = fs;
vf->m_isWritable = false;
vf->m_fileSize = fs->GetSize();
fscStatus = FSC_STATUS_OK;
return vf;
}
}
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
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION) ||
HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALLOW_CREATE) ||
HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALWAYS_CREATE))
throw std::logic_error("writing and creating a file is not supported with SAF");
// attempt to open as file
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
{
FileStream* fs = FileStream::openFile2(path);
if (fs)
{
FSCVirtualFile_AndroidSAF* vf = new FSCVirtualFile_AndroidSAF(FSC_TYPE_FILE);
vf->m_fs = fs;
vf->m_isWritable = false;
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))
{
bool isExistingDir = FilesystemAndroid::exists(path);
if (isExistingDir)
{
FSCVirtualFile_AndroidSAF* vf = new FSCVirtualFile_AndroidSAF(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;
// attempt to open as directory
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
{
bool isExistingDir = FilesystemAndroid::Exists(path);
if (isExistingDir)
{
FSCVirtualFile_AndroidSAF* vf = new FSCVirtualFile_AndroidSAF(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 fscDeviceAndroidSAFFSC : 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_AndroidSAF::OpenFile(_utf8ToPath(path), accessFlags, *fscStatus);
cemu_assert_debug((bool)vf == (*fscStatus == FSC_STATUS_OK));
return vf;
}
public:
FSCVirtualFile* fscDeviceOpenByPath(std::string_view path, FSC_ACCESS_FLAG accessFlags, void* ctx, sint32* fscStatus) override
{
*fscStatus = FSC_STATUS_OK;
FSCVirtualFile* vf = FSCVirtualFile_AndroidSAF::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
{
throw std::logic_error("creating a directory is not supported with SAF");
bool fscDeviceCreateDir(std::string_view path, void* ctx, sint32* fscStatus) override
{
throw std::runtime_error("creating a directory is not supported with SAF");
}
bool fscDeviceRemoveFileOrDir(std::string_view path, void* ctx, sint32* fscStatus) override
{
throw std::runtime_error("removing a file or dir is not supported with SAF");
return false;
}
}
bool fscDeviceRemoveFileOrDir(std::string_view path, void* ctx, sint32* fscStatus) override
{
throw std::logic_error("removing a file or dir is not supported with SAF");
return false;
}
bool fscDeviceRename(std::string_view srcPath, std::string_view dstPath, void* ctx, sint32* fscStatus) override
{
throw std::runtime_error("renaming not supported with SAF");
}
bool fscDeviceRename(std::string_view srcPath, std::string_view dstPath, void* ctx, sint32* fscStatus) override
{
throw std::logic_error("renaming not supported with SAF");
return false;
}
// singleton
public:
static fscDeviceAndroidSAFFSC& instance()
{
static fscDeviceAndroidSAFFSC _instance;
return _instance;
}
// singleton
public:
static fscDeviceAndroidSAFFSC& instance()
{
static fscDeviceAndroidSAFFSC _instance;
return _instance;
}
};
bool FSCDeviceAndroidSAF_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
{
return fsc_mount(mountPath, hostTargetPath, &fscDeviceAndroidSAFFSC::instance(), nullptr, priority) == FSC_STATUS_OK;
return fsc_mount(mountPath, hostTargetPath, &fscDeviceAndroidSAFFSC::instance(), nullptr, priority) == FSC_STATUS_OK;
}
+5 -18
View File
@@ -5,15 +5,12 @@
#include "pugixml.hpp"
#include "Common/FileStream.h"
#if __ANDROID__
#include "Common/unix/ContentUriStream.h"
#endif // __ANDROID__
#include <zarchive/zarchivereader.h>
#include "util/IniParser/IniParser.h"
#include "util/crypto/crc32.h"
#include "config/ActiveSettings.h"
#include "util/helpers/helpers.h"
#include "util/helpers/ZArchiveHelpers.h"
// detect format by reading file header/footer
CafeTitleFileType DetermineCafeSystemFileType(fs::path filePath)
@@ -225,13 +222,8 @@ bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataF
pathOut = path;
// a Wii U archive file can contain multiple titles but TitleInfo only maps to one
// we use the first base title that we find. This is the most intuitive behavior when someone launches "game.wua"
ZArchiveReader* zar = nullptr;
#if __ANDROID__
if(FilesystemAndroid::isContentUri(path))
zar = ZArchiveReader::OpenFromStream(std::make_unique<ContentUriStream>(path));
else
#endif // __ANDROID__
zar = ZArchiveReader::OpenFromFile(path);
ZArchiveReader* zar = ZArchiveHelpers::OpenReader(path);
if (!zar)
return false;
ZArchiveNodeHandle rootDir = zar->LookUp("", false, true);
@@ -366,13 +358,8 @@ ZArchiveReader* _ZArchivePool_AcquireInstance(const fs::path& path)
}
_lock.unlock();
// opening wua files can be expensive, so we do it outside of the lock
ZArchiveReader* zar = nullptr;
#if __ANDROID__
if(FilesystemAndroid::isContentUri(path))
zar = ZArchiveReader::OpenFromStream(std::make_unique<ContentUriStream>(path));
else
#endif // __ANDROID__
zar = ZArchiveReader::OpenFromFile(path);
ZArchiveReader* zar = ZArchiveHelpers::OpenReader(path);
if (!zar)
return nullptr;
_lock.lock();
+11 -19
View File
@@ -2,11 +2,8 @@
#include "Common/FileStream.h"
#include "util/helpers/helpers.h"
#include "util/helpers/ZArchiveHelpers.h"
#if __ANDROID__
#include "Common/unix/FilesystemAndroid.h"
#include "Common/unix/ContentUriStream.h"
#endif // __ANDROID__
#include <zarchive/zarchivereader.h>
@@ -221,13 +218,8 @@ void CafeTitleList::AddTitleFromPath(fs::path path)
{
if (path.has_extension() && boost::iequals(_pathToUtf8(path.extension()), ".wua"))
{
ZArchiveReader* zar = nullptr;
#if __ANDROID__
if(FilesystemAndroid::isContentUri(path))
zar = ZArchiveReader::OpenFromStream(std::make_unique<ContentUriStream>(path));
else
#endif // __ANDROID__
zar = ZArchiveReader::OpenFromFile(path);
ZArchiveReader* zar = ZArchiveHelpers::OpenReader(path);
if (!zar)
{
cemuLog_log(LogType::Force, "Found {} but it is not a valid Wii U archive file", _pathToUtf8(path));
@@ -374,23 +366,23 @@ void CafeTitleList::ScanGamePath(const fs::path& path)
hasMetaFolder = true;
};
#if __ANDROID__
if(FilesystemAndroid::isContentUri(path))
if (FilesystemAndroid::IsContentUri(path))
{
for(auto&& file:FilesystemAndroid::listFiles(path))
for (auto&& file : FilesystemAndroid::ListFiles(path))
{
if(FilesystemAndroid::isFile(file))
{
filesInDirectory.emplace_back(file);
}
else if(FilesystemAndroid::isDirectory(file))
if (FilesystemAndroid::IsDirectory(file))
{
dirsInDirectory.emplace_back(file);
checkForTitleFolders(_pathToUtf8(file.filename()));
}
else
{
filesInDirectory.emplace_back(file);
}
}
}
else
else
#endif // __ANDROID__
{
std::error_code ec;
+5 -5
View File
@@ -56,11 +56,11 @@ endif()
if(ANDROID)
target_sources(CemuCommon PRIVATE
unix/ContentUriStream.h
unix/FilesystemAndroid.cpp
unix/FilesystemAndroid.h
unix/FileStream_contentUri.cpp
unix/FileStream_contentUri.h
android/FdStream.h
android/FileStream_saf.cpp
android/FileStream_saf.h
android/FilesystemAndroid.cpp
android/FilesystemAndroid.h
)
endif()
+87 -86
View File
@@ -3,153 +3,154 @@
#if BOOST_OS_UNIX
#if __ANDROID__
#include "Common/unix/FileStream_contentUri.h"
#include "Common/unix/FilesystemAndroid.h"
#endif // __ANDROID__
#include "Common/android/FileStream_saf.h"
#include "Common/android/FilesystemAndroid.h"
#endif // __ANDROID__
#include "Common/unix/FileStream_unix.h"
FileStream* FileStream::openFile(std::string_view path)
{
return openFile2(path, false);
return openFile2(path, false);
}
FileStream* FileStream::openFile(const wchar_t* path, bool allowWrite)
{
return openFile2(path, allowWrite);
return openFile2(path, allowWrite);
}
FileStream* FileStream::openFile2(const fs::path& path, bool allowWrite)
{
#if __ANDROID__
if (FilesystemAndroid::isContentUri(path))
{
if (allowWrite || FilesystemAndroid::isDirectory(path))
{
return nullptr;
}
FileStreamContentUri* fs = new FileStreamContentUri(path);
if (fs->m_isValid)
return fs;
delete fs;
return nullptr;
}
#endif // __ANDROID__
FileStreamUnix* fs = new FileStreamUnix(path, true, allowWrite);
if (fs->m_isValid)
return fs;
delete fs;
return nullptr;
if (FilesystemAndroid::IsContentUri(path))
{
if (allowWrite || FilesystemAndroid::IsDirectory(path))
{
return nullptr;
}
auto fs = new FileStreamSAF(path);
if (fs->m_isValid)
return fs;
delete fs;
return nullptr;
}
#endif // __ANDROID__
FileStreamUnix* fs = new FileStreamUnix(path, true, allowWrite);
if (fs->m_isValid)
return fs;
delete fs;
return nullptr;
}
FileStream* FileStream::createFile(const wchar_t* path)
{
return createFile2(path);
return createFile2(path);
}
FileStream* FileStream::createFile(std::string_view path)
{
return createFile2(path);
return createFile2(path);
}
FileStream* FileStream::createFile2(const fs::path& path)
{
FileStreamUnix* fs = new FileStreamUnix(path, false, false);
if (fs->m_isValid)
return fs;
delete fs;
return nullptr;
FileStreamUnix* fs = new FileStreamUnix(path, false, false);
if (fs->m_isValid)
return fs;
delete fs;
return nullptr;
}
std::optional<std::vector<uint8>> FileStream::LoadIntoMemory(const fs::path& path)
{
FileStream* fs = openFile2(path);
if (!fs)
return std::nullopt;
uint64 fileSize = fs->GetSize();
if (fileSize > 0xFFFFFFFFull)
{
delete fs;
return std::nullopt;
}
std::optional<std::vector<uint8>> v(fileSize);
if (fs->readData(v->data(), (uint32)fileSize) != (uint32)fileSize)
{
delete fs;
return std::nullopt;
}
delete fs;
return v;
FileStream* fs = openFile2(path);
if (!fs)
return std::nullopt;
uint64 fileSize = fs->GetSize();
if (fileSize > 0xFFFFFFFFull)
{
delete fs;
return std::nullopt;
}
std::optional<std::vector<uint8>> v(fileSize);
if (fs->readData(v->data(), (uint32)fileSize) != (uint32)fileSize)
{
delete fs;
return std::nullopt;
}
delete fs;
return v;
}
#endif // BOOST_OS_UNIX
#endif // BOOST_OS_UNIX
#ifdef _WIN32
#if BOOST_OS_WINDOWS
#include "Common/windows/FileStream_win32.h"
FileStream* FileStream::openFile(std::string_view path)
{
HANDLE hFile = CreateFileW(boost::nowide::widen(path.data(), path.size()).c_str(), FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return nullptr;
return new FileStreamWin32(hFile);
HANDLE hFile = CreateFileW(boost::nowide::widen(path.data(), path.size()).c_str(), FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return nullptr;
return new FileStreamWin32(hFile);
}
FileStream* FileStream::openFile(const wchar_t* path, bool allowWrite)
{
HANDLE hFile = CreateFileW(path, allowWrite ? (FILE_GENERIC_READ | FILE_GENERIC_WRITE) : FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return nullptr;
return new FileStreamWin32(hFile);
HANDLE hFile = CreateFileW(path, allowWrite ? (FILE_GENERIC_READ | FILE_GENERIC_WRITE) : FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return nullptr;
return new FileStreamWin32(hFile);
}
FileStream* FileStream::openFile2(const fs::path& path, bool allowWrite)
{
return openFile(path.generic_wstring().c_str(), allowWrite);
return openFile(path.generic_wstring().c_str(), allowWrite);
}
FileStream* FileStream::createFile(const wchar_t* path)
{
HANDLE hFile = CreateFileW(path, FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return nullptr;
return new FileStreamWin32(hFile);
HANDLE hFile = CreateFileW(path, FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return nullptr;
return new FileStreamWin32(hFile);
}
FileStream* FileStream::createFile(std::string_view path)
{
auto w = boost::nowide::widen(path.data(), path.size());
HANDLE hFile = CreateFileW(w.c_str(), FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return nullptr;
return new FileStreamWin32(hFile);
auto w = boost::nowide::widen(path.data(), path.size());
HANDLE hFile = CreateFileW(w.c_str(), FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return nullptr;
return new FileStreamWin32(hFile);
}
FileStream* FileStream::createFile2(const fs::path& path)
{
return createFile(path.generic_wstring().c_str());
return createFile(path.generic_wstring().c_str());
}
std::optional<std::vector<uint8>> FileStream::LoadIntoMemory(const fs::path& path)
{
FileStream* fs = openFile2(path);
if (!fs)
return std::nullopt;
uint64 fileSize = fs->GetSize();
if (fileSize > 0xFFFFFFFFull)
{
delete fs;
return std::nullopt;
}
std::optional<std::vector<uint8>> v(fileSize);
if (fs->readData(v->data(), (uint32)fileSize) != (uint32)fileSize)
{
delete fs;
return std::nullopt;
}
delete fs;
return v;
FileStream* fs = openFile2(path);
if (!fs)
return std::nullopt;
uint64 fileSize = fs->GetSize();
if (fileSize > 0xFFFFFFFFull)
{
delete fs;
return std::nullopt;
}
std::optional<std::vector<uint8>> v(fileSize);
if (fs->readData(v->data(), (uint32)fileSize) != (uint32)fileSize)
{
delete fs;
return std::nullopt;
}
delete fs;
return v;
}
#endif // _WIN32
#endif // BOOST_OS_WINDOWS
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream_buffer.hpp>
class FdStream : private boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_source>, public std::istream
{
public:
explicit FdStream(int fd)
: boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_source>(fd, boost::iostreams::close_handle), std::istream(this) {}
bool IsValid()
{
return component()->is_open();
}
};
+114
View File
@@ -0,0 +1,114 @@
#include "FileStream_saf.h"
#include "FilesystemAndroid.h"
void FileStreamSAF::SetPosition(uint64 pos)
{
if (!m_isValid)
return;
m_stream.seekg((std::streampos)pos);
}
uint64 FileStreamSAF::GetSize()
{
cemu_assert(m_isValid);
auto currentPos = m_stream.tellg();
m_stream.seekg(0, std::ios::end);
auto fileSize = m_stream.tellg();
m_stream.seekg(currentPos, std::ios::beg);
uint64 fs = (uint64)fileSize;
return fs;
}
bool FileStreamSAF::SetEndOfFile()
{
return true;
}
void FileStreamSAF::extract(std::vector<uint8>& data)
{
if (!m_isValid)
return;
uint64 fileSize = GetSize();
SetPosition(0);
data.resize(fileSize);
readData(data.data(), fileSize);
}
uint32 FileStreamSAF::readData(void* data, uint32 length)
{
m_stream.read((char*)data, length);
size_t bytesRead = m_stream.gcount();
return (uint32)bytesRead;
}
bool FileStreamSAF::readU64(uint64& v)
{
return readData(&v, sizeof(uint64)) == sizeof(uint64);
}
bool FileStreamSAF::readU32(uint32& v)
{
return readData(&v, sizeof(uint32)) == sizeof(uint32);
}
bool FileStreamSAF::readU8(uint8& v)
{
return readData(&v, sizeof(uint8)) == sizeof(uint8);
}
bool FileStreamSAF::readLine(std::string& line)
{
line.clear();
uint8 c;
bool isEOF = true;
while (readU8(c))
{
isEOF = false;
if (c == '\r')
continue;
if (c == '\n')
break;
line.push_back((char)c);
}
return !isEOF;
}
sint32 FileStreamSAF::writeData(const void* data, sint32 length)
{
throw std::runtime_error("write operation not supported");
}
void FileStreamSAF::writeU64(uint64 v)
{
throw std::runtime_error("write operation not supported");
}
void FileStreamSAF::writeU32(uint32 v)
{
throw std::runtime_error("write operation not supported");
}
void FileStreamSAF::writeU8(uint8 v)
{
throw std::runtime_error("write operation not supported");
}
void FileStreamSAF::writeStringFmt(const char* format, ...)
{
throw std::runtime_error("write operation not supported");
}
void FileStreamSAF::writeString(const char* str)
{
throw std::runtime_error("write operation not supported");
}
void FileStreamSAF::writeLine(const char* str)
{
throw std::runtime_error("write operation not supported");
}
FileStreamSAF::FileStreamSAF(const fs::path& uri) : m_stream(FilesystemAndroid::OpenContentUri(uri))
{
m_isValid = m_stream.IsValid();
}
+38
View File
@@ -0,0 +1,38 @@
#pragma once
#include "Common/FileStream.h"
#include "FdStream.h"
class FileStreamSAF : public FileStream
{
public:
// size and seek
void SetPosition(uint64 pos) override;
uint64 GetSize() override;
bool SetEndOfFile() override;
void extract(std::vector<uint8>& data) override;
// reading
uint32 readData(void* data, uint32 length) override;
bool readU64(uint64& v) override;
bool readU32(uint32& v) override;
bool readU8(uint8& v) override;
bool readLine(std::string& line) override;
// writing (not supported)
sint32 writeData(const void* data, sint32 length) override;
void writeU64(uint64 v) override;
void writeU32(uint32 v) override;
void writeU8(uint8 v) override;
void writeStringFmt(const char* format, ...) override;
void writeString(const char* str) override;
void writeLine(const char* str) override;
private:
friend class FileStream;
FileStreamSAF(const fs::path& uri);
FdStream m_stream;
bool m_isValid = false;
};
+58
View File
@@ -0,0 +1,58 @@
#include "FilesystemAndroid.h"
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream_buffer.hpp>
using namespace boost::iostreams;
namespace FilesystemAndroid
{
std::shared_ptr<FilesystemCallbacks> g_filesystemCallbacks = nullptr;
void SetFilesystemCallbacks(const std::shared_ptr<FilesystemCallbacks> &filesystemCallbacks)
{
g_filesystemCallbacks = filesystemCallbacks;
}
int OpenContentUri(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->OpenContentUri(uri);
return -1;
}
std::vector<fs::path> ListFiles(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->ListFiles(uri);
return {};
}
bool IsDirectory(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->IsDirectory(uri);
return false;
}
bool IsFile(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->IsFile(uri);
return false;
}
bool Exists(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->Exists(uri);
return false;
}
bool IsContentUri(const std::string &uri)
{
static constexpr auto content = "content://";
return uri.starts_with(content);
}
} // namespace FilesystemAndroid
+29
View File
@@ -0,0 +1,29 @@
#pragma once
namespace FilesystemAndroid
{
class FilesystemCallbacks
{
public:
virtual int OpenContentUri(const fs::path &uri) = 0;
virtual std::vector<fs::path> ListFiles(const fs::path &uri) = 0;
virtual bool IsDirectory(const fs::path &uri) = 0;
virtual bool IsFile(const fs::path &uri) = 0;
virtual bool Exists(const fs::path &uri) = 0;
};
void SetFilesystemCallbacks(const std::shared_ptr<FilesystemCallbacks> &filesystemCallbacks);
int OpenContentUri(const fs::path &uri);
std::vector<fs::path> ListFiles(const fs::path &uri);
bool IsDirectory(const fs::path &uri);
bool IsFile(const fs::path &uri);
bool Exists(const fs::path& uri);
bool IsContentUri(const std::string &uri);
} // namespace FilesystemAndroid
+13 -13
View File
@@ -85,7 +85,7 @@
namespace fs = std::filesystem;
#if __ANDROID__
#include "Common/unix/FilesystemAndroid.h"
#include "Common/android/FilesystemAndroid.h"
#endif // __ANDROID
namespace cemu
@@ -95,48 +95,48 @@ namespace fs
inline bool is_directory(const std::filesystem::path& p)
{
#if __ANDROID__
if (FilesystemAndroid::isContentUri(p))
return FilesystemAndroid::isDirectory(p);
if (FilesystemAndroid::IsContentUri(p))
return FilesystemAndroid::IsDirectory(p);
#endif // __ANDROID__
return std::filesystem::is_directory(p);
}
inline bool is_directory(const std::filesystem::path& p, std::error_code& ec)
{
#if __ANDROID__
if (FilesystemAndroid::isContentUri(p))
return FilesystemAndroid::isDirectory(p);
if (FilesystemAndroid::IsContentUri(p))
return FilesystemAndroid::IsDirectory(p);
#endif // __ANDROID__
return std::filesystem::is_directory(p, ec);
}
inline bool is_file(const std::filesystem::path& p)
{
#if __ANDROID__
if (FilesystemAndroid::isContentUri(p))
return FilesystemAndroid::isFile(p);
if (FilesystemAndroid::IsContentUri(p))
return FilesystemAndroid::IsFile(p);
#endif // __ANDROID__
return std::filesystem::is_regular_file(p);
}
inline bool is_file(const std::filesystem::path& p, std::error_code& ec)
{
#if __ANDROID__
if (FilesystemAndroid::isContentUri(p))
return FilesystemAndroid::isFile(p);
if (FilesystemAndroid::IsContentUri(p))
return FilesystemAndroid::IsFile(p);
#endif // __ANDROID__
return std::filesystem::is_regular_file(p, ec);
}
inline bool exists(const std::filesystem::path& p)
{
#if __ANDROID__
if (FilesystemAndroid::isContentUri(p))
return FilesystemAndroid::exists(p);
if (FilesystemAndroid::IsContentUri(p))
return FilesystemAndroid::Exists(p);
#endif // __ANDROID__
return std::filesystem::exists(p);
}
inline bool exists(const std::filesystem::path& p, std::error_code& ec)
{
#if __ANDROID__
if (FilesystemAndroid::isContentUri(p))
return FilesystemAndroid::exists(p);
if (FilesystemAndroid::IsContentUri(p))
return FilesystemAndroid::Exists(p);
#endif // __ANDROID__
return std::filesystem::exists(p, ec);
}
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include "Common/unix/FilesystemAndroid.h"
class ContentUriStream : public boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_source>, public std::istream
{
public:
explicit ContentUriStream(const std::filesystem::path& path)
: boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_source>(FilesystemAndroid::openContentUri(path), boost::iostreams::close_handle), std::istream(this) {}
bool is_open()
{
return component()->is_open();
}
};
-122
View File
@@ -1,122 +0,0 @@
#include "FileStream_contentUri.h"
#include "unix/ContentUriStream.h"
void ThrowWriteNotSupportedError()
{
throw std::logic_error("write operation not supported");
}
void FileStreamContentUri::SetPosition(uint64 pos)
{
if (!m_isValid)
return;
m_contentUriStream.seekg((std::streampos)pos);
}
uint64 FileStreamContentUri::GetSize()
{
cemu_assert(m_isValid);
auto currentPos = m_contentUriStream.tellg();
m_contentUriStream.seekg(0, std::ios::end);
auto fileSize = m_contentUriStream.tellg();
m_contentUriStream.seekg(currentPos, std::ios::beg);
uint64 fs = (uint64)fileSize;
return fs;
}
bool FileStreamContentUri::SetEndOfFile()
{
return true;
}
void FileStreamContentUri::extract(std::vector<uint8>& data)
{
if (!m_isValid)
return;
uint64 fileSize = GetSize();
SetPosition(0);
data.resize(fileSize);
readData(data.data(), fileSize);
}
uint32 FileStreamContentUri::readData(void* data, uint32 length)
{
m_contentUriStream.read((char*)data, length);
size_t bytesRead = m_contentUriStream.gcount();
return (uint32)bytesRead;
}
bool FileStreamContentUri::readU64(uint64& v)
{
return readData(&v, sizeof(uint64)) == sizeof(uint64);
}
bool FileStreamContentUri::readU32(uint32& v)
{
return readData(&v, sizeof(uint32)) == sizeof(uint32);
}
bool FileStreamContentUri::readU8(uint8& v)
{
return readData(&v, sizeof(uint8)) == sizeof(uint8);
}
bool FileStreamContentUri::readLine(std::string& line)
{
line.clear();
uint8 c;
bool isEOF = true;
while (readU8(c))
{
isEOF = false;
if (c == '\r')
continue;
if (c == '\n')
break;
line.push_back((char)c);
}
return !isEOF;
}
sint32 FileStreamContentUri::writeData(const void* data, sint32 length)
{
ThrowWriteNotSupportedError();
return -1;
}
void FileStreamContentUri::writeU64(uint64 v)
{
ThrowWriteNotSupportedError();
}
void FileStreamContentUri::writeU32(uint32 v)
{
ThrowWriteNotSupportedError();
}
void FileStreamContentUri::writeU8(uint8 v)
{
ThrowWriteNotSupportedError();
}
void FileStreamContentUri::writeStringFmt(const char* format, ...)
{
ThrowWriteNotSupportedError();
}
void FileStreamContentUri::writeString(const char* str)
{
ThrowWriteNotSupportedError();
}
void FileStreamContentUri::writeLine(const char* str)
{
ThrowWriteNotSupportedError();
}
FileStreamContentUri::FileStreamContentUri(const std::string& uri)
: m_contentUriStream(uri)
{
m_isValid = m_contentUriStream.is_open();
}
-37
View File
@@ -1,37 +0,0 @@
#pragma once
#include "Common/FileStream.h"
#include "Common/unix/ContentUriStream.h"
class FileStreamContentUri : public FileStream
{
public:
// size and seek
void SetPosition(uint64 pos) override;
uint64 GetSize() override;
bool SetEndOfFile() override;
void extract(std::vector<uint8>& data) override;
// reading
uint32 readData(void* data, uint32 length) override;
bool readU64(uint64& v) override;
bool readU32(uint32& v) override;
bool readU8(uint8& v) override;
bool readLine(std::string& line) override;
// writing (not supported)
sint32 writeData(const void* data, sint32 length) override;
void writeU64(uint64 v) override;
void writeU32(uint32 v) override;
void writeU8(uint8 v) override;
void writeStringFmt(const char* format, ...) override;
void writeString(const char* str) override;
void writeLine(const char* str) override;
private:
friend class FileStream;
FileStreamContentUri(const std::string& uri);
ContentUriStream m_contentUriStream;
bool m_isValid = false;
};
-48
View File
@@ -1,48 +0,0 @@
#include "FilesystemAndroid.h"
namespace FilesystemAndroid
{
std::shared_ptr<FilesystemCallbacks> g_filesystemCallbacks = nullptr;
void setFilesystemCallbacks(const std::shared_ptr<FilesystemCallbacks> &filesystemCallbacks)
{
g_filesystemCallbacks = filesystemCallbacks;
}
int openContentUri(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->openContentUri(uri);
return -1;
}
std::vector<fs::path> listFiles(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->listFiles(uri);
return {};
}
bool isDirectory(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->isDirectory(uri);
return false;
}
bool isFile(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->isFile(uri);
return false;
}
bool exists(const fs::path &uri)
{
if (g_filesystemCallbacks)
return g_filesystemCallbacks->exists(uri);
return false;
}
bool isContentUri(const std::string &uri)
{
static constexpr auto content = "content://";
return uri.starts_with(content);
}
} // namespace FilesystemAndroid
-29
View File
@@ -1,29 +0,0 @@
#pragma once
namespace FilesystemAndroid
{
class FilesystemCallbacks
{
public:
virtual int openContentUri(const std::filesystem::path &uri) = 0;
virtual std::vector<std::filesystem::path> listFiles(const std::filesystem::path &uri) = 0;
virtual bool isDirectory(const std::filesystem::path &uri) = 0;
virtual bool isFile(const std::filesystem::path &uri) = 0;
virtual bool exists(const std::filesystem::path &uri) = 0;
};
void setFilesystemCallbacks(const std::shared_ptr<FilesystemCallbacks> &filesystemCallbacks);
int openContentUri(const std::filesystem::path &uri);
std::vector<std::filesystem::path> listFiles(const std::filesystem::path &uri);
bool isDirectory(const std::filesystem::path &uri);
bool isFile(const std::filesystem::path &uri);
bool exists(const std::filesystem::path& uri);
bool isContentUri(const std::string &uri);
} // namespace FilesystemAndroid
@@ -1,7 +1,7 @@
#pragma once
#include "JNIUtils.h"
#include "Common/unix/FilesystemAndroid.h"
#include "Common/android/FilesystemAndroid.h"
class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks {
jmethodID m_openContentUriMid;
@@ -11,7 +11,7 @@ class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks
jmethodID m_existsMid;
JNIUtils::Scopedjclass m_fileUtilClass;
bool callBooleanFunction(const std::filesystem::path& uri, jmethodID methodId)
bool CallBooleanFunction(const std::filesystem::path& uri, jmethodID methodId)
{
bool result = false;
JNIUtils::fiberSafeJNICall([&](JNIEnv* env) {
@@ -34,7 +34,7 @@ class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks
m_existsMid = env->GetStaticMethodID(*m_fileUtilClass, "exists", "(Ljava/lang/String;)Z");
}
int openContentUri(const std::filesystem::path& uri) override
int OpenContentUri(const std::filesystem::path& uri) override
{
int fd = -1;
JNIUtils::fiberSafeJNICall([&](JNIEnv* env) {
@@ -45,7 +45,7 @@ class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks
return fd;
}
std::vector<std::filesystem::path> listFiles(const std::filesystem::path& uri) override
std::vector<std::filesystem::path> ListFiles(const std::filesystem::path& uri) override
{
std::vector<std::filesystem::path> paths;
JNIUtils::fiberSafeJNICall([&](JNIEnv* env) {
@@ -65,18 +65,18 @@ class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks
return paths;
}
bool isDirectory(const std::filesystem::path& uri) override
bool IsDirectory(const std::filesystem::path& uri) override
{
return callBooleanFunction(uri, m_isDirectoryMid);
return CallBooleanFunction(uri, m_isDirectoryMid);
}
bool isFile(const std::filesystem::path& uri) override
bool IsFile(const std::filesystem::path& uri) override
{
return callBooleanFunction(uri, m_isFileMid);
return CallBooleanFunction(uri, m_isFileMid);
}
bool exists(const std::filesystem::path& uri) override
bool Exists(const std::filesystem::path& uri) override
{
return callBooleanFunction(uri, m_existsMid);
return CallBooleanFunction(uri, m_existsMid);
}
};
@@ -161,7 +161,7 @@ Java_info_cemu_cemu_nativeinterface_NativeEmulation_setReplaceTVWithPadView([[ma
extern "C" [[maybe_unused]] JNIEXPORT void JNICALL
Java_info_cemu_cemu_nativeinterface_NativeEmulation_initializeEmulation([[maybe_unused]] JNIEnv* env, [[maybe_unused]] jclass clazz)
{
FilesystemAndroid::setFilesystemCallbacks(std::make_shared<AndroidFilesystemCallbacks>());
FilesystemAndroid::SetFilesystemCallbacks(std::make_shared<AndroidFilesystemCallbacks>());
GetConfigHandle().SetFilename(ActiveSettings::GetConfigPath("settings.xml").generic_wstring());
NativeEmulation::createCemuDirectories();
NetworkConfig::LoadOnce();
+2
View File
@@ -37,6 +37,7 @@ add_library(CemuUtil
helpers/StringParser.h
helpers/SystemException.h
helpers/TempState.h
helpers/ZArchiveHelpers.h
highresolutiontimer/HighResolutionTimer.cpp
highresolutiontimer/HighResolutionTimer.h
ImageWriter/bmp.h
@@ -99,6 +100,7 @@ endif()
target_link_libraries(CemuUtil PRIVATE
CemuCommon
CemuConfig
ZArchive::zarchive
Boost::headers
Boost::nowide
OpenSSL::Crypto

Some files were not shown because too many files have changed in this diff Show More