mirror of
https://github.com/izzy2lost/WeeU.git
synced 2026-07-06 00:19:59 -07:00
Fix crash when calling JNI methods while inside a fiber & add support reading games from folder
This commit is contained in:
@@ -473,6 +473,13 @@ if(APPLE)
|
||||
target_sources(CemuCafe PRIVATE "HW/Latte/Renderer/Vulkan/CocoaSurface.mm")
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
target_sources(CemuCafe PRIVATE
|
||||
Filesystem/fscDeviceAndroidSAF.cpp
|
||||
Filesystem/fscDeviceAndroidSAF.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set_property(TARGET CemuCafe PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
target_include_directories(CemuCafe PUBLIC "../")
|
||||
|
||||
@@ -631,10 +631,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(std::string("/vol/content").c_str(), _pathToUtf8(contentPath), FSC_PRIORITY_BASE);
|
||||
if (!r)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to mount {}", _pathToUtf8(contentPath));
|
||||
@@ -643,7 +643,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(std::string("/internal/code/").c_str(), _pathToUtf8(executablePath.parent_path()), FSC_PRIORITY_BASE);
|
||||
std::string internalExecutablePath = "/internal/code/";
|
||||
internalExecutablePath.append(_pathToUtf8(executablePath.filename()));
|
||||
_pathToExecutable = internalExecutablePath;
|
||||
|
||||
@@ -200,3 +200,18 @@ bool FSCDeviceHostFS_Mount(std::string_view mountPath, std::string_view hostTarg
|
||||
// redirect device
|
||||
void fscDeviceRedirect_map();
|
||||
void fscDeviceRedirect_add(std::string_view virtualSourcePath, const fs::path& targetFilePath, sint32 priority);
|
||||
|
||||
#if __ANDROID__
|
||||
#include "Common/unix/FilesystemAndroid.h"
|
||||
bool FSCDeviceAndroidSAF_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority);
|
||||
#endif // __ANDROID__
|
||||
|
||||
inline bool FSCDeviceHost_Mount(std::string_view mountPath, std::string_view hostTargetPath, sint32 priority)
|
||||
{
|
||||
#if __ANDROID__
|
||||
if (FilesystemAndroid::isContentUri(std::string(hostTargetPath)))
|
||||
return FSCDeviceAndroidSAF_Mount(mountPath, hostTargetPath, priority);
|
||||
else
|
||||
#endif // __ANDROID__
|
||||
return FSCDeviceHostFS_Mount(mountPath, hostTargetPath, priority);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
#include "Cafe/Filesystem/fscDeviceAndroidSAF.h"
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Cafe/Filesystem/fsc.h"
|
||||
#include "Common/FileStream.h"
|
||||
#include "Common/unix/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)
|
||||
{
|
||||
throw std::logic_error("write not supported with SAF");
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 FSCVirtualFile_AndroidSAF::fscReadData(void* buffer, uint32 size)
|
||||
{
|
||||
if (m_type != FSC_TYPE_FILE)
|
||||
return 0;
|
||||
if (size >= (2UL * 1024UL * 1024UL * 1024UL))
|
||||
{
|
||||
cemu_assert_suspicious();
|
||||
return 0;
|
||||
}
|
||||
uint32 bytesLeft = (uint32)(m_fileSize - m_seek);
|
||||
bytesLeft = std::min(bytesLeft, 0x7FFFFFFFu);
|
||||
sint32 bytesToRead = std::min(bytesLeft, size);
|
||||
uint32 bytesRead = m_fs->readData(buffer, bytesToRead);
|
||||
m_seek += bytesRead;
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
void FSCVirtualFile_AndroidSAF::fscSetSeek(uint64 seek)
|
||||
{
|
||||
if (m_type != FSC_TYPE_FILE)
|
||||
return;
|
||||
this->m_seek = seek;
|
||||
cemu_assert_debug(seek <= m_fileSize);
|
||||
m_fs->SetPosition(seek);
|
||||
}
|
||||
|
||||
uint64 FSCVirtualFile_AndroidSAF::fscGetSeek()
|
||||
{
|
||||
if (m_type != FSC_TYPE_FILE)
|
||||
return 0;
|
||||
return m_seek;
|
||||
}
|
||||
|
||||
void FSCVirtualFile_AndroidSAF::fscSetFileLength(uint64 endOffset)
|
||||
{
|
||||
if (m_type != FSC_TYPE_FILE)
|
||||
return;
|
||||
m_fs->SetPosition(endOffset);
|
||||
bool r = m_fs->SetEndOfFile();
|
||||
m_seek = std::min(m_seek, endOffset);
|
||||
m_fileSize = m_seek;
|
||||
m_fs->SetPosition(m_seek);
|
||||
if (!r)
|
||||
cemuLog_log(LogType::Force, "fscSetFileLength: Failed to set size to 0x{:x}", endOffset);
|
||||
}
|
||||
|
||||
bool FSCVirtualFile_AndroidSAF::fscDirNext(FSCDirEntry* dirEntry)
|
||||
{
|
||||
if (m_type != FSC_TYPE_DIRECTORY)
|
||||
return false;
|
||||
|
||||
if (!m_files)
|
||||
{
|
||||
// init iterator on first iteration attempt
|
||||
m_files = std::make_unique<std::vector<fs::path>>(FilesystemAndroid::listFiles(*m_path));
|
||||
m_filesIterator = m_files->begin();
|
||||
if (!m_files)
|
||||
{
|
||||
cemuLog_log(LogType::Force, "Failed to iterate directory: {}", _pathToUtf8(*m_path));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (m_filesIterator == m_files->end())
|
||||
return false;
|
||||
|
||||
const fs::path& file = *m_filesIterator;
|
||||
|
||||
std::string fileName = file.filename().generic_string();
|
||||
if (fileName.size() >= sizeof(dirEntry->path) - 1)
|
||||
fileName.resize(sizeof(dirEntry->path) - 1);
|
||||
strncpy(dirEntry->path, fileName.data(), sizeof(dirEntry->path));
|
||||
if (FilesystemAndroid::isDirectory(file))
|
||||
{
|
||||
dirEntry->isDirectory = true;
|
||||
dirEntry->isFile = false;
|
||||
dirEntry->fileSize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
dirEntry->isDirectory = false;
|
||||
dirEntry->isFile = true;
|
||||
dirEntry->fileSize = 0;
|
||||
auto fs = FileStream::openFile2(file);
|
||||
if (fs)
|
||||
{
|
||||
dirEntry->fileSize = fs->GetSize();
|
||||
delete fs;
|
||||
}
|
||||
}
|
||||
m_filesIterator++;
|
||||
return true;
|
||||
}
|
||||
|
||||
FSCVirtualFile* FSCVirtualFile_AndroidSAF::OpenFile(const fs::path& path, FSC_ACCESS_FLAG accessFlags, sint32& fscStatus)
|
||||
{
|
||||
if (!HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE) && !HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
|
||||
cemu_assert_debug(false); // not allowed. At least one of both flags must be set
|
||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::WRITE_PERMISSION) ||
|
||||
HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALLOW_CREATE) ||
|
||||
HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::FILE_ALWAYS_CREATE))
|
||||
throw std::logic_error("writing and creating a file is not supported with SAF");
|
||||
// attempt to open as file
|
||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_FILE))
|
||||
{
|
||||
FileStream* fs = FileStream::openFile2(path);
|
||||
if (fs)
|
||||
{
|
||||
FSCVirtualFile_AndroidSAF* vf = new FSCVirtualFile_AndroidSAF(FSC_TYPE_FILE);
|
||||
vf->m_fs = fs;
|
||||
vf->m_isWritable = false;
|
||||
vf->m_fileSize = fs->GetSize();
|
||||
fscStatus = FSC_STATUS_OK;
|
||||
return vf;
|
||||
}
|
||||
}
|
||||
|
||||
// attempt to open as directory
|
||||
if (HAS_FLAG(accessFlags, FSC_ACCESS_FLAG::OPEN_DIR))
|
||||
{
|
||||
bool isExistingDir = FilesystemAndroid::exists(path);
|
||||
if (isExistingDir)
|
||||
{
|
||||
FSCVirtualFile_AndroidSAF* vf = new FSCVirtualFile_AndroidSAF(FSC_TYPE_DIRECTORY);
|
||||
vf->m_path.reset(new std::filesystem::path(path));
|
||||
fscStatus = FSC_STATUS_OK;
|
||||
return vf;
|
||||
}
|
||||
}
|
||||
fscStatus = FSC_STATUS_FILE_NOT_FOUND;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
bool fscDeviceCreateDir(std::string_view path, void* ctx, sint32* fscStatus) override
|
||||
{
|
||||
throw std::logic_error("creating a directory is not supported with SAF");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fscDeviceRemoveFileOrDir(std::string_view path, void* ctx, sint32* fscStatus) override
|
||||
{
|
||||
throw std::logic_error("removing a file or dir is not supported with SAF");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool fscDeviceRename(std::string_view srcPath, std::string_view dstPath, void* ctx, sint32* fscStatus) override
|
||||
{
|
||||
throw std::logic_error("renaming not supported with SAF");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cafe/Filesystem/fsc.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_*
|
||||
class FileStream* m_fs{};
|
||||
// file
|
||||
uint64 m_seek{0};
|
||||
uint64 m_fileSize{0};
|
||||
bool m_isWritable{false};
|
||||
// directory
|
||||
std::unique_ptr<fs::path> m_path{};
|
||||
std::unique_ptr<std::vector<fs::path>> m_files{};
|
||||
std::vector<fs::path>::iterator m_filesIterator;
|
||||
};
|
||||
@@ -7,7 +7,6 @@
|
||||
#include "Common/FileStream.h"
|
||||
|
||||
#if __ANDROID__
|
||||
#include "Common/unix/FilesystemAndroid.h"
|
||||
#include "Common/unix/ContentUriIStream.h"
|
||||
#endif // __ANDROID__
|
||||
|
||||
@@ -180,7 +179,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 +191,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;
|
||||
@@ -265,7 +264,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;
|
||||
@@ -386,7 +385,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)
|
||||
{
|
||||
|
||||
@@ -83,6 +83,62 @@
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
#if __ANDROID__
|
||||
#endif // __ANDROID
|
||||
#include "Common/unix/FilesystemAndroid.h"
|
||||
|
||||
namespace cemu
|
||||
{
|
||||
namespace fs
|
||||
{
|
||||
inline bool is_directory(const std::filesystem::path& p)
|
||||
{
|
||||
if (FilesystemAndroid::isContentUri(p))
|
||||
return FilesystemAndroid::isDirectory(p);
|
||||
return std::filesystem::is_directory(p);
|
||||
}
|
||||
inline bool is_directory(const std::filesystem::path& p, std::error_code& ec)
|
||||
{
|
||||
#if __ANDROID__
|
||||
if (FilesystemAndroid::isContentUri(p))
|
||||
return FilesystemAndroid::isDirectory(p);
|
||||
#endif // __ANDROID__
|
||||
return std::filesystem::is_directory(p, ec);
|
||||
}
|
||||
inline bool is_file(const std::filesystem::path& p)
|
||||
{
|
||||
#if __ANDROID__
|
||||
if (FilesystemAndroid::isContentUri(p))
|
||||
return FilesystemAndroid::isDirectory(p);
|
||||
#endif // __ANDROID__
|
||||
return std::filesystem::is_regular_file(p);
|
||||
}
|
||||
inline bool is_file(const std::filesystem::path& p, std::error_code& ec)
|
||||
{
|
||||
#if __ANDROID__
|
||||
if (FilesystemAndroid::isContentUri(p))
|
||||
return FilesystemAndroid::isDirectory(p);
|
||||
#endif // __ANDROID__
|
||||
return std::filesystem::is_regular_file(p, ec);
|
||||
}
|
||||
inline bool exists(const std::filesystem::path& p)
|
||||
{
|
||||
#if __ANDROID__
|
||||
if (FilesystemAndroid::isContentUri(p))
|
||||
return FilesystemAndroid::isDirectory(p);
|
||||
#endif // __ANDROID__
|
||||
return std::filesystem::exists(p);
|
||||
}
|
||||
inline bool exists(const std::filesystem::path& p, std::error_code& ec)
|
||||
{
|
||||
#if __ANDROID__
|
||||
if (FilesystemAndroid::isContentUri(p))
|
||||
return FilesystemAndroid::isDirectory(p);
|
||||
#endif // __ANDROID__
|
||||
return std::filesystem::exists(p, ec);
|
||||
}
|
||||
} // namespace fs
|
||||
} // namespace cemu);
|
||||
|
||||
#include "enumFlags.h"
|
||||
|
||||
|
||||
@@ -155,9 +155,143 @@ Java_info_cemu_Cemu_NativeLibrary_initializeActiveSettings(JNIEnv *env, jclass c
|
||||
|
||||
int mainEmulatorHLE();
|
||||
|
||||
|
||||
class AndroidFilesystemCallbacks : public FilesystemAndroid::FilesystemCallbacks {
|
||||
jmethodID m_openContentUriMid;
|
||||
jmethodID m_listFilesMid;
|
||||
jmethodID m_isDirectoryMid;
|
||||
jmethodID m_isFileMid;
|
||||
jmethodID m_existsMid;
|
||||
JNIUtils::Scopedjclass m_fileUtilClass;
|
||||
std::function<void(JNIEnv *)> m_function = nullptr;
|
||||
std::atomic_bool m_functionFinished;
|
||||
std::mutex m_functionMutex;
|
||||
std::condition_variable m_functionCV;
|
||||
std::thread m_thread;
|
||||
std::mutex m_threadMutex;
|
||||
std::condition_variable m_threadCV;
|
||||
std::atomic_bool m_continue = true;
|
||||
|
||||
bool callBooleanFunction(const std::filesystem::path &uri, jmethodID methodId) {
|
||||
std::unique_lock functionLock(m_functionMutex);
|
||||
m_functionFinished = false;
|
||||
bool condition;
|
||||
{
|
||||
std::lock_guard threadLock(m_threadMutex);
|
||||
m_function = [&, this](JNIEnv *env) {
|
||||
jstring uriString = env->NewStringUTF(uri.c_str());
|
||||
condition = env->CallStaticBooleanMethod(*m_fileUtilClass, methodId, uriString);
|
||||
env->DeleteLocalRef(uriString);
|
||||
};
|
||||
}
|
||||
m_threadCV.notify_one();
|
||||
m_functionCV.wait(functionLock, [this]() -> bool { return m_functionFinished; });
|
||||
return condition;
|
||||
}
|
||||
|
||||
public:
|
||||
AndroidFilesystemCallbacks() {
|
||||
JNIUtils::ScopedJNIENV env;
|
||||
m_fileUtilClass = JNIUtils::Scopedjclass("info/cemu/Cemu/FileUtil");
|
||||
m_openContentUriMid = env->GetStaticMethodID(*m_fileUtilClass, "openContentUri",
|
||||
"(Ljava/lang/String;)I");
|
||||
m_listFilesMid = env->GetStaticMethodID(*m_fileUtilClass, "listFiles",
|
||||
"(Ljava/lang/String;)[Ljava/lang/String;");
|
||||
m_isDirectoryMid = env->GetStaticMethodID(*m_fileUtilClass, "isDirectory",
|
||||
"(Ljava/lang/String;)Z");
|
||||
m_isFileMid = env->GetStaticMethodID(*m_fileUtilClass, "isFile",
|
||||
"(Ljava/lang/String;)Z");
|
||||
m_existsMid = env->GetStaticMethodID(*m_fileUtilClass, "exists",
|
||||
"(Ljava/lang/String;)Z");
|
||||
m_thread = std::thread([this]() {
|
||||
JNIUtils::ScopedJNIENV env;
|
||||
while (m_continue) {
|
||||
std::unique_lock threadLock(m_threadMutex);
|
||||
m_threadCV.wait(threadLock, [&] {
|
||||
return m_function || !m_continue;
|
||||
});
|
||||
if (!m_continue)
|
||||
return;
|
||||
m_function(*env);
|
||||
m_function = nullptr;
|
||||
m_functionFinished = true;
|
||||
m_functionCV.notify_one();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
~AndroidFilesystemCallbacks() {
|
||||
m_continue = false;
|
||||
m_threadCV.notify_one();
|
||||
m_thread.join();
|
||||
}
|
||||
|
||||
int openContentUri(const std::filesystem::path &uri) override {
|
||||
std::unique_lock functionLock(m_functionMutex);
|
||||
m_functionFinished = false;
|
||||
int fd;
|
||||
{
|
||||
std::lock_guard threadLock(m_threadMutex);
|
||||
m_function = [&, this](JNIEnv *env) {
|
||||
jstring uriString = env->NewStringUTF(uri.c_str());
|
||||
fd = env->CallStaticIntMethod(*m_fileUtilClass, m_openContentUriMid, uriString);
|
||||
env->DeleteLocalRef(uriString);
|
||||
};
|
||||
}
|
||||
m_threadCV.notify_one();
|
||||
m_functionCV.wait(functionLock, [this]() -> bool { return m_functionFinished; });
|
||||
return fd;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> listFiles(const std::filesystem::path &uri) override {
|
||||
std::unique_lock functionLock(m_functionMutex);
|
||||
m_functionFinished = false;
|
||||
std::vector<std::filesystem::path> paths;
|
||||
{
|
||||
std::lock_guard threadLock(m_threadMutex);
|
||||
m_function = [&, this](JNIEnv *env) {
|
||||
jstring uriString = env->NewStringUTF(uri.c_str());
|
||||
jobjectArray pathsObjArray = static_cast<jobjectArray>(env->CallStaticObjectMethod(
|
||||
*m_fileUtilClass,
|
||||
m_listFilesMid, uriString));
|
||||
env->DeleteLocalRef(uriString);
|
||||
jsize arrayLength = env->GetArrayLength(pathsObjArray);
|
||||
paths.reserve(arrayLength);
|
||||
for (jsize i = 0; i < arrayLength; i++) {
|
||||
jstring pathStr = static_cast<jstring>(env->GetObjectArrayElement(
|
||||
pathsObjArray,
|
||||
i));
|
||||
paths.push_back(JNIUtils::JStringToString(env, pathStr));
|
||||
env->DeleteLocalRef(pathStr);
|
||||
}
|
||||
env->DeleteLocalRef(pathsObjArray);
|
||||
};
|
||||
}
|
||||
m_threadCV.notify_one();
|
||||
m_functionCV.wait(functionLock, [this]() -> bool { return m_functionFinished; });
|
||||
return paths;
|
||||
}
|
||||
|
||||
bool isDirectory(const std::filesystem::path &uri) override {
|
||||
return callBooleanFunction(uri, m_isDirectoryMid);
|
||||
}
|
||||
|
||||
bool isFile(const std::filesystem::path &uri) override {
|
||||
return callBooleanFunction(uri, m_isFileMid);
|
||||
}
|
||||
|
||||
bool exists(const std::filesystem::path &uri) override {
|
||||
return callBooleanFunction(uri, m_existsMid);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<AndroidFilesystemCallbacks> g_androidFilesystemCallbacks = nullptr;
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_info_cemu_Cemu_NativeLibrary_initializeEmulation(JNIEnv *env, jclass clazz) {
|
||||
g_androidFilesystemCallbacks = std::make_shared<AndroidFilesystemCallbacks>();
|
||||
FilesystemAndroid::setFilesystemCallbacks(g_androidFilesystemCallbacks);
|
||||
NetworkConfig::LoadOnce();
|
||||
mainEmulatorHLE();
|
||||
InputManager::instance().load();
|
||||
@@ -202,100 +336,6 @@ Java_info_cemu_Cemu_NativeLibrary_recreateRenderSurface(JNIEnv *env, jclass claz
|
||||
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) {
|
||||
|
||||
@@ -12,33 +12,8 @@ public class CemuApplication extends Application {
|
||||
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());
|
||||
FileUtil.setCemuApplication(this);
|
||||
NativeLibrary.initializeActiveSettings(getExternalFilesDir(null).getAbsoluteFile().toString(), getExternalFilesDir(null).getAbsoluteFile().toString());
|
||||
NativeLibrary.initializeEmulation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,12 @@ public class FileUtil {
|
||||
private static final String COLON_ENCODED = "%3A";
|
||||
private static final String MODE = "r";
|
||||
|
||||
private static CemuApplication cemuApplication;
|
||||
|
||||
public static void setCemuApplication(CemuApplication cemuApplication) {
|
||||
FileUtil.cemuApplication = cemuApplication;
|
||||
}
|
||||
|
||||
private static String toCppPath(Uri uri) {
|
||||
String uriPath = uri.toString();
|
||||
int delimiterPos = uriPath.lastIndexOf(COLON_ENCODED);
|
||||
@@ -30,12 +36,12 @@ public class FileUtil {
|
||||
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) {
|
||||
public static int openContentUri(String uri) {
|
||||
try {
|
||||
if (!exists(context, uri)) {
|
||||
if (!exists(uri)) {
|
||||
return -1;
|
||||
}
|
||||
ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(fromCppPath(uri), MODE);
|
||||
ParcelFileDescriptor parcelFileDescriptor = cemuApplication.getApplicationContext().getContentResolver().openFileDescriptor(fromCppPath(uri), MODE);
|
||||
if (parcelFileDescriptor != null) {
|
||||
int fd = parcelFileDescriptor.detachFd();
|
||||
parcelFileDescriptor.close();
|
||||
@@ -47,11 +53,11 @@ public class FileUtil {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static String[] listFiles(Context context, String uri) {
|
||||
public static String[] listFiles(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)) {
|
||||
try (Cursor cursor = cemuApplication.getApplicationContext().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);
|
||||
@@ -65,17 +71,17 @@ public class FileUtil {
|
||||
return filesArray;
|
||||
}
|
||||
|
||||
public static boolean isDirectory(Context context, String uri) {
|
||||
String mimeType = context.getContentResolver().getType(fromCppPath(uri));
|
||||
public static boolean isDirectory(String uri) {
|
||||
String mimeType = cemuApplication.getApplicationContext().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 isFile(String uri) {
|
||||
return !isDirectory(uri);
|
||||
}
|
||||
|
||||
public static boolean exists(Context context, String uri) {
|
||||
try (Cursor cursor = context.getContentResolver().query(fromCppPath(uri), null, null, null, null)) {
|
||||
public static boolean exists(String uri) {
|
||||
try (Cursor cursor = cemuApplication.getApplicationContext().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());
|
||||
|
||||
Reference in New Issue
Block a user