mirror of
https://github.com/izzy2lost/WeeU.git
synced 2026-07-06 00:19:59 -07:00
Add support for reading game files using SAF
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
[submodule "dependencies/ZArchive"]
|
||||
path = dependencies/ZArchive
|
||||
url = https://github.com/Exzap/ZArchive
|
||||
url = https://github.com/SSimco/ZArchive
|
||||
shallow = true
|
||||
[submodule "dependencies/cubeb"]
|
||||
path = dependencies/cubeb
|
||||
|
||||
+8
-3
@@ -57,8 +57,10 @@ endif()
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
# enable link time optimization for release builds
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
|
||||
if(NOT ANDROID)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT CemuBin)
|
||||
@@ -117,7 +119,10 @@ endif()
|
||||
find_package(CURL REQUIRED)
|
||||
find_package(pugixml REQUIRED)
|
||||
find_package(RapidJSON REQUIRED)
|
||||
find_package(Boost COMPONENTS program_options filesystem nowide context REQUIRED)
|
||||
find_package(Boost COMPONENTS program_options filesystem nowide REQUIRED)
|
||||
if(ANDROID)
|
||||
find_package(Boost COMPONENTS context iostreams REQUIRED)
|
||||
endif()
|
||||
find_package(libzip REQUIRED)
|
||||
find_package(glslang REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
Vendored
+1
-1
Submodule dependencies/ZArchive updated: d2c7177300...c50b5d222b
@@ -6,6 +6,11 @@
|
||||
#include "pugixml.hpp"
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
#if __ANDROID__
|
||||
#include "Common/unix/FilesystemAndroid.h"
|
||||
#include "Common/unix/ContentUriIStream.h"
|
||||
#endif // __ANDROID__
|
||||
|
||||
#include <zarchive/zarchivereader.h>
|
||||
#include "config/ActiveSettings.h"
|
||||
|
||||
@@ -209,7 +214,13 @@ 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 = nullptr;
|
||||
#if __ANDROID__
|
||||
if(FilesystemAndroid::isContentUri(path))
|
||||
zar = ZArchiveReader::OpenFromStream(std::make_unique<ContentUriIStream>(path));
|
||||
else
|
||||
#endif // __ANDROID__
|
||||
zar = ZArchiveReader::OpenFromFile(path);
|
||||
if (!zar)
|
||||
return false;
|
||||
ZArchiveNodeHandle rootDir = zar->LookUp("", false, true);
|
||||
@@ -327,7 +338,13 @@ 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 = nullptr;
|
||||
#if __ANDROID__
|
||||
if(FilesystemAndroid::isContentUri(path))
|
||||
zar = ZArchiveReader::OpenFromStream(std::make_unique<ContentUriIStream>(path));
|
||||
else
|
||||
#endif // __ANDROID__
|
||||
zar = ZArchiveReader::OpenFromFile(path);
|
||||
if (!zar)
|
||||
return nullptr;
|
||||
_lock.lock();
|
||||
|
||||
@@ -3,6 +3,11 @@
|
||||
|
||||
#include "util/helpers/helpers.h"
|
||||
|
||||
#if __ANDROID__
|
||||
#include "Common/unix/FilesystemAndroid.h"
|
||||
#include "Common/unix/ContentUriIStream.h"
|
||||
#endif // __ANDROID__
|
||||
|
||||
#include <zarchive/zarchivereader.h>
|
||||
|
||||
bool sTLInitialized{ false };
|
||||
@@ -213,7 +218,13 @@ void CafeTitleList::AddTitleFromPath(fs::path path)
|
||||
{
|
||||
if (path.has_extension() && boost::iequals(_pathToUtf8(path.extension()), ".wua"))
|
||||
{
|
||||
ZArchiveReader* zar = ZArchiveReader::OpenFromFile(path);
|
||||
ZArchiveReader* zar = nullptr;
|
||||
#if __ANDROID__
|
||||
if(FilesystemAndroid::isContentUri(path))
|
||||
zar = ZArchiveReader::OpenFromStream(std::make_unique<ContentUriIStream>(path));
|
||||
else
|
||||
#endif // __ANDROID__
|
||||
zar = ZArchiveReader::OpenFromFile(path);
|
||||
if (!zar)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Found {} but it is not a valid Wii U archive file", _pathToUtf8(path));
|
||||
@@ -340,26 +351,51 @@ 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 = [&](const std::string& 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 __ANDROID__
|
||||
if(FilesystemAndroid::isContentUri(path))
|
||||
{
|
||||
for(auto&& file:FilesystemAndroid::listFiles(path))
|
||||
{
|
||||
filesInDirectory.emplace_back(it.path());
|
||||
}
|
||||
else if (it.is_directory(ec))
|
||||
{
|
||||
dirsInDirectory.emplace_back(it.path());
|
||||
if(FilesystemAndroid::isFile(file))
|
||||
{
|
||||
filesInDirectory.emplace_back(file);
|
||||
}
|
||||
else if(FilesystemAndroid::isDirectory(file))
|
||||
{
|
||||
dirsInDirectory.emplace_back(file);
|
||||
|
||||
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;
|
||||
checkForTitleFolders(_pathToUtf8(file.filename()));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif // __ANDROID__
|
||||
{
|
||||
std::error_code ec;
|
||||
for (auto& it : fs::directory_iterator(path, ec))
|
||||
{
|
||||
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
|
||||
for (auto& it : filesInDirectory)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ add_library(CemuCommon
|
||||
ExceptionHandler/ExceptionHandler.cpp
|
||||
ExceptionHandler/ExceptionHandler.h
|
||||
FileStream.h
|
||||
FileStream.cpp
|
||||
GLInclude/glext.h
|
||||
GLInclude/glFunctions.h
|
||||
GLInclude/GLInclude.h
|
||||
@@ -52,6 +53,16 @@ if(UNIX AND NOT APPLE)
|
||||
)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(CemuCommon PRIVATE
|
||||
unix/ContentUriIStream.h
|
||||
unix/FilesystemAndroid.cpp
|
||||
unix/FilesystemAndroid.h
|
||||
unix/FileStream_contentUri.cpp
|
||||
unix/FileStream_contentUri.h
|
||||
)
|
||||
endif()
|
||||
|
||||
# All the targets wanting to use the precompiled.h header
|
||||
# have to link to CemuCommon
|
||||
target_precompile_headers(CemuCommon PUBLIC precompiled.h)
|
||||
@@ -66,6 +77,10 @@ target_link_libraries(CemuCommon PRIVATE
|
||||
glm::glm
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
target_link_libraries(CemuCommon PRIVATE Boost::iostreams)
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
target_link_libraries(CemuCommon PRIVATE X11::X11 X11::Xrender X11::Xutil)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
#if BOOST_OS_UNIX
|
||||
|
||||
#if __ANDROID__
|
||||
#include "Common/unix/FileStream_contentUri.h"
|
||||
#include "Common/unix/FilesystemAndroid.h"
|
||||
#endif // __ANDROID__
|
||||
|
||||
#include "Common/unix/FileStream_unix.h"
|
||||
|
||||
FileStream* FileStream::openFile(std::string_view path)
|
||||
{
|
||||
return openFile2(path, false);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile(const wchar_t* path, bool allowWrite)
|
||||
{
|
||||
return openFile2(path, allowWrite);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile2(const fs::path& path, bool allowWrite)
|
||||
{
|
||||
#if __ANDROID__
|
||||
if (FilesystemAndroid::isContentUri(path))
|
||||
{
|
||||
if (allowWrite || FilesystemAndroid::isDirectory(path))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
FileStreamContentUri* fs = new FileStreamContentUri(path);
|
||||
if (fs->m_isValid)
|
||||
return fs;
|
||||
delete fs;
|
||||
return nullptr;
|
||||
}
|
||||
#endif // __ANDROID__
|
||||
FileStreamUnix* fs = new FileStreamUnix(path, true, allowWrite);
|
||||
if (fs->m_isValid)
|
||||
return fs;
|
||||
delete fs;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(const wchar_t* path)
|
||||
{
|
||||
return createFile2(path);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(std::string_view path)
|
||||
{
|
||||
return createFile2(path);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile2(const fs::path& path)
|
||||
{
|
||||
FileStreamUnix* fs = new FileStreamUnix(path, false, false);
|
||||
if (fs->m_isValid)
|
||||
return fs;
|
||||
delete fs;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<std::vector<uint8>> FileStream::LoadIntoMemory(const fs::path& path)
|
||||
{
|
||||
FileStream* fs = openFile2(path);
|
||||
if (!fs)
|
||||
return std::nullopt;
|
||||
uint64 fileSize = fs->GetSize();
|
||||
if (fileSize > 0xFFFFFFFFull)
|
||||
{
|
||||
delete fs;
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::vector<uint8>> v(fileSize);
|
||||
if (fs->readData(v->data(), (uint32)fileSize) != (uint32)fileSize)
|
||||
{
|
||||
delete fs;
|
||||
return std::nullopt;
|
||||
}
|
||||
delete fs;
|
||||
return v;
|
||||
}
|
||||
|
||||
#endif // BOOST_OS_UNIX
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "Common/windows/FileStream_win32.h"
|
||||
|
||||
FileStream* FileStream::openFile(std::string_view path)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(boost::nowide::widen(path.data(), path.size()).c_str(), FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
return new FileStreamWin32(hFile);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile(const wchar_t* path, bool allowWrite)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(path, allowWrite ? (FILE_GENERIC_READ | FILE_GENERIC_WRITE) : FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
return new FileStreamWin32(hFile);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile2(const fs::path& path, bool allowWrite)
|
||||
{
|
||||
return openFile(path.generic_wstring().c_str(), allowWrite);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(const wchar_t* path)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(path, FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
return new FileStreamWin32(hFile);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(std::string_view path)
|
||||
{
|
||||
auto w = boost::nowide::widen(path.data(), path.size());
|
||||
HANDLE hFile = CreateFileW(w.c_str(), FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
return new FileStreamWin32(hFile);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile2(const fs::path& path)
|
||||
{
|
||||
return createFile(path.generic_wstring().c_str());
|
||||
}
|
||||
|
||||
std::optional<std::vector<uint8>> FileStream::LoadIntoMemory(const fs::path& path)
|
||||
{
|
||||
FileStream* fs = openFile2(path);
|
||||
if (!fs)
|
||||
return std::nullopt;
|
||||
uint64 fileSize = fs->GetSize();
|
||||
if (fileSize > 0xFFFFFFFFull)
|
||||
{
|
||||
delete fs;
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::vector<uint8>> v(fileSize);
|
||||
if (fs->readData(v->data(), (uint32)fileSize) != (uint32)fileSize)
|
||||
{
|
||||
delete fs;
|
||||
return std::nullopt;
|
||||
}
|
||||
delete fs;
|
||||
return v;
|
||||
}
|
||||
|
||||
#endif // _WIN32
|
||||
+41
-5
@@ -1,8 +1,44 @@
|
||||
#pragma once
|
||||
#include "Common/precompiled.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Common/windows/FileStream_win32.h"
|
||||
#else
|
||||
#include "Common/unix/FileStream_unix.h"
|
||||
#endif
|
||||
class FileStream
|
||||
{
|
||||
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 FileStream* createFile(const wchar_t* path);
|
||||
static FileStream* createFile(std::string_view path);
|
||||
static FileStream* 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
|
||||
virtual void SetPosition(uint64 pos) = 0;
|
||||
|
||||
virtual uint64 GetSize() = 0;
|
||||
virtual bool SetEndOfFile() = 0;
|
||||
virtual void extract(std::vector<uint8>& data) = 0;
|
||||
|
||||
// reading
|
||||
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;
|
||||
|
||||
// writing (binary)
|
||||
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;
|
||||
|
||||
// writing (strings)
|
||||
virtual void writeStringFmt(const char* format, ...) = 0;
|
||||
virtual void writeString(const char* str) = 0;
|
||||
virtual void writeLine(const char* str) = 0;
|
||||
|
||||
virtual ~FileStream() = default;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
#include <boost/iostreams/device/file_descriptor.hpp>
|
||||
#include <boost/iostreams/stream_buffer.hpp>
|
||||
|
||||
#include "Common/unix/FilesystemAndroid.h"
|
||||
|
||||
class ContentUriIStream : public std::istream
|
||||
{
|
||||
public:
|
||||
ContentUriIStream(const std::filesystem::path& path)
|
||||
: m_fd(FilesystemAndroid::openContentUri(path)),
|
||||
m_fileDescriptorStreamBuffer(m_fd, boost::iostreams::close_handle),
|
||||
std::istream(&m_fileDescriptorStreamBuffer) {}
|
||||
|
||||
virtual ~ContentUriIStream() = default;
|
||||
|
||||
inline bool isOpen() const
|
||||
{
|
||||
return m_fd != -1;
|
||||
}
|
||||
|
||||
private:
|
||||
int m_fd;
|
||||
boost::iostreams::stream_buffer<boost::iostreams::file_descriptor_source> m_fileDescriptorStreamBuffer;
|
||||
};
|
||||
@@ -0,0 +1,122 @@
|
||||
#include "FileStream_contentUri.h"
|
||||
|
||||
#include "unix/ContentUriIStream.h"
|
||||
|
||||
void ThrowWriteNotSupportedError()
|
||||
{
|
||||
throw std::logic_error("write operation not supported");
|
||||
}
|
||||
|
||||
void FileStreamContentUri::SetPosition(uint64 pos)
|
||||
{
|
||||
if (!m_isValid)
|
||||
return;
|
||||
m_contentUriIStream.seekg((std::streampos)pos);
|
||||
}
|
||||
|
||||
uint64 FileStreamContentUri::GetSize()
|
||||
{
|
||||
cemu_assert(m_isValid);
|
||||
auto currentPos = m_contentUriIStream.tellg();
|
||||
m_contentUriIStream.seekg(0, std::ios::end);
|
||||
auto fileSize = m_contentUriIStream.tellg();
|
||||
m_contentUriIStream.seekg(currentPos, std::ios::beg);
|
||||
uint64 fs = (uint64)fileSize;
|
||||
return fs;
|
||||
}
|
||||
|
||||
bool FileStreamContentUri::SetEndOfFile()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileStreamContentUri::extract(std::vector<uint8>& data)
|
||||
{
|
||||
if (!m_isValid)
|
||||
return;
|
||||
uint64 fileSize = GetSize();
|
||||
SetPosition(0);
|
||||
data.resize(fileSize);
|
||||
readData(data.data(), fileSize);
|
||||
}
|
||||
|
||||
uint32 FileStreamContentUri::readData(void* data, uint32 length)
|
||||
{
|
||||
m_contentUriIStream.read((char*)data, length);
|
||||
size_t bytesRead = m_contentUriIStream.gcount();
|
||||
return (uint32)bytesRead;
|
||||
}
|
||||
|
||||
bool FileStreamContentUri::readU64(uint64& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint64)) == sizeof(uint64);
|
||||
}
|
||||
|
||||
bool FileStreamContentUri::readU32(uint32& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint32)) == sizeof(uint32);
|
||||
}
|
||||
|
||||
bool FileStreamContentUri::readU8(uint8& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint8)) == sizeof(uint8);
|
||||
}
|
||||
|
||||
bool FileStreamContentUri::readLine(std::string& line)
|
||||
{
|
||||
line.clear();
|
||||
uint8 c;
|
||||
bool isEOF = true;
|
||||
while (readU8(c))
|
||||
{
|
||||
isEOF = false;
|
||||
if (c == '\r')
|
||||
continue;
|
||||
if (c == '\n')
|
||||
break;
|
||||
line.push_back((char)c);
|
||||
}
|
||||
return !isEOF;
|
||||
}
|
||||
|
||||
sint32 FileStreamContentUri::writeData(const void* data, sint32 length)
|
||||
{
|
||||
ThrowWriteNotSupportedError();
|
||||
return -1;
|
||||
}
|
||||
|
||||
void FileStreamContentUri::writeU64(uint64 v)
|
||||
{
|
||||
ThrowWriteNotSupportedError();
|
||||
}
|
||||
|
||||
void FileStreamContentUri::writeU32(uint32 v)
|
||||
{
|
||||
ThrowWriteNotSupportedError();
|
||||
}
|
||||
|
||||
void FileStreamContentUri::writeU8(uint8 v)
|
||||
{
|
||||
ThrowWriteNotSupportedError();
|
||||
}
|
||||
|
||||
void FileStreamContentUri::writeStringFmt(const char* format, ...)
|
||||
{
|
||||
ThrowWriteNotSupportedError();
|
||||
}
|
||||
|
||||
void FileStreamContentUri::writeString(const char* str)
|
||||
{
|
||||
ThrowWriteNotSupportedError();
|
||||
}
|
||||
|
||||
void FileStreamContentUri::writeLine(const char* str)
|
||||
{
|
||||
ThrowWriteNotSupportedError();
|
||||
}
|
||||
|
||||
FileStreamContentUri::FileStreamContentUri(const std::string& uri)
|
||||
: m_contentUriIStream(uri)
|
||||
{
|
||||
m_isValid = m_contentUriIStream.isOpen();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/FileStream.h"
|
||||
#include "Common/unix/ContentUriIStream.h"
|
||||
|
||||
class FileStreamContentUri : public FileStream
|
||||
{
|
||||
public:
|
||||
// size and seek
|
||||
void SetPosition(uint64 pos) override;
|
||||
|
||||
uint64 GetSize() override;
|
||||
bool SetEndOfFile() override;
|
||||
void extract(std::vector<uint8>& data) override;
|
||||
|
||||
// reading
|
||||
uint32 readData(void* data, uint32 length) override;
|
||||
bool readU64(uint64& v) override;
|
||||
bool readU32(uint32& v) override;
|
||||
bool readU8(uint8& v) override;
|
||||
bool readLine(std::string& line) override;
|
||||
|
||||
// writing (not supported)
|
||||
sint32 writeData(const void* data, sint32 length) override;
|
||||
void writeU64(uint64 v) override;
|
||||
void writeU32(uint32 v) override;
|
||||
void writeU8(uint8 v) override;
|
||||
void writeStringFmt(const char* format, ...) override;
|
||||
void writeString(const char* str) override;
|
||||
void writeLine(const char* str) override;
|
||||
|
||||
private:
|
||||
friend class FileStream;
|
||||
FileStreamContentUri(const std::string& uri);
|
||||
ContentUriIStream m_contentUriIStream;
|
||||
bool m_isValid = false;
|
||||
};
|
||||
@@ -21,67 +21,7 @@ fs::path findPathCI(const fs::path& path)
|
||||
return parentPath / fName;
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile(std::string_view path)
|
||||
{
|
||||
return openFile2(path, false);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile(const wchar_t* path, bool allowWrite)
|
||||
{
|
||||
return openFile2(path, allowWrite);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile2(const fs::path& path, bool allowWrite)
|
||||
{
|
||||
//return openFile(path.generic_wstring().c_str(), allowWrite);
|
||||
FileStream* fs = new FileStream(path, true, allowWrite);
|
||||
if (fs->m_isValid)
|
||||
return fs;
|
||||
delete fs;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(const wchar_t* path)
|
||||
{
|
||||
return createFile2(path);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(std::string_view path)
|
||||
{
|
||||
return createFile2(path);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile2(const fs::path& path)
|
||||
{
|
||||
FileStream* fs = new FileStream(path, false, false);
|
||||
if (fs->m_isValid)
|
||||
return fs;
|
||||
delete fs;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<std::vector<uint8>> FileStream::LoadIntoMemory(const fs::path& path)
|
||||
{
|
||||
FileStream* fs = openFile2(path);
|
||||
if (!fs)
|
||||
return std::nullopt;
|
||||
uint64 fileSize = fs->GetSize();
|
||||
if (fileSize > 0xFFFFFFFFull)
|
||||
{
|
||||
delete fs;
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::vector<uint8>> v(fileSize);
|
||||
if (fs->readData(v->data(), (uint32)fileSize) != (uint32)fileSize)
|
||||
{
|
||||
delete fs;
|
||||
return std::nullopt;
|
||||
}
|
||||
delete fs;
|
||||
return v;
|
||||
}
|
||||
|
||||
void FileStream::SetPosition(uint64 pos)
|
||||
void FileStreamUnix::SetPosition(uint64 pos)
|
||||
{
|
||||
cemu_assert(m_isValid);
|
||||
if (m_prevOperationWasWrite)
|
||||
@@ -90,7 +30,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();
|
||||
@@ -101,14 +41,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);
|
||||
@@ -116,7 +56,7 @@ void FileStream::extract(std::vector<uint8>& data)
|
||||
readData(data.data(), fileSize);
|
||||
}
|
||||
|
||||
uint32 FileStream::readData(void* data, uint32 length)
|
||||
uint32 FileStreamUnix::readData(void* data, uint32 length)
|
||||
{
|
||||
SyncReadWriteSeek(false);
|
||||
m_fileStream.read((char*)data, length);
|
||||
@@ -124,22 +64,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;
|
||||
@@ -156,29 +96,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;
|
||||
@@ -187,18 +127,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)
|
||||
{
|
||||
@@ -207,7 +147,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)
|
||||
@@ -227,7 +167,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,56 +1,44 @@
|
||||
#pragma once
|
||||
#include "Common/precompiled.h"
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
class FileStream
|
||||
class FileStreamUnix : public FileStream
|
||||
{
|
||||
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);
|
||||
public:
|
||||
// size and seek
|
||||
void SetPosition(uint64 pos) override;
|
||||
|
||||
static FileStream* createFile(const wchar_t* path);
|
||||
static FileStream* createFile(std::string_view path);
|
||||
static FileStream* createFile2(const fs::path& path);
|
||||
uint64 GetSize() override;
|
||||
bool SetEndOfFile() override;
|
||||
void extract(std::vector<uint8>& data) override;
|
||||
|
||||
// helper function to load a file into memory
|
||||
static std::optional<std::vector<uint8>> LoadIntoMemory(const fs::path& path);
|
||||
// reading
|
||||
uint32 readData(void* data, uint32 length) override;
|
||||
bool readU64(uint64& v) override;
|
||||
bool readU32(uint32& v) override;
|
||||
bool readU8(uint8& v) override;
|
||||
bool readLine(std::string& line) override;
|
||||
|
||||
// size and seek
|
||||
void SetPosition(uint64 pos);
|
||||
// writing (binary)
|
||||
sint32 writeData(const void* data, sint32 length) override;
|
||||
void writeU64(uint64 v) override;
|
||||
void writeU32(uint32 v) override;
|
||||
void writeU8(uint8 v) override;
|
||||
|
||||
uint64 GetSize();
|
||||
bool SetEndOfFile();
|
||||
void extract(std::vector<uint8>& data);
|
||||
// writing (strings)
|
||||
void writeStringFmt(const char* format, ...) override;
|
||||
void writeString(const char* str) override;
|
||||
void writeLine(const char* str) override;
|
||||
|
||||
// reading
|
||||
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);
|
||||
virtual ~FileStreamUnix();
|
||||
FileStreamUnix() = default;
|
||||
|
||||
// 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);
|
||||
|
||||
~FileStream();
|
||||
FileStream() {};
|
||||
|
||||
private:
|
||||
void SyncReadWriteSeek(bool nextOpIsWrite);
|
||||
FileStream(const fs::path& path, bool isOpen, bool isWriteable);
|
||||
|
||||
bool m_isValid{};
|
||||
std::fstream m_fileStream;
|
||||
bool m_prevOperationWasWrite{false};
|
||||
private:
|
||||
friend class FileStream;
|
||||
void SyncReadWriteSeek(bool nextOpIsWrite);
|
||||
FileStreamUnix(const fs::path& path, bool isOpen, bool isWriteable);
|
||||
|
||||
bool m_isValid{};
|
||||
std::fstream m_fileStream;
|
||||
bool m_prevOperationWasWrite{false};
|
||||
};
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "FilesystemAndroid.h"
|
||||
|
||||
namespace FilesystemAndroid
|
||||
{
|
||||
std::shared_ptr<FilesystemCallbacks> g_filesystemCallbacks = nullptr;
|
||||
|
||||
void setFilesystemCallbacks(const std::shared_ptr<FilesystemCallbacks> &filesystemCallbacks)
|
||||
{
|
||||
g_filesystemCallbacks = filesystemCallbacks;
|
||||
}
|
||||
|
||||
int openContentUri(const fs::path &uri)
|
||||
{
|
||||
if (g_filesystemCallbacks)
|
||||
return g_filesystemCallbacks->openContentUri(uri);
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::vector<fs::path> listFiles(const fs::path &uri)
|
||||
{
|
||||
if (g_filesystemCallbacks)
|
||||
return g_filesystemCallbacks->listFiles(uri);
|
||||
return {};
|
||||
}
|
||||
bool isDirectory(const fs::path &uri)
|
||||
{
|
||||
if (g_filesystemCallbacks)
|
||||
return g_filesystemCallbacks->isDirectory(uri);
|
||||
return false;
|
||||
}
|
||||
bool isFile(const fs::path &uri)
|
||||
{
|
||||
if (g_filesystemCallbacks)
|
||||
return g_filesystemCallbacks->isFile(uri);
|
||||
return false;
|
||||
}
|
||||
bool exists(const fs::path &uri)
|
||||
{
|
||||
if (g_filesystemCallbacks)
|
||||
return g_filesystemCallbacks->exists(uri);
|
||||
return false;
|
||||
}
|
||||
bool isContentUri(const std::string &uri)
|
||||
{
|
||||
static constexpr auto content = "content://";
|
||||
return uri.starts_with(content);
|
||||
}
|
||||
} // namespace FilesystemAndroid
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
namespace FilesystemAndroid
|
||||
{
|
||||
class FilesystemCallbacks
|
||||
{
|
||||
public:
|
||||
virtual int openContentUri(const std::filesystem::path &uri) = 0;
|
||||
virtual std::vector<std::filesystem::path> listFiles(const std::filesystem::path &uri) = 0;
|
||||
virtual bool isDirectory(const std::filesystem::path &uri) = 0;
|
||||
virtual bool isFile(const std::filesystem::path &uri) = 0;
|
||||
virtual bool exists(const std::filesystem::path &uri) = 0;
|
||||
};
|
||||
|
||||
void setFilesystemCallbacks(const std::shared_ptr<FilesystemCallbacks> &filesystemCallbacks);
|
||||
|
||||
int openContentUri(const std::filesystem::path &uri);
|
||||
|
||||
std::vector<std::filesystem::path> listFiles(const std::filesystem::path &uri);
|
||||
|
||||
bool isDirectory(const std::filesystem::path &uri);
|
||||
|
||||
bool isFile(const std::filesystem::path &uri);
|
||||
|
||||
bool exists(const std::filesystem::path& uri);
|
||||
|
||||
bool isContentUri(const std::string &uri);
|
||||
|
||||
} // namespace FilesystemAndroid
|
||||
@@ -1,77 +1,13 @@
|
||||
#include "Common/windows/FileStream_win32.h"
|
||||
|
||||
FileStream* FileStream::openFile(std::string_view path)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(boost::nowide::widen(path.data(), path.size()).c_str(), FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
return new FileStream(hFile);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile(const wchar_t* path, bool allowWrite)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(path, allowWrite ? (FILE_GENERIC_READ | FILE_GENERIC_WRITE) : FILE_GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
return new FileStream(hFile);
|
||||
}
|
||||
|
||||
FileStream* FileStream::openFile2(const fs::path& path, bool allowWrite)
|
||||
{
|
||||
return openFile(path.generic_wstring().c_str(), allowWrite);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(const wchar_t* path)
|
||||
{
|
||||
HANDLE hFile = CreateFileW(path, FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
return new FileStream(hFile);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile(std::string_view path)
|
||||
{
|
||||
auto w = boost::nowide::widen(path.data(), path.size());
|
||||
HANDLE hFile = CreateFileW(w.c_str(), FILE_GENERIC_READ | FILE_GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE)
|
||||
return nullptr;
|
||||
return new FileStream(hFile);
|
||||
}
|
||||
|
||||
FileStream* FileStream::createFile2(const fs::path& path)
|
||||
{
|
||||
return createFile(path.generic_wstring().c_str());
|
||||
}
|
||||
|
||||
std::optional<std::vector<uint8>> FileStream::LoadIntoMemory(const fs::path& path)
|
||||
{
|
||||
FileStream* fs = openFile2(path);
|
||||
if (!fs)
|
||||
return std::nullopt;
|
||||
uint64 fileSize = fs->GetSize();
|
||||
if(fileSize > 0xFFFFFFFFull)
|
||||
{
|
||||
delete fs;
|
||||
return std::nullopt;
|
||||
}
|
||||
std::optional<std::vector<uint8>> v(fileSize);
|
||||
if (fs->readData(v->data(), (uint32)fileSize) != (uint32)fileSize)
|
||||
{
|
||||
delete fs;
|
||||
return std::nullopt;
|
||||
}
|
||||
delete fs;
|
||||
return v;
|
||||
}
|
||||
|
||||
void FileStream::SetPosition(uint64 pos)
|
||||
void FileStreamWin32::SetPosition(uint64 pos)
|
||||
{
|
||||
LONG posHigh = (LONG)(pos >> 32);
|
||||
LONG posLow = (LONG)(pos);
|
||||
SetFilePointer(m_hFile, posLow, &posHigh, FILE_BEGIN);
|
||||
}
|
||||
|
||||
uint64 FileStream::GetSize()
|
||||
uint64 FileStreamWin32::GetSize()
|
||||
{
|
||||
DWORD fileSizeHigh = 0;
|
||||
DWORD fileSizeLow = 0;
|
||||
@@ -79,12 +15,12 @@ uint64 FileStream::GetSize()
|
||||
return ((uint64)fileSizeHigh << 32) | (uint64)fileSizeLow;
|
||||
}
|
||||
|
||||
bool FileStream::SetEndOfFile()
|
||||
bool FileStreamWin32::SetEndOfFile()
|
||||
{
|
||||
return ::SetEndOfFile(m_hFile) != 0;
|
||||
}
|
||||
|
||||
void FileStream::extract(std::vector<uint8>& data)
|
||||
void FileStreamWin32::extract(std::vector<uint8>& data)
|
||||
{
|
||||
DWORD fileSize = GetFileSize(m_hFile, nullptr);
|
||||
data.resize(fileSize);
|
||||
@@ -93,29 +29,29 @@ void FileStream::extract(std::vector<uint8>& data)
|
||||
ReadFile(m_hFile, data.data(), fileSize, &bt, nullptr);
|
||||
}
|
||||
|
||||
uint32 FileStream::readData(void* data, uint32 length)
|
||||
uint32 FileStreamWin32::readData(void* data, uint32 length)
|
||||
{
|
||||
DWORD bytesRead = 0;
|
||||
ReadFile(m_hFile, data, length, &bytesRead, NULL);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool FileStream::readU64(uint64& v)
|
||||
bool FileStreamWin32::readU64(uint64& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint64)) == sizeof(uint64);
|
||||
}
|
||||
|
||||
bool FileStream::readU32(uint32& v)
|
||||
bool FileStreamWin32::readU32(uint32& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint32)) == sizeof(uint32);
|
||||
}
|
||||
|
||||
bool FileStream::readU8(uint8& v)
|
||||
bool FileStreamWin32::readU8(uint8& v)
|
||||
{
|
||||
return readData(&v, sizeof(uint8)) == sizeof(uint8);
|
||||
}
|
||||
|
||||
bool FileStream::readLine(std::string& line)
|
||||
bool FileStreamWin32::readLine(std::string& line)
|
||||
{
|
||||
line.clear();
|
||||
uint8 c;
|
||||
@@ -132,29 +68,29 @@ bool FileStream::readLine(std::string& line)
|
||||
return !isEOF;
|
||||
}
|
||||
|
||||
sint32 FileStream::writeData(const void* data, sint32 length)
|
||||
sint32 FileStreamWin32::writeData(const void* data, sint32 length)
|
||||
{
|
||||
DWORD bytesWritten = 0;
|
||||
WriteFile(m_hFile, data, length, &bytesWritten, NULL);
|
||||
return bytesWritten;
|
||||
}
|
||||
|
||||
void FileStream::writeU64(uint64 v)
|
||||
void FileStreamWin32::writeU64(uint64 v)
|
||||
{
|
||||
writeData(&v, sizeof(uint64));
|
||||
}
|
||||
|
||||
void FileStream::writeU32(uint32 v)
|
||||
void FileStreamWin32::writeU32(uint32 v)
|
||||
{
|
||||
writeData(&v, sizeof(uint32));
|
||||
}
|
||||
|
||||
void FileStream::writeU8(uint8 v)
|
||||
void FileStreamWin32::writeU8(uint8 v)
|
||||
{
|
||||
writeData(&v, sizeof(uint8));
|
||||
}
|
||||
|
||||
void FileStream::writeStringFmt(const char* format, ...)
|
||||
void FileStreamWin32::writeStringFmt(const char* format, ...)
|
||||
{
|
||||
char buffer[2048];
|
||||
va_list args;
|
||||
@@ -163,24 +99,24 @@ void FileStream::writeStringFmt(const char* format, ...)
|
||||
writeData(buffer, (sint32)strlen(buffer));
|
||||
}
|
||||
|
||||
void FileStream::writeString(const char* str)
|
||||
void FileStreamWin32::writeString(const char* str)
|
||||
{
|
||||
writeData(str, (sint32)strlen(str));
|
||||
}
|
||||
|
||||
void FileStream::writeLine(const char* str)
|
||||
void FileStreamWin32::writeLine(const char* str)
|
||||
{
|
||||
writeData(str, (sint32)strlen(str));
|
||||
writeData("\r\n", 2);
|
||||
}
|
||||
|
||||
FileStream::~FileStream()
|
||||
FileStreamWin32::~FileStreamWin32()
|
||||
{
|
||||
if(m_isValid)
|
||||
CloseHandle(m_hFile);
|
||||
}
|
||||
|
||||
FileStream::FileStream(HANDLE hFile)
|
||||
FileStreamWin32::FileStreamWin32(HANDLE hFile)
|
||||
{
|
||||
m_hFile = hFile;
|
||||
m_isValid = true;
|
||||
|
||||
@@ -1,52 +1,44 @@
|
||||
#pragma once
|
||||
#include "Common/precompiled.h"
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
class FileStream
|
||||
class FileStreamWin32 : public FileStream
|
||||
{
|
||||
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 FileStream* createFile(const wchar_t* path);
|
||||
static FileStream* createFile(std::string_view path);
|
||||
static FileStream* 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);
|
||||
void SetPosition(uint64 pos) override;
|
||||
|
||||
uint64 GetSize();
|
||||
bool SetEndOfFile();
|
||||
void extract(std::vector<uint8>& data);
|
||||
uint64 GetSize() override;
|
||||
bool SetEndOfFile() override;
|
||||
void extract(std::vector<uint8>& data) override;
|
||||
|
||||
// reading
|
||||
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);
|
||||
uint32 readData(void* data, uint32 length) override;
|
||||
bool readU64(uint64& v) override;
|
||||
bool readU32(uint32& v) override;
|
||||
bool readU8(uint8& v) override;
|
||||
bool readLine(std::string& line) override;
|
||||
|
||||
// writing (binary)
|
||||
sint32 writeData(const void* data, sint32 length);
|
||||
void writeU64(uint64 v);
|
||||
void writeU32(uint32 v);
|
||||
void writeU16(uint16 v);
|
||||
void writeU8(uint8 v);
|
||||
sint32 writeData(const void* data, sint32 length) override;
|
||||
void writeU64(uint64 v) override;
|
||||
void writeU32(uint32 v) override;
|
||||
void writeU8(uint8 v) override;
|
||||
|
||||
// writing (strings)
|
||||
void writeStringFmt(const char* format, ...);
|
||||
void writeString(const char* str);
|
||||
void writeLine(const char* str);
|
||||
void writeStringFmt(const char* format, ...) override;
|
||||
void writeString(const char* str) override;
|
||||
void writeLine(const char* str) override;
|
||||
|
||||
~FileStream();
|
||||
FileStream() = default;
|
||||
virtual ~FileStreamWin32();
|
||||
FileStreamWin32() = default;
|
||||
|
||||
private:
|
||||
FileStream(HANDLE hFile);
|
||||
friend class FileStream;
|
||||
FileStreamWin32(HANDLE hFile);
|
||||
|
||||
bool m_isValid{};
|
||||
HANDLE m_hFile;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <AndroidGui/AndroidGuiWrapper.h>
|
||||
#include <Cafe/HW/Latte/Renderer/Renderer.h>
|
||||
#include <Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h>
|
||||
#include <Common/unix/FilesystemAndroid.h>
|
||||
#include "AndroidGui/Utils.h"
|
||||
#include "config/ActiveSettings.h"
|
||||
#include "config/NetworkSettings.h"
|
||||
@@ -199,4 +200,104 @@ JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_recreateRenderSurface(JNIEnv *env, jclass clazz,
|
||||
jboolean is_main_canvas) {
|
||||
VulkanRenderer::GetInstance()->NotifySurfaceChanged(is_main_canvas);
|
||||
}
|
||||
|
||||
|
||||
class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks {
|
||||
JNIUtils::Scopedjobject m_filesystemCallbacksObj;
|
||||
jmethodID m_openContentUriMid;
|
||||
jmethodID m_listFilesMid;
|
||||
jmethodID m_isDirectoryMid;
|
||||
jmethodID m_isFileMid;
|
||||
jmethodID m_existsMid;
|
||||
public:
|
||||
AndroidFilesystemCallbacks(jobject filesystemCallbacksObj, jmethodID openContentUriMid,
|
||||
jmethodID listFilesMid,
|
||||
jmethodID isDirectoryMid,
|
||||
jmethodID isFileMid,
|
||||
jmethodID existsMid)
|
||||
: m_filesystemCallbacksObj(filesystemCallbacksObj),
|
||||
m_openContentUriMid(openContentUriMid),
|
||||
m_listFilesMid(listFilesMid),
|
||||
m_isDirectoryMid(isDirectoryMid),
|
||||
m_isFileMid(isFileMid),
|
||||
m_existsMid(existsMid) {}
|
||||
|
||||
int openContentUri(const std::filesystem::path &uri) override {
|
||||
JNIUtils::ScopedJNIENV env;
|
||||
jstring uriString = env->NewStringUTF(uri.c_str());
|
||||
int fd = env->CallIntMethod(*m_filesystemCallbacksObj, m_openContentUriMid, uriString);
|
||||
env->DeleteLocalRef(uriString);
|
||||
return fd;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> listFiles(const std::filesystem::path &uri) override {
|
||||
JNIUtils::ScopedJNIENV env;
|
||||
jstring uriString = env->NewStringUTF(uri.c_str());
|
||||
jobjectArray pathsObjArray = static_cast<jobjectArray>(env->CallObjectMethod(
|
||||
*m_filesystemCallbacksObj, m_listFilesMid, uriString));
|
||||
env->DeleteLocalRef(uriString);
|
||||
jsize arrayLength = env->GetArrayLength(pathsObjArray);
|
||||
std::vector<std::filesystem::path> paths;
|
||||
paths.reserve(arrayLength);
|
||||
for (jsize i = 0; i < arrayLength; i++) {
|
||||
jstring pathStrObj = static_cast<jstring>(env->GetObjectArrayElement(pathsObjArray, i));
|
||||
paths.push_back(JNIUtils::JStringToString(*env, pathStrObj));
|
||||
env->DeleteLocalRef(pathStrObj);
|
||||
}
|
||||
env->DeleteLocalRef(pathsObjArray);
|
||||
return paths;
|
||||
}
|
||||
|
||||
bool isDirectory(const std::filesystem::path &uri) override {
|
||||
JNIUtils::ScopedJNIENV env;
|
||||
jstring uriString = env->NewStringUTF(uri.c_str());
|
||||
bool isDir = env->CallBooleanMethod(*m_filesystemCallbacksObj, m_isDirectoryMid, uriString);
|
||||
env->DeleteLocalRef(uriString);
|
||||
return isDir;
|
||||
}
|
||||
|
||||
bool isFile(const std::filesystem::path &uri) override {
|
||||
JNIUtils::ScopedJNIENV env;
|
||||
jstring uriString = env->NewStringUTF(uri.c_str());
|
||||
bool isFile = env->CallBooleanMethod(*m_filesystemCallbacksObj, m_isFileMid, uriString);
|
||||
env->DeleteLocalRef(uriString);
|
||||
return isFile;
|
||||
}
|
||||
|
||||
bool exists(const std::filesystem::path &uri) override {
|
||||
JNIUtils::ScopedJNIENV env;
|
||||
jstring uriString = env->NewStringUTF(uri.c_str());
|
||||
bool exists = env->CallBooleanMethod(*m_filesystemCallbacksObj, m_existsMid, uriString);
|
||||
env->DeleteLocalRef(uriString);
|
||||
return exists;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
std::shared_ptr<AndroidFilesystemCallbacks> g_androidFilesystemCallbacks = nullptr;
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_setFileSystemCallbacks(JNIEnv *env, jclass clazz,
|
||||
jobject file_system_callbacks) {
|
||||
jclass fileSystemCallbackClass = env->GetObjectClass(file_system_callbacks);
|
||||
jmethodID openContentUriMid = env->GetMethodID(fileSystemCallbackClass, "openContentUri",
|
||||
"(Ljava/lang/String;)I");
|
||||
jmethodID listFilesMid = env->GetMethodID(fileSystemCallbackClass, "listFiles",
|
||||
"(Ljava/lang/String;)[Ljava/lang/String;");
|
||||
jmethodID isDirectoryMid = env->GetMethodID(fileSystemCallbackClass, "isDirectory",
|
||||
"(Ljava/lang/String;)Z");
|
||||
jmethodID isFileMid = env->GetMethodID(fileSystemCallbackClass, "isFile",
|
||||
"(Ljava/lang/String;)Z");
|
||||
jmethodID existsMid = env->GetMethodID(fileSystemCallbackClass, "exists",
|
||||
"(Ljava/lang/String;)Z");
|
||||
g_androidFilesystemCallbacks = std::make_shared<AndroidFilesystemCallbacks>(
|
||||
file_system_callbacks, openContentUriMid, listFilesMid, isDirectoryMid, isFileMid,
|
||||
existsMid);
|
||||
FilesystemAndroid::setFilesystemCallbacks(g_androidFilesystemCallbacks);
|
||||
}
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_addGamePath(JNIEnv *env, jclass clazz, jstring uri) {
|
||||
gui_addGamePath(JNIUtils::JStringToString(env, uri));
|
||||
}
|
||||
@@ -1,14 +1,43 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.app.Application;
|
||||
import android.os.FileUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class CemuApplication extends Application {
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
|
||||
NativeLibrary.setDPI(displayMetrics.density);
|
||||
NativeLibrary.setFileSystemCallbacks(new NativeLibrary.FileSystemCallbacks() {
|
||||
@Override
|
||||
public int openContentUri(String uri) {
|
||||
return FileUtil.openContentUri(getApplicationContext(), uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] listFiles(String uri) {
|
||||
return FileUtil.listFiles(getApplicationContext(), uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory(String uri) {
|
||||
return FileUtil.isDirectory(getApplicationContext(), uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile(String uri) {
|
||||
return FileUtil.isFile(getApplicationContext(), uri);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String uri) {
|
||||
return FileUtil.exists(getApplicationContext(), uri);
|
||||
}
|
||||
});
|
||||
NativeLibrary.initializeActiveSettings(getExternalFilesDir(null).getAbsoluteFile().toString(), getCacheDir().toString());
|
||||
NativeLibrary.initializeEmulation();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package info.cemu.Cemu;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
public class FileUtil {
|
||||
private static final String PATH_SEPARATOR_ENCODED = "%2F";
|
||||
private static final String PATH_SEPARATOR_DECODED = "/";
|
||||
private static final String COLON_ENCODED = "%3A";
|
||||
private static final String MODE = "r";
|
||||
|
||||
private static String toCppPath(Uri uri) {
|
||||
String uriPath = uri.toString();
|
||||
int delimiterPos = uriPath.lastIndexOf(COLON_ENCODED);
|
||||
return uriPath.substring(0, delimiterPos) + uriPath.substring(delimiterPos).replace(PATH_SEPARATOR_ENCODED, PATH_SEPARATOR_DECODED);
|
||||
}
|
||||
|
||||
private static Uri fromCppPath(String cppPath) {
|
||||
int delimiterPos = cppPath.lastIndexOf(COLON_ENCODED);
|
||||
return Uri.parse(cppPath.substring(0, delimiterPos) + cppPath.substring(delimiterPos).replace(PATH_SEPARATOR_DECODED, PATH_SEPARATOR_ENCODED));
|
||||
}
|
||||
|
||||
public static int openContentUri(Context context, String uri) {
|
||||
try {
|
||||
if (!exists(context, uri)) {
|
||||
return -1;
|
||||
}
|
||||
ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(fromCppPath(uri), MODE);
|
||||
if (parcelFileDescriptor != null) {
|
||||
int fd = parcelFileDescriptor.detachFd();
|
||||
parcelFileDescriptor.close();
|
||||
return fd;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("FileUtil", "Cannot open content uri, error: " + e.getMessage());
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static String[] listFiles(Context context, String uri) {
|
||||
ArrayList<String> files = new ArrayList<>();
|
||||
Uri directoryUri = fromCppPath(uri);
|
||||
Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(directoryUri, DocumentsContract.getDocumentId(directoryUri));
|
||||
try (Cursor cursor = context.getContentResolver().query(childrenUri, new String[]{DocumentsContract.Document.COLUMN_DOCUMENT_ID}, null, null, null)) {
|
||||
while (cursor != null && cursor.moveToNext()) {
|
||||
String documentId = cursor.getString(0);
|
||||
Uri documentUri = DocumentsContract.buildDocumentUriUsingTree(directoryUri, documentId);
|
||||
files.add(toCppPath(documentUri));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("FileUtil", "Cannot list files: " + e.getMessage());
|
||||
}
|
||||
String[] filesArray = new String[files.size()];
|
||||
filesArray = files.toArray(filesArray);
|
||||
return filesArray;
|
||||
}
|
||||
|
||||
public static boolean isDirectory(Context context, String uri) {
|
||||
String mimeType = context.getContentResolver().getType(fromCppPath(uri));
|
||||
return DocumentsContract.Document.MIME_TYPE_DIR.equals(mimeType);
|
||||
}
|
||||
|
||||
public static boolean isFile(Context context, String uri) {
|
||||
return !isDirectory(context, uri);
|
||||
}
|
||||
|
||||
public static boolean exists(Context context, String uri) {
|
||||
try (Cursor cursor = context.getContentResolver().query(fromCppPath(uri), null, null, null, null)) {
|
||||
return cursor != null && cursor.moveToFirst();
|
||||
} catch (Exception e) {
|
||||
Log.e("FileUtil", "Failed checking if file exists: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user