citra_android: Storage Access Framework implementation (#6313)

This commit is contained in:
hank121314 2023-03-23 21:30:52 +08:00 committed by GitHub
parent 8c12eb4905
commit 8d563d37b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
68 changed files with 1972 additions and 545 deletions

View file

@ -7,6 +7,8 @@
#include <memory>
#include <sstream>
#include <unordered_map>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_paths.h"
@ -66,6 +68,11 @@
#endif
#ifdef ANDROID
#include "common/android_storage.h"
#include "common/string_util.h"
#endif
#include <algorithm>
#include <sys/stat.h>
@ -104,6 +111,8 @@ bool Exists(const std::string& filename) {
copy += DIR_SEP_CHR;
int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info);
#elif ANDROID
int result = AndroidStorage::FileExists(filename) ? 0 : -1;
#else
int result = stat(copy.c_str(), &file_info);
#endif
@ -112,6 +121,10 @@ bool Exists(const std::string& filename) {
}
bool IsDirectory(const std::string& filename) {
#ifdef ANDROID
return AndroidStorage::IsDirectory(filename);
#endif
struct stat file_info;
std::string copy(filename);
@ -156,6 +169,11 @@ bool Delete(const std::string& filename) {
LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg());
return false;
}
#elif ANDROID
if (!AndroidStorage::DeleteDocument(filename)) {
LOG_ERROR(Common_Filesystem, "unlink failed on {}", filename);
return false;
}
#else
if (unlink(filename.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg());
@ -178,6 +196,24 @@ bool CreateDir(const std::string& path) {
}
LOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error);
return false;
#elif ANDROID
std::string directory = path;
std::string filename = path;
if (Common::EndsWith(path, "/")) {
directory = GetParentPath(path);
filename = GetParentPath(path);
}
directory = GetParentPath(directory);
filename = GetFilename(filename);
// If directory path is empty, set it to root.
if (directory.empty()) {
directory = "/";
}
if (!AndroidStorage::CreateDir(directory, filename)) {
LOG_ERROR(Common_Filesystem, "mkdir failed on {}", path);
return false;
};
return true;
#else
if (mkdir(path.c_str(), 0755) == 0)
return true;
@ -241,6 +277,9 @@ bool DeleteDir(const std::string& filename) {
#ifdef _WIN32
if (::RemoveDirectoryW(Common::UTF8ToUTF16W(filename).c_str()))
return true;
#elif ANDROID
if (AndroidStorage::DeleteDocument(filename))
return true;
#else
if (rmdir(filename.c_str()) == 0)
return true;
@ -256,6 +295,9 @@ bool Rename(const std::string& srcFilename, const std::string& destFilename) {
if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(),
Common::UTF8ToUTF16W(destFilename).c_str()) == 0)
return true;
#elif ANDROID
if (AndroidStorage::RenameFile(srcFilename, std::string(GetFilename(destFilename))))
return true;
#else
if (rename(srcFilename.c_str(), destFilename.c_str()) == 0)
return true;
@ -275,6 +317,9 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename,
GetLastErrorMsg());
return false;
#elif ANDROID
return AndroidStorage::CopyFile(srcFilename, std::string(GetParentPath(destFilename)),
std::string(GetFilename(destFilename)));
#else
using CFilePointer = std::unique_ptr<FILE, decltype(&std::fclose)>;
@ -334,6 +379,10 @@ u64 GetSize(const std::string& filename) {
struct stat buf;
#ifdef _WIN32
if (_wstat64(Common::UTF8ToUTF16W(filename).c_str(), &buf) == 0)
#elif ANDROID
u64 result = AndroidStorage::GetSize(filename);
LOG_TRACE(Common_Filesystem, "{}: {}", filename, result);
return result;
#else
if (stat(filename.c_str(), &buf) == 0)
#endif
@ -403,6 +452,10 @@ bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory,
// windows loop
do {
const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName));
#elif ANDROID
// android loop
auto result = AndroidStorage::GetFilesName(directory);
for (auto virtual_name : result) {
#else
DIR* dirp = opendir(directory.c_str());
if (!dirp)
@ -426,6 +479,8 @@ bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory,
#ifdef _WIN32
} while (FindNextFileW(handle_find, &ffd) != 0);
FindClose(handle_find);
#elif ANDROID
}
#else
}
closedir(dirp);
@ -514,12 +569,18 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
if (!FileUtil::Exists(dest_path))
FileUtil::CreateFullPath(dest_path);
#ifdef ANDROID
auto result = AndroidStorage::GetFilesName(source_path);
for (auto virtualName : result) {
#else
DIR* dirp = opendir(source_path.c_str());
if (!dirp)
return;
while (struct dirent* result = readdir(dirp)) {
const std::string virtualName(result->d_name);
#endif // ANDROID
// check for "." and ".."
if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0')))
@ -537,8 +598,11 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
} else if (!FileUtil::Exists(dest))
FileUtil::Copy(source, dest);
}
#ifndef ANDROID
closedir(dirp);
#endif
#endif // ANDROID
#endif // _WIN32
}
std::optional<std::string> GetCurrentDir() {
@ -698,11 +762,9 @@ void SetUserPath(const std::string& path) {
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
#elif ANDROID
if (FileUtil::Exists(DIR_SEP SDCARD_DIR)) {
user_path = DIR_SEP SDCARD_DIR DIR_SEP EMU_DATA_DIR DIR_SEP;
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
}
user_path = "/";
g_paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
g_paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
#else
if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
@ -929,6 +991,9 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
std::string path(path_);
#ifdef ANDROID
return std::string(RemoveTrailingSlash(path));
#endif
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
@ -975,6 +1040,7 @@ IOFile& IOFile::operator=(IOFile&& other) noexcept {
void IOFile::Swap(IOFile& other) noexcept {
std::swap(m_file, other.m_file);
std::swap(m_fd, other.m_fd);
std::swap(m_good, other.m_good);
std::swap(filename, other.filename);
std::swap(openmode, other.openmode);
@ -993,6 +1059,36 @@ bool IOFile::Open() {
m_good = _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(),
Common::UTF8ToUTF16W(openmode).c_str()) == 0;
}
#elif ANDROID
// Check whether filepath is startsWith content
AndroidStorage::AndroidOpenMode android_open_mode = AndroidStorage::ParseOpenmode(openmode);
if (android_open_mode == AndroidStorage::AndroidOpenMode::WRITE ||
android_open_mode == AndroidStorage::AndroidOpenMode::READ_WRITE ||
android_open_mode == AndroidStorage::AndroidOpenMode::WRITE_APPEND ||
android_open_mode == AndroidStorage::AndroidOpenMode::WRITE_TRUNCATE ||
android_open_mode == AndroidStorage::AndroidOpenMode::READ_WRITE_TRUNCATE ||
android_open_mode == AndroidStorage::AndroidOpenMode::READ_WRITE_APPEND) {
if (!FileUtil::Exists(filename)) {
std::string directory(GetParentPath(filename));
std::string display_name(GetFilename(filename));
if (!AndroidStorage::CreateFile(directory, display_name)) {
m_good = m_file != nullptr;
return m_good;
}
}
}
m_fd = AndroidStorage::OpenContentUri(filename, android_open_mode);
if (m_fd != -1) {
int error_num = 0;
m_file = fdopen(m_fd, openmode.c_str());
error_num = errno;
if (error_num != 0 && m_file == nullptr) {
LOG_ERROR(Common_Filesystem, "Error on file: {}, error: {}", filename,
strerror(error_num));
}
}
m_good = m_file != nullptr;
#else
m_file = std::fopen(filename.c_str(), openmode.c_str());
m_good = m_file != nullptr;
@ -1083,4 +1179,30 @@ bool IOFile::Resize(u64 size) {
return m_good;
}
template <typename T>
using boost_iostreams = boost::iostreams::stream<T>;
template <>
void OpenFStream<std::ios_base::in>(
boost_iostreams<boost::iostreams::file_descriptor_source>& fstream,
const std::string& filename) {
IOFile file(filename, "r");
int fd = dup(file.GetFd());
if (fd == -1)
return;
boost::iostreams::file_descriptor_source file_descriptor_source(fd,
boost::iostreams::close_handle);
fstream.open(file_descriptor_source);
}
template <>
void OpenFStream<std::ios_base::out>(
boost_iostreams<boost::iostreams::file_descriptor_sink>& fstream, const std::string& filename) {
IOFile file(filename, "w");
int fd = dup(file.GetFd());
if (fd == -1)
return;
boost::iostreams::file_descriptor_sink file_descriptor_sink(fd, boost::iostreams::close_handle);
fstream.open(file_descriptor_sink);
}
} // namespace FileUtil