common: refactor FileStream & implement Android filesystem access

This commit is contained in:
SSimco 2026-05-09 16:45:59 +03:00
parent 42b2aacf41
commit e75d2fe9c6
32 changed files with 1109 additions and 77 deletions

View File

@ -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)

@ -1 +1 @@
Subproject commit d2c717730092c7bf8cbb033b12fd4001b7c4d932
Subproject commit 965b66c8d67b6b7e30fd63b3b75aa91a99ff303b

View File

@ -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());

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);
};

View File

@ -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

View File

@ -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);
}

View 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;
}

View 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;
};

View File

@ -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 };

View File

@ -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)
{

View File

@ -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

View File

@ -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

View File

@ -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 "../")

View File

@ -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

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();
}
};

View 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);
}

View 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;
};

View 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();
}

View 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;
};

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> 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

View 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

View File

@ -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>

View File

@ -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"

View File

@ -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)

View File

@ -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;

View File

@ -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)

View File

@ -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

View 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

View File

@ -27,6 +27,10 @@
"boost-ptr-container",
"boost-property-tree",
"boost-static-string",
{
"name": "boost-iostreams",
"platform": "android"
},
{
"name": "boost-context",
"platform": "android"