diff --git a/CMakeLists.txt b/CMakeLists.txt index f28251e2..76d26311 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/dependencies/ZArchive b/dependencies/ZArchive index d2c71773..965b66c8 160000 --- a/dependencies/ZArchive +++ b/dependencies/ZArchive @@ -1 +1 @@ -Subproject commit d2c717730092c7bf8cbb033b12fd4001b7c4d932 +Subproject commit 965b66c8d67b6b7e30fd63b3b75aa91a99ff303b diff --git a/src/Cafe/Account/Account.cpp b/src/Cafe/Account/Account.cpp index 6f576ae1..c3912024 100644 --- a/src/Cafe/Account/Account.cpp +++ b/src/Cafe/Account/Account.cpp @@ -459,7 +459,7 @@ OnlineValidator Account::ValidateOnlineFiles() const return result; } -void Account::ParseFile(class FileStream* file) +void Account::ParseFile(FileStream* file) { std::vector buffer; buffer.resize(file->GetSize()); diff --git a/src/Cafe/Account/Account.h b/src/Cafe/Account/Account.h index c9abd395..f3fcaa7a 100644 --- a/src/Cafe/Account/Account.h +++ b/src/Cafe/Account/Account.h @@ -1,5 +1,6 @@ #pragma once +#include "Common/FileStream.h" #include #include #include @@ -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; diff --git a/src/Cafe/CMakeLists.txt b/src/Cafe/CMakeLists.txt index b509e722..91c16cba 100644 --- a/src/Cafe/CMakeLists.txt +++ b/src/Cafe/CMakeLists.txt @@ -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 diff --git a/src/Cafe/CafeSystem.cpp b/src/Cafe/CafeSystem.cpp index 10d65c77..5aefb605 100644 --- a/src/Cafe/CafeSystem.cpp +++ b/src/Cafe/CafeSystem.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; diff --git a/src/Cafe/Filesystem/FST/FST.h b/src/Cafe/Filesystem/FST/FST.h index 26201c32..cb9879b9 100644 --- a/src/Cafe/Filesystem/FST/FST.h +++ b/src/Cafe/Filesystem/FST/FST.h @@ -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); }; \ No newline at end of file diff --git a/src/Cafe/Filesystem/WUD/wud.h b/src/Cafe/Filesystem/WUD/wud.h index e58b4073..f246544d 100644 --- a/src/Cafe/Filesystem/WUD/wud.h +++ b/src/Cafe/Filesystem/WUD/wud.h @@ -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 diff --git a/src/Cafe/Filesystem/fsc.h b/src/Cafe/Filesystem/fsc.h index 8b8ed5ef..42436ac9 100644 --- a/src/Cafe/Filesystem/fsc.h +++ b/src/Cafe/Filesystem/fsc.h @@ -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); +} diff --git a/src/Cafe/Filesystem/fscDeviceAndroidSAF.cpp b/src/Cafe/Filesystem/fscDeviceAndroidSAF.cpp new file mode 100644 index 00000000..29a8902d --- /dev/null +++ b/src/Cafe/Filesystem/fscDeviceAndroidSAF.cpp @@ -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>(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; +} diff --git a/src/Cafe/Filesystem/fscDeviceAndroidSAF.h b/src/Cafe/Filesystem/fscDeviceAndroidSAF.h new file mode 100644 index 00000000..3ca0dde6 --- /dev/null +++ b/src/Cafe/Filesystem/fscDeviceAndroidSAF.h @@ -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 m_path{}; + std::unique_ptr> m_files{}; + std::vector::iterator m_filesIterator; +}; diff --git a/src/Cafe/Filesystem/fscDeviceHostFS.h b/src/Cafe/Filesystem/fscDeviceHostFS.h index 7121594c..25414065 100644 --- a/src/Cafe/Filesystem/fscDeviceHostFS.h +++ b/src/Cafe/Filesystem/fscDeviceHostFS.h @@ -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 }; diff --git a/src/Cafe/TitleList/TitleInfo.cpp b/src/Cafe/TitleList/TitleInfo.cpp index 12131058..595a378e 100644 --- a/src/Cafe/TitleList/TitleInfo.cpp +++ b/src/Cafe/TitleList/TitleInfo.cpp @@ -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) { diff --git a/src/Cafe/TitleList/TitleList.cpp b/src/Cafe/TitleList/TitleList.cpp index 7b75fac7..fca60539 100644 --- a/src/Cafe/TitleList/TitleList.cpp +++ b/src/Cafe/TitleList/TitleList.cpp @@ -2,6 +2,8 @@ #include "Common/FileStream.h" #include "util/helpers/helpers.h" +#include "util/helpers/ZArchiveHelpers.h" + #include @@ -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 filesInDirectory; std::vector 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 diff --git a/src/Cemu/FileCache/FileCache.h b/src/Cemu/FileCache/FileCache.h index de1996c5..13ca2817 100644 --- a/src/Cemu/FileCache/FileCache.h +++ b/src/Cemu/FileCache/FileCache.h @@ -1,5 +1,6 @@ #pragma once +#include "Common/FileStream.h" #include 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& dataOut); - class FileStream* fileStream{}; + FileStream* fileStream{}; uint64 dataOffset{}; uint32 extraVersion{}; // file table diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index b1b6ad09..3add41af 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -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 "../") diff --git a/src/Common/FileStream.h b/src/Common/FileStream.h index f5d8ff0e..ce02d02a 100644 --- a/src/Common/FileStream.h +++ b/src/Common/FileStream.h @@ -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 diff --git a/src/Common/android/FdStream.h b/src/Common/android/FdStream.h new file mode 100644 index 00000000..7f4b69ce --- /dev/null +++ b/src/Common/android/FdStream.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +class FdStream : private boost::iostreams::stream_buffer, public std::istream +{ + public: + explicit FdStream(int fd) + : boost::iostreams::stream_buffer(fd, boost::iostreams::close_handle), std::istream(this) {} + + bool IsValid() + { + return component()->is_open(); + } +}; diff --git a/src/Common/android/FileStream_android.cpp b/src/Common/android/FileStream_android.cpp new file mode 100644 index 00000000..d3d145fa --- /dev/null +++ b/src/Common/android/FileStream_android.cpp @@ -0,0 +1,295 @@ +#include "FileStream_saf.h" +#include "FileStream_android.h" +#include "FilesystemAndroid.h" +#include "Common/unix/FileStream_unix.h" + +#include + +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& 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 +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& 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> 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> 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& 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); +} diff --git a/src/Common/android/FileStream_android.h b/src/Common/android/FileStream_android.h new file mode 100644 index 00000000..855789ad --- /dev/null +++ b/src/Common/android/FileStream_android.h @@ -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> LoadIntoMemory(const fs::path& path); + + // size and seek + void SetPosition(uint64 pos); + + uint64 GetSize(); + bool SetEndOfFile(); + void extract(std::vector& 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; +}; diff --git a/src/Common/android/FileStream_saf.cpp b/src/Common/android/FileStream_saf.cpp new file mode 100644 index 00000000..8b68e47f --- /dev/null +++ b/src/Common/android/FileStream_saf.cpp @@ -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& 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(); +} diff --git a/src/Common/android/FileStream_saf.h b/src/Common/android/FileStream_saf.h new file mode 100644 index 00000000..86459714 --- /dev/null +++ b/src/Common/android/FileStream_saf.h @@ -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& 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; +}; diff --git a/src/Common/android/FilesystemAndroid.cpp b/src/Common/android/FilesystemAndroid.cpp new file mode 100644 index 00000000..98631e75 --- /dev/null +++ b/src/Common/android/FilesystemAndroid.cpp @@ -0,0 +1,58 @@ +#include "FilesystemAndroid.h" + +#include +#include + +using namespace boost::iostreams; + +namespace FilesystemAndroid +{ +std::shared_ptr s_filesystemCallbacks = nullptr; + +void SetFilesystemCallbacks(std::shared_ptr filesystemCallbacks) +{ + s_filesystemCallbacks = std::move(filesystemCallbacks); +} + +int OpenContentUri(const fs::path &uri) +{ + if (s_filesystemCallbacks) + return s_filesystemCallbacks->OpenContentUri(uri); + return -1; +} + +std::vector 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 \ No newline at end of file diff --git a/src/Common/android/FilesystemAndroid.h b/src/Common/android/FilesystemAndroid.h new file mode 100644 index 00000000..f8f393e3 --- /dev/null +++ b/src/Common/android/FilesystemAndroid.h @@ -0,0 +1,34 @@ +#pragma once + +namespace FilesystemAndroid +{ + class FilesystemCallbacks + { + public: + virtual int OpenContentUri(const fs::path& uri) = 0; + virtual std::vector 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); + + int OpenContentUri(const fs::path& uri); + + std::vector 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 diff --git a/src/Common/platform.h b/src/Common/platform.h index 08462a97..8eae0dd7 100644 --- a/src/Common/platform.h +++ b/src/Common/platform.h @@ -1,10 +1,14 @@ #pragma once #include +#include #include #if BOOST_OS_WINDOWS #include "Common/windows/platform.h" +#elif BOOST_PLAT_ANDROID +#include +#include "Common/unix/platform.h" #elif BOOST_OS_LINUX || BOOST_OS_BSD #if BOOST_OS_LINUX #include diff --git a/src/Common/precompiled.h b/src/Common/precompiled.h index e0373952..6a9c9fd6 100644 --- a/src/Common/precompiled.h +++ b/src/Common/precompiled.h @@ -84,6 +84,61 @@ #include 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" diff --git a/src/Common/unix/FileStream_unix.cpp b/src/Common/unix/FileStream_unix.cpp index 53880527..9fa9cc95 100644 --- a/src/Common/unix/FileStream_unix.cpp +++ b/src/Common/unix/FileStream_unix.cpp @@ -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> FileStream::LoadIntoMemory(const fs::path& path) +std::optional> 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> 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& data) +void FileStreamUnix::extract(std::vector& data) { uint64 fileSize = GetSize(); SetPosition(0); @@ -114,12 +114,12 @@ void FileStream::extract(std::vector& 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) diff --git a/src/Common/unix/FileStream_unix.h b/src/Common/unix/FileStream_unix.h index 0a2fa7ed..c9373568 100644 --- a/src/Common/unix/FileStream_unix.h +++ b/src/Common/unix/FileStream_unix.h @@ -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> 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; diff --git a/src/Common/windows/FileStream_win32.h b/src/Common/windows/FileStream_win32.h index a0b1b30a..d8b66e3e 100644 --- a/src/Common/windows/FileStream_win32.h +++ b/src/Common/windows/FileStream_win32.h @@ -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) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 5705825d..64375761 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -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 diff --git a/src/util/helpers/ZArchiveHelpers.h b/src/util/helpers/ZArchiveHelpers.h new file mode 100644 index 00000000..4ece20d8 --- /dev/null +++ b/src/util/helpers/ZArchiveHelpers.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#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(fd)); + } +#endif // BOOST_PLAT_ANDROID + + return ZArchiveReader::OpenFromFile(path); + } +} // namespace ZArchiveHelpers diff --git a/vcpkg.json b/vcpkg.json index 37591e2a..c08fcb7c 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -27,6 +27,10 @@ "boost-ptr-container", "boost-property-tree", "boost-static-string", + { + "name": "boost-iostreams", + "platform": "android" + }, { "name": "boost-context", "platform": "android"