mirror of
https://github.com/cemu-project/Cemu.git
synced 2026-07-09 17:14:47 -06:00
common: refactor FileStream & implement Android filesystem access
This commit is contained in:
parent
42b2aacf41
commit
e75d2fe9c6
@ -170,7 +170,7 @@ find_package(pugixml REQUIRED)
|
||||
find_package(RapidJSON REQUIRED)
|
||||
find_package(Boost COMPONENTS program_options filesystem nowide REQUIRED)
|
||||
if(ANDROID)
|
||||
find_package(Boost COMPONENTS context REQUIRED)
|
||||
find_package(Boost COMPONENTS context iostreams REQUIRED)
|
||||
endif()
|
||||
find_package(libzip REQUIRED)
|
||||
find_package(glslang REQUIRED)
|
||||
|
||||
2
dependencies/ZArchive
vendored
2
dependencies/ZArchive
vendored
@ -1 +1 @@
|
||||
Subproject commit d2c717730092c7bf8cbb033b12fd4001b7c4d932
|
||||
Subproject commit 965b66c8d67b6b7e30fd63b3b75aa91a99ff303b
|
||||
@ -459,7 +459,7 @@ OnlineValidator Account::ValidateOnlineFiles() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void Account::ParseFile(class FileStream* file)
|
||||
void Account::ParseFile(FileStream* file)
|
||||
{
|
||||
std::vector<uint8> buffer;
|
||||
buffer.resize(file->GetSize());
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/FileStream.h"
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
@ -106,7 +107,7 @@ private:
|
||||
Account(uint32 persistent_id);
|
||||
|
||||
[[nodiscard]] std::error_code CheckValid() const;
|
||||
void ParseFile(class FileStream* file);
|
||||
void ParseFile(FileStream* file);
|
||||
|
||||
uint32 m_persistent_id = 0;
|
||||
uint64 m_transferable_id_base = 0;
|
||||
|
||||
@ -618,6 +618,13 @@ if(ENABLE_METAL)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(CemuCafe PRIVATE
|
||||
Filesystem/fscDeviceAndroidSAF.cpp
|
||||
Filesystem/fscDeviceAndroidSAF.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CEMU_ARCHITECTURE MATCHES "(aarch64)|(AARCH64)|(arm64)|(ARM64)")
|
||||
target_sources(CemuCafe PRIVATE
|
||||
HW/Espresso/Recompiler/BackendAArch64/BackendAArch64.cpp
|
||||
|
||||
@ -873,10 +873,10 @@ namespace CafeSystem
|
||||
// check for content folder
|
||||
fs::path contentPath = executablePath.parent_path().parent_path().append("content");
|
||||
std::error_code ec;
|
||||
if (fs::is_directory(contentPath, ec))
|
||||
if (cemu::fs::is_directory(contentPath, ec))
|
||||
{
|
||||
// mounting content folder
|
||||
bool r = FSCDeviceHostFS_Mount(std::string("/vol/content").c_str(), _pathToUtf8(contentPath), FSC_PRIORITY_BASE);
|
||||
bool r = FSCDeviceHost_Mount("/vol/content", _pathToUtf8(contentPath), FSC_PRIORITY_BASE);
|
||||
if (!r)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath));
|
||||
@ -885,7 +885,7 @@ namespace CafeSystem
|
||||
}
|
||||
}
|
||||
// mount code folder to a virtual temporary path
|
||||
FSCDeviceHostFS_Mount(std::string("/internal/code/").c_str(), _pathToUtf8(executablePath.parent_path()), FSC_PRIORITY_BASE);
|
||||
FSCDeviceHost_Mount("/internal/code/", _pathToUtf8(executablePath.parent_path()), FSC_PRIORITY_BASE);
|
||||
std::string internalExecutablePath = "/internal/code/";
|
||||
internalExecutablePath.append(_pathToUtf8(executablePath.filename()));
|
||||
_pathToExecutable = internalExecutablePath;
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "Cemu/ncrypto/ncrypto.h"
|
||||
#include "Common/FileStream.h"
|
||||
#include "openssl/evp.h"
|
||||
|
||||
struct FSTFileHandle
|
||||
@ -336,7 +337,7 @@ private:
|
||||
class FSTVerifier
|
||||
{
|
||||
public:
|
||||
static bool VerifyContentFile(class FileStream* fileContent, const NCrypto::AesKey* key, uint32 contentIndex, uint32 contentSize, uint32 contentSizePadded, bool isSHA1, const uint8* tmdContentHash);
|
||||
static bool VerifyHashedContentFile(class FileStream* fileContent, const NCrypto::AesKey* key, uint32 contentIndex, uint32 contentSize, uint32 contentSizePadded, bool isSHA1, const uint8* tmdContentHash);
|
||||
static bool VerifyContentFile(FileStream* fileContent, const NCrypto::AesKey* key, uint32 contentIndex, uint32 contentSize, uint32 contentSizePadded, bool isSHA1, const uint8* tmdContentHash);
|
||||
static bool VerifyHashedContentFile(FileStream* fileContent, const NCrypto::AesKey* key, uint32 contentIndex, uint32 contentSize, uint32 contentSizePadded, bool isSHA1, const uint8* tmdContentHash);
|
||||
|
||||
};
|
||||
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
struct wuxHeader_t
|
||||
{
|
||||
unsigned int magic0;
|
||||
@ -11,7 +13,7 @@ struct wuxHeader_t
|
||||
|
||||
struct wud_t
|
||||
{
|
||||
class FileStream* fs;
|
||||
FileStream* fs;
|
||||
long long uncompressedSize;
|
||||
bool isCompressed;
|
||||
// data used when compressed
|
||||
|
||||
@ -213,3 +213,16 @@ bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTarg
|
||||
// redirect device
|
||||
void fscDeviceRedirect_map();
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, size_t fileSize, const fs::path& targetFilePath, sint32 priority);
|
||||
|
||||
#if BOOST_PLAT_ANDROID
|
||||
bool FSCDeviceAndroidSAF_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
|
||||
#endif
|
||||
|
||||
inline bool FSCDeviceHost_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
|
||||
{
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(hostTargetPath))
|
||||
return FSCDeviceAndroidSAF_Mount(mountPath, hostTargetPath, priority);
|
||||
#endif
|
||||
return FSCDeviceHostFS_Mount(mountPath, hostTargetPath, priority);
|
||||
}
|
||||
|
||||
213
src/Cafe/Filesystem/fscDeviceAndroidSAF.cpp
Normal file
213
src/Cafe/Filesystem/fscDeviceAndroidSAF.cpp
Normal file
@ -0,0 +1,213 @@
|
||||
#include "Cafe/Filesystem/fscDeviceAndroidSAF.h"
|
||||
|
||||
#include "Cafe/Filesystem/fsc.h"
|
||||
#include "Common/FileStream.h"
|
||||
#include "Common/android/FilesystemAndroid.h"
|
||||
|
||||
FSCVirtualFile_AndroidSAF::~FSCVirtualFile_AndroidSAF()
|
||||
{
|
||||
if (m_type == FSC_TYPE_FILE)
|
||||
delete m_fs;
|
||||
}
|
||||
|
||||
sint32 FSCVirtualFile_AndroidSAF::fscGetType()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint32 FSCVirtualFile_AndroidSAF::fscWriteData(void* buffer, uint32 size)
|
||||
{
|
||||
cemu_assert_error();
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
uint64 FSCVirtualFile_AndroidSAF::fscGetSeek()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
bool FSCVirtualFile_AndroidSAF::fscDirNext(FSCDirEntry* dirEntry)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
cemu_assert_debug(!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION)); // writing 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
37
src/Cafe/Filesystem/fscDeviceAndroidSAF.h
Normal file
37
src/Cafe/Filesystem/fscDeviceAndroidSAF.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cafe/Filesystem/fsc.h"
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
class FSCVirtualFile_AndroidSAF : public FSCVirtualFile
|
||||
{
|
||||
public:
|
||||
static FSCVirtualFile* OpenFile(const fs::path& path, FSC_ACCESS_FLAG accessFlags, sint32& fscStatus);
|
||||
~FSCVirtualFile_AndroidSAF() override;
|
||||
|
||||
sint32 fscGetType() override;
|
||||
|
||||
uint32 fscDeviceAndroidSAFFSFile_getFileSize();
|
||||
|
||||
uint64 fscQueryValueU64(uint32 id) override;
|
||||
uint32 fscWriteData(void* buffer, uint32 size) override;
|
||||
uint32 fscReadData(void* buffer, uint32 size) override;
|
||||
void fscSetSeek(uint64 seek) override;
|
||||
uint64 fscGetSeek() override;
|
||||
void fscSetFileLength(uint64 endOffset) override;
|
||||
bool fscDirNext(FSCDirEntry* dirEntry) override;
|
||||
|
||||
private:
|
||||
FSCVirtualFile_AndroidSAF(uint32 type) : m_type(type){};
|
||||
|
||||
uint32 m_type; // FSC_TYPE_*
|
||||
FileStream* m_fs{};
|
||||
// file
|
||||
uint64 m_seek{0};
|
||||
uint64 m_fileSize{0};
|
||||
bool m_isWritable{false};
|
||||
// directory
|
||||
std::unique_ptr<fs::path> m_path{};
|
||||
std::unique_ptr<std::vector<fs::path>> m_files{};
|
||||
std::vector<fs::path>::iterator m_filesIterator;
|
||||
};
|
||||
@ -1,4 +1,5 @@
|
||||
#include "Cafe/Filesystem/fsc.h"
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
class FSCVirtualFile_Host : public FSCVirtualFile
|
||||
{
|
||||
@ -23,7 +24,7 @@ private:
|
||||
|
||||
private:
|
||||
uint32 m_type; // FSC_TYPE_*
|
||||
class FileStream* m_fs{};
|
||||
FileStream* m_fs{};
|
||||
// file
|
||||
uint64 m_seek{ 0 };
|
||||
uint64 m_fileSize{ 0 };
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#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)
|
||||
@ -180,7 +181,7 @@ bool TitleInfo::ParseWuaTitleFolderName(std::string_view name, TitleId& titleIdO
|
||||
bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataFormat& formatOut)
|
||||
{
|
||||
std::error_code ec;
|
||||
if (path.has_extension() && fs::is_regular_file(path, ec))
|
||||
if (path.has_extension() && cemu::fs::is_file(path, ec))
|
||||
{
|
||||
std::string filenameStr = _pathToUtf8(path.filename());
|
||||
if (boost::iends_with(filenameStr, ".rpx"))
|
||||
@ -192,7 +193,7 @@ bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataF
|
||||
parentPath = parentPath.parent_path();
|
||||
// next to content and meta?
|
||||
std::error_code ec;
|
||||
if (fs::exists(parentPath / "content", ec) && fs::exists(parentPath / "meta", ec))
|
||||
if (cemu::fs::exists(parentPath / "content", ec) && cemu::fs::exists(parentPath / "meta", ec))
|
||||
{
|
||||
formatOut = TitleDataFormat::HOST_FS;
|
||||
pathOut = parentPath;
|
||||
@ -220,7 +221,7 @@ 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 = ZArchiveReader::OpenFromFile(path);
|
||||
ZArchiveReader* zar = ZArchiveHelpers::OpenReader(path);
|
||||
if (!zar)
|
||||
return false;
|
||||
ZArchiveNodeHandle rootDir = zar->LookUp("", false, true);
|
||||
@ -275,7 +276,7 @@ bool TitleInfo::DetectFormat(const fs::path& path, fs::path& pathOut, TitleDataF
|
||||
{
|
||||
// does it point to the root folder of a title?
|
||||
std::error_code ec;
|
||||
if (fs::exists(path / "content", ec) && fs::exists(path / "meta", ec) && fs::exists(path / "code", ec))
|
||||
if (cemu::fs::exists(path / "content", ec) && cemu::fs::exists(path / "meta", ec) && cemu::fs::exists(path / "code", ec))
|
||||
{
|
||||
formatOut = TitleDataFormat::HOST_FS;
|
||||
pathOut = path;
|
||||
@ -355,7 +356,7 @@ 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 = ZArchiveReader::OpenFromFile(path);
|
||||
ZArchiveReader* zar = ZArchiveHelpers::OpenReader(path);
|
||||
if (!zar)
|
||||
return nullptr;
|
||||
_lock.lock();
|
||||
@ -397,7 +398,7 @@ bool TitleInfo::Mount(std::string_view virtualPath, std::string_view subfolder,
|
||||
{
|
||||
fs::path hostFSPath = m_fullPath;
|
||||
hostFSPath.append(subfolder);
|
||||
bool r = FSCDeviceHostFS_Mount(std::string(virtualPath).c_str(), _pathToUtf8(hostFSPath), mountPriority);
|
||||
bool r = FSCDeviceHost_Mount(std::string(virtualPath).c_str(), _pathToUtf8(hostFSPath), mountPriority);
|
||||
cemu_assert_debug(r);
|
||||
if (!r)
|
||||
{
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
#include "util/helpers/helpers.h"
|
||||
#include "util/helpers/ZArchiveHelpers.h"
|
||||
|
||||
|
||||
#include <zarchive/zarchivereader.h>
|
||||
|
||||
@ -216,7 +218,7 @@ void CafeTitleList::AddTitleFromPath(fs::path path)
|
||||
{
|
||||
if (path.has_extension() && boost::iequals(_pathToUtf8(path.extension()), ".wua"))
|
||||
{
|
||||
ZArchiveReader* 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));
|
||||
@ -353,23 +355,47 @@ void CafeTitleList::ScanGamePath(const fs::path& path)
|
||||
std::vector<fs::path> filesInDirectory;
|
||||
std::vector<fs::path> dirsInDirectory;
|
||||
bool hasContentFolder = false, hasCodeFolder = false, hasMetaFolder = false;
|
||||
std::error_code ec;
|
||||
for (auto& it : fs::directory_iterator(path, ec))
|
||||
{
|
||||
if (it.is_regular_file(ec))
|
||||
auto checkForTitleFolders = [&](std::string_view dirName)
|
||||
{
|
||||
if (boost::iequals(dirName, "content"))
|
||||
hasContentFolder = true;
|
||||
else if (boost::iequals(dirName, "code"))
|
||||
hasCodeFolder = true;
|
||||
else if (boost::iequals(dirName, "meta"))
|
||||
hasMetaFolder = true;
|
||||
};
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(path))
|
||||
{
|
||||
for (auto&& file : FilesystemAndroid::ListFiles(path))
|
||||
{
|
||||
filesInDirectory.emplace_back(it.path());
|
||||
if (FilesystemAndroid::IsDirectory(file))
|
||||
{
|
||||
dirsInDirectory.emplace_back(file);
|
||||
|
||||
checkForTitleFolders(_pathToUtf8(file.filename()));
|
||||
}
|
||||
else
|
||||
{
|
||||
filesInDirectory.emplace_back(file);
|
||||
}
|
||||
}
|
||||
else if (it.is_directory(ec))
|
||||
}
|
||||
else
|
||||
#endif // BOOST_PLAT_ANDROID
|
||||
{
|
||||
std::error_code ec;
|
||||
for (auto& it : fs::directory_iterator(path, ec))
|
||||
{
|
||||
dirsInDirectory.emplace_back(it.path());
|
||||
std::string dirName = _pathToUtf8(it.path().filename());
|
||||
if (boost::iequals(dirName, "content"))
|
||||
hasContentFolder = true;
|
||||
else if (boost::iequals(dirName, "code"))
|
||||
hasCodeFolder = true;
|
||||
else if (boost::iequals(dirName, "meta"))
|
||||
hasMetaFolder = true;
|
||||
if (it.is_regular_file(ec))
|
||||
{
|
||||
filesInDirectory.emplace_back(it.path());
|
||||
}
|
||||
else if (it.is_directory(ec))
|
||||
{
|
||||
dirsInDirectory.emplace_back(it.path());
|
||||
checkForTitleFolders(_pathToUtf8(it.path().filename()));
|
||||
}
|
||||
}
|
||||
}
|
||||
// always check individual files
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/FileStream.h"
|
||||
#include <mutex>
|
||||
|
||||
class FileCache
|
||||
@ -80,7 +81,7 @@ private:
|
||||
void _addFileInternal(uint64 name1, uint64 name2, const uint8* fileData, sint32 fileSize, bool noCompression);
|
||||
bool _getFileDataInternal(const FileTableEntry* entry, std::vector<uint8>& dataOut);
|
||||
|
||||
class FileStream* fileStream{};
|
||||
FileStream* fileStream{};
|
||||
uint64 dataOffset{};
|
||||
uint32 extraVersion{};
|
||||
// file table
|
||||
|
||||
@ -53,6 +53,19 @@ if(LINUX)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(CemuCommon PRIVATE
|
||||
android/FileStream_android.cpp
|
||||
android/FileStream_android.h
|
||||
android/FdStream.h
|
||||
android/FileStream_saf.cpp
|
||||
android/FileStream_saf.h
|
||||
android/FilesystemAndroid.cpp
|
||||
android/FilesystemAndroid.h
|
||||
)
|
||||
target_link_libraries(CemuCommon PRIVATE Boost::iostreams)
|
||||
endif()
|
||||
|
||||
# CemuCommon always builts the precompiled header
|
||||
target_precompile_headers(CemuCommon PRIVATE precompiled.h)
|
||||
target_include_directories(CemuCommon PUBLIC "../")
|
||||
|
||||
@ -3,6 +3,10 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Common/windows/FileStream_win32.h"
|
||||
#elif BOOST_PLAT_ANDROID
|
||||
#include "Common/android/FileStream_android.h"
|
||||
using FileStream = FileStreamAndroid;
|
||||
#else
|
||||
#include "Common/unix/FileStream_unix.h"
|
||||
using FileStream = FileStreamUnix;
|
||||
#endif
|
||||
|
||||
16
src/Common/android/FdStream.h
Normal file
16
src/Common/android/FdStream.h
Normal 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();
|
||||
}
|
||||
};
|
||||
295
src/Common/android/FileStream_android.cpp
Normal file
295
src/Common/android/FileStream_android.cpp
Normal file
@ -0,0 +1,295 @@
|
||||
#include "FileStream_saf.h"
|
||||
#include "FileStream_android.h"
|
||||
#include "FilesystemAndroid.h"
|
||||
#include "Common/unix/FileStream_unix.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
class IFileStream
|
||||
{
|
||||
public:
|
||||
virtual ~IFileStream() = default;
|
||||
|
||||
virtual void SetPosition(uint64 pos) = 0;
|
||||
virtual uint64 GetSize() = 0;
|
||||
virtual bool SetEndOfFile() = 0;
|
||||
virtual void extract(std::vector<uint8>& data) = 0;
|
||||
virtual void Flush() = 0;
|
||||
|
||||
virtual uint32 readData(void* data, uint32 length) = 0;
|
||||
virtual bool readU64(uint64& v) = 0;
|
||||
virtual bool readU32(uint32& v) = 0;
|
||||
virtual bool readU8(uint8& v) = 0;
|
||||
virtual bool readLine(std::string& line) = 0;
|
||||
|
||||
virtual sint32 writeData(const void* data, sint32 length) = 0;
|
||||
virtual void writeU64(uint64 v) = 0;
|
||||
virtual void writeU32(uint32 v) = 0;
|
||||
virtual void writeU8(uint8 v) = 0;
|
||||
virtual void writeStringFmt(const char* format, ...) = 0;
|
||||
virtual void writeString(const char* str) = 0;
|
||||
virtual void writeLine(const char* str) = 0;
|
||||
};
|
||||
|
||||
template<typename Impl>
|
||||
class FileStreamAdapter : public IFileStream
|
||||
{
|
||||
public:
|
||||
explicit FileStreamAdapter(Impl* impl) : m_impl(impl) {}
|
||||
~FileStreamAdapter() override
|
||||
{
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
void SetPosition(uint64 pos) override
|
||||
{
|
||||
m_impl->SetPosition(pos);
|
||||
}
|
||||
uint64 GetSize() override
|
||||
{
|
||||
return m_impl->GetSize();
|
||||
}
|
||||
bool SetEndOfFile() override
|
||||
{
|
||||
return m_impl->SetEndOfFile();
|
||||
}
|
||||
void extract(std::vector<uint8>& data) override
|
||||
{
|
||||
m_impl->extract(data);
|
||||
}
|
||||
void Flush() override
|
||||
{
|
||||
m_impl->Flush();
|
||||
}
|
||||
|
||||
uint32 readData(void* data, uint32 length) override
|
||||
{
|
||||
return m_impl->readData(data, length);
|
||||
}
|
||||
bool readU64(uint64& v) override
|
||||
{
|
||||
return m_impl->readU64(v);
|
||||
}
|
||||
bool readU32(uint32& v) override
|
||||
{
|
||||
return m_impl->readU32(v);
|
||||
}
|
||||
bool readU8(uint8& v) override
|
||||
{
|
||||
return m_impl->readU8(v);
|
||||
}
|
||||
bool readLine(std::string& line) override
|
||||
{
|
||||
return m_impl->readLine(line);
|
||||
}
|
||||
|
||||
sint32 writeData(const void* data, sint32 length) override
|
||||
{
|
||||
return m_impl->writeData(data, length);
|
||||
}
|
||||
void writeU64(uint64 v) override
|
||||
{
|
||||
m_impl->writeU64(v);
|
||||
}
|
||||
void writeU32(uint32 v) override
|
||||
{
|
||||
m_impl->writeU32(v);
|
||||
}
|
||||
void writeU8(uint8 v) override
|
||||
{
|
||||
m_impl->writeU8(v);
|
||||
}
|
||||
void writeStringFmt(const char* format, ...) override
|
||||
{
|
||||
va_list arg_list;
|
||||
va_start(arg_list, format);
|
||||
m_impl->writeStringFmt(format, arg_list);
|
||||
va_end(arg_list);
|
||||
}
|
||||
void writeString(const char* str) override
|
||||
{
|
||||
m_impl->writeString(str);
|
||||
}
|
||||
void writeLine(const char* str) override
|
||||
{
|
||||
m_impl->writeLine(str);
|
||||
}
|
||||
|
||||
private:
|
||||
Impl* m_impl;
|
||||
};
|
||||
|
||||
FileStreamAndroid::FileStreamAndroid(IFileStream* impl) : m_impl(impl) {}
|
||||
|
||||
FileStreamAndroid::~FileStreamAndroid()
|
||||
{
|
||||
if (m_impl)
|
||||
delete m_impl;
|
||||
}
|
||||
|
||||
FileStreamAndroid* FileStreamAndroid::openFile(std::string_view path)
|
||||
{
|
||||
return openFile2(path, false);
|
||||
}
|
||||
|
||||
FileStreamAndroid* FileStreamAndroid::openFile(const wchar_t* path, bool allowWrite)
|
||||
{
|
||||
return openFile2(path, allowWrite);
|
||||
}
|
||||
|
||||
FileStreamAndroid* FileStreamAndroid::openFile2(const fs::path& path, bool allowWrite)
|
||||
{
|
||||
if (FilesystemAndroid::IsContentUri(path))
|
||||
{
|
||||
if (allowWrite || FilesystemAndroid::IsDirectory(path))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto fileStream = FileStreamSAF::OpenFile(path);
|
||||
if (fileStream == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new FileStreamAndroid(new FileStreamAdapter(fileStream));
|
||||
}
|
||||
|
||||
auto fileStream = FileStreamUnix::openFile2(path, allowWrite);
|
||||
if (fileStream == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new FileStreamAndroid(new FileStreamAdapter(fileStream));
|
||||
}
|
||||
|
||||
FileStreamAndroid* FileStreamAndroid::createFile(const wchar_t* path)
|
||||
{
|
||||
return createFile2(path);
|
||||
}
|
||||
|
||||
FileStreamAndroid* FileStreamAndroid::createFile(std::string_view path)
|
||||
{
|
||||
return createFile2(path);
|
||||
}
|
||||
|
||||
FileStreamAndroid* FileStreamAndroid::createFile2(const fs::path& path)
|
||||
{
|
||||
if (FilesystemAndroid::IsContentUri(path))
|
||||
{
|
||||
throw std::runtime_error("write operation not supported");
|
||||
}
|
||||
|
||||
auto fileStream = FileStreamUnix::createFile2(path);
|
||||
if (fileStream == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IFileStream* adapter = new FileStreamAdapter(FileStreamUnix::createFile2(path));
|
||||
|
||||
return new FileStreamAndroid(adapter);
|
||||
}
|
||||
|
||||
std::optional<std::vector<uint8>> FileStreamAndroid::LoadIntoMemory(const fs::path& path)
|
||||
{
|
||||
auto 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;
|
||||
}
|
||||
|
||||
// size and seek
|
||||
void FileStreamAndroid::SetPosition(uint64 pos)
|
||||
{
|
||||
m_impl->SetPosition(pos);
|
||||
}
|
||||
|
||||
uint64 FileStreamAndroid::GetSize()
|
||||
{
|
||||
return m_impl->GetSize();
|
||||
}
|
||||
bool FileStreamAndroid::SetEndOfFile()
|
||||
{
|
||||
return m_impl->SetEndOfFile();
|
||||
}
|
||||
void FileStreamAndroid::extract(std::vector<uint8>& data)
|
||||
{
|
||||
return m_impl->extract(data);
|
||||
}
|
||||
|
||||
void FileStreamAndroid::Flush()
|
||||
{
|
||||
return m_impl->Flush();
|
||||
}
|
||||
|
||||
// reading
|
||||
uint32 FileStreamAndroid::readData(void* data, uint32 length)
|
||||
{
|
||||
return m_impl->readData(data, length);
|
||||
}
|
||||
bool FileStreamAndroid::readU64(uint64& v)
|
||||
{
|
||||
return m_impl->readU64(v);
|
||||
}
|
||||
bool FileStreamAndroid::readU32(uint32& v)
|
||||
{
|
||||
return m_impl->readU32(v);
|
||||
}
|
||||
bool FileStreamAndroid::readU8(uint8& v)
|
||||
{
|
||||
return m_impl->readU8(v);
|
||||
}
|
||||
bool FileStreamAndroid::readLine(std::string& line)
|
||||
{
|
||||
return m_impl->readLine(line);
|
||||
}
|
||||
|
||||
// writing (binary)
|
||||
sint32 FileStreamAndroid::writeData(const void* data, sint32 length)
|
||||
{
|
||||
return m_impl->writeData(data, length);
|
||||
}
|
||||
void FileStreamAndroid::writeU64(uint64 v)
|
||||
{
|
||||
m_impl->writeU64(v);
|
||||
}
|
||||
void FileStreamAndroid::writeU32(uint32 v)
|
||||
{
|
||||
m_impl->writeU32(v);
|
||||
}
|
||||
void FileStreamAndroid::writeU8(uint8 v)
|
||||
{
|
||||
m_impl->writeU8(v);
|
||||
}
|
||||
// writing (strings)
|
||||
void FileStreamAndroid::writeStringFmt(const char* format, ...)
|
||||
{
|
||||
va_list arg_list;
|
||||
va_start(arg_list, format);
|
||||
m_impl->writeStringFmt(format, arg_list);
|
||||
va_end(arg_list);
|
||||
}
|
||||
|
||||
void FileStreamAndroid::writeString(const char* str)
|
||||
{
|
||||
m_impl->writeString(str);
|
||||
}
|
||||
void FileStreamAndroid::writeLine(const char* str)
|
||||
{
|
||||
m_impl->writeLine(str);
|
||||
}
|
||||
52
src/Common/android/FileStream_android.h
Normal file
52
src/Common/android/FileStream_android.h
Normal file
@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
class IFileStream;
|
||||
|
||||
class FileStreamAndroid
|
||||
{
|
||||
public:
|
||||
static FileStreamAndroid* openFile(std::string_view path);
|
||||
static FileStreamAndroid* openFile(const wchar_t* path, bool allowWrite = false);
|
||||
static FileStreamAndroid* openFile2(const fs::path& path, bool allowWrite = false);
|
||||
|
||||
static FileStreamAndroid* createFile(const wchar_t* path);
|
||||
static FileStreamAndroid* createFile(std::string_view path);
|
||||
static FileStreamAndroid* createFile2(const fs::path& path);
|
||||
|
||||
// helper function to load a file into memory
|
||||
static std::optional<std::vector<uint8>> LoadIntoMemory(const fs::path& path);
|
||||
|
||||
// size and seek
|
||||
void SetPosition(uint64 pos);
|
||||
|
||||
uint64 GetSize();
|
||||
bool SetEndOfFile();
|
||||
void extract(std::vector<uint8>& data);
|
||||
|
||||
void Flush();
|
||||
|
||||
// reading
|
||||
uint32 readData(void* data, uint32 length);
|
||||
bool readU64(uint64& v);
|
||||
bool readU32(uint32& v);
|
||||
bool readU8(uint8& v);
|
||||
bool readLine(std::string& line);
|
||||
|
||||
// writing (binary)
|
||||
sint32 writeData(const void* data, sint32 length);
|
||||
void writeU64(uint64 v);
|
||||
void writeU32(uint32 v);
|
||||
void writeU16(uint16 v);
|
||||
void writeU8(uint8 v);
|
||||
|
||||
// writing (strings)
|
||||
void writeStringFmt(const char* format, ...);
|
||||
void writeString(const char* str);
|
||||
void writeLine(const char* str);
|
||||
|
||||
~FileStreamAndroid();
|
||||
|
||||
private:
|
||||
explicit FileStreamAndroid(IFileStream* impl);
|
||||
IFileStream* m_impl;
|
||||
};
|
||||
131
src/Common/android/FileStream_saf.cpp
Normal file
131
src/Common/android/FileStream_saf.cpp
Normal file
@ -0,0 +1,131 @@
|
||||
#include "FileStream_saf.h"
|
||||
#include "FilesystemAndroid.h"
|
||||
|
||||
FileStreamSAF* FileStreamSAF::OpenFile(const fs::path& path)
|
||||
{
|
||||
auto fileStream = new FileStreamSAF(path);
|
||||
if (fileStream->m_isValid)
|
||||
{
|
||||
return fileStream;
|
||||
}
|
||||
|
||||
delete fileStream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
void FileStreamSAF::Flush()
|
||||
{
|
||||
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
src/Common/android/FileStream_saf.h
Normal file
38
src/Common/android/FileStream_saf.h
Normal file
@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
|
||||
#include "FdStream.h"
|
||||
|
||||
class FileStreamSAF
|
||||
{
|
||||
public:
|
||||
static FileStreamSAF* OpenFile(const fs::path& path);
|
||||
|
||||
// size and seek
|
||||
void SetPosition(uint64 pos);
|
||||
|
||||
uint64 GetSize();
|
||||
bool SetEndOfFile();
|
||||
void extract(std::vector<uint8>& data);
|
||||
|
||||
// reading
|
||||
uint32 readData(void* data, uint32 length);
|
||||
bool readU64(uint64& v);
|
||||
bool readU32(uint32& v);
|
||||
bool readU8(uint8& v);
|
||||
bool readLine(std::string& line);
|
||||
|
||||
// writing (not supported)
|
||||
sint32 writeData(const void* data, sint32 length);
|
||||
void writeU64(uint64 v);
|
||||
void writeU32(uint32 v);
|
||||
void writeU8(uint8 v);
|
||||
void writeStringFmt(const char* format, ...);
|
||||
void writeString(const char* str);
|
||||
void writeLine(const char* str);
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
FileStreamSAF(const fs::path& uri);
|
||||
FdStream m_stream;
|
||||
bool m_isValid = false;
|
||||
};
|
||||
58
src/Common/android/FilesystemAndroid.cpp
Normal file
58
src/Common/android/FilesystemAndroid.cpp
Normal 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> s_filesystemCallbacks = nullptr;
|
||||
|
||||
void SetFilesystemCallbacks(std::shared_ptr<FilesystemCallbacks> filesystemCallbacks)
|
||||
{
|
||||
s_filesystemCallbacks = std::move(filesystemCallbacks);
|
||||
}
|
||||
|
||||
int OpenContentUri(const fs::path &uri)
|
||||
{
|
||||
if (s_filesystemCallbacks)
|
||||
return s_filesystemCallbacks->OpenContentUri(uri);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<fs::path> ListFiles(const fs::path &uri)
|
||||
{
|
||||
if (s_filesystemCallbacks)
|
||||
return s_filesystemCallbacks->ListFiles(uri);
|
||||
return {};
|
||||
}
|
||||
|
||||
bool IsDirectory(const fs::path &uri)
|
||||
{
|
||||
if (s_filesystemCallbacks)
|
||||
return s_filesystemCallbacks->IsDirectory(uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsFile(const fs::path &uri)
|
||||
{
|
||||
if (s_filesystemCallbacks)
|
||||
return s_filesystemCallbacks->IsFile(uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Exists(const fs::path &uri)
|
||||
{
|
||||
if (s_filesystemCallbacks)
|
||||
return s_filesystemCallbacks->Exists(uri);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsContentUri(std::string_view uri)
|
||||
{
|
||||
static constexpr auto content = "content://";
|
||||
return uri.starts_with(content);
|
||||
}
|
||||
|
||||
} // namespace FilesystemAndroid
|
||||
34
src/Common/android/FilesystemAndroid.h
Normal file
34
src/Common/android/FilesystemAndroid.h
Normal file
@ -0,0 +1,34 @@
|
||||
#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(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(std::string_view uri);
|
||||
|
||||
inline bool IsContentUri(const fs::path& path)
|
||||
{
|
||||
return IsContentUri(std::string_view{path.native()});
|
||||
}
|
||||
|
||||
} // namespace FilesystemAndroid
|
||||
@ -1,10 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/predef/os.h>
|
||||
#include <boost/predef/platform.h>
|
||||
#include <cstdint>
|
||||
|
||||
#if BOOST_OS_WINDOWS
|
||||
#include "Common/windows/platform.h"
|
||||
#elif BOOST_PLAT_ANDROID
|
||||
#include <byteswap.h>
|
||||
#include "Common/unix/platform.h"
|
||||
#elif BOOST_OS_LINUX || BOOST_OS_BSD
|
||||
#if BOOST_OS_LINUX
|
||||
#include <byteswap.h>
|
||||
|
||||
@ -84,6 +84,61 @@
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
#if BOOST_PLAT_ANDROID
|
||||
#include "Common/android/FilesystemAndroid.h"
|
||||
#endif
|
||||
|
||||
namespace cemu::fs
|
||||
{
|
||||
inline bool is_directory(const std::filesystem::path& p)
|
||||
{
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(p))
|
||||
return FilesystemAndroid::IsDirectory(p);
|
||||
#endif
|
||||
return std::filesystem::is_directory(p);
|
||||
}
|
||||
inline bool is_directory(const std::filesystem::path& p, std::error_code& ec)
|
||||
{
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(p))
|
||||
return FilesystemAndroid::IsDirectory(p);
|
||||
#endif
|
||||
return std::filesystem::is_directory(p, ec);
|
||||
}
|
||||
inline bool is_file(const std::filesystem::path& p)
|
||||
{
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(p))
|
||||
return FilesystemAndroid::IsFile(p);
|
||||
#endif
|
||||
return std::filesystem::is_regular_file(p);
|
||||
}
|
||||
inline bool is_file(const std::filesystem::path& p, std::error_code& ec)
|
||||
{
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(p))
|
||||
return FilesystemAndroid::IsFile(p);
|
||||
#endif
|
||||
return std::filesystem::is_regular_file(p, ec);
|
||||
}
|
||||
inline bool exists(const std::filesystem::path& p)
|
||||
{
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(p))
|
||||
return FilesystemAndroid::Exists(p);
|
||||
#endif
|
||||
return std::filesystem::exists(p);
|
||||
}
|
||||
inline bool exists(const std::filesystem::path& p, std::error_code& ec)
|
||||
{
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(p))
|
||||
return FilesystemAndroid::Exists(p);
|
||||
#endif
|
||||
return std::filesystem::exists(p, ec);
|
||||
}
|
||||
} // namespace cemu::fs
|
||||
|
||||
#include "enumFlags.h"
|
||||
|
||||
|
||||
@ -20,47 +20,47 @@ fs::path findPathCI(const fs::path& path)
|
||||
return parentPath / fName;
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile(std::string_view path)
|
||||
FileStreamUnix* FileStreamUnix::openFile(std::string_view path)
|
||||
{
|
||||
return openFile2(path, false);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile(const wchar_t* path, bool allowWrite)
|
||||
FileStreamUnix* FileStreamUnix::openFile(const wchar_t* path, bool allowWrite)
|
||||
{
|
||||
return openFile2(path, allowWrite);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile2(const fs::path& path, bool allowWrite)
|
||||
FileStreamUnix* FileStreamUnix::openFile2(const fs::path& path, bool allowWrite)
|
||||
{
|
||||
FileStream* fs = new FileStream(path, true, allowWrite);
|
||||
FileStreamUnix* fs = new FileStreamUnix(path, true, allowWrite);
|
||||
if (fs->m_isValid)
|
||||
return fs;
|
||||
delete fs;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(const wchar_t* path)
|
||||
FileStreamUnix* FileStreamUnix::createFile(const wchar_t* path)
|
||||
{
|
||||
return createFile2(path);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(std::string_view path)
|
||||
FileStreamUnix* FileStreamUnix::createFile(std::string_view path)
|
||||
{
|
||||
return createFile2(path);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile2(const fs::path& path)
|
||||
FileStreamUnix* FileStreamUnix::createFile2(const fs::path& path)
|
||||
{
|
||||
FileStream* fs = new FileStream(path, false, false);
|
||||
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)
|
||||
std::optional<std::vector<uint8>> FileStreamUnix::LoadIntoMemory(const fs::path& path)
|
||||
{
|
||||
FileStream* fs = openFile2(path);
|
||||
FileStreamUnix* fs = openFile2(path);
|
||||
if (!fs)
|
||||
return std::nullopt;
|
||||
uint64 fileSize = fs->GetSize();
|
||||
@ -79,7 +79,7 @@ std::optional<std::vector<uint8>> FileStream::LoadIntoMemory(const fs::path& pat
|
||||
return v;
|
||||
}
|
||||
|
||||
void FileStream::SetPosition(uint64 pos)
|
||||
void FileStreamUnix::SetPosition(uint64 pos)
|
||||
{
|
||||
cemu_assert(m_isValid);
|
||||
if (m_prevOperationWasWrite)
|
||||
@ -88,7 +88,7 @@ void FileStream::SetPosition(uint64 pos)
|
||||
m_fileStream.seekg((std::streampos)pos);
|
||||
}
|
||||
|
||||
uint64 FileStream::GetSize()
|
||||
uint64 FileStreamUnix::GetSize()
|
||||
{
|
||||
cemu_assert(m_isValid);
|
||||
auto currentPos = m_fileStream.tellg();
|
||||
@ -99,14 +99,14 @@ uint64 FileStream::GetSize()
|
||||
return fs;
|
||||
}
|
||||
|
||||
bool FileStream::SetEndOfFile()
|
||||
bool FileStreamUnix::SetEndOfFile()
|
||||
{
|
||||
assert_dbg();
|
||||
return true;
|
||||
//return ::SetEndOfFile(m_hFile) != 0;
|
||||
}
|
||||
|
||||
void FileStream::extract(std::vector<uint8>& data)
|
||||
void FileStreamUnix::extract(std::vector<uint8>& data)
|
||||
{
|
||||
uint64 fileSize = GetSize();
|
||||
SetPosition(0);
|
||||
@ -114,12 +114,12 @@ void FileStream::extract(std::vector<uint8>& data)
|
||||
readData(data.data(), fileSize);
|
||||
}
|
||||
|
||||
void FileStream::Flush()
|
||||
void FileStreamUnix::Flush()
|
||||
{
|
||||
m_fileStream.flush();
|
||||
}
|
||||
|
||||
uint32 FileStream::readData(void* data, uint32 length)
|
||||
uint32 FileStreamUnix::readData(void* data, uint32 length)
|
||||
{
|
||||
SyncReadWriteSeek(false);
|
||||
m_fileStream.read((char*)data, length);
|
||||
@ -127,22 +127,22 @@ uint32 FileStream::readData(void* data, uint32 length)
|
||||
return (uint32)bytesRead;
|
||||
}
|
||||
|
||||
bool FileStream::readU64(uint64& v)
|
||||
bool FileStreamUnix::readU64(uint64& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint64)) == sizeof(uint64);
|
||||
}
|
||||
|
||||
bool FileStream::readU32(uint32& v)
|
||||
bool FileStreamUnix::readU32(uint32& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint32)) == sizeof(uint32);
|
||||
}
|
||||
|
||||
bool FileStream::readU8(uint8& v)
|
||||
bool FileStreamUnix::readU8(uint8& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint8)) == sizeof(uint8);
|
||||
}
|
||||
|
||||
bool FileStream::readLine(std::string& line)
|
||||
bool FileStreamUnix::readLine(std::string& line)
|
||||
{
|
||||
line.clear();
|
||||
uint8 c;
|
||||
@ -159,29 +159,29 @@ bool FileStream::readLine(std::string& line)
|
||||
return !isEOF;
|
||||
}
|
||||
|
||||
sint32 FileStream::writeData(const void* data, sint32 length)
|
||||
sint32 FileStreamUnix::writeData(const void* data, sint32 length)
|
||||
{
|
||||
SyncReadWriteSeek(true);
|
||||
m_fileStream.write((const char*)data, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
void FileStream::writeU64(uint64 v)
|
||||
void FileStreamUnix::writeU64(uint64 v)
|
||||
{
|
||||
writeData(&v, sizeof(uint64));
|
||||
}
|
||||
|
||||
void FileStream::writeU32(uint32 v)
|
||||
void FileStreamUnix::writeU32(uint32 v)
|
||||
{
|
||||
writeData(&v, sizeof(uint32));
|
||||
}
|
||||
|
||||
void FileStream::writeU8(uint8 v)
|
||||
void FileStreamUnix::writeU8(uint8 v)
|
||||
{
|
||||
writeData(&v, sizeof(uint8));
|
||||
}
|
||||
|
||||
void FileStream::writeStringFmt(const char* format, ...)
|
||||
void FileStreamUnix::writeStringFmt(const char* format, ...)
|
||||
{
|
||||
char buffer[2048];
|
||||
va_list args;
|
||||
@ -190,18 +190,18 @@ void FileStream::writeStringFmt(const char* format, ...)
|
||||
writeData(buffer, (sint32)strlen(buffer));
|
||||
}
|
||||
|
||||
void FileStream::writeString(const char* str)
|
||||
void FileStreamUnix::writeString(const char* str)
|
||||
{
|
||||
writeData(str, (sint32)strlen(str));
|
||||
}
|
||||
|
||||
void FileStream::writeLine(const char* str)
|
||||
void FileStreamUnix::writeLine(const char* str)
|
||||
{
|
||||
writeData(str, (sint32)strlen(str));
|
||||
writeData("\r\n", 2);
|
||||
}
|
||||
|
||||
FileStream::~FileStream()
|
||||
FileStreamUnix::~FileStreamUnix()
|
||||
{
|
||||
if (m_isValid)
|
||||
{
|
||||
@ -210,7 +210,7 @@ FileStream::~FileStream()
|
||||
// CloseHandle(m_hFile);
|
||||
}
|
||||
|
||||
FileStream::FileStream(const fs::path& path, bool isOpen, bool isWriteable)
|
||||
FileStreamUnix::FileStreamUnix(const fs::path& path, bool isOpen, bool isWriteable)
|
||||
{
|
||||
fs::path CIPath = findPathCI(path);
|
||||
if (isOpen)
|
||||
@ -230,7 +230,7 @@ FileStream::FileStream(const fs::path& path, bool isOpen, bool isWriteable)
|
||||
}
|
||||
}
|
||||
|
||||
void FileStream::SyncReadWriteSeek(bool nextOpIsWrite)
|
||||
void FileStreamUnix::SyncReadWriteSeek(bool nextOpIsWrite)
|
||||
{
|
||||
// nextOpIsWrite == false -> read. Otherwise write
|
||||
if (nextOpIsWrite == m_prevOperationWasWrite)
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
#include "Common/precompiled.h"
|
||||
|
||||
class FileStream
|
||||
class FileStreamUnix
|
||||
{
|
||||
public:
|
||||
static FileStream* openFile(std::string_view path);
|
||||
static FileStream* openFile(const wchar_t* path, bool allowWrite = false);
|
||||
static FileStream* openFile2(const fs::path& path, bool allowWrite = false);
|
||||
static FileStreamUnix* openFile(std::string_view path);
|
||||
static FileStreamUnix* openFile(const wchar_t* path, bool allowWrite = false);
|
||||
static FileStreamUnix* openFile2(const fs::path& path, bool allowWrite = false);
|
||||
|
||||
static FileStream* createFile(const wchar_t* path);
|
||||
static FileStream* createFile(std::string_view path);
|
||||
static FileStream* createFile2(const fs::path& path);
|
||||
static FileStreamUnix* createFile(const wchar_t* path);
|
||||
static FileStreamUnix* createFile(std::string_view path);
|
||||
static FileStreamUnix* createFile2(const fs::path& path);
|
||||
|
||||
// helper function to load a file into memory
|
||||
static std::optional<std::vector<uint8>> LoadIntoMemory(const fs::path& path);
|
||||
@ -28,7 +28,6 @@ class FileStream
|
||||
uint32 readData(void* data, uint32 length);
|
||||
bool readU64(uint64& v);
|
||||
bool readU32(uint32& v);
|
||||
bool readU16(uint16& v);
|
||||
bool readU8(uint8& v);
|
||||
bool readLine(std::string& line);
|
||||
|
||||
@ -36,7 +35,6 @@ class FileStream
|
||||
sint32 writeData(const void* data, sint32 length);
|
||||
void writeU64(uint64 v);
|
||||
void writeU32(uint32 v);
|
||||
void writeU16(uint16 v);
|
||||
void writeU8(uint8 v);
|
||||
|
||||
// writing (strings)
|
||||
@ -44,12 +42,12 @@ class FileStream
|
||||
void writeString(const char* str);
|
||||
void writeLine(const char* str);
|
||||
|
||||
~FileStream();
|
||||
FileStream() {};
|
||||
~FileStreamUnix();
|
||||
FileStreamUnix() {};
|
||||
|
||||
private:
|
||||
void SyncReadWriteSeek(bool nextOpIsWrite);
|
||||
FileStream(const fs::path& path, bool isOpen, bool isWriteable);
|
||||
FileStreamUnix(const fs::path& path, bool isOpen, bool isWriteable);
|
||||
|
||||
bool m_isValid{};
|
||||
std::fstream m_fileStream;
|
||||
|
||||
@ -26,7 +26,6 @@ class FileStream
|
||||
uint32 readData(void* data, uint32 length);
|
||||
bool readU64(uint64& v);
|
||||
bool readU32(uint32& v);
|
||||
bool readU16(uint16& v);
|
||||
bool readU8(uint8& v);
|
||||
bool readLine(std::string& line);
|
||||
|
||||
@ -34,7 +33,6 @@ class FileStream
|
||||
sint32 writeData(const void* data, sint32 length);
|
||||
void writeU64(uint64 v);
|
||||
void writeU32(uint32 v);
|
||||
void writeU16(uint16 v);
|
||||
void writeU8(uint8 v);
|
||||
|
||||
// writing (strings)
|
||||
|
||||
@ -38,6 +38,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
|
||||
|
||||
28
src/util/helpers/ZArchiveHelpers.h
Normal file
28
src/util/helpers/ZArchiveHelpers.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <zarchive/zarchivereader.h>
|
||||
|
||||
#if BOOST_PLAT_ANDROID
|
||||
#include "Common/android/FilesystemAndroid.h"
|
||||
#include "Common/android/FdStream.h"
|
||||
#endif // BOOST_PLAT_ANDROID
|
||||
|
||||
namespace ZArchiveHelpers
|
||||
{
|
||||
inline ZArchiveReader* OpenReader(const fs::path& path)
|
||||
{
|
||||
#if BOOST_PLAT_ANDROID
|
||||
if (FilesystemAndroid::IsContentUri(path))
|
||||
{
|
||||
int fd = FilesystemAndroid::OpenContentUri(path);
|
||||
|
||||
if (fd == -1)
|
||||
return nullptr;
|
||||
|
||||
return ZArchiveReader::OpenFromStream(std::make_unique<FdStream>(fd));
|
||||
}
|
||||
#endif // BOOST_PLAT_ANDROID
|
||||
|
||||
return ZArchiveReader::OpenFromFile(path);
|
||||
}
|
||||
} // namespace ZArchiveHelpers
|
||||
@ -27,6 +27,10 @@
|
||||
"boost-ptr-container",
|
||||
"boost-property-tree",
|
||||
"boost-static-string",
|
||||
{
|
||||
"name": "boost-iostreams",
|
||||
"platform": "android"
|
||||
},
|
||||
{
|
||||
"name": "boost-context",
|
||||
"platform": "android"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user