Compare commits
154 Commits
vulkan-sch
...
fs-fixes
Author | SHA1 | Date | |
---|---|---|---|
c02b0a0113 | |||
97cbcc6e49 | |||
9d6aa2db81 | |||
0675bd946c | |||
281f2926bb | |||
349ac6ac05 | |||
16571a96a2 | |||
a0e9836386 | |||
fb10d05f97 | |||
182bebf272 | |||
7626804a90 | |||
20496e8ca4 | |||
b4184a3c2b | |||
2251d96d4d | |||
c028a8c7c5 | |||
359f97be22 | |||
748f8a0658 | |||
a7611bb2d3 | |||
6f35a3bf37 | |||
58573dd7b3 | |||
11728d6772 | |||
a99be221b2 | |||
f656610a41 | |||
cfa1a7b91c | |||
52b1fc4889 | |||
01e2b6cdaa | |||
f75380fc93 | |||
18af49a0ca | |||
c7e64f6c7b | |||
0a40a513a6 | |||
a4dc6a55b7 | |||
7dce5be263 | |||
3f7d97da4c | |||
a11b4dd051 | |||
c78847b2b6 | |||
8faa7a6e02 | |||
acf4b4e5fb | |||
3a0ca63d91 | |||
1e96775203 | |||
6d27e8be8d | |||
c357a8b9b6 | |||
7ad982f123 | |||
dd3d24bfec | |||
489bbb98b2 | |||
5c9543e39d | |||
49085d400c | |||
8be9ea4f4a | |||
a71c288252 | |||
159809eb32 | |||
2a4f0ce8de | |||
4c8f1c83c8 | |||
98f6d697d8 | |||
4636735783 | |||
8a2770bf83 | |||
f1e09c1ea1 | |||
2423e645f1 | |||
0eaae31f9f | |||
4f9b545296 | |||
628d70e112 | |||
9991b9b12b | |||
e4bcf73c5a | |||
b693d205e4 | |||
91621ec202 | |||
948f72d320 | |||
8c5b417486 | |||
51685ee2db | |||
079e4aa205 | |||
48edfb891b | |||
915406354c | |||
6f3fc32a93 | |||
ebfa98d31d | |||
3095ee91a8 | |||
fadeecfe6d | |||
2a1598036e | |||
58f01112c5 | |||
7e7b3dc18c | |||
d4a3f60575 | |||
f8cbf783cb | |||
dc0cddb7de | |||
9cef9d4c58 | |||
dada05801f | |||
9bc71a3307 | |||
98274273b1 | |||
269db2bfb8 | |||
d27c1c8606 | |||
891b4bff18 | |||
eeccdc02fc | |||
29ee94c3f5 | |||
8936641841 | |||
e1f6b88e7b | |||
64d809f06a | |||
ebd23026a0 | |||
6e1bfe9949 | |||
854092ce4f | |||
90d24caaf8 | |||
65400936c7 | |||
3f9e5a2b42 | |||
34ba320c3d | |||
ec9f1902f5 | |||
c72a365d78 | |||
8f211613a3 | |||
30885b72be | |||
beb078a71b | |||
a65f9ea5a8 | |||
f9c11eab96 | |||
794f6e4a67 | |||
c85731f3ae | |||
e1542cea84 | |||
887ef51f04 | |||
d809687aeb | |||
96ec85a72e | |||
0ed4d493ad | |||
78be1e7c17 | |||
2963682722 | |||
98eea4dcca | |||
98a4a18201 | |||
3619bd33b1 | |||
fa870be263 | |||
3a6d19f51f | |||
73d6a9d585 | |||
5d48107dd6 | |||
a306931e1c | |||
b3803c5002 | |||
c412c116d8 | |||
6ce4493e14 | |||
d6e545932a | |||
542bae4581 | |||
307154a06f | |||
725afe33ef | |||
bb58056ebe | |||
f69a33574c | |||
1a48cf7e7d | |||
2833d94a3b | |||
9b0aa5135e | |||
9787efc7ee | |||
623293d272 | |||
5ab5fdcc22 | |||
1b1988a37a | |||
f584d143ff | |||
56c679595f | |||
a93d7a8d3a | |||
0fb792d216 | |||
4d684174e0 | |||
a2daef2985 | |||
e74b2575d7 | |||
ae6b7edc25 | |||
fbe06234b1 | |||
98d3b9c776 | |||
4a590d1fcb | |||
726964ff20 | |||
e5f30fdbf8 | |||
9626bdf385 | |||
67c4b87184 | |||
ea26f46b0d |
@ -138,13 +138,13 @@ if (NOT ENABLE_GENERIC)
|
||||
if (MSVC)
|
||||
detect_architecture("_M_AMD64" x86_64)
|
||||
detect_architecture("_M_IX86" x86)
|
||||
detect_architecture("_M_ARM" ARM)
|
||||
detect_architecture("_M_ARM64" ARM64)
|
||||
detect_architecture("_M_ARM" arm)
|
||||
detect_architecture("_M_ARM64" arm64)
|
||||
else()
|
||||
detect_architecture("__x86_64__" x86_64)
|
||||
detect_architecture("__i386__" x86)
|
||||
detect_architecture("__arm__" ARM)
|
||||
detect_architecture("__aarch64__" ARM64)
|
||||
detect_architecture("__arm__" arm)
|
||||
detect_architecture("__aarch64__" arm64)
|
||||
endif()
|
||||
endif()
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
|
13
externals/CMakeLists.txt
vendored
13
externals/CMakeLists.txt
vendored
@ -31,24 +31,27 @@ add_subdirectory(catch2)
|
||||
# Crypto++
|
||||
add_subdirectory(cryptopp)
|
||||
|
||||
# fmt and Xbyak need to be added before dynarmic
|
||||
# libfmt
|
||||
add_subdirectory(fmt)
|
||||
|
||||
# Xbyak
|
||||
if (ARCHITECTURE_x86_64)
|
||||
add_library(xbyak INTERFACE)
|
||||
target_include_directories(xbyak SYSTEM INTERFACE ./xbyak/xbyak)
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/xbyak/xbyak DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
target_include_directories(xbyak SYSTEM INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/xbyak/include)
|
||||
target_compile_definitions(xbyak INTERFACE XBYAK_NO_OP_NAMES)
|
||||
endif()
|
||||
|
||||
# Dynarmic
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_ARM64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
set(DYNARMIC_TESTS OFF)
|
||||
set(DYNARMIC_NO_BUNDLED_FMT ON)
|
||||
set(DYNARMIC_FRONTENDS "A32")
|
||||
add_subdirectory(dynarmic)
|
||||
endif()
|
||||
|
||||
# libfmt
|
||||
add_subdirectory(fmt)
|
||||
|
||||
# getopt
|
||||
if (MSVC)
|
||||
add_subdirectory(getopt)
|
||||
|
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 4606179019...3946dcf005
2
externals/xbyak
vendored
2
externals/xbyak
vendored
Submodule externals/xbyak updated: c306b8e578...48457bfa0d
@ -16,6 +16,7 @@
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "config.ini";
|
||||
sdl2_config_loc = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + "config.ini";
|
||||
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
|
||||
|
||||
Reload();
|
||||
@ -38,8 +38,8 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
||||
if (sdl2_config->ParseError() < 0) {
|
||||
if (retry) {
|
||||
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
|
||||
FileUtil::CreateFullPath(location);
|
||||
FileUtil::WriteStringToFile(true, location, default_contents);
|
||||
Common::FS::CreateFullPath(location);
|
||||
Common::FS::WriteStringToFile(true, location, default_contents);
|
||||
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
|
||||
|
||||
return LoadINI(default_contents, false);
|
||||
|
@ -35,7 +35,7 @@ std::vector<u8> GetSMDHData(std::string physical_name) {
|
||||
std::string update_path = Service::AM::GetTitleContentPath(
|
||||
Service::FS::MediaType::SDMC, program_id + 0x0000000E'00000000);
|
||||
|
||||
if (!FileUtil::Exists(update_path))
|
||||
if (!Common::FS::Exists(update_path))
|
||||
return original_smdh;
|
||||
|
||||
std::unique_ptr<Loader::AppLoader> update_loader = Loader::GetLoader(update_path);
|
||||
|
@ -159,9 +159,9 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
Log::AddBackend(std::make_unique<Log::LogcatBackend>());
|
||||
FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||
Common::FS::CreateFullPath(Common::FS::GetUserPath(Common::FS::UserPath::LogDir));
|
||||
Log::AddBackend(std::make_unique<Log::FileBackend>(
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + LOG_FILE));
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + LOG_FILE));
|
||||
LOG_INFO(Frontend, "Logging backend initialised");
|
||||
|
||||
// Initialize misc classes
|
||||
|
@ -154,7 +154,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
|
||||
Config{};
|
||||
// Replace with game-specific settings
|
||||
u64 program_id{};
|
||||
FileUtil::SetCurrentRomPath(filepath);
|
||||
Common::FS::SetCurrentRomPath(filepath);
|
||||
auto app_loader = Loader::GetLoader(filepath);
|
||||
if (app_loader) {
|
||||
app_loader->ReadProgramId(program_id);
|
||||
@ -305,18 +305,18 @@ void Java_org_citra_citra_1emu_NativeLibrary_SwapScreens(JNIEnv* env, [[maybe_un
|
||||
void Java_org_citra_citra_1emu_NativeLibrary_SetUserDirectory(JNIEnv* env,
|
||||
[[maybe_unused]] jclass clazz,
|
||||
jstring j_directory) {
|
||||
FileUtil::SetCurrentDir(GetJString(env, j_directory));
|
||||
Common::FS::SetCurrentDir(GetJString(env, j_directory));
|
||||
}
|
||||
|
||||
jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetInstalledGamePaths(
|
||||
JNIEnv* env, [[maybe_unused]] jclass clazz) {
|
||||
std::vector<std::string> games;
|
||||
const FileUtil::DirectoryEntryCallable ScanDir =
|
||||
const Common::FS::DirectoryEntryCallable ScanDir =
|
||||
[&games, &ScanDir](u64*, const std::string& directory, const std::string& virtual_name) {
|
||||
std::string path = directory + virtual_name;
|
||||
if (FileUtil::IsDirectory(path)) {
|
||||
if (Common::FS::IsDirectory(path)) {
|
||||
path += '/';
|
||||
FileUtil::ForeachDirectoryEntry(nullptr, path, ScanDir);
|
||||
Common::FS::ForeachDirectoryEntry(nullptr, path, ScanDir);
|
||||
} else {
|
||||
auto loader = Loader::GetLoader(path);
|
||||
if (loader) {
|
||||
@ -330,12 +330,12 @@ jobjectArray Java_org_citra_citra_1emu_NativeLibrary_GetInstalledGamePaths(
|
||||
return true;
|
||||
};
|
||||
ScanDir(nullptr, "",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
|
||||
"Nintendo "
|
||||
"3DS/00000000000000000000000000000000/"
|
||||
"00000000000000000000000000000000/title/00040000");
|
||||
ScanDir(nullptr, "",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
|
||||
"00000000000000000000000000000000/title/00040010");
|
||||
jobjectArray jgames = env->NewObjectArray(static_cast<jsize>(games.size()),
|
||||
env->FindClass("java/lang/String"), nullptr);
|
||||
|
@ -43,8 +43,8 @@ FuncDL<int(AVCodecParserContext*, AVCodecContext*, uint8_t**, int*, const uint8_
|
||||
FuncDL<void(AVCodecParserContext*)> av_parser_close_dl;
|
||||
|
||||
bool InitFFmpegDL() {
|
||||
std::string dll_path = FileUtil::GetUserPath(FileUtil::UserPath::DLLDir);
|
||||
FileUtil::CreateDir(dll_path);
|
||||
std::string dll_path = Common::FS::GetUserPath(Common::FS::UserPath::DLLDir);
|
||||
Common::FS::CreateDir(dll_path);
|
||||
std::wstring w_dll_path = Common::UTF8ToUTF16W(dll_path);
|
||||
SetDllDirectoryW(w_dll_path.c_str());
|
||||
|
||||
|
@ -170,8 +170,8 @@ static void InitializeLogging() {
|
||||
|
||||
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
|
||||
|
||||
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
|
||||
FileUtil::CreateFullPath(log_dir);
|
||||
const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
|
||||
Common::FS::CreateFullPath(log_dir);
|
||||
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
|
||||
#ifdef _WIN32
|
||||
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
|
||||
|
@ -22,7 +22,7 @@
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini";
|
||||
sdl2_config_loc = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + "sdl2-config.ini";
|
||||
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
|
||||
|
||||
Reload();
|
||||
@ -35,8 +35,8 @@ bool Config::LoadINI(const std::string& default_contents, bool retry) {
|
||||
if (sdl2_config->ParseError() < 0) {
|
||||
if (retry) {
|
||||
LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location);
|
||||
FileUtil::CreateFullPath(location);
|
||||
FileUtil::WriteStringToFile(true, location, default_contents);
|
||||
Common::FS::CreateFullPath(location);
|
||||
Common::FS::WriteStringToFile(true, location, default_contents);
|
||||
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
|
||||
|
||||
return LoadINI(default_contents, false);
|
||||
@ -204,14 +204,15 @@ void Config::ReadValues() {
|
||||
Settings::values.use_virtual_sd =
|
||||
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
|
||||
|
||||
const std::string default_nand_dir = FileUtil::GetDefaultUserPath(FileUtil::UserPath::NANDDir);
|
||||
FileUtil::UpdateUserPath(
|
||||
FileUtil::UserPath::NANDDir,
|
||||
sdl2_config->GetString("Data Storage", "nand_directory", default_nand_dir));
|
||||
const std::string default_sdmc_dir = FileUtil::GetDefaultUserPath(FileUtil::UserPath::SDMCDir);
|
||||
FileUtil::UpdateUserPath(
|
||||
FileUtil::UserPath::SDMCDir,
|
||||
sdl2_config->GetString("Data Storage", "sdmc_directory", default_sdmc_dir));
|
||||
Settings::values.use_custom_storage =
|
||||
sdl2_config->GetBoolean("Data Storage", "use_custom_storage", false);
|
||||
|
||||
if (Settings::values.use_custom_storage) {
|
||||
Common::FS::UpdateUserPath(Common::FS::UserPath::NANDDir,
|
||||
sdl2_config->GetString("Data Storage", "nand_directory", ""));
|
||||
Common::FS::UpdateUserPath(Common::FS::UserPath::SDMCDir,
|
||||
sdl2_config->GetString("Data Storage", "sdmc_directory", ""));
|
||||
}
|
||||
|
||||
// System
|
||||
Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", true);
|
||||
|
@ -250,6 +250,10 @@ volume =
|
||||
# 1 (default): Yes, 0: No
|
||||
use_virtual_sd =
|
||||
|
||||
# Whether to use custom storage locations
|
||||
# 1: Yes, 0 (default): No
|
||||
use_custom_storage =
|
||||
|
||||
# The path of the virtual SD card directory.
|
||||
# empty (default) will use the user_path
|
||||
sdmc_directory =
|
||||
|
@ -19,8 +19,8 @@
|
||||
|
||||
Config::Config() {
|
||||
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
|
||||
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
|
||||
FileUtil::CreateFullPath(qt_config_loc);
|
||||
qt_config_loc = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + "qt-config.ini";
|
||||
Common::FS::CreateFullPath(qt_config_loc);
|
||||
qt_config =
|
||||
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
|
||||
Reload();
|
||||
@ -304,21 +304,17 @@ void Config::ReadDataStorageValues() {
|
||||
|
||||
Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
|
||||
|
||||
Settings::values.use_custom_storage =
|
||||
ReadSetting(QStringLiteral("use_custom_storage"), false).toBool();
|
||||
const std::string nand_dir =
|
||||
ReadSetting(
|
||||
QStringLiteral("nand_directory"),
|
||||
QString::fromStdString(FileUtil::GetDefaultUserPath(FileUtil::UserPath::NANDDir)))
|
||||
.toString()
|
||||
.toStdString();
|
||||
ReadSetting(QStringLiteral("nand_directory"), QStringLiteral("")).toString().toStdString();
|
||||
const std::string sdmc_dir =
|
||||
ReadSetting(
|
||||
QStringLiteral("sdmc_directory"),
|
||||
QString::fromStdString(FileUtil::GetDefaultUserPath(FileUtil::UserPath::SDMCDir)))
|
||||
.toString()
|
||||
.toStdString();
|
||||
ReadSetting(QStringLiteral("sdmc_directory"), QStringLiteral("")).toString().toStdString();
|
||||
|
||||
FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir, nand_dir);
|
||||
FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir, sdmc_dir);
|
||||
if (Settings::values.use_custom_storage) {
|
||||
Common::FS::UpdateUserPath(Common::FS::UserPath::NANDDir, nand_dir);
|
||||
Common::FS::UpdateUserPath(Common::FS::UserPath::SDMCDir, sdmc_dir);
|
||||
}
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -489,6 +485,7 @@ void Config::ReadRendererValues() {
|
||||
ReadSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::GraphicsAPI::OpenGL))
|
||||
.toUInt());
|
||||
Settings::values.physical_device = ReadSetting(QStringLiteral("physical_device"), 0).toUInt();
|
||||
Settings::values.async_command_recording = ReadSetting(QStringLiteral("async_command_recording"), true).toBool();
|
||||
Settings::values.use_hw_renderer =
|
||||
ReadSetting(QStringLiteral("use_hw_renderer"), true).toBool();
|
||||
Settings::values.use_hw_shader = ReadSetting(QStringLiteral("use_hw_shader"), true).toBool();
|
||||
@ -876,12 +873,13 @@ void Config::SaveDataStorageValues() {
|
||||
qt_config->beginGroup(QStringLiteral("Data Storage"));
|
||||
|
||||
WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
|
||||
WriteSetting(QStringLiteral("use_custom_storage"), Settings::values.use_custom_storage, false);
|
||||
WriteSetting(QStringLiteral("nand_directory"),
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
|
||||
QString::fromStdString(FileUtil::GetDefaultUserPath(FileUtil::UserPath::NANDDir)));
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)),
|
||||
QStringLiteral(""));
|
||||
WriteSetting(QStringLiteral("sdmc_directory"),
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
|
||||
QString::fromStdString(FileUtil::GetDefaultUserPath(FileUtil::UserPath::SDMCDir)));
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)),
|
||||
QStringLiteral(""));
|
||||
|
||||
qt_config->endGroup();
|
||||
}
|
||||
@ -1007,6 +1005,7 @@ void Config::SaveRendererValues() {
|
||||
WriteSetting(QStringLiteral("graphics_api"), static_cast<u32>(Settings::values.graphics_api),
|
||||
static_cast<u32>(Settings::GraphicsAPI::OpenGL));
|
||||
WriteSetting(QStringLiteral("physical_device"), Settings::values.physical_device, 0);
|
||||
WriteSetting(QStringLiteral("async_command_recording"), Settings::values.async_command_recording, true);
|
||||
WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true);
|
||||
WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true);
|
||||
#ifdef __APPLE__
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
#include <QMessageBox>
|
||||
#include "citra_qt/configuration/configure_debug.h"
|
||||
#include "citra_qt/debugger/console.h"
|
||||
#include "citra_qt/uisettings.h"
|
||||
@ -11,7 +12,9 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core.h"
|
||||
#include "core/settings.h"
|
||||
#include "qcheckbox.h"
|
||||
#include "ui_configure_debug.h"
|
||||
#include "video_core/renderer_vulkan/vk_instance.h"
|
||||
|
||||
ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
: QWidget(parent), ui(std::make_unique<Ui::ConfigureDebug>()) {
|
||||
@ -19,10 +22,40 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->open_log_button, &QPushButton::clicked, []() {
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
|
||||
QString path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LogDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
|
||||
connect(ui->toggle_renderer_debug, &QCheckBox::clicked, this, [this](bool checked) {
|
||||
if (checked && Settings::values.graphics_api == Settings::GraphicsAPI::Vulkan) {
|
||||
try {
|
||||
Vulkan::Instance debug_inst{true};
|
||||
} catch (vk::LayerNotPresentError& err) {
|
||||
ui->toggle_renderer_debug->toggle();
|
||||
QMessageBox::warning(
|
||||
this, tr("Validation layer not available"),
|
||||
tr("Unable to enable debug renderer because the layer "
|
||||
"<strong>VK_LAYER_KHRONOS_validation</strong> is missing. "
|
||||
"Please install the Vulkan SDK or the appropriate package of your distribution"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->toggle_dump_command_buffers, &QCheckBox::clicked, this, [this](bool checked) {
|
||||
if (checked && Settings::values.graphics_api == Settings::GraphicsAPI::Vulkan) {
|
||||
try {
|
||||
Vulkan::Instance debug_inst{false, true};
|
||||
} catch (vk::LayerNotPresentError& err) {
|
||||
ui->toggle_dump_command_buffers->toggle();
|
||||
QMessageBox::warning(
|
||||
this, tr("Command buffer dumping not available"),
|
||||
tr("Unable to enable command buffer dumping because the layer "
|
||||
"<strong>VK_LAYER_LUNARG_api_dump</strong> is missing. "
|
||||
"Please install the Vulkan SDK or the appropriate package of your distribution"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const bool is_powered_on = Core::System::GetInstance().IsPoweredOn();
|
||||
ui->toggle_cpu_jit->setEnabled(!is_powered_on);
|
||||
ui->toggle_renderer_debug->setEnabled(!is_powered_on);
|
||||
|
@ -22,5 +22,6 @@ public:
|
||||
void RetranslateUI();
|
||||
void SetConfiguration();
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::ConfigureDebug> ui;
|
||||
};
|
||||
|
@ -117,9 +117,9 @@ void ConfigureGeneral::SetConfiguration() {
|
||||
QString screenshot_path = UISettings::values.screenshot_path;
|
||||
if (screenshot_path.isEmpty()) {
|
||||
screenshot_path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir));
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir));
|
||||
screenshot_path.append(QStringLiteral("screenshots/"));
|
||||
FileUtil::CreateFullPath(screenshot_path.toStdString());
|
||||
Common::FS::CreateFullPath(screenshot_path.toStdString());
|
||||
UISettings::values.screenshot_path = screenshot_path;
|
||||
}
|
||||
ui->screenshot_dir_path->setText(screenshot_path);
|
||||
@ -134,7 +134,7 @@ void ConfigureGeneral::ResetDefaults() {
|
||||
if (answer == QMessageBox::No)
|
||||
return;
|
||||
|
||||
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini");
|
||||
Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + "qt-config.ini");
|
||||
std::exit(0);
|
||||
}
|
||||
|
||||
|
@ -26,6 +26,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
|
||||
ui->graphics_api_combo->setEnabled(not_running);
|
||||
ui->toggle_shader_jit->setEnabled(not_running);
|
||||
ui->toggle_disk_shader_cache->setEnabled(hw_renderer_enabled && not_running);
|
||||
ui->toggle_async_recording->setEnabled(hw_renderer_enabled && not_running);
|
||||
ui->physical_device_combo->setEnabled(not_running);
|
||||
SetPhysicalDeviceComboVisibility(ui->graphics_api_combo->currentIndex());
|
||||
|
||||
@ -83,6 +84,7 @@ void ConfigureGraphics::SetConfiguration() {
|
||||
ui->toggle_vsync_new->setChecked(Settings::values.use_vsync_new);
|
||||
ui->graphics_api_combo->setCurrentIndex(static_cast<int>(Settings::values.graphics_api));
|
||||
ui->physical_device_combo->setCurrentIndex(static_cast<int>(Settings::values.physical_device));
|
||||
ui->toggle_async_recording->setChecked(Settings::values.async_command_recording);
|
||||
}
|
||||
|
||||
void ConfigureGraphics::ApplyConfiguration() {
|
||||
@ -96,6 +98,7 @@ void ConfigureGraphics::ApplyConfiguration() {
|
||||
Settings::values.graphics_api =
|
||||
static_cast<Settings::GraphicsAPI>(ui->graphics_api_combo->currentIndex());
|
||||
Settings::values.physical_device = static_cast<u16>(ui->physical_device_combo->currentIndex());
|
||||
Settings::values.async_command_recording = ui->toggle_async_recording->isChecked();
|
||||
}
|
||||
|
||||
void ConfigureGraphics::RetranslateUI() {
|
||||
|
@ -171,6 +171,16 @@
|
||||
<string>Advanced</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_async_recording">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Offloads command buffer recording and fragment shader generation to a worker thread. Can improve performance especially on weaker systems. Disable if you notice better performance. If unsure leave it enabled,</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Async Command Recording</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="toggle_disk_shader_cache">
|
||||
<property name="toolTip">
|
||||
|
@ -16,33 +16,33 @@ ConfigureStorage::ConfigureStorage(QWidget* parent)
|
||||
SetConfiguration();
|
||||
|
||||
connect(ui->open_nand_dir, &QPushButton::clicked, []() {
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
QString path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
|
||||
connect(ui->change_nand_dir, &QPushButton::clicked, this, [this]() {
|
||||
const QString dir_path = QFileDialog::getExistingDirectory(
|
||||
this, tr("Select NAND Directory"),
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)),
|
||||
QFileDialog::ShowDirsOnly);
|
||||
if (!dir_path.isEmpty()) {
|
||||
FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir, dir_path.toStdString());
|
||||
Common::FS::UpdateUserPath(Common::FS::UserPath::NANDDir, dir_path.toStdString());
|
||||
SetConfiguration();
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->open_sdmc_dir, &QPushButton::clicked, []() {
|
||||
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
|
||||
QString path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
|
||||
});
|
||||
|
||||
connect(ui->change_sdmc_dir, &QPushButton::clicked, this, [this]() {
|
||||
const QString dir_path = QFileDialog::getExistingDirectory(
|
||||
this, tr("Select SDMC Directory"),
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)),
|
||||
QFileDialog::ShowDirsOnly);
|
||||
if (!dir_path.isEmpty()) {
|
||||
FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir, dir_path.toStdString());
|
||||
Common::FS::UpdateUserPath(Common::FS::UserPath::SDMCDir, dir_path.toStdString());
|
||||
SetConfiguration();
|
||||
}
|
||||
});
|
||||
@ -51,28 +51,42 @@ ConfigureStorage::ConfigureStorage(QWidget* parent)
|
||||
ApplyConfiguration();
|
||||
SetConfiguration();
|
||||
});
|
||||
connect(ui->toggle_custom_storage, &QCheckBox::clicked, this, [this]() {
|
||||
ApplyConfiguration();
|
||||
SetConfiguration();
|
||||
});
|
||||
}
|
||||
|
||||
ConfigureStorage::~ConfigureStorage() = default;
|
||||
|
||||
void ConfigureStorage::SetConfiguration() {
|
||||
ui->nand_group->setVisible(Settings::values.use_virtual_sd);
|
||||
QString nand_path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
|
||||
ui->nand_group->setVisible(Settings::values.use_custom_storage);
|
||||
QString nand_path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
|
||||
ui->nand_dir_path->setText(nand_path);
|
||||
ui->open_nand_dir->setEnabled(!nand_path.isEmpty());
|
||||
|
||||
ui->sdmc_group->setVisible(Settings::values.use_virtual_sd);
|
||||
QString sdmc_path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
|
||||
ui->sdmc_group->setVisible(Settings::values.use_virtual_sd &&
|
||||
Settings::values.use_custom_storage);
|
||||
QString sdmc_path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
|
||||
ui->sdmc_dir_path->setText(sdmc_path);
|
||||
ui->open_sdmc_dir->setEnabled(!sdmc_path.isEmpty());
|
||||
|
||||
ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd);
|
||||
ui->toggle_custom_storage->setChecked(Settings::values.use_custom_storage);
|
||||
|
||||
ui->storage_group->setEnabled(!Core::System::GetInstance().IsPoweredOn());
|
||||
}
|
||||
|
||||
void ConfigureStorage::ApplyConfiguration() {
|
||||
Settings::values.use_virtual_sd = ui->toggle_virtual_sd->isChecked();
|
||||
Settings::values.use_custom_storage = ui->toggle_custom_storage->isChecked();
|
||||
|
||||
if (!Settings::values.use_custom_storage) {
|
||||
Common::FS::UpdateUserPath(Common::FS::UserPath::NANDDir,
|
||||
GetDefaultUserPath(Common::FS::UserPath::NANDDir));
|
||||
Common::FS::UpdateUserPath(Common::FS::UserPath::SDMCDir,
|
||||
GetDefaultUserPath(Common::FS::UserPath::SDMCDir));
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigureStorage::RetranslateUI() {
|
||||
|
@ -34,131 +34,147 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="nand_group">
|
||||
<widget class="QGroupBox" name="custom_storage_group">
|
||||
<property name="title">
|
||||
<string/>
|
||||
<string>Custom Storage</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>NAND Directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="nand_dir_path">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="open_nand_dir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QCheckBox" name="toggle_custom_storage">
|
||||
<property name="text">
|
||||
<string>Use Custom Storage</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>NOTE: This does not move the contents of the previous directory to the new one.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="change_nand_dir">
|
||||
<property name="text">
|
||||
<string>Change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="sdmc_group">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>SDMC Directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="sdmc_dir_path">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="open_sdmc_dir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QGroupBox" name="nand_group">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>NAND Directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="nand_dir_path">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="open_nand_dir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>NOTE: This does not move the contents of the previous directory to the new one.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="change_nand_dir">
|
||||
<property name="text">
|
||||
<string>Change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>NOTE: This does not move the contents of the previous directory to the new one.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="change_sdmc_dir">
|
||||
<property name="text">
|
||||
<string>Change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<widget class="QGroupBox" name="sdmc_group">
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>SDMC Directory</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="sdmc_dir_path">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="open_sdmc_dir">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>NOTE: This does not move the contents of the previous directory to the new one.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="change_sdmc_dir">
|
||||
<property name="text">
|
||||
<string>Change</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
@ -3,6 +3,7 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QFileSystemWatcher>
|
||||
#include <QHBoxLayout>
|
||||
@ -31,6 +32,8 @@
|
||||
#include "core/file_sys/archive_extsavedata.h"
|
||||
#include "core/file_sys/archive_source_sd_savedata.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/settings.h"
|
||||
#include "qcursor.h"
|
||||
|
||||
GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent)
|
||||
: QObject(parent), gamelist{gamelist} {}
|
||||
@ -462,6 +465,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
|
||||
}
|
||||
|
||||
@ -475,30 +479,38 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
|
||||
QAction* open_texture_load_location =
|
||||
context_menu.addAction(tr("Open Custom Texture Location"));
|
||||
QAction* open_mods_location = context_menu.addAction(tr("Open Mods Location"));
|
||||
QMenu* shader_menu = context_menu.addMenu(tr("Disk Shader Cache"));
|
||||
QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS"));
|
||||
QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
|
||||
|
||||
QAction* open_shader_cache_location = shader_menu->addAction(tr("Open Shader Cache Location"));
|
||||
shader_menu->addSeparator();
|
||||
QAction* delete_opengl_disk_shader_cache =
|
||||
shader_menu->addAction(tr("Delete OpenGL Shader Cache"));
|
||||
QAction* delete_vulkan_disk_shader_cache =
|
||||
shader_menu->addAction(tr("Delete Vulkan Shader Cache"));
|
||||
|
||||
const bool is_application =
|
||||
0x0004000000000000 <= program_id && program_id <= 0x00040000FFFFFFFF;
|
||||
|
||||
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||
open_save_location->setVisible(
|
||||
is_application && FileUtil::Exists(FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(
|
||||
std::string sdmc_dir = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
|
||||
open_save_location->setEnabled(
|
||||
is_application && Common::FS::Exists(FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(
|
||||
sdmc_dir, program_id)));
|
||||
|
||||
if (extdata_id) {
|
||||
open_extdata_location->setVisible(
|
||||
open_extdata_location->setEnabled(
|
||||
is_application &&
|
||||
FileUtil::Exists(FileSys::GetExtDataPathFromId(sdmc_dir, extdata_id)));
|
||||
Common::FS::Exists(FileSys::GetExtDataPathFromId(sdmc_dir, extdata_id)));
|
||||
} else {
|
||||
open_extdata_location->setVisible(false);
|
||||
}
|
||||
|
||||
auto media_type = Service::AM::GetTitleMediaType(program_id);
|
||||
open_application_location->setVisible(path.toStdString() ==
|
||||
open_application_location->setEnabled(path.toStdString() ==
|
||||
Service::AM::GetTitleContentPath(media_type, program_id));
|
||||
open_update_location->setVisible(
|
||||
is_application && FileUtil::Exists(Service::AM::GetTitlePath(Service::FS::MediaType::SDMC,
|
||||
open_update_location->setEnabled(
|
||||
is_application && Common::FS::Exists(Service::AM::GetTitlePath(Service::FS::MediaType::SDMC,
|
||||
program_id + 0xe00000000) +
|
||||
"content/"));
|
||||
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
|
||||
@ -523,22 +535,29 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::UPDATE_DATA);
|
||||
});
|
||||
connect(open_texture_dump_location, &QAction::triggered, this, [this, program_id] {
|
||||
if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
||||
if (Common::FS::CreateFullPath(fmt::format("{}textures/{:016X}/",
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
|
||||
program_id))) {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_DUMP);
|
||||
}
|
||||
});
|
||||
connect(open_texture_load_location, &QAction::triggered, this, [this, program_id] {
|
||||
if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
if (Common::FS::CreateFullPath(fmt::format("{}textures/{:016X}/",
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
|
||||
program_id))) {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_LOAD);
|
||||
}
|
||||
});
|
||||
connect(open_texture_load_location, &QAction::triggered, this, [this, program_id] {
|
||||
if (Common::FS::CreateFullPath(fmt::format("{}textures/{:016X}/",
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
|
||||
program_id))) {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_LOAD);
|
||||
}
|
||||
});
|
||||
connect(open_mods_location, &QAction::triggered, this, [this, program_id] {
|
||||
if (FileUtil::CreateFullPath(fmt::format("{}mods/{:016X}/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
if (Common::FS::CreateFullPath(fmt::format("{}mods/{:016X}/",
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
|
||||
program_id))) {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::MODS);
|
||||
}
|
||||
@ -548,6 +567,26 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
|
||||
connect(navigate_to_gamedb_entry, &QAction::triggered, this, [this, program_id]() {
|
||||
emit NavigateToGamedbEntryRequested(program_id, compatibility_list);
|
||||
});
|
||||
connect(open_shader_cache_location, &QAction::triggered, this, [this, program_id] {
|
||||
if (Common::FS::CreateFullPath(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir))) {
|
||||
emit OpenFolderRequested(program_id, GameListOpenTarget::SHADER_CACHE);
|
||||
}
|
||||
});
|
||||
connect(delete_opengl_disk_shader_cache, &QAction::triggered, this, [program_id] {
|
||||
const std::string_view cache_type =
|
||||
Settings::values.separable_shader ? "separable" : "conventional";
|
||||
const std::string path = fmt::format("{}opengl/precompiled/{}/{:016X}.bin",
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir),
|
||||
cache_type, program_id);
|
||||
QFile file{QString::fromStdString(path)};
|
||||
file.remove();
|
||||
});
|
||||
connect(delete_vulkan_disk_shader_cache, &QAction::triggered, this, [] {
|
||||
const std::string path =
|
||||
fmt::format("{}vulkan", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir));
|
||||
QDir dir{QString::fromStdString(path)};
|
||||
dir.removeRecursively();
|
||||
});
|
||||
};
|
||||
|
||||
void GameList::AddCustomDirPopup(QMenu& context_menu, QModelIndex selected) {
|
||||
|
@ -37,6 +37,7 @@ enum class GameListOpenTarget {
|
||||
TEXTURE_DUMP = 4,
|
||||
TEXTURE_LOAD = 5,
|
||||
MODS = 6,
|
||||
SHADER_CACHE = 7
|
||||
};
|
||||
|
||||
class GameList : public QWidget {
|
||||
|
@ -43,7 +43,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
}
|
||||
|
||||
const std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
const bool is_dir = FileUtil::IsDirectory(physical_name);
|
||||
const bool is_dir = Common::FS::IsDirectory(physical_name);
|
||||
if (!is_dir && HasSupportedFileExtension(physical_name)) {
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
|
||||
if (!loader) {
|
||||
@ -67,7 +67,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
if (!(program_id & ~0x00040000FFFFFFFF)) {
|
||||
std::string update_path = Service::AM::GetTitleContentPath(
|
||||
Service::FS::MediaType::SDMC, program_id | 0x0000000E00000000);
|
||||
if (FileUtil::Exists(update_path)) {
|
||||
if (Common::FS::Exists(update_path)) {
|
||||
std::unique_ptr<Loader::AppLoader> update_loader =
|
||||
Loader::GetLoader(update_path);
|
||||
if (update_loader) {
|
||||
@ -101,7 +101,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
new GameListItemRegion(smdh),
|
||||
new GameListItem(
|
||||
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
|
||||
new GameListItemSize(FileUtil::GetSize(physical_name)),
|
||||
new GameListItemSize(Common::FS::GetSize(physical_name)),
|
||||
},
|
||||
parent_dir);
|
||||
|
||||
@ -113,7 +113,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
|
||||
return true;
|
||||
};
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(nullptr, dir_path, callback);
|
||||
Common::FS::ForeachDirectoryEntry(nullptr, dir_path, callback);
|
||||
}
|
||||
|
||||
void GameListWorker::run() {
|
||||
@ -121,12 +121,12 @@ void GameListWorker::run() {
|
||||
for (UISettings::GameDir& game_dir : game_dirs) {
|
||||
if (game_dir.path == QStringLiteral("INSTALLED")) {
|
||||
QString games_path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) +
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)) +
|
||||
QStringLiteral("Nintendo "
|
||||
"3DS/00000000000000000000000000000000/"
|
||||
"00000000000000000000000000000000/title/00040000");
|
||||
QString demos_path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) +
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)) +
|
||||
QStringLiteral(
|
||||
"Nintendo "
|
||||
"3DS/00000000000000000000000000000000/00000000000000000000000000000000/title/"
|
||||
@ -139,7 +139,7 @@ void GameListWorker::run() {
|
||||
AddFstEntriesToGameList(demos_path.toStdString(), 2, game_list_dir);
|
||||
} else if (game_dir.path == QStringLiteral("SYSTEM")) {
|
||||
QString path =
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) +
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)) +
|
||||
QStringLiteral("00000000000000000000000000000000/title/00040010");
|
||||
watch_list.append(path);
|
||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir);
|
||||
|
@ -138,8 +138,8 @@ static void InitializeLogging() {
|
||||
log_filter.ParseFilterString(Settings::values.log_filter);
|
||||
Log::SetGlobalFilter(log_filter);
|
||||
|
||||
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
|
||||
FileUtil::CreateFullPath(log_dir);
|
||||
const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
|
||||
Common::FS::CreateFullPath(log_dir);
|
||||
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
|
||||
#ifdef _WIN32
|
||||
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());
|
||||
@ -1324,13 +1324,13 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) {
|
||||
switch (target) {
|
||||
case GameListOpenTarget::SAVE_DATA: {
|
||||
open_target = "Save Data";
|
||||
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||
std::string sdmc_dir = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
|
||||
path = FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(sdmc_dir, data_id);
|
||||
break;
|
||||
}
|
||||
case GameListOpenTarget::EXT_DATA: {
|
||||
open_target = "Extra Data";
|
||||
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||
std::string sdmc_dir = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
|
||||
path = FileSys::GetExtDataPathFromId(sdmc_dir, data_id);
|
||||
break;
|
||||
}
|
||||
@ -1340,26 +1340,35 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) {
|
||||
path = Service::AM::GetTitlePath(media_type, data_id) + "content/";
|
||||
break;
|
||||
}
|
||||
case GameListOpenTarget::UPDATE_DATA:
|
||||
case GameListOpenTarget::UPDATE_DATA: {
|
||||
open_target = "Update Data";
|
||||
path = Service::AM::GetTitlePath(Service::FS::MediaType::SDMC, data_id + 0xe00000000) +
|
||||
"content/";
|
||||
break;
|
||||
case GameListOpenTarget::TEXTURE_DUMP:
|
||||
}
|
||||
case GameListOpenTarget::TEXTURE_DUMP: {
|
||||
open_target = "Dumped Textures";
|
||||
path = fmt::format("{}textures/{:016X}/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), data_id);
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), data_id);
|
||||
break;
|
||||
case GameListOpenTarget::TEXTURE_LOAD:
|
||||
}
|
||||
case GameListOpenTarget::TEXTURE_LOAD: {
|
||||
open_target = "Custom Textures";
|
||||
path = fmt::format("{}textures/{:016X}/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), data_id);
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::LoadDir), data_id);
|
||||
break;
|
||||
case GameListOpenTarget::MODS:
|
||||
}
|
||||
case GameListOpenTarget::MODS: {
|
||||
open_target = "Mods";
|
||||
path = fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
path = fmt::format("{}mods/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
|
||||
data_id);
|
||||
break;
|
||||
}
|
||||
case GameListOpenTarget::SHADER_CACHE: {
|
||||
open_target = "Shader Cache";
|
||||
path = Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_ERROR(Frontend, "Unexpected target {}", static_cast<int>(target));
|
||||
return;
|
||||
@ -1401,9 +1410,9 @@ void GMainWindow::OnGameListDumpRomFS(QString game_path, u64 program_id) {
|
||||
dialog->setValue(0);
|
||||
|
||||
const auto base_path = fmt::format(
|
||||
"{}romfs/{:016X}", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
|
||||
"{}romfs/{:016X}", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), program_id);
|
||||
const auto update_path =
|
||||
fmt::format("{}romfs/{:016X}", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
|
||||
fmt::format("{}romfs/{:016X}", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
|
||||
program_id | 0x0004000e00000000);
|
||||
using FutureWatcher = QFutureWatcher<std::pair<Loader::ResultStatus, Loader::ResultStatus>>;
|
||||
auto* future_watcher = new FutureWatcher(this);
|
||||
@ -1434,12 +1443,12 @@ void GMainWindow::OnGameListDumpRomFS(QString game_path, u64 program_id) {
|
||||
void GMainWindow::OnGameListOpenDirectory(const QString& directory) {
|
||||
QString path;
|
||||
if (directory == QStringLiteral("INSTALLED")) {
|
||||
path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
|
||||
path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
|
||||
"Nintendo "
|
||||
"3DS/00000000000000000000000000000000/"
|
||||
"00000000000000000000000000000000/title/00040000");
|
||||
} else if (directory == QStringLiteral("SYSTEM")) {
|
||||
path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
|
||||
"00000000000000000000000000000000/title/00040010");
|
||||
} else {
|
||||
path = directory;
|
||||
@ -1864,7 +1873,7 @@ void GMainWindow::OnRemoveAmiibo() {
|
||||
|
||||
void GMainWindow::OnOpenCitraFolder() {
|
||||
QDesktopServices::openUrl(QUrl::fromLocalFile(
|
||||
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
|
||||
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir))));
|
||||
}
|
||||
|
||||
void GMainWindow::OnToggleFilterBar() {
|
||||
@ -1964,12 +1973,12 @@ void GMainWindow::OnSaveMovie() {
|
||||
void GMainWindow::OnCaptureScreenshot() {
|
||||
OnPauseGame();
|
||||
QString path = UISettings::values.screenshot_path;
|
||||
if (!FileUtil::IsDirectory(path.toStdString())) {
|
||||
if (!FileUtil::CreateFullPath(path.toStdString())) {
|
||||
if (!Common::FS::IsDirectory(path.toStdString())) {
|
||||
if (!Common::FS::CreateFullPath(path.toStdString())) {
|
||||
QMessageBox::information(this, tr("Invalid Screenshot Directory"),
|
||||
tr("Cannot create specified screenshot directory. Screenshot "
|
||||
"path is set back to its default value."));
|
||||
path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir));
|
||||
path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir));
|
||||
path.append(QStringLiteral("screenshots/"));
|
||||
UISettings::values.screenshot_path = path;
|
||||
};
|
||||
@ -2473,7 +2482,7 @@ int main(int argc, char* argv[]) {
|
||||
QCoreApplication::setApplicationName(QStringLiteral("Citra"));
|
||||
|
||||
#ifdef __APPLE__
|
||||
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
|
||||
std::string bin_path = Common::FS::GetBundleDirectory() + DIR_SEP + "..";
|
||||
chdir(bin_path.c_str());
|
||||
#endif
|
||||
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);
|
||||
|
@ -58,6 +58,7 @@ add_library(common STATIC
|
||||
announce_multiplayer_room.h
|
||||
archives.h
|
||||
assert.h
|
||||
atomic_ops.h
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
bit_field.h
|
||||
@ -68,9 +69,20 @@ add_library(common STATIC
|
||||
common_funcs.h
|
||||
common_paths.h
|
||||
common_types.h
|
||||
concepts.h
|
||||
construct.h
|
||||
file_util.cpp
|
||||
file_util.h
|
||||
#file_util.cpp
|
||||
#file_util.h
|
||||
fs/file.cpp
|
||||
fs/file.h
|
||||
fs/fs.cpp
|
||||
fs/fs.h
|
||||
fs/fs_paths.h
|
||||
fs/fs_types.h
|
||||
fs/fs_util.cpp
|
||||
fs/fs_util.h
|
||||
fs/path_util.cpp
|
||||
fs/path_util.h
|
||||
hash.h
|
||||
linear_disk_cache.h
|
||||
logging/backend.cpp
|
||||
@ -128,7 +140,7 @@ if(ARCHITECTURE_x86_64)
|
||||
x64/xbyak_abi.h
|
||||
x64/xbyak_util.h
|
||||
)
|
||||
elseif(ARCHITECTURE_ARM64)
|
||||
elseif(ARCHITECTURE_arm64)
|
||||
target_sources(common
|
||||
PRIVATE
|
||||
aarch64/cpu_detect.cpp
|
||||
|
166
src/common/atomic_ops.h
Normal file
166
src/common/atomic_ops.h
Normal file
@ -0,0 +1,166 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#if _MSC_VER
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#if _MSC_VER
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
|
||||
const u8 result =
|
||||
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
|
||||
const u16 result =
|
||||
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
|
||||
const u32 result =
|
||||
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
|
||||
const u64 result = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer),
|
||||
value, expected);
|
||||
return result == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
|
||||
return _InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
|
||||
value[0],
|
||||
reinterpret_cast<__int64*>(expected.data())) != 0;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
|
||||
u8& actual) {
|
||||
actual =
|
||||
_InterlockedCompareExchange8(reinterpret_cast<volatile char*>(pointer), value, expected);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
|
||||
u16& actual) {
|
||||
actual =
|
||||
_InterlockedCompareExchange16(reinterpret_cast<volatile short*>(pointer), value, expected);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
|
||||
u32& actual) {
|
||||
actual =
|
||||
_InterlockedCompareExchange(reinterpret_cast<volatile long*>(pointer), value, expected);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
|
||||
u64& actual) {
|
||||
actual = _InterlockedCompareExchange64(reinterpret_cast<volatile __int64*>(pointer), value,
|
||||
expected);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
|
||||
u128& actual) {
|
||||
const bool result =
|
||||
_InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), value[1],
|
||||
value[0], reinterpret_cast<__int64*>(expected.data())) != 0;
|
||||
actual = expected;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
|
||||
u128 result{};
|
||||
_InterlockedCompareExchange128(reinterpret_cast<volatile __int64*>(pointer), result[1],
|
||||
result[0], reinterpret_cast<__int64*>(result.data()));
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected) {
|
||||
return __sync_bool_compare_and_swap(pointer, expected, value);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected) {
|
||||
unsigned __int128 value_a;
|
||||
unsigned __int128 expected_a;
|
||||
std::memcpy(&value_a, value.data(), sizeof(u128));
|
||||
std::memcpy(&expected_a, expected.data(), sizeof(u128));
|
||||
return __sync_bool_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u8* pointer, u8 value, u8 expected,
|
||||
u8& actual) {
|
||||
actual = __sync_val_compare_and_swap(pointer, expected, value);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u16* pointer, u16 value, u16 expected,
|
||||
u16& actual) {
|
||||
actual = __sync_val_compare_and_swap(pointer, expected, value);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u32* pointer, u32 value, u32 expected,
|
||||
u32& actual) {
|
||||
actual = __sync_val_compare_and_swap(pointer, expected, value);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u64 value, u64 expected,
|
||||
u64& actual) {
|
||||
actual = __sync_val_compare_and_swap(pointer, expected, value);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool AtomicCompareAndSwap(volatile u64* pointer, u128 value, u128 expected,
|
||||
u128& actual) {
|
||||
unsigned __int128 value_a;
|
||||
unsigned __int128 expected_a;
|
||||
unsigned __int128 actual_a;
|
||||
std::memcpy(&value_a, value.data(), sizeof(u128));
|
||||
std::memcpy(&expected_a, expected.data(), sizeof(u128));
|
||||
actual_a = __sync_val_compare_and_swap((unsigned __int128*)pointer, expected_a, value_a);
|
||||
std::memcpy(actual.data(), &actual_a, sizeof(u128));
|
||||
return actual_a == expected_a;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline u128 AtomicLoad128(volatile u64* pointer) {
|
||||
unsigned __int128 zeros_a = 0;
|
||||
unsigned __int128 result_a =
|
||||
__sync_val_compare_and_swap((unsigned __int128*)pointer, zeros_a, zeros_a);
|
||||
|
||||
u128 result;
|
||||
std::memcpy(result.data(), &result_a, sizeof(u128));
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace Common
|
@ -24,6 +24,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@ -50,6 +51,9 @@ typedef double f64; ///< 64-bit floating point
|
||||
typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space.
|
||||
typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space.
|
||||
|
||||
using u128 = std::array<std::uint64_t, 2>;
|
||||
static_assert(sizeof(u128) == 16, "u128 must be 128 bits wide");
|
||||
|
||||
// An inheritable class to disallow the copy constructor and operator= functions
|
||||
class NonCopyable {
|
||||
protected:
|
||||
|
35
src/common/concepts.h
Normal file
35
src/common/concepts.h
Normal file
@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Check if type satisfies the ContiguousContainer named requirement.
|
||||
template <typename T>
|
||||
concept IsContiguousContainer = std::contiguous_iterator<typename T::iterator>;
|
||||
|
||||
// TODO: Replace with std::derived_from when the <concepts> header
|
||||
// is available on all supported platforms.
|
||||
template <typename Derived, typename Base>
|
||||
concept DerivedFrom = requires {
|
||||
std::is_base_of_v<Base, Derived>;
|
||||
std::is_convertible_v<const volatile Derived*, const volatile Base*>;
|
||||
};
|
||||
|
||||
// TODO: Replace with std::convertible_to when libc++ implements it.
|
||||
template <typename From, typename To>
|
||||
concept ConvertibleTo = std::is_convertible_v<From, To>;
|
||||
|
||||
// No equivalents in the stdlib
|
||||
|
||||
template <typename T>
|
||||
concept IsArithmetic = std::is_arithmetic_v<T>;
|
||||
|
||||
template <typename T>
|
||||
concept IsIntegral = std::is_integral_v<T>;
|
||||
|
||||
} // namespace Common
|
File diff suppressed because it is too large
Load Diff
@ -1,396 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/wrapper.hpp>
|
||||
#include "common/common_types.h"
|
||||
#ifdef _MSC_VER
|
||||
#include "common/string_util.h"
|
||||
#endif
|
||||
|
||||
namespace FileUtil {
|
||||
|
||||
// User paths for GetUserPath
|
||||
enum class UserPath {
|
||||
CacheDir,
|
||||
CheatsDir,
|
||||
ConfigDir,
|
||||
DLLDir,
|
||||
DumpDir,
|
||||
LoadDir,
|
||||
LogDir,
|
||||
NANDDir,
|
||||
RootDir,
|
||||
SDMCDir,
|
||||
ShaderDir,
|
||||
StatesDir,
|
||||
SysDataDir,
|
||||
UserDir,
|
||||
};
|
||||
|
||||
// Replaces install-specific paths with standard placeholders, and back again
|
||||
std::string SerializePath(const std::string& input, bool is_saving);
|
||||
|
||||
// A serializable path string
|
||||
struct Path : public boost::serialization::wrapper_traits<const Path> {
|
||||
std::string& str;
|
||||
|
||||
explicit Path(std::string& _str) : str(_str) {}
|
||||
|
||||
static const Path make(std::string& str) {
|
||||
return Path(str);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void save(Archive& ar, const unsigned int) const {
|
||||
auto s_path = SerializePath(str, true);
|
||||
ar << s_path;
|
||||
}
|
||||
template <class Archive>
|
||||
void load(Archive& ar, const unsigned int) const {
|
||||
ar >> str;
|
||||
str = SerializePath(str, false);
|
||||
}
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER();
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
// FileSystem tree node/
|
||||
struct FSTEntry {
|
||||
bool isDirectory;
|
||||
u64 size; // file length or number of entries from children
|
||||
std::string physicalName; // name on disk
|
||||
std::string virtualName; // name in FST names table
|
||||
std::vector<FSTEntry> children;
|
||||
|
||||
private:
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& isDirectory;
|
||||
ar& size;
|
||||
ar& Path::make(physicalName);
|
||||
ar& Path::make(virtualName);
|
||||
ar& children;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
// Returns true if file filename exists
|
||||
[[nodiscard]] bool Exists(const std::string& filename);
|
||||
|
||||
// Returns true if filename is a directory
|
||||
[[nodiscard]] bool IsDirectory(const std::string& filename);
|
||||
|
||||
// Returns the size of filename (64bit)
|
||||
[[nodiscard]] u64 GetSize(const std::string& filename);
|
||||
|
||||
// Overloaded GetSize, accepts file descriptor
|
||||
[[nodiscard]] u64 GetSize(int fd);
|
||||
|
||||
// Overloaded GetSize, accepts FILE*
|
||||
[[nodiscard]] u64 GetSize(FILE* f);
|
||||
|
||||
// Returns true if successful, or path already exists.
|
||||
bool CreateDir(const std::string& filename);
|
||||
|
||||
// Creates the full path of fullPath returns true on success
|
||||
bool CreateFullPath(const std::string& fullPath);
|
||||
|
||||
// Deletes a given filename, return true on success
|
||||
// Doesn't supports deleting a directory
|
||||
bool Delete(const std::string& filename);
|
||||
|
||||
// Deletes a directory filename, returns true on success
|
||||
bool DeleteDir(const std::string& filename);
|
||||
|
||||
// renames file srcFilename to destFilename, returns true on success
|
||||
bool Rename(const std::string& srcFilename, const std::string& destFilename);
|
||||
|
||||
// copies file srcFilename to destFilename, returns true on success
|
||||
bool Copy(const std::string& srcFilename, const std::string& destFilename);
|
||||
|
||||
// creates an empty file filename, returns true on success
|
||||
bool CreateEmptyFile(const std::string& filename);
|
||||
|
||||
/**
|
||||
* @param num_entries_out to be assigned by the callable with the number of iterated directory
|
||||
* entries, never null
|
||||
* @param directory the path to the enclosing directory
|
||||
* @param virtual_name the entry name, without any preceding directory info
|
||||
* @return whether handling the entry succeeded
|
||||
*/
|
||||
using DirectoryEntryCallable = std::function<bool(
|
||||
u64* num_entries_out, const std::string& directory, const std::string& virtual_name)>;
|
||||
|
||||
/**
|
||||
* Scans a directory, calling the callback for each file/directory contained within.
|
||||
* If the callback returns failure, scanning halts and this function returns failure as well
|
||||
* @param num_entries_out assigned by the function with the number of iterated directory entries,
|
||||
* can be null
|
||||
* @param directory the directory to scan
|
||||
* @param callback The callback which will be called for each entry
|
||||
* @return whether scanning the directory succeeded
|
||||
*/
|
||||
bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory,
|
||||
DirectoryEntryCallable callback);
|
||||
|
||||
/**
|
||||
* Scans the directory tree, storing the results.
|
||||
* @param directory the parent directory to start scanning from
|
||||
* @param parent_entry FSTEntry where the filesystem tree results will be stored.
|
||||
* @param recursion Number of children directories to read before giving up.
|
||||
* @return the total number of files/directories found
|
||||
*/
|
||||
u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
|
||||
unsigned int recursion = 0);
|
||||
|
||||
/**
|
||||
* Recursively searches through a FSTEntry for files, and stores them.
|
||||
* @param directory The FSTEntry to start scanning from
|
||||
* @param parent_entry FSTEntry vector where the results will be stored.
|
||||
*/
|
||||
void GetAllFilesFromNestedEntries(FSTEntry& directory, std::vector<FSTEntry>& output);
|
||||
|
||||
// deletes the given directory and anything under it. Returns true on success.
|
||||
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256);
|
||||
|
||||
// Returns the current directory
|
||||
[[nodiscard]] std::optional<std::string> GetCurrentDir();
|
||||
|
||||
// Create directory and copy contents (does not overwrite existing files)
|
||||
void CopyDir(const std::string& source_path, const std::string& dest_path);
|
||||
|
||||
// Set the current directory to given directory
|
||||
bool SetCurrentDir(const std::string& directory);
|
||||
|
||||
void SetUserPath(const std::string& path = "");
|
||||
|
||||
void SetCurrentRomPath(const std::string& path);
|
||||
|
||||
// Returns a pointer to a string with a Citra data dir in the user's home
|
||||
// directory. To be used in "multi-user" mode (that is, installed).
|
||||
[[nodiscard]] const std::string& GetUserPath(UserPath path);
|
||||
|
||||
// Returns a pointer to a string with the default Citra data dir in the user's home
|
||||
// directory.
|
||||
[[nodiscard]] const std::string& GetDefaultUserPath(UserPath path);
|
||||
|
||||
// Update the Global Path with the new value
|
||||
const void UpdateUserPath(UserPath path, const std::string& filename);
|
||||
|
||||
// Returns the path to where the sys file are
|
||||
[[nodiscard]] std::string GetSysDirectory();
|
||||
|
||||
#ifdef __APPLE__
|
||||
[[nodiscard]] std::string GetBundleDirectory();
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
[[nodiscard]] const std::string& GetExeDirectory();
|
||||
[[nodiscard]] std::string AppDataRoamingDirectory();
|
||||
#endif
|
||||
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str);
|
||||
|
||||
std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str);
|
||||
|
||||
/**
|
||||
* Splits the filename into 8.3 format
|
||||
* Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename
|
||||
* @param filename The normal filename to use
|
||||
* @param short_name A 9-char array in which the short name will be written
|
||||
* @param extension A 4-char array in which the extension will be written
|
||||
*/
|
||||
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
|
||||
std::array<char, 4>& extension);
|
||||
|
||||
// Splits the path on '/' or '\' and put the components into a vector
|
||||
// i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" }
|
||||
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
|
||||
|
||||
// Gets all of the text up to the last '/' or '\' in the path.
|
||||
[[nodiscard]] std::string_view GetParentPath(std::string_view path);
|
||||
|
||||
// Gets all of the text after the first '/' or '\' in the path.
|
||||
[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
|
||||
|
||||
// Gets the filename of the path
|
||||
[[nodiscard]] std::string_view GetFilename(std::string_view path);
|
||||
|
||||
// Gets the extension of the filename
|
||||
[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
|
||||
|
||||
// Removes the final '/' or '\' if one exists
|
||||
[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
|
||||
|
||||
// Creates a new vector containing indices [first, last) from the original.
|
||||
template <typename T>
|
||||
[[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first,
|
||||
std::size_t last) {
|
||||
if (first >= last) {
|
||||
return {};
|
||||
}
|
||||
last = std::min<std::size_t>(last, vector.size());
|
||||
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
|
||||
}
|
||||
|
||||
enum class DirectorySeparator {
|
||||
ForwardSlash,
|
||||
BackwardSlash,
|
||||
PlatformDefault,
|
||||
};
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
||||
[[nodiscard]] std::string SanitizePath(
|
||||
std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
|
||||
// simple wrapper for cstdlib file functions to
|
||||
// hopefully will make error checking easier
|
||||
// and make forgetting an fclose() harder
|
||||
class IOFile : public NonCopyable {
|
||||
public:
|
||||
IOFile();
|
||||
|
||||
// flags is used for windows specific file open mode flags, which
|
||||
// allows citra to open the logs in shared write mode, so that the file
|
||||
// isn't considered "locked" while citra is open and people can open the log file and view it
|
||||
IOFile(const std::string& filename, const char openmode[], int flags = 0);
|
||||
|
||||
~IOFile();
|
||||
|
||||
IOFile(IOFile&& other) noexcept;
|
||||
IOFile& operator=(IOFile&& other) noexcept;
|
||||
|
||||
void Swap(IOFile& other) noexcept;
|
||||
|
||||
bool Close();
|
||||
|
||||
template <typename T>
|
||||
std::size_t ReadArray(T* data, std::size_t length) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
|
||||
std::size_t items_read = ReadImpl(data, length, sizeof(T));
|
||||
if (items_read != length)
|
||||
m_good = false;
|
||||
|
||||
return items_read;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t WriteArray(const T* data, std::size_t length) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
|
||||
std::size_t items_written = WriteImpl(data, length, sizeof(T));
|
||||
if (items_written != length)
|
||||
m_good = false;
|
||||
|
||||
return items_written;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t ReadBytes(T* data, std::size_t length) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return ReadArray(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t WriteBytes(const T* data, std::size_t length) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return WriteArray(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::size_t WriteObject(const T& object) {
|
||||
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
std::size_t WriteString(std::string_view str) {
|
||||
return WriteArray(str.data(), str.length());
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsOpen() const {
|
||||
return nullptr != m_file;
|
||||
}
|
||||
|
||||
// m_good is set to false when a read, write or other function fails
|
||||
[[nodiscard]] bool IsGood() const {
|
||||
return m_good;
|
||||
}
|
||||
[[nodiscard]] explicit operator bool() const {
|
||||
return IsGood();
|
||||
}
|
||||
|
||||
bool Seek(s64 off, int origin);
|
||||
[[nodiscard]] u64 Tell() const;
|
||||
[[nodiscard]] u64 GetSize() const;
|
||||
bool Resize(u64 size);
|
||||
bool Flush();
|
||||
|
||||
// clear error state
|
||||
void Clear() {
|
||||
m_good = true;
|
||||
std::clearerr(m_file);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size);
|
||||
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
|
||||
|
||||
bool Open();
|
||||
|
||||
std::FILE* m_file = nullptr;
|
||||
bool m_good = true;
|
||||
|
||||
std::string filename;
|
||||
std::string openmode;
|
||||
u32 flags;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& Path::make(filename);
|
||||
ar& openmode;
|
||||
ar& flags;
|
||||
u64 pos;
|
||||
if (Archive::is_saving::value) {
|
||||
pos = Tell();
|
||||
}
|
||||
ar& pos;
|
||||
if (Archive::is_loading::value) {
|
||||
Open();
|
||||
Seek(pos, SEEK_SET);
|
||||
}
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace FileUtil
|
||||
|
||||
// To deal with Windows being dumb at unicode:
|
||||
template <typename T>
|
||||
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
|
||||
#ifdef _MSC_VER
|
||||
fstream.open(Common::UTF8ToUTF16W(filename), openmode);
|
||||
#else
|
||||
fstream.open(filename, openmode);
|
||||
#endif
|
||||
}
|
415
src/common/fs/file.cpp
Normal file
415
src/common/fs/file.cpp
Normal file
@ -0,0 +1,415 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <share.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define fileno _fileno
|
||||
#define fseeko _fseeki64
|
||||
#define ftello _ftelli64
|
||||
#endif
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
/**
|
||||
* Converts the file access mode and file type enums to a file access mode wide string.
|
||||
*
|
||||
* @param mode File access mode
|
||||
* @param type File type
|
||||
*
|
||||
* @returns A pointer to a wide string representing the file access mode.
|
||||
*/
|
||||
[[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileType type) {
|
||||
switch (type) {
|
||||
case FileType::BinaryFile:
|
||||
switch (mode) {
|
||||
case FileAccessMode::Read:
|
||||
return L"rb";
|
||||
case FileAccessMode::Write:
|
||||
return L"wb";
|
||||
case FileAccessMode::Append:
|
||||
return L"ab";
|
||||
case FileAccessMode::ReadWrite:
|
||||
return L"r+b";
|
||||
case FileAccessMode::ReadAppend:
|
||||
return L"a+b";
|
||||
}
|
||||
break;
|
||||
case FileType::TextFile:
|
||||
switch (mode) {
|
||||
case FileAccessMode::Read:
|
||||
return L"r";
|
||||
case FileAccessMode::Write:
|
||||
return L"w";
|
||||
case FileAccessMode::Append:
|
||||
return L"a";
|
||||
case FileAccessMode::ReadWrite:
|
||||
return L"r+";
|
||||
case FileAccessMode::ReadAppend:
|
||||
return L"a+";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the file-share access flag enum to a Windows defined file-share access flag.
|
||||
*
|
||||
* @param flag File-share access flag
|
||||
*
|
||||
* @returns Windows defined file-share access flag.
|
||||
*/
|
||||
[[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) {
|
||||
switch (flag) {
|
||||
case FileShareFlag::ShareNone:
|
||||
default:
|
||||
return _SH_DENYRW;
|
||||
case FileShareFlag::ShareReadOnly:
|
||||
return _SH_DENYWR;
|
||||
case FileShareFlag::ShareWriteOnly:
|
||||
return _SH_DENYRD;
|
||||
case FileShareFlag::ShareReadWrite:
|
||||
return _SH_DENYNO;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* Converts the file access mode and file type enums to a file access mode string.
|
||||
*
|
||||
* @param mode File access mode
|
||||
* @param type File type
|
||||
*
|
||||
* @returns A pointer to a string representing the file access mode.
|
||||
*/
|
||||
[[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileType type) {
|
||||
switch (type) {
|
||||
case FileType::BinaryFile:
|
||||
switch (mode) {
|
||||
case FileAccessMode::Read:
|
||||
return "rb";
|
||||
case FileAccessMode::Write:
|
||||
return "wb";
|
||||
case FileAccessMode::Append:
|
||||
return "ab";
|
||||
case FileAccessMode::ReadWrite:
|
||||
return "r+b";
|
||||
case FileAccessMode::ReadAppend:
|
||||
return "a+b";
|
||||
}
|
||||
break;
|
||||
case FileType::TextFile:
|
||||
switch (mode) {
|
||||
case FileAccessMode::Read:
|
||||
return "r";
|
||||
case FileAccessMode::Write:
|
||||
return "w";
|
||||
case FileAccessMode::Append:
|
||||
return "a";
|
||||
case FileAccessMode::ReadWrite:
|
||||
return "r+";
|
||||
case FileAccessMode::ReadAppend:
|
||||
return "a+";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Converts the seek origin enum to a seek origin integer.
|
||||
*
|
||||
* @param origin Seek origin
|
||||
*
|
||||
* @returns Seek origin integer.
|
||||
*/
|
||||
[[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) {
|
||||
switch (origin) {
|
||||
case SeekOrigin::SetOrigin:
|
||||
default:
|
||||
return SEEK_SET;
|
||||
case SeekOrigin::CurrentPosition:
|
||||
return SEEK_CUR;
|
||||
case SeekOrigin::End:
|
||||
return SEEK_END;
|
||||
}
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string ReadStringFromFile(const std::filesystem::path& path, FileType type) {
|
||||
if (!IsFile(path)) {
|
||||
return "";
|
||||
}
|
||||
|
||||
IOFile io_file{path, FileAccessMode::Read, type};
|
||||
|
||||
return io_file.ReadString(io_file.GetSize());
|
||||
}
|
||||
|
||||
size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
|
||||
std::string_view string) {
|
||||
if (Exists(path) && !IsFile(path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IOFile io_file{path, FileAccessMode::Write, type};
|
||||
|
||||
return io_file.WriteString(string);
|
||||
}
|
||||
|
||||
size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
|
||||
std::string_view string) {
|
||||
if (Exists(path) && !IsFile(path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
IOFile io_file{path, FileAccessMode::Append, type};
|
||||
|
||||
return io_file.WriteString(string);
|
||||
}
|
||||
|
||||
IOFile::IOFile() = default;
|
||||
|
||||
IOFile::IOFile(const std::string& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
|
||||
Open(path, mode, type, flag);
|
||||
}
|
||||
|
||||
IOFile::IOFile(std::string_view path, FileAccessMode mode, FileType type, FileShareFlag flag) {
|
||||
Open(path, mode, type, flag);
|
||||
}
|
||||
|
||||
IOFile::IOFile(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
|
||||
Open(path, mode, type, flag);
|
||||
}
|
||||
|
||||
IOFile::~IOFile() {
|
||||
Close();
|
||||
}
|
||||
|
||||
IOFile::IOFile(IOFile&& other) noexcept {
|
||||
std::swap(file_path, other.file_path);
|
||||
std::swap(file_access_mode, other.file_access_mode);
|
||||
std::swap(file_type, other.file_type);
|
||||
std::swap(file, other.file);
|
||||
}
|
||||
|
||||
IOFile& IOFile::operator=(IOFile&& other) noexcept {
|
||||
std::swap(file_path, other.file_path);
|
||||
std::swap(file_access_mode, other.file_access_mode);
|
||||
std::swap(file_type, other.file_type);
|
||||
std::swap(file, other.file);
|
||||
return *this;
|
||||
}
|
||||
|
||||
fs::path IOFile::GetPath() const {
|
||||
return file_path;
|
||||
}
|
||||
|
||||
FileAccessMode IOFile::GetAccessMode() const {
|
||||
return file_access_mode;
|
||||
}
|
||||
|
||||
FileType IOFile::GetType() const {
|
||||
return file_type;
|
||||
}
|
||||
|
||||
void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
|
||||
Close();
|
||||
|
||||
file_path = path;
|
||||
file_access_mode = mode;
|
||||
file_type = type;
|
||||
|
||||
errno = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (flag != FileShareFlag::ShareNone) {
|
||||
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
|
||||
} else {
|
||||
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
|
||||
}
|
||||
#else
|
||||
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
|
||||
#endif
|
||||
|
||||
if (!IsOpen()) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
}
|
||||
}
|
||||
|
||||
void IOFile::Close() {
|
||||
if (!IsOpen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
const auto close_result = std::fclose(file) == 0;
|
||||
|
||||
if (!close_result) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
}
|
||||
|
||||
file = nullptr;
|
||||
}
|
||||
|
||||
bool IOFile::IsOpen() const {
|
||||
return file != nullptr;
|
||||
}
|
||||
|
||||
std::string IOFile::ReadString(size_t length) const {
|
||||
std::vector<char> string_buffer(length);
|
||||
|
||||
const auto chars_read = ReadSpan<char>(string_buffer);
|
||||
const auto string_size = chars_read != length ? chars_read : length;
|
||||
|
||||
return std::string{string_buffer.data(), string_size};
|
||||
}
|
||||
|
||||
size_t IOFile::WriteString(std::span<const char> string) const {
|
||||
return WriteSpan(string);
|
||||
}
|
||||
|
||||
bool IOFile::Flush() const {
|
||||
if (!IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto flush_result = std::fflush(file) == 0;
|
||||
#else
|
||||
const auto flush_result = std::fflush(file) == 0;
|
||||
#endif
|
||||
|
||||
if (!flush_result) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
}
|
||||
|
||||
return flush_result;
|
||||
}
|
||||
|
||||
bool IOFile::Commit() const {
|
||||
if (!IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
|
||||
#else
|
||||
const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
|
||||
#endif
|
||||
|
||||
if (!commit_result) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
}
|
||||
|
||||
return commit_result;
|
||||
}
|
||||
|
||||
bool IOFile::SetSize(u64 size) const {
|
||||
if (!IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
#ifdef _WIN32
|
||||
const auto set_size_result = _chsize_s(fileno(file), static_cast<s64>(size)) == 0;
|
||||
#else
|
||||
const auto set_size_result = ftruncate(fileno(file), static_cast<s64>(size)) == 0;
|
||||
#endif
|
||||
|
||||
if (!set_size_result) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
|
||||
PathToUTF8String(file_path), size, ec.message());
|
||||
}
|
||||
|
||||
return set_size_result;
|
||||
}
|
||||
|
||||
u64 IOFile::GetSize() const {
|
||||
if (!IsOpen()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Flush any unwritten buffered data into the file prior to retrieving the file size.
|
||||
std::fflush(file);
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
const auto file_size = fs::file_size(file_path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
|
||||
PathToUTF8String(file_path), ec.message());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
|
||||
if (!IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
|
||||
|
||||
if (!seek_result) {
|
||||
const auto ec = std::error_code{errno, std::generic_category()};
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
|
||||
PathToUTF8String(file_path), offset, origin, ec.message());
|
||||
}
|
||||
|
||||
return seek_result;
|
||||
}
|
||||
|
||||
s64 IOFile::Tell() const {
|
||||
if (!IsOpen()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
return ftello(file);
|
||||
}
|
||||
|
||||
} // namespace Common::FS
|
459
src/common/fs/file.h
Normal file
459
src/common/fs/file.h
Normal file
@ -0,0 +1,459 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
|
||||
#include "common/concepts.h"
|
||||
#include "common/fs/fs_types.h"
|
||||
#include "common/fs/fs_util.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
enum class SeekOrigin {
|
||||
SetOrigin, // Seeks from the start of the file.
|
||||
CurrentPosition, // Seeks from the current file pointer position.
|
||||
End, // Seeks from the end of the file.
|
||||
};
|
||||
|
||||
/**
|
||||
* Opens a file stream at path with the specified open mode.
|
||||
*
|
||||
* @param file_stream Reference to file stream
|
||||
* @param path Filesystem path
|
||||
* @param open_mode File stream open mode
|
||||
*/
|
||||
template <typename FileStream>
|
||||
void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
|
||||
std::ios_base::openmode open_mode) {
|
||||
file_stream.open(path, open_mode);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename FileStream, typename Path>
|
||||
void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
file_stream.open(ToU8String(path), open_mode);
|
||||
} else {
|
||||
file_stream.open(std::filesystem::path{path}, open_mode);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Reads an entire file at path and returns a string of the contents read from the file.
|
||||
* If the filesystem object at path is not a regular file, this function returns an empty string.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param type File type
|
||||
*
|
||||
* @returns A string of the contents read from the file.
|
||||
*/
|
||||
[[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] std::string ReadStringFromFile(const Path& path, FileType type) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return ReadStringFromFile(ToU8String(path), type);
|
||||
} else {
|
||||
return ReadStringFromFile(std::filesystem::path{path}, type);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Writes a string to a file at path and returns the number of characters successfully written.
|
||||
* If a file already exists at path, its contents will be erased.
|
||||
* If a file does not exist at path, it creates and opens a new empty file for writing.
|
||||
* If the filesystem object at path exists and is not a regular file, this function returns 0.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param type File type
|
||||
*
|
||||
* @returns Number of characters successfully written.
|
||||
*/
|
||||
[[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
|
||||
std::string_view string);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] size_t WriteStringToFile(const Path& path, FileType type, std::string_view string) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return WriteStringToFile(ToU8String(path), type, string);
|
||||
} else {
|
||||
return WriteStringToFile(std::filesystem::path{path}, type, string);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Appends a string to a file at path and returns the number of characters successfully written.
|
||||
* If a file does not exist at path, it creates and opens a new empty file for appending.
|
||||
* If the filesystem object at path exists and is not a regular file, this function returns 0.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param type File type
|
||||
*
|
||||
* @returns Number of characters successfully written.
|
||||
*/
|
||||
[[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
|
||||
std::string_view string);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] size_t AppendStringToFile(const Path& path, FileType type, std::string_view string) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return AppendStringToFile(ToU8String(path), type, string);
|
||||
} else {
|
||||
return AppendStringToFile(std::filesystem::path{path}, type, string);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
class IOFile final {
|
||||
public:
|
||||
IOFile();
|
||||
|
||||
explicit IOFile(const std::string& path, FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
||||
|
||||
explicit IOFile(std::string_view path, FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
||||
|
||||
/**
|
||||
* An IOFile is a lightweight wrapper on C Library file operations.
|
||||
* Automatically closes an open file on the destruction of an IOFile object.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param mode File access mode
|
||||
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
|
||||
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
|
||||
*/
|
||||
explicit IOFile(const std::filesystem::path& path, FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
||||
|
||||
~IOFile();
|
||||
|
||||
IOFile(const IOFile&) = delete;
|
||||
IOFile& operator=(const IOFile&) = delete;
|
||||
|
||||
IOFile(IOFile&& other) noexcept;
|
||||
IOFile& operator=(IOFile&& other) noexcept;
|
||||
|
||||
/**
|
||||
* Gets the path of the file.
|
||||
*
|
||||
* @returns The path of the file.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path GetPath() const;
|
||||
|
||||
/**
|
||||
* Gets the access mode of the file.
|
||||
*
|
||||
* @returns The access mode of the file.
|
||||
*/
|
||||
[[nodiscard]] FileAccessMode GetAccessMode() const;
|
||||
|
||||
/**
|
||||
* Gets the type of the file.
|
||||
*
|
||||
* @returns The type of the file.
|
||||
*/
|
||||
[[nodiscard]] FileType GetType() const;
|
||||
|
||||
/**
|
||||
* Opens a file at path with the specified file access mode.
|
||||
* This function behaves differently depending on the FileAccessMode.
|
||||
* These behaviors are documented in each enum value of FileAccessMode.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param mode File access mode
|
||||
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
|
||||
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
|
||||
*/
|
||||
void Open(const std::filesystem::path& path, FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
|
||||
using ValueType = typename Path::value_type;
|
||||
if constexpr (IsChar<ValueType>) {
|
||||
Open(ToU8String(path), mode, type, flag);
|
||||
} else {
|
||||
Open(std::filesystem::path{path}, mode, type, flag);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Closes the file if it is opened.
|
||||
void Close();
|
||||
|
||||
/**
|
||||
* Checks whether the file is open.
|
||||
* Use this to check whether the calls to Open() or Close() succeeded.
|
||||
*
|
||||
* @returns True if the file is open, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool IsOpen() const;
|
||||
|
||||
/**
|
||||
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
|
||||
* If T is not a contiguous container as defined by the concept IsContiguousContainer, this
|
||||
* calls ReadObject and T must be a trivially copyable object.
|
||||
*
|
||||
* See ReadSpan for more details if T is a contiguous container.
|
||||
* See ReadObject for more details if T is a trivially copyable object.
|
||||
*
|
||||
* @tparam T Contiguous container or trivially copyable object
|
||||
*
|
||||
* @param data Container of T::value_type data or reference to object
|
||||
*
|
||||
* @returns Count of T::value_type data or objects successfully read.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t Read(T& data) const {
|
||||
if constexpr (IsContiguousContainer<T>) {
|
||||
using ContiguousType = typename T::value_type;
|
||||
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
||||
"Data type must be trivially copyable.");
|
||||
return ReadSpan<ContiguousType>(data);
|
||||
} else {
|
||||
return ReadObject(data) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
|
||||
* If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
|
||||
* calls WriteObject and T must be a trivially copyable object.
|
||||
*
|
||||
* See WriteSpan for more details if T is a contiguous container.
|
||||
* See WriteObject for more details if T is a trivially copyable object.
|
||||
*
|
||||
* @tparam T Contiguous container or trivially copyable object
|
||||
*
|
||||
* @param data Container of T::value_type data or const reference to object
|
||||
*
|
||||
* @returns Count of T::value_type data or objects successfully written.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t Write(const T& data) const {
|
||||
if constexpr (IsContiguousContainer<T>) {
|
||||
using ContiguousType = typename T::value_type;
|
||||
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
||||
"Data type must be trivially copyable.");
|
||||
return WriteSpan<ContiguousType>(data);
|
||||
} else {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
return WriteObject(data) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a span of T data from a file sequentially.
|
||||
* This function reads from the current position of the file pointer and
|
||||
* advances it by the (count of T * sizeof(T)) bytes successfully read.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - The file is not open
|
||||
* - The opened file lacks read permissions
|
||||
* - Attempting to read beyond the end-of-file
|
||||
*
|
||||
* @tparam T Data type
|
||||
*
|
||||
* @param data Span of T data
|
||||
*
|
||||
* @returns Count of T data successfully read.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t ReadSpan(std::span<T> data) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::fread(data.data(), sizeof(T), data.size(), file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a span of T data to a file sequentially.
|
||||
* This function writes from the current position of the file pointer and
|
||||
* advances it by the (count of T * sizeof(T)) bytes successfully written.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - The file is not open
|
||||
* - The opened file lacks write permissions
|
||||
*
|
||||
* @tparam T Data type
|
||||
*
|
||||
* @param data Span of T data
|
||||
*
|
||||
* @returns Count of T data successfully written.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return std::fwrite(data.data(), sizeof(T), data.size(), file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a T object from a file sequentially.
|
||||
* This function reads from the current position of the file pointer and
|
||||
* advances it by the sizeof(T) bytes successfully read.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - The file is not open
|
||||
* - The opened file lacks read permissions
|
||||
* - Attempting to read beyond the end-of-file
|
||||
*
|
||||
* @tparam T Data type
|
||||
*
|
||||
* @param object Reference to object
|
||||
*
|
||||
* @returns True if the object is successfully read from the file, false otherwise.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] bool ReadObject(T& object) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::fread(&object, sizeof(T), 1, file) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a T object to a file sequentially.
|
||||
* This function writes from the current position of the file pointer and
|
||||
* advances it by the sizeof(T) bytes successfully written.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - The file is not open
|
||||
* - The opened file lacks write permissions
|
||||
*
|
||||
* @tparam T Data type
|
||||
*
|
||||
* @param object Const reference to object
|
||||
*
|
||||
* @returns True if the object is successfully written to the file, false otherwise.
|
||||
*/
|
||||
template <typename T>
|
||||
[[nodiscard]] bool WriteObject(const T& object) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
|
||||
|
||||
if (!IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return std::fwrite(&object, sizeof(T), 1, file) == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized function to read a string of a given length from a file sequentially.
|
||||
* This function writes from the current position of the file pointer and
|
||||
* advances it by the number of characters successfully read.
|
||||
* The size of the returned string may not match length if not all bytes are successfully read.
|
||||
*
|
||||
* @param length Length of the string
|
||||
*
|
||||
* @returns A string read from the file.
|
||||
*/
|
||||
[[nodiscard]] std::string ReadString(size_t length) const;
|
||||
|
||||
/**
|
||||
* Specialized function to write a string to a file sequentially.
|
||||
* This function writes from the current position of the file pointer and
|
||||
* advances it by the number of characters successfully written.
|
||||
*
|
||||
* @param string Span of const char backed std::string or std::string_view
|
||||
*
|
||||
* @returns Number of characters successfully written.
|
||||
*/
|
||||
[[nodiscard]] size_t WriteString(std::span<const char> string) const;
|
||||
|
||||
/**
|
||||
* Attempts to flush any unwritten buffered data into the file.
|
||||
*
|
||||
* @returns True if the flush was successful, false otherwise.
|
||||
*/
|
||||
bool Flush() const;
|
||||
|
||||
/**
|
||||
* Attempts to commit the file into the disk.
|
||||
* Note that this is an expensive operation as this forces the operating system to write
|
||||
* the contents of the file associated with the file descriptor into the disk.
|
||||
*
|
||||
* @returns True if the commit was successful, false otherwise.
|
||||
*/
|
||||
bool Commit() const;
|
||||
|
||||
/**
|
||||
* Resizes the file to a given size.
|
||||
* If the file is resized to a smaller size, the remainder of the file is discarded.
|
||||
* If the file is resized to a larger size, the new area appears as if zero-filled.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - The file is not open
|
||||
*
|
||||
* @param size File size in bytes
|
||||
*
|
||||
* @returns True if the file resize succeeded, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool SetSize(u64 size) const;
|
||||
|
||||
/**
|
||||
* Gets the size of the file.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - The file is not open
|
||||
*
|
||||
* @returns The file size in bytes of the file. Returns 0 on failure.
|
||||
*/
|
||||
[[nodiscard]] u64 GetSize() const;
|
||||
|
||||
/**
|
||||
* Moves the current position of the file pointer with the specified offset and seek origin.
|
||||
*
|
||||
* @param offset Offset from seek origin
|
||||
* @param origin Seek origin
|
||||
*
|
||||
* @returns True if the file pointer has moved to the specified offset, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const;
|
||||
|
||||
/**
|
||||
* Gets the current position of the file pointer.
|
||||
*
|
||||
* @returns The current position of the file pointer.
|
||||
*/
|
||||
[[nodiscard]] s64 Tell() const;
|
||||
|
||||
private:
|
||||
std::filesystem::path file_path;
|
||||
FileAccessMode file_access_mode{};
|
||||
FileType file_type{};
|
||||
|
||||
std::FILE* file = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Common::FS
|
624
src/common/fs/fs.cpp
Normal file
624
src/common/fs/fs.cpp
Normal file
@ -0,0 +1,624 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/fs/file.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
// File Operations
|
||||
|
||||
bool NewFile(const fs::path& path, u64 size) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(path.parent_path())) {
|
||||
LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Exists(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} exists", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
IOFile io_file{path, FileAccessMode::Write};
|
||||
|
||||
if (!io_file.IsOpen()) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create a file at path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!io_file.SetSize(size)) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={} to size={}",
|
||||
PathToUTF8String(path), size);
|
||||
return false;
|
||||
}
|
||||
|
||||
io_file.Close();
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully created a file at path={} with size={}",
|
||||
PathToUTF8String(path), size);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveFile(const fs::path& path) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(path)) {
|
||||
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!IsFile(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file",
|
||||
PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
fs::remove(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to remove the file at path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully removed the file at path={}",
|
||||
PathToUTF8String(path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenameFile(const fs::path& old_path, const fs::path& new_path) {
|
||||
if (!ValidatePath(old_path) || !ValidatePath(new_path)) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"One or both input path(s) is not valid, old_path={}, new_path={}",
|
||||
PathToUTF8String(old_path), PathToUTF8String(new_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(old_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist",
|
||||
PathToUTF8String(old_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsFile(old_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a file",
|
||||
PathToUTF8String(old_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Exists(new_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists",
|
||||
PathToUTF8String(new_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
fs::rename(old_path, new_path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to rename the file from old_path={} to new_path={}, ec_message={}",
|
||||
PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}",
|
||||
PathToUTF8String(old_path), PathToUTF8String(new_path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<IOFile> FileOpen(const fs::path& path, FileAccessMode mode, FileType type,
|
||||
FileShareFlag flag) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Exists(path) && !IsFile(path)) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Filesystem object at path={} exists and is not a regular file",
|
||||
PathToUTF8String(path));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto io_file = std::make_shared<IOFile>(path, mode, type, flag);
|
||||
|
||||
if (!io_file->IsOpen()) {
|
||||
io_file.reset();
|
||||
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to open the file at path={} with mode={}, type={}, flag={}",
|
||||
PathToUTF8String(path), mode, type, flag);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem,
|
||||
"Successfully opened the file at path={} with mode={}, type={}, flag={}",
|
||||
PathToUTF8String(path), mode, type, flag);
|
||||
|
||||
return io_file;
|
||||
}
|
||||
|
||||
// Directory Operations
|
||||
|
||||
bool CreateDir(const fs::path& path) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(path.parent_path())) {
|
||||
LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsDir(path)) {
|
||||
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
|
||||
PathToUTF8String(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
fs::create_directory(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create the directory at path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully created the directory at path={}",
|
||||
PathToUTF8String(path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateDirs(const fs::path& path) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsDir(path)) {
|
||||
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory",
|
||||
PathToUTF8String(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
fs::create_directories(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to create the directories at path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully created the directories at path={}",
|
||||
PathToUTF8String(path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateParentDir(const fs::path& path) {
|
||||
return CreateDir(path.parent_path());
|
||||
}
|
||||
|
||||
bool CreateParentDirs(const fs::path& path) {
|
||||
return CreateDirs(path.parent_path());
|
||||
}
|
||||
|
||||
bool RemoveDir(const fs::path& path) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(path)) {
|
||||
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!IsDir(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
|
||||
PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
fs::remove(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to remove the directory at path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully removed the directory at path={}",
|
||||
PathToUTF8String(path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveDirRecursively(const fs::path& path) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(path)) {
|
||||
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!IsDir(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
|
||||
PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
fs::remove_all(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to remove the directory and its contents at path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully removed the directory and its contents at path={}",
|
||||
PathToUTF8String(path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoveDirContentsRecursively(const fs::path& path) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(path)) {
|
||||
LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!IsDir(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
|
||||
PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
|
||||
for (const auto& entry : fs::directory_iterator(path, ec)) {
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to completely enumerate the directory at path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
break;
|
||||
}
|
||||
|
||||
fs::remove(entry.path(), ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to remove the filesystem object at path={}, ec_message={}",
|
||||
PathToUTF8String(entry.path()), ec.message());
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
|
||||
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
|
||||
if (entry.status().type() == fs::file_type::directory) {
|
||||
return RemoveDirContentsRecursively(entry.path());
|
||||
}
|
||||
}
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to remove all the contents of the directory at path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem,
|
||||
"Successfully removed all the contents of the directory at path={}",
|
||||
PathToUTF8String(path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RenameDir(const fs::path& old_path, const fs::path& new_path) {
|
||||
if (!ValidatePath(old_path) || !ValidatePath(new_path)) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"One or both input path(s) is not valid, old_path={}, new_path={}",
|
||||
PathToUTF8String(old_path), PathToUTF8String(new_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Exists(old_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist",
|
||||
PathToUTF8String(old_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsDir(old_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a directory",
|
||||
PathToUTF8String(old_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Exists(new_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists",
|
||||
PathToUTF8String(new_path));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
fs::rename(old_path, new_path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to rename the file from old_path={} to new_path={}, ec_message={}",
|
||||
PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}",
|
||||
PathToUTF8String(old_path), PathToUTF8String(new_path));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback,
|
||||
DirEntryFilter filter) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Exists(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDir(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
|
||||
PathToUTF8String(path));
|
||||
return;
|
||||
}
|
||||
|
||||
bool callback_error = false;
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
for (const auto& entry : fs::directory_iterator(path, ec)) {
|
||||
if (ec) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (True(filter & DirEntryFilter::File) &&
|
||||
entry.status().type() == fs::file_type::regular) {
|
||||
if (!callback(entry.path())) {
|
||||
callback_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (True(filter & DirEntryFilter::Directory) &&
|
||||
entry.status().type() == fs::file_type::directory) {
|
||||
if (!callback(entry.path())) {
|
||||
callback_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (callback_error || ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to visit all the directory entries of path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}",
|
||||
PathToUTF8String(path));
|
||||
}
|
||||
|
||||
void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
||||
const DirEntryCallable& callback, DirEntryFilter filter) {
|
||||
if (!ValidatePath(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Exists(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist",
|
||||
PathToUTF8String(path));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDir(path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory",
|
||||
PathToUTF8String(path));
|
||||
return;
|
||||
}
|
||||
|
||||
bool callback_error = false;
|
||||
|
||||
std::error_code ec;
|
||||
|
||||
// TODO (Morph): Replace this with recursive_directory_iterator once it's fixed in MSVC.
|
||||
for (const auto& entry : fs::directory_iterator(path, ec)) {
|
||||
if (ec) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (True(filter & DirEntryFilter::File) &&
|
||||
entry.status().type() == fs::file_type::regular) {
|
||||
if (!callback(entry.path())) {
|
||||
callback_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (True(filter & DirEntryFilter::Directory) &&
|
||||
entry.status().type() == fs::file_type::directory) {
|
||||
if (!callback(entry.path())) {
|
||||
callback_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO (Morph): Remove this when MSVC fixes recursive_directory_iterator.
|
||||
// recursive_directory_iterator throws an exception despite passing in a std::error_code.
|
||||
if (entry.status().type() == fs::file_type::directory) {
|
||||
IterateDirEntriesRecursively(entry.path(), callback, filter);
|
||||
}
|
||||
}
|
||||
|
||||
if (callback_error || ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to visit all the directory entries of path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}",
|
||||
PathToUTF8String(path));
|
||||
}
|
||||
|
||||
// Generic Filesystem Operations
|
||||
|
||||
bool Exists(const fs::path& path) {
|
||||
return fs::exists(path);
|
||||
}
|
||||
|
||||
bool IsFile(const fs::path& path) {
|
||||
return fs::is_regular_file(path);
|
||||
}
|
||||
|
||||
bool IsDir(const fs::path& path) {
|
||||
return fs::is_directory(path);
|
||||
}
|
||||
|
||||
fs::path GetCurrentDir() {
|
||||
std::error_code ec;
|
||||
|
||||
const auto current_path = fs::current_path(ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to get the current path, ec_message={}", ec.message());
|
||||
return {};
|
||||
}
|
||||
|
||||
return current_path;
|
||||
}
|
||||
|
||||
bool SetCurrentDir(const fs::path& path) {
|
||||
std::error_code ec;
|
||||
|
||||
fs::current_path(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to set the current path to path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fs::file_type GetEntryType(const fs::path& path) {
|
||||
std::error_code ec;
|
||||
|
||||
const auto file_status = fs::status(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to retrieve the entry type of path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return fs::file_type::not_found;
|
||||
}
|
||||
|
||||
return file_status.type();
|
||||
}
|
||||
|
||||
u64 GetSize(const fs::path& path) {
|
||||
std::error_code ec;
|
||||
|
||||
const auto file_size = fs::file_size(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return file_size;
|
||||
}
|
||||
|
||||
u64 GetFreeSpaceSize(const fs::path& path) {
|
||||
std::error_code ec;
|
||||
|
||||
const auto space_info = fs::space(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to retrieve the available free space of path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return space_info.free;
|
||||
}
|
||||
|
||||
u64 GetTotalSpaceSize(const fs::path& path) {
|
||||
std::error_code ec;
|
||||
|
||||
const auto space_info = fs::space(path, ec);
|
||||
|
||||
if (ec) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to retrieve the total capacity of path={}, ec_message={}",
|
||||
PathToUTF8String(path), ec.message());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return space_info.capacity;
|
||||
}
|
||||
|
||||
} // namespace Common::FS
|
583
src/common/fs/fs.h
Normal file
583
src/common/fs/fs.h
Normal file
@ -0,0 +1,583 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
#include "common/fs/fs_types.h"
|
||||
#include "common/fs/fs_util.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
class IOFile;
|
||||
|
||||
// File Operations
|
||||
|
||||
/**
|
||||
* Creates a new file at path with the specified size.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - The input path's parent directory does not exist
|
||||
* - Filesystem object at path exists
|
||||
* - Filesystem at path is read only
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param size File size
|
||||
*
|
||||
* @returns True if the file creation succeeds, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool NewFile(const std::filesystem::path& path, u64 size = 0);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool NewFile(const Path& path, u64 size = 0) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return NewFile(ToU8String(path), size);
|
||||
} else {
|
||||
return NewFile(std::filesystem::path{path}, size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Removes a file at path.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - Filesystem object at path is not a regular file
|
||||
* - Filesystem at path is read only
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if file removal succeeds or file does not exist, false otherwise.
|
||||
*/
|
||||
bool RemoveFile(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
bool RemoveFile(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return RemoveFile(ToU8String(path));
|
||||
} else {
|
||||
return RemoveFile(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Renames a file from old_path to new_path.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - One or both input path(s) is not valid
|
||||
* - Filesystem object at old_path does not exist
|
||||
* - Filesystem object at old_path is not a regular file
|
||||
* - Filesystem object at new_path exists
|
||||
* - Filesystem at either path is read only
|
||||
*
|
||||
* @param old_path Old filesystem path
|
||||
* @param new_path New filesystem path
|
||||
*
|
||||
* @returns True if file rename succeeds, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool RenameFile(const std::filesystem::path& old_path,
|
||||
const std::filesystem::path& new_path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path1, typename Path2>
|
||||
[[nodiscard]] bool RenameFile(const Path1& old_path, const Path2& new_path) {
|
||||
using ValueType1 = typename Path1::value_type;
|
||||
using ValueType2 = typename Path2::value_type;
|
||||
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return RenameFile(ToU8String(old_path), ToU8String(new_path));
|
||||
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
|
||||
return RenameFile(ToU8String(old_path), new_path);
|
||||
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return RenameFile(old_path, ToU8String(new_path));
|
||||
} else {
|
||||
return RenameFile(std::filesystem::path{old_path}, std::filesystem::path{new_path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Opens a file at path with the specified file access mode.
|
||||
* This function behaves differently depending on the FileAccessMode.
|
||||
* These behaviors are documented in each enum value of FileAccessMode.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - Filesystem object at path exists and is not a regular file
|
||||
* - The file is not open
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param mode File access mode
|
||||
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
|
||||
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
|
||||
*
|
||||
* @returns A shared pointer to the opened file. Returns nullptr on failure.
|
||||
*/
|
||||
[[nodiscard]] std::shared_ptr<IOFile> FileOpen(const std::filesystem::path& path,
|
||||
FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] std::shared_ptr<IOFile> FileOpen(const Path& path, FileAccessMode mode,
|
||||
FileType type = FileType::BinaryFile,
|
||||
FileShareFlag flag = FileShareFlag::ShareReadOnly) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return FileOpen(ToU8String(path), mode, type, flag);
|
||||
} else {
|
||||
return FileOpen(std::filesystem::path{path}, mode, type, flag);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Directory Operations
|
||||
|
||||
/**
|
||||
* Creates a directory at path.
|
||||
* Note that this function will *always* assume that the input path is a directory. For example,
|
||||
* if the input path is /path/to/directory/file.txt, it will create a directory called "file.txt".
|
||||
* If you intend to create the parent directory of a file, use CreateParentDir instead.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - The input path's parent directory does not exist
|
||||
* - Filesystem at path is read only
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if directory creation succeeds or directory already exists, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool CreateDir(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool CreateDir(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return CreateDir(ToU8String(path));
|
||||
} else {
|
||||
return CreateDir(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Recursively creates a directory at path.
|
||||
* Note that this function will *always* assume that the input path is a directory. For example,
|
||||
* if the input path is /path/to/directory/file.txt, it will create a directory called "file.txt".
|
||||
* If you intend to create the parent directory of a file, use CreateParentDirs instead.
|
||||
* Unlike CreateDir, this creates all of input path's parent directories if they do not exist.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - Filesystem at path is read only
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if directory creation succeeds or directory already exists, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool CreateDirs(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool CreateDirs(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return CreateDirs(ToU8String(path));
|
||||
} else {
|
||||
return CreateDirs(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Creates the parent directory of a given path.
|
||||
* This function calls CreateDir(path.parent_path()), see CreateDir for more details.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if directory creation succeeds or directory already exists, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool CreateParentDir(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool CreateParentDir(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return CreateParentDir(ToU8String(path));
|
||||
} else {
|
||||
return CreateParentDir(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Recursively creates the parent directory of a given path.
|
||||
* This function calls CreateDirs(path.parent_path()), see CreateDirs for more details.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if directory creation succeeds or directory already exists, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool CreateParentDirs(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool CreateParentDirs(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return CreateParentDirs(ToU8String(path));
|
||||
} else {
|
||||
return CreateParentDirs(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Removes a directory at path.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - Filesystem object at path is not a directory
|
||||
* - The given directory is not empty
|
||||
* - Filesystem at path is read only
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if directory removal succeeds or directory does not exist, false otherwise.
|
||||
*/
|
||||
bool RemoveDir(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
bool RemoveDir(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return RemoveDir(ToU8String(path));
|
||||
} else {
|
||||
return RemoveDir(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Removes all the contents within the given directory and removes the directory itself.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - Filesystem object at path is not a directory
|
||||
* - Filesystem at path is read only
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if the directory and all of its contents are removed successfully, false otherwise.
|
||||
*/
|
||||
bool RemoveDirRecursively(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
bool RemoveDirRecursively(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return RemoveDirRecursively(ToU8String(path));
|
||||
} else {
|
||||
return RemoveDirRecursively(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Removes all the contents within the given directory without removing the directory itself.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - Filesystem object at path is not a directory
|
||||
* - Filesystem at path is read only
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if all of the directory's contents are removed successfully, false otherwise.
|
||||
*/
|
||||
bool RemoveDirContentsRecursively(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
bool RemoveDirContentsRecursively(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return RemoveDirContentsRecursively(ToU8String(path));
|
||||
} else {
|
||||
return RemoveDirContentsRecursively(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Renames a directory from old_path to new_path.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - One or both input path(s) is not valid
|
||||
* - Filesystem object at old_path does not exist
|
||||
* - Filesystem object at old_path is not a directory
|
||||
* - Filesystem object at new_path exists
|
||||
* - Filesystem at either path is read only
|
||||
*
|
||||
* @param old_path Old filesystem path
|
||||
* @param new_path New filesystem path
|
||||
*
|
||||
* @returns True if directory rename succeeds, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool RenameDir(const std::filesystem::path& old_path,
|
||||
const std::filesystem::path& new_path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path1, typename Path2>
|
||||
[[nodiscard]] bool RenameDir(const Path1& old_path, const Path2& new_path) {
|
||||
using ValueType1 = typename Path1::value_type;
|
||||
using ValueType2 = typename Path2::value_type;
|
||||
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return RenameDir(ToU8String(old_path), ToU8String(new_path));
|
||||
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
|
||||
return RenameDir(ToU8String(old_path), new_path);
|
||||
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return RenameDir(old_path, ToU8String(new_path));
|
||||
} else {
|
||||
return RenameDir(std::filesystem::path{old_path}, std::filesystem::path{new_path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Iterates over the directory entries of a given directory.
|
||||
* This does not iterate over the sub-directories of the given directory.
|
||||
* The DirEntryCallable callback is called for each visited directory entry.
|
||||
* A filter can be set to control which directory entries are visited based on their type.
|
||||
* By default, both files and directories are visited.
|
||||
* If the callback returns false or there is an error, the iteration is immediately halted.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - Filesystem object at path is not a directory
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param callback Callback to be called for each visited directory entry
|
||||
* @param filter Directory entry type filter
|
||||
*/
|
||||
void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback,
|
||||
DirEntryFilter filter = DirEntryFilter::All);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
void IterateDirEntries(const Path& path, const DirEntryCallable& callback,
|
||||
DirEntryFilter filter = DirEntryFilter::All) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
IterateDirEntries(ToU8String(path), callback, filter);
|
||||
} else {
|
||||
IterateDirEntries(std::filesystem::path{path}, callback, filter);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Iterates over the directory entries of a given directory and its sub-directories.
|
||||
* The DirEntryCallable callback is called for each visited directory entry.
|
||||
* A filter can be set to control which directory entries are visited based on their type.
|
||||
* By default, both files and directories are visited.
|
||||
* If the callback returns false or there is an error, the iteration is immediately halted.
|
||||
*
|
||||
* Failures occur when:
|
||||
* - Input path is not valid
|
||||
* - Filesystem object at path does not exist
|
||||
* - Filesystem object at path is not a directory
|
||||
*
|
||||
* @param path Filesystem path
|
||||
* @param callback Callback to be called for each visited directory entry
|
||||
* @param filter Directory entry type filter
|
||||
*/
|
||||
void IterateDirEntriesRecursively(const std::filesystem::path& path,
|
||||
const DirEntryCallable& callback,
|
||||
DirEntryFilter filter = DirEntryFilter::All);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
void IterateDirEntriesRecursively(const Path& path, const DirEntryCallable& callback,
|
||||
DirEntryFilter filter = DirEntryFilter::All) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
IterateDirEntriesRecursively(ToU8String(path), callback, filter);
|
||||
} else {
|
||||
IterateDirEntriesRecursively(std::filesystem::path{path}, callback, filter);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Generic Filesystem Operations
|
||||
|
||||
/**
|
||||
* Returns whether a filesystem object at path exists.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if a filesystem object at path exists, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool Exists(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool Exists(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return Exists(ToU8String(path));
|
||||
} else {
|
||||
return Exists(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns whether a filesystem object at path is a regular file.
|
||||
* A regular file is a file that stores text or binary data.
|
||||
* It is not a directory, symlink, FIFO, socket, block device, or character device.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if a filesystem object at path is a regular file, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool IsFile(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool IsFile(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return IsFile(ToU8String(path));
|
||||
} else {
|
||||
return IsFile(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns whether a filesystem object at path is a directory.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if a filesystem object at path is a directory, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool IsDir(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool IsDir(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return IsDir(ToU8String(path));
|
||||
} else {
|
||||
return IsDir(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the current working directory.
|
||||
*
|
||||
* @returns The current working directory. Returns an empty path on failure.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path GetCurrentDir();
|
||||
|
||||
/**
|
||||
* Sets the current working directory to path.
|
||||
*
|
||||
* @returns True if the current working directory is successfully set, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool SetCurrentDir(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool SetCurrentDir(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return SetCurrentDir(ToU8String(path));
|
||||
} else {
|
||||
return SetCurrentDir(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the entry type of the filesystem object at path.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns The entry type of the filesystem object. Returns file_type::not_found on failure.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::file_type GetEntryType(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] std::filesystem::file_type GetEntryType(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return GetEntryType(ToU8String(path));
|
||||
} else {
|
||||
return GetEntryType(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the size of the filesystem object at path.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns The size in bytes of the filesystem object. Returns 0 on failure.
|
||||
*/
|
||||
[[nodiscard]] u64 GetSize(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] u64 GetSize(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return GetSize(ToU8String(path));
|
||||
} else {
|
||||
return GetSize(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the free space size of the filesystem at path.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns The free space size in bytes of the filesystem at path. Returns 0 on failure.
|
||||
*/
|
||||
[[nodiscard]] u64 GetFreeSpaceSize(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] u64 GetFreeSpaceSize(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return GetFreeSpaceSize(ToU8String(path));
|
||||
} else {
|
||||
return GetFreeSpaceSize(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the total capacity of the filesystem at path.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns The total capacity in bytes of the filesystem at path. Returns 0 on failure.
|
||||
*/
|
||||
[[nodiscard]] u64 GetTotalSpaceSize(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] u64 GetTotalSpaceSize(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return GetTotalSpaceSize(ToU8String(path));
|
||||
} else {
|
||||
return GetTotalSpaceSize(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common::FS
|
25
src/common/fs/fs_paths.h
Normal file
25
src/common/fs/fs_paths.h
Normal file
@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
// yuzu data directories
|
||||
|
||||
#define CITRA_DIR "citra"
|
||||
#define PORTABLE_DIR "user"
|
||||
|
||||
// Sub-directories contained within a citra data directory
|
||||
|
||||
#define CONFIG_DIR "config"
|
||||
#define CACHE_DIR "cache"
|
||||
#define SDMC_DIR "sdmc"
|
||||
#define NAND_DIR "nand"
|
||||
#define SYSDATA_DIR "sysdata"
|
||||
#define LOG_DIR "log"
|
||||
#define CHEATS_DIR "cheats"
|
||||
#define DLL_DIR "external_dlls"
|
||||
#define SHADER_DIR "shaders"
|
||||
#define DUMP_DIR "dump"
|
||||
#define LOAD_DIR "load"
|
||||
#define SHADER_DIR "shaders"
|
||||
#define STATES_DIR "states"
|
6
src/common/fs/fs_serialize.cpp
Normal file
6
src/common/fs/fs_serialize.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "fs_serialize.h"
|
||||
|
||||
fs_serialize::fs_serialize()
|
||||
{
|
||||
|
||||
}
|
43
src/common/fs/fs_serialize.h
Normal file
43
src/common/fs/fs_serialize.h
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2022 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include <boost/serialization/split_member.hpp>
|
||||
#include <boost/serialization/string.hpp>
|
||||
#include <boost/serialization/wrapper.hpp>
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
// Replaces install-specific paths with standard placeholders, and back again
|
||||
std::filesystem::path SerializePath(const std::filesystem::path& input, bool is_saving);
|
||||
|
||||
// A serializable path string
|
||||
struct Path : public boost::serialization::wrapper_traits<const Path> {
|
||||
std::filesystem::path& path;
|
||||
|
||||
explicit Path(std::filesystem::path& path) : path{path} {}
|
||||
|
||||
static const Path make(std::filesystem::path& path) {
|
||||
return Path(path);
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void save(Archive& ar, const unsigned int) const {
|
||||
auto s_path = SerializePath(path, true);
|
||||
ar << s_path;
|
||||
}
|
||||
template <class Archive>
|
||||
void load(Archive& ar, const unsigned int) const {
|
||||
ar >> path;
|
||||
path = SerializePath(path, false);
|
||||
}
|
||||
|
||||
BOOST_SERIALIZATION_SPLIT_MEMBER();
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
||||
} // namespace Common::FS
|
71
src/common/fs/fs_types.h
Normal file
71
src/common/fs/fs_types.h
Normal file
@ -0,0 +1,71 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <filesystem>
|
||||
#include "common/common_funcs.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
enum class FileAccessMode {
|
||||
/**
|
||||
* If the file at path exists, it opens the file for reading.
|
||||
* If the file at path does not exist, it fails to open the file.
|
||||
*/
|
||||
Read = 1 << 0,
|
||||
/**
|
||||
* If the file at path exists, the existing contents of the file are erased.
|
||||
* The empty file is then opened for writing.
|
||||
* If the file at path does not exist, it creates and opens a new empty file for writing.
|
||||
*/
|
||||
Write = 1 << 1,
|
||||
/**
|
||||
* If the file at path exists, it opens the file for reading and writing.
|
||||
* If the file at path does not exist, it fails to open the file.
|
||||
*/
|
||||
ReadWrite = Read | Write,
|
||||
/**
|
||||
* If the file at path exists, it opens the file for appending.
|
||||
* If the file at path does not exist, it creates and opens a new empty file for appending.
|
||||
*/
|
||||
Append = 1 << 2,
|
||||
/**
|
||||
* If the file at path exists, it opens the file for both reading and appending.
|
||||
* If the file at path does not exist, it creates and opens a new empty file for both
|
||||
* reading and appending.
|
||||
*/
|
||||
ReadAppend = Read | Append,
|
||||
};
|
||||
|
||||
enum class FileType {
|
||||
BinaryFile,
|
||||
TextFile,
|
||||
};
|
||||
|
||||
enum class FileShareFlag {
|
||||
ShareNone, // Provides exclusive access to the file.
|
||||
ShareReadOnly, // Provides read only shared access to the file.
|
||||
ShareWriteOnly, // Provides write only shared access to the file.
|
||||
ShareReadWrite, // Provides read and write shared access to the file.
|
||||
};
|
||||
|
||||
enum class DirEntryFilter {
|
||||
File = 1 << 0,
|
||||
Directory = 1 << 1,
|
||||
All = File | Directory,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter);
|
||||
|
||||
/**
|
||||
* A callback function which takes in the path of a directory entry.
|
||||
*
|
||||
* @param path The path of a directory entry
|
||||
*
|
||||
* @returns A boolean value.
|
||||
* Return true to indicate whether the callback is successful, false otherwise.
|
||||
*/
|
||||
using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>;
|
||||
|
||||
} // namespace Common::FS
|
38
src/common/fs/fs_util.cpp
Normal file
38
src/common/fs/fs_util.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/fs/fs_util.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
std::u8string ToU8String(std::string_view utf8_string) {
|
||||
return std::u8string{utf8_string.begin(), utf8_string.end()};
|
||||
}
|
||||
|
||||
std::u8string BufferToU8String(std::span<const u8> buffer) {
|
||||
return std::u8string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::u8string_view BufferToU8StringView(std::span<const u8> buffer) {
|
||||
return std::u8string_view{reinterpret_cast<const char8_t*>(buffer.data())};
|
||||
}
|
||||
|
||||
std::string ToUTF8String(std::u8string_view u8_string) {
|
||||
return std::string{u8_string.begin(), u8_string.end()};
|
||||
}
|
||||
|
||||
std::string BufferToUTF8String(std::span<const u8> buffer) {
|
||||
return std::string{buffer.begin(), std::ranges::find(buffer, u8{0})};
|
||||
}
|
||||
|
||||
std::string_view BufferToUTF8StringView(std::span<const u8> buffer) {
|
||||
return std::string_view{reinterpret_cast<const char*>(buffer.data())};
|
||||
}
|
||||
|
||||
std::string PathToUTF8String(const std::filesystem::path& path) {
|
||||
return ToUTF8String(path.u8string());
|
||||
}
|
||||
|
||||
} // namespace Common::FS
|
85
src/common/fs/fs_util.h
Normal file
85
src/common/fs/fs_util.h
Normal file
@ -0,0 +1,85 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <concepts>
|
||||
#include <filesystem>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
template <typename T>
|
||||
concept IsChar = std::same_as<T, char>;
|
||||
|
||||
/**
|
||||
* Converts a UTF-8 encoded std::string or std::string_view to a std::u8string.
|
||||
*
|
||||
* @param utf8_string UTF-8 encoded string
|
||||
*
|
||||
* @returns UTF-8 encoded std::u8string.
|
||||
*/
|
||||
[[nodiscard]] std::u8string ToU8String(std::string_view utf8_string);
|
||||
|
||||
/**
|
||||
* Converts a buffer of bytes to a UTF8-encoded std::u8string.
|
||||
* This converts from the start of the buffer until the first encountered null-terminator.
|
||||
* If no null-terminator is found, this converts the entire buffer instead.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::u8string.
|
||||
*/
|
||||
[[nodiscard]] std::u8string BufferToU8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Same as BufferToU8String, but returns a string view of the buffer.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::u8string_view.
|
||||
*/
|
||||
[[nodiscard]] std::u8string_view BufferToU8StringView(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a std::u8string or std::u8string_view to a UTF-8 encoded std::string.
|
||||
*
|
||||
* @param u8_string UTF-8 encoded u8string
|
||||
*
|
||||
* @returns UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string ToUTF8String(std::u8string_view u8_string);
|
||||
|
||||
/**
|
||||
* Converts a buffer of bytes to a UTF8-encoded std::string.
|
||||
* This converts from the start of the buffer until the first encountered null-terminator.
|
||||
* If no null-terminator is found, this converts the entire buffer instead.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string BufferToUTF8String(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Same as BufferToUTF8String, but returns a string view of the buffer.
|
||||
*
|
||||
* @param buffer Buffer of bytes
|
||||
*
|
||||
* @returns UTF-8 encoded std::string_view.
|
||||
*/
|
||||
[[nodiscard]] std::string_view BufferToUTF8StringView(std::span<const u8> buffer);
|
||||
|
||||
/**
|
||||
* Converts a filesystem path to a UTF-8 encoded std::string.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path);
|
||||
|
||||
} // namespace Common::FS
|
428
src/common/fs/path_util.cpp
Normal file
428
src/common/fs/path_util.cpp
Normal file
@ -0,0 +1,428 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/fs/fs_paths.h"
|
||||
#include "common/fs/path_util.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <shlobj.h> // Used in GetExeDirectory()
|
||||
#else
|
||||
#include <cstdlib> // Used in Get(Home/Data)Directory()
|
||||
#include <pwd.h> // Used in GetHomeDirectory()
|
||||
#include <sys/types.h> // Used in GetHomeDirectory()
|
||||
#include <unistd.h> // Used in GetDataDirectory()
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/param.h> // Used in GetBundleDirectory()
|
||||
|
||||
// CFURL contains __attribute__ directives that gcc does not know how to parse, so we need to just
|
||||
// ignore them if we're not using clang. The macro is only used to prevent linking against
|
||||
// functions that don't exist on older versions of macOS, and the worst case scenario is a linker
|
||||
// error, so this is perfectly safe, just inconvenient.
|
||||
#ifndef __clang__
|
||||
#define availability(...)
|
||||
#endif
|
||||
#include <CoreFoundation/CFBundle.h> // Used in GetBundleDirectory()
|
||||
#include <CoreFoundation/CFString.h> // Used in GetBundleDirectory()
|
||||
#include <CoreFoundation/CFURL.h> // Used in GetBundleDirectory()
|
||||
#ifdef availability
|
||||
#undef availability
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MAX_PATH
|
||||
#ifdef _WIN32
|
||||
// This is the maximum number of UTF-16 code units permissible in Windows file paths
|
||||
#define MAX_PATH 260
|
||||
#else
|
||||
// This is the maximum number of UTF-8 code units permissible in all other OSes' file paths
|
||||
#define MAX_PATH 1024
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
/**
|
||||
* The PathManagerImpl is a singleton allowing to manage the mapping of
|
||||
* UserPath enums to real filesystem paths.
|
||||
* This class provides 2 functions: GetUserPathImpl and SetUserPathImpl.
|
||||
* These are used by GetUserPath and SetUserPath respectively to get or modify
|
||||
* the path mapped by the UserPath enum.
|
||||
*/
|
||||
class PathManagerImpl {
|
||||
public:
|
||||
static PathManagerImpl& GetInstance() {
|
||||
static PathManagerImpl path_manager_impl;
|
||||
|
||||
return path_manager_impl;
|
||||
}
|
||||
|
||||
PathManagerImpl(const PathManagerImpl&) = delete;
|
||||
PathManagerImpl& operator=(const PathManagerImpl&) = delete;
|
||||
|
||||
PathManagerImpl(PathManagerImpl&&) = delete;
|
||||
PathManagerImpl& operator=(PathManagerImpl&&) = delete;
|
||||
|
||||
[[nodiscard]] const fs::path& GetUserPathImpl(UserPath user_path) {
|
||||
return user_paths.at(user_path);
|
||||
}
|
||||
|
||||
void SetUserPathImpl(UserPath user_path, const fs::path& new_path) {
|
||||
user_paths.insert_or_assign(user_path, new_path);
|
||||
}
|
||||
|
||||
private:
|
||||
PathManagerImpl() {
|
||||
fs::path user_path;
|
||||
fs::path user_path_cache;
|
||||
fs::path user_path_config;
|
||||
|
||||
#ifdef _WIN32
|
||||
user_path = GetExeDirectory() / PORTABLE_DIR;
|
||||
|
||||
if (!IsDir(user_path)) {
|
||||
user_path = GetAppDataRoamingDirectory() / CITRA_DIR;
|
||||
}
|
||||
|
||||
user_path_cache = user_path / CACHE_DIR;
|
||||
user_path_config = user_path / CONFIG_DIR;
|
||||
#else
|
||||
user_path = GetCurrentDir() / PORTABLE_DIR;
|
||||
|
||||
if (Exists(user_path) && IsDir(user_path)) {
|
||||
user_path_cache = user_path / CACHE_DIR;
|
||||
user_path_config = user_path / CONFIG_DIR;
|
||||
} else {
|
||||
user_path = GetDataDirectory("XDG_DATA_HOME") / CITRA_DIR;
|
||||
user_path_cache = GetDataDirectory("XDG_CACHE_HOME") / CITRA_DIR;
|
||||
user_path_config = GetDataDirectory("XDG_CONFIG_HOME") / CITRA_DIR;
|
||||
}
|
||||
#endif
|
||||
|
||||
GenerateUserPath(UserPath::UserDir, user_path);
|
||||
GenerateUserPath(UserPath::CacheDir, user_path_cache);
|
||||
GenerateUserPath(UserPath::ConfigDir, user_path_config);
|
||||
GenerateUserPath(UserPath::CheatsDir, user_path / CHEATS_DIR);
|
||||
GenerateUserPath(UserPath::DLLDir, user_path / DLL_DIR);
|
||||
GenerateUserPath(UserPath::DumpDir, user_path / DUMP_DIR);
|
||||
GenerateUserPath(UserPath::LoadDir, user_path / LOAD_DIR);
|
||||
GenerateUserPath(UserPath::LogDir, user_path / LOG_DIR);
|
||||
GenerateUserPath(UserPath::NANDDir, user_path / NAND_DIR);
|
||||
GenerateUserPath(UserPath::SDMCDir, user_path / SDMC_DIR);
|
||||
GenerateUserPath(UserPath::SysDataDir, user_path / SYSDATA_DIR);
|
||||
GenerateUserPath(UserPath::StatesDir, user_path / SYSDATA_DIR);
|
||||
GenerateUserPath(UserPath::ShaderDir, user_path / STATES_DIR);
|
||||
}
|
||||
|
||||
~PathManagerImpl() = default;
|
||||
|
||||
void GenerateUserPath(UserPath user_path, const fs::path& new_path) {
|
||||
void(FS::CreateDir(new_path));
|
||||
|
||||
SetUserPathImpl(user_path, new_path);
|
||||
}
|
||||
|
||||
std::unordered_map<UserPath, fs::path> user_paths;
|
||||
};
|
||||
|
||||
bool ValidatePath(const fs::path& path) {
|
||||
if (path.empty()) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is empty, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (path.u16string().size() >= MAX_PATH) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is too long, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (path.u8string().size() >= MAX_PATH) {
|
||||
LOG_ERROR(Common_Filesystem, "Input path is too long, path={}", PathToUTF8String(path));
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fs::path ConcatPath(const fs::path& first, const fs::path& second) {
|
||||
const bool second_has_dir_sep = IsDirSeparator(second.u8string().front());
|
||||
|
||||
if (!second_has_dir_sep) {
|
||||
return (first / second).lexically_normal();
|
||||
}
|
||||
|
||||
fs::path concat_path = first;
|
||||
concat_path += second;
|
||||
|
||||
return concat_path.lexically_normal();
|
||||
}
|
||||
|
||||
fs::path ConcatPathSafe(const fs::path& base, const fs::path& offset) {
|
||||
const auto concatenated_path = ConcatPath(base, offset);
|
||||
|
||||
if (!IsPathSandboxed(base, concatenated_path)) {
|
||||
return base;
|
||||
}
|
||||
|
||||
return concatenated_path;
|
||||
}
|
||||
|
||||
bool IsPathSandboxed(const fs::path& base, const fs::path& path) {
|
||||
const auto base_string = RemoveTrailingSeparators(base.lexically_normal()).u8string();
|
||||
const auto path_string = RemoveTrailingSeparators(path.lexically_normal()).u8string();
|
||||
|
||||
if (path_string.size() < base_string.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return base_string.compare(0, base_string.size(), path_string, 0, base_string.size()) == 0;
|
||||
}
|
||||
|
||||
bool IsDirSeparator(char character) {
|
||||
return character == '/' || character == '\\';
|
||||
}
|
||||
|
||||
bool IsDirSeparator(char8_t character) {
|
||||
return character == u8'/' || character == u8'\\';
|
||||
}
|
||||
|
||||
fs::path RemoveTrailingSeparators(const fs::path& path) {
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
auto string_path = path.u8string();
|
||||
|
||||
while (IsDirSeparator(string_path.back())) {
|
||||
string_path.pop_back();
|
||||
}
|
||||
|
||||
return fs::path{string_path};
|
||||
}
|
||||
|
||||
const fs::path& GetUserPath(UserPath user_path) {
|
||||
return PathManagerImpl::GetInstance().GetUserPathImpl(user_path);
|
||||
}
|
||||
|
||||
std::string GetUserPathString(UserPath user_path) {
|
||||
return PathToUTF8String(GetUserPath(user_path));
|
||||
}
|
||||
|
||||
void SetUserPath(UserPath user_path, const fs::path& new_path) {
|
||||
if (!FS::IsDir(new_path)) {
|
||||
LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} is not a directory",
|
||||
PathToUTF8String(new_path));
|
||||
return;
|
||||
}
|
||||
|
||||
PathManagerImpl::GetInstance().SetUserPathImpl(user_path, new_path);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
fs::path GetExeDirectory() {
|
||||
wchar_t exe_path[MAX_PATH];
|
||||
|
||||
if (GetModuleFileNameW(nullptr, exe_path, MAX_PATH) == 0) {
|
||||
LOG_ERROR(Common_Filesystem,
|
||||
"Failed to get the path to the executable of the current process");
|
||||
}
|
||||
|
||||
return fs::path{exe_path}.parent_path();
|
||||
}
|
||||
|
||||
fs::path GetAppDataRoamingDirectory() {
|
||||
PWSTR appdata_roaming_path = nullptr;
|
||||
|
||||
SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &appdata_roaming_path);
|
||||
|
||||
auto fs_appdata_roaming_path = fs::path{appdata_roaming_path};
|
||||
|
||||
CoTaskMemFree(appdata_roaming_path);
|
||||
|
||||
if (fs_appdata_roaming_path.empty()) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to get the path to the %APPDATA% directory");
|
||||
}
|
||||
|
||||
return fs_appdata_roaming_path;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
fs::path GetHomeDirectory() {
|
||||
const char* home_env_var = getenv("HOME");
|
||||
|
||||
if (home_env_var) {
|
||||
return fs::path{home_env_var};
|
||||
}
|
||||
|
||||
LOG_INFO(Common_Filesystem,
|
||||
"$HOME is not defined in the environment variables, "
|
||||
"attempting to query passwd to get the home path of the current user");
|
||||
|
||||
const auto* pw = getpwuid(getuid());
|
||||
|
||||
if (!pw) {
|
||||
LOG_ERROR(Common_Filesystem, "Failed to get the home path of the current user");
|
||||
return {};
|
||||
}
|
||||
|
||||
return fs::path{pw->pw_dir};
|
||||
}
|
||||
|
||||
fs::path GetDataDirectory(const std::string& env_name) {
|
||||
const char* data_env_var = getenv(env_name.c_str());
|
||||
|
||||
if (data_env_var) {
|
||||
return fs::path{data_env_var};
|
||||
}
|
||||
|
||||
if (env_name == "XDG_DATA_HOME") {
|
||||
return GetHomeDirectory() / ".local/share";
|
||||
} else if (env_name == "XDG_CACHE_HOME") {
|
||||
return GetHomeDirectory() / ".cache";
|
||||
} else if (env_name == "XDG_CONFIG_HOME") {
|
||||
return GetHomeDirectory() / ".config";
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
fs::path GetBundleDirectory() {
|
||||
char app_bundle_path[MAXPATHLEN];
|
||||
|
||||
// Get the main bundle for the app
|
||||
CFURLRef bundle_ref = CFBundleCopyBundleURL(CFBundleGetMainBundle());
|
||||
CFStringRef bundle_path = CFURLCopyFileSystemPath(bundle_ref, kCFURLPOSIXPathStyle);
|
||||
|
||||
CFStringGetFileSystemRepresentation(bundle_path, app_bundle_path, sizeof(app_bundle_path));
|
||||
|
||||
CFRelease(bundle_ref);
|
||||
CFRelease(bundle_path);
|
||||
|
||||
return fs::path{app_bundle_path};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// vvvvvvvvvv Deprecated vvvvvvvvvv //
|
||||
|
||||
std::string_view RemoveTrailingSlash(std::string_view path) {
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
if (path.back() == '\\' || path.back() == '/') {
|
||||
path.remove_suffix(1);
|
||||
return path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::vector<std::string> SplitPathComponents(std::string_view filename) {
|
||||
std::string copy(filename);
|
||||
std::replace(copy.begin(), copy.end(), '\\', '/');
|
||||
std::vector<std::string> out;
|
||||
|
||||
std::stringstream stream(copy);
|
||||
std::string item;
|
||||
while (std::getline(stream, item, '/')) {
|
||||
out.push_back(std::move(item));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
|
||||
std::string path(path_);
|
||||
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
|
||||
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
|
||||
|
||||
if (directory_separator == DirectorySeparator::PlatformDefault) {
|
||||
#ifdef _WIN32
|
||||
type1 = '/';
|
||||
type2 = '\\';
|
||||
#endif
|
||||
}
|
||||
|
||||
std::replace(path.begin(), path.end(), type1, type2);
|
||||
|
||||
auto start = path.begin();
|
||||
#ifdef _WIN32
|
||||
// allow network paths which start with a double backslash (e.g. \\server\share)
|
||||
if (start != path.end())
|
||||
++start;
|
||||
#endif
|
||||
path.erase(std::unique(start, path.end(),
|
||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||
path.end());
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
std::string_view GetParentPath(std::string_view path) {
|
||||
const auto name_bck_index = path.rfind('\\');
|
||||
const auto name_fwd_index = path.rfind('/');
|
||||
std::size_t name_index;
|
||||
|
||||
if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) {
|
||||
name_index = std::min(name_bck_index, name_fwd_index);
|
||||
} else {
|
||||
name_index = std::max(name_bck_index, name_fwd_index);
|
||||
}
|
||||
|
||||
return path.substr(0, name_index);
|
||||
}
|
||||
|
||||
std::string_view GetPathWithoutTop(std::string_view path) {
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
|
||||
while (path[0] == '\\' || path[0] == '/') {
|
||||
path.remove_prefix(1);
|
||||
if (path.empty()) {
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
const auto name_bck_index = path.find('\\');
|
||||
const auto name_fwd_index = path.find('/');
|
||||
return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
|
||||
}
|
||||
|
||||
std::string_view GetFilename(std::string_view path) {
|
||||
const auto name_index = path.find_last_of("\\/");
|
||||
|
||||
if (name_index == std::string_view::npos) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return path.substr(name_index + 1);
|
||||
}
|
||||
|
||||
std::string_view GetExtensionFromFilename(std::string_view name) {
|
||||
const std::size_t index = name.rfind('.');
|
||||
|
||||
if (index == std::string_view::npos) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return name.substr(index + 1);
|
||||
}
|
||||
|
||||
} // namespace Common::FS
|
302
src/common/fs/path_util.h
Normal file
302
src/common/fs/path_util.h
Normal file
@ -0,0 +1,302 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "common/fs/fs_util.h"
|
||||
|
||||
namespace Common::FS {
|
||||
|
||||
enum class UserPath {
|
||||
CacheDir,
|
||||
CheatsDir,
|
||||
ConfigDir,
|
||||
DLLDir,
|
||||
DumpDir,
|
||||
LoadDir,
|
||||
LogDir,
|
||||
NANDDir,
|
||||
RootDir,
|
||||
SDMCDir,
|
||||
ShaderDir,
|
||||
StatesDir,
|
||||
SysDataDir,
|
||||
UserDir,
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates a given path.
|
||||
*
|
||||
* A given path is valid if it meets these conditions:
|
||||
* - The path is not empty
|
||||
* - The path is not too long
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if the path is valid, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool ValidatePath(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] bool ValidatePath(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return ValidatePath(ToU8String(path));
|
||||
} else {
|
||||
return ValidatePath(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Concatenates two filesystem paths together.
|
||||
*
|
||||
* This is needed since the following occurs when using std::filesystem::path's operator/:
|
||||
* first: "/first/path"
|
||||
* second: "/second/path" (Note that the second path has a directory separator in the front)
|
||||
* first / second yields "/second/path" when the desired result is first/path/second/path
|
||||
*
|
||||
* @param first First filesystem path
|
||||
* @param second Second filesystem path
|
||||
*
|
||||
* @returns A concatenated filesystem path.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path ConcatPath(const std::filesystem::path& first,
|
||||
const std::filesystem::path& second);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path1, typename Path2>
|
||||
[[nodiscard]] std::filesystem::path ConcatPath(const Path1& first, const Path2& second) {
|
||||
using ValueType1 = typename Path1::value_type;
|
||||
using ValueType2 = typename Path2::value_type;
|
||||
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return ConcatPath(ToU8String(first), ToU8String(second));
|
||||
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
|
||||
return ConcatPath(ToU8String(first), second);
|
||||
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return ConcatPath(first, ToU8String(second));
|
||||
} else {
|
||||
return ConcatPath(std::filesystem::path{first}, std::filesystem::path{second});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Safe variant of ConcatPath that takes in a base path and an offset path from the given base path.
|
||||
*
|
||||
* If ConcatPath(base, offset) resolves to a path that is sandboxed within the base path,
|
||||
* this will return the concatenated path. Otherwise this will return the base path.
|
||||
*
|
||||
* @param base Base filesystem path
|
||||
* @param offset Offset filesystem path
|
||||
*
|
||||
* @returns A concatenated filesystem path if it is within the base path,
|
||||
* returns the base path otherwise.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path ConcatPathSafe(const std::filesystem::path& base,
|
||||
const std::filesystem::path& offset);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path1, typename Path2>
|
||||
[[nodiscard]] std::filesystem::path ConcatPathSafe(const Path1& base, const Path2& offset) {
|
||||
using ValueType1 = typename Path1::value_type;
|
||||
using ValueType2 = typename Path2::value_type;
|
||||
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return ConcatPathSafe(ToU8String(base), ToU8String(offset));
|
||||
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
|
||||
return ConcatPathSafe(ToU8String(base), offset);
|
||||
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return ConcatPathSafe(base, ToU8String(offset));
|
||||
} else {
|
||||
return ConcatPathSafe(std::filesystem::path{base}, std::filesystem::path{offset});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks whether a given path is sandboxed within a given base path.
|
||||
*
|
||||
* @param base Base filesystem path
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns True if the given path is sandboxed within the given base path, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool IsPathSandboxed(const std::filesystem::path& base,
|
||||
const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path1, typename Path2>
|
||||
[[nodiscard]] bool IsPathSandboxed(const Path1& base, const Path2& path) {
|
||||
using ValueType1 = typename Path1::value_type;
|
||||
using ValueType2 = typename Path2::value_type;
|
||||
if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return IsPathSandboxed(ToU8String(base), ToU8String(path));
|
||||
} else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) {
|
||||
return IsPathSandboxed(ToU8String(base), path);
|
||||
} else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) {
|
||||
return IsPathSandboxed(base, ToU8String(path));
|
||||
} else {
|
||||
return IsPathSandboxed(std::filesystem::path{base}, std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Checks if a character is a directory separator (either a forward slash or backslash).
|
||||
*
|
||||
* @param character Character
|
||||
*
|
||||
* @returns True if the character is a directory separator, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool IsDirSeparator(char character);
|
||||
|
||||
/**
|
||||
* Checks if a character is a directory separator (either a forward slash or backslash).
|
||||
*
|
||||
* @param character Character
|
||||
*
|
||||
* @returns True if the character is a directory separator, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool IsDirSeparator(char8_t character);
|
||||
|
||||
/**
|
||||
* Removes any trailing directory separators if they exist in the given path.
|
||||
*
|
||||
* @param path Filesystem path
|
||||
*
|
||||
* @returns The filesystem path without any trailing directory separators.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path RemoveTrailingSeparators(const std::filesystem::path& path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
[[nodiscard]] std::filesystem::path RemoveTrailingSeparators(const Path& path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
return RemoveTrailingSeparators(ToU8String(path));
|
||||
} else {
|
||||
return RemoveTrailingSeparators(std::filesystem::path{path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Gets the filesystem path associated with the UserPath enum.
|
||||
*
|
||||
* @param user_path UserPath enum
|
||||
*
|
||||
* @returns The filesystem path associated with the UserPath enum.
|
||||
*/
|
||||
[[nodiscard]] const std::filesystem::path& GetUserPath(UserPath user_path);
|
||||
|
||||
/**
|
||||
* Gets the filesystem path associated with the UserPath enum as a UTF-8 encoded std::string.
|
||||
*
|
||||
* @param user_path UserPath enum
|
||||
*
|
||||
* @returns The filesystem path associated with the UserPath enum as a UTF-8 encoded std::string.
|
||||
*/
|
||||
[[nodiscard]] std::string GetUserPathString(UserPath user_path);
|
||||
|
||||
/**
|
||||
* Sets a new filesystem path associated with the User enum.
|
||||
* If the filesystem object at new_path is not a directory, this function will not do anything.
|
||||
*
|
||||
* @param user_path User enum
|
||||
* @param new_path New filesystem path
|
||||
*/
|
||||
void SetUserPath(UserPath user_path, const std::filesystem::path& new_path);
|
||||
|
||||
#ifdef _WIN32
|
||||
template <typename Path>
|
||||
void SetUserPath(UserPath user_path, const Path& new_path) {
|
||||
if constexpr (IsChar<typename Path::value_type>) {
|
||||
SetUserPath(user_path, ToU8String(new_path));
|
||||
} else {
|
||||
SetUserPath(user_path, std::filesystem::path{new_path});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
/**
|
||||
* Gets the path of the directory containing the executable of the current process.
|
||||
*
|
||||
* @returns The path of the directory containing the executable of the current process.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path GetExeDirectory();
|
||||
|
||||
/**
|
||||
* Gets the path of the current user's %APPDATA% directory (%USERPROFILE%/AppData/Roaming).
|
||||
*
|
||||
* @returns The path of the current user's %APPDATA% directory.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path GetAppDataRoamingDirectory();
|
||||
|
||||
#else
|
||||
|
||||
/**
|
||||
* Gets the path of the directory specified by the #HOME environment variable.
|
||||
* If $HOME is not defined, it will attempt to query the user database in passwd instead.
|
||||
*
|
||||
* @returns The path of the current user's home directory.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path GetHomeDirectory();
|
||||
|
||||
/**
|
||||
* Gets the relevant paths for yuzu to store its data based on the given XDG environment variable.
|
||||
* See https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
* Defaults to $HOME/.local/share for main application data,
|
||||
* $HOME/.cache for cached data, and $HOME/.config for configuration files.
|
||||
*
|
||||
* @param env_name XDG environment variable name
|
||||
*
|
||||
* @returns The path where yuzu should store its data.
|
||||
*/
|
||||
[[nodiscard]] std::filesystem::path GetDataDirectory(const std::string& env_name);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
[[nodiscard]] std::filesystem::path GetBundleDirectory();
|
||||
|
||||
#endif
|
||||
|
||||
// vvvvvvvvvv Deprecated vvvvvvvvvv //
|
||||
|
||||
// Removes the final '/' or '\' if one exists
|
||||
[[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path);
|
||||
|
||||
enum class DirectorySeparator {
|
||||
ForwardSlash,
|
||||
BackwardSlash,
|
||||
PlatformDefault,
|
||||
};
|
||||
|
||||
// Splits the path on '/' or '\' and put the components into a vector
|
||||
// i.e. "C:\Users\User\Documents\save.bin" becomes {"C:", "Users", "User", "Documents", "save.bin" }
|
||||
[[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename);
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
||||
[[nodiscard]] std::string SanitizePath(
|
||||
std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
|
||||
// Gets all of the text up to the last '/' or '\' in the path.
|
||||
[[nodiscard]] std::string_view GetParentPath(std::string_view path);
|
||||
|
||||
// Gets all of the text after the first '/' or '\' in the path.
|
||||
[[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path);
|
||||
|
||||
// Gets the filename of the path
|
||||
[[nodiscard]] std::string_view GetFilename(std::string_view path);
|
||||
|
||||
// Gets the extension of the filename
|
||||
[[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name);
|
||||
|
||||
} // namespace Common::FS
|
@ -12,12 +12,10 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#ifdef _WIN32
|
||||
#include <share.h> // For _SH_DENYWR
|
||||
#include <windows.h> // For OutputDebugStringW
|
||||
#else
|
||||
#define _SH_DENYWR 0
|
||||
#endif
|
||||
#include "common/assert.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/logging/backend.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/logging/text_formatter.h"
|
||||
@ -144,17 +142,16 @@ void LogcatBackend::Write(const Entry& entry) {
|
||||
PrintMessageToLogcat(entry);
|
||||
}
|
||||
|
||||
FileBackend::FileBackend(const std::string& filename) : bytes_written(0) {
|
||||
if (FileUtil::Exists(filename + ".old.txt")) {
|
||||
FileUtil::Delete(filename + ".old.txt");
|
||||
}
|
||||
if (FileUtil::Exists(filename)) {
|
||||
FileUtil::Rename(filename, filename + ".old.txt");
|
||||
}
|
||||
FileBackend::FileBackend(const std::filesystem::path& filename) : bytes_written(0) {
|
||||
auto old_filename = filename;
|
||||
old_filename += ".old.txt";
|
||||
|
||||
// _SH_DENYWR allows read only access to the file for other programs.
|
||||
// It is #defined to 0 on other platforms
|
||||
file = FileUtil::IOFile(filename, "w", _SH_DENYWR);
|
||||
// Existence checks are done within the functions themselves.
|
||||
// We don't particularly care if these succeed or not.
|
||||
static_cast<void>(Common::FS::RemoveFile(old_filename));
|
||||
static_cast<void>(Common::FS::RenameFile(filename, old_filename));
|
||||
|
||||
file = Common::FS::IOFile(filename, Common::FS::FileAccessMode::Write, Common::FS::FileType::TextFile);
|
||||
}
|
||||
|
||||
void FileBackend::Write(const Entry& entry) {
|
||||
|
@ -5,11 +5,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/filter.h"
|
||||
#include "common/fs/file.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Log {
|
||||
@ -101,7 +101,7 @@ public:
|
||||
*/
|
||||
class FileBackend : public Backend {
|
||||
public:
|
||||
explicit FileBackend(const std::string& filename);
|
||||
explicit FileBackend(const std::filesystem::path& filename);
|
||||
|
||||
static const char* Name() {
|
||||
return "file";
|
||||
@ -114,7 +114,7 @@ public:
|
||||
void Write(const Entry& entry) override;
|
||||
|
||||
private:
|
||||
FileUtil::IOFile file;
|
||||
Common::FS::IOFile file;
|
||||
std::size_t bytes_written;
|
||||
};
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include <bitset>
|
||||
#include <initializer_list>
|
||||
#include <xbyak.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <xbyak.h>
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "common/x64/xbyak_abi.h"
|
||||
|
||||
namespace Common::X64 {
|
||||
|
@ -12,6 +12,8 @@ add_library(core STATIC
|
||||
arm/dyncom/arm_dyncom_thumb.h
|
||||
arm/dyncom/arm_dyncom_trans.cpp
|
||||
arm/dyncom/arm_dyncom_trans.h
|
||||
arm/exclusive_monitor.cpp
|
||||
arm/exclusive_monitor.h
|
||||
arm/skyeye_common/arm_regformat.h
|
||||
arm/skyeye_common/armstate.cpp
|
||||
arm/skyeye_common/armstate.h
|
||||
@ -480,12 +482,14 @@ if (ENABLE_WEB_SERVICE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_ARM64)
|
||||
if (ARCHITECTURE_x86_64 OR ARCHITECTURE_arm64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.cpp
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
arm/dynarmic/arm_dynarmic_cp15.cpp
|
||||
arm/dynarmic/arm_dynarmic_cp15.h
|
||||
arm/dynarmic/arm_exclusive_monitor.cpp
|
||||
arm/dynarmic/arm_exclusive_monitor.h
|
||||
)
|
||||
target_link_libraries(core PRIVATE dynarmic)
|
||||
endif()
|
||||
|
@ -122,6 +122,9 @@ public:
|
||||
*/
|
||||
virtual void InvalidateCacheRange(u32 start_address, std::size_t length) = 0;
|
||||
|
||||
/// Clears the exclusive monitor's state.
|
||||
virtual void ClearExclusiveState() = 0;
|
||||
|
||||
/// Notify CPU emulation that page tables have changed
|
||||
virtual void SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) = 0;
|
||||
|
||||
|
@ -3,12 +3,14 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <dynarmic/A32/a32.h>
|
||||
#include <dynarmic/A32/context.h>
|
||||
#include <dynarmic/interface/A32/a32.h>
|
||||
#include <dynarmic/interface/A32/context.h>
|
||||
#include <dynarmic/interface/optimization_flags.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/gdbstub/gdbstub.h"
|
||||
@ -100,10 +102,23 @@ public:
|
||||
memory.Write64(vaddr, value);
|
||||
}
|
||||
|
||||
bool MemoryWriteExclusive8(u32 vaddr, u8 value, u8 expected) override {
|
||||
return memory.WriteExclusive8(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive16(u32 vaddr, u16 value, u16 expected) override {
|
||||
return memory.WriteExclusive16(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive32(u32 vaddr, u32 value, u32 expected) override {
|
||||
return memory.WriteExclusive32(vaddr, value, expected);
|
||||
}
|
||||
bool MemoryWriteExclusive64(u32 vaddr, u64 value, u64 expected) override {
|
||||
return memory.WriteExclusive64(vaddr, value, expected);
|
||||
}
|
||||
|
||||
void InterpreterFallback(VAddr pc, std::size_t num_instructions) override {
|
||||
// Should never happen.
|
||||
UNREACHABLE_MSG("InterpeterFallback reached with pc = 0x{:08x}, code = 0x{:08x}, num = {}",
|
||||
pc, MemoryReadCode(pc), num_instructions);
|
||||
pc, MemoryReadCode(pc).value(), num_instructions);
|
||||
}
|
||||
|
||||
void CallSVC(std::uint32_t swi) override {
|
||||
@ -114,6 +129,8 @@ public:
|
||||
switch (exception) {
|
||||
case Dynarmic::A32::Exception::UndefinedInstruction:
|
||||
case Dynarmic::A32::Exception::UnpredictableInstruction:
|
||||
case Dynarmic::A32::Exception::DecodeError:
|
||||
case Dynarmic::A32::Exception::NoExecuteFault:
|
||||
break;
|
||||
case Dynarmic::A32::Exception::Breakpoint:
|
||||
if (GDBStub::IsConnected()) {
|
||||
@ -130,10 +147,11 @@ public:
|
||||
case Dynarmic::A32::Exception::Yield:
|
||||
case Dynarmic::A32::Exception::PreloadData:
|
||||
case Dynarmic::A32::Exception::PreloadDataWithIntentToWrite:
|
||||
case Dynarmic::A32::Exception::PreloadInstruction:
|
||||
return;
|
||||
}
|
||||
ASSERT_MSG(false, "ExceptionRaised(exception = {}, pc = {:08X}, code = {:08X})", exception,
|
||||
pc, MemoryReadCode(pc));
|
||||
pc, MemoryReadCode(pc).value());
|
||||
}
|
||||
|
||||
void AddTicks(std::uint64_t ticks) override {
|
||||
@ -149,10 +167,12 @@ public:
|
||||
Memory::MemorySystem& memory;
|
||||
};
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u32 id,
|
||||
std::shared_ptr<Core::Timing::Timer> timer)
|
||||
: ARM_Interface(id, timer), system(*system), memory(memory),
|
||||
cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
|
||||
ARM_Dynarmic::ARM_Dynarmic(Core::System* system_, Memory::MemorySystem& memory_, u32 core_id_,
|
||||
std::shared_ptr<Core::Timing::Timer> timer_,
|
||||
Core::ExclusiveMonitor& exclusive_monitor_)
|
||||
: ARM_Interface(core_id_, timer_), system(*system_), memory(memory_),
|
||||
cb(std::make_unique<DynarmicUserCallbacks>(*this)),
|
||||
exclusive_monitor{dynamic_cast<Core::DynarmicExclusiveMonitor&>(exclusive_monitor_)} {
|
||||
SetPageTable(memory.GetCurrentPageTable());
|
||||
}
|
||||
|
||||
@ -208,8 +228,7 @@ u32 ARM_Dynarmic::GetVFPSystemReg(VFPSystemRegister reg) const {
|
||||
default:
|
||||
UNREACHABLE_MSG("Unknown VFP system register: {}", reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetVFPSystemReg(VFPSystemRegister reg, u32 value) {
|
||||
@ -295,6 +314,10 @@ void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, std::size_t length) {
|
||||
jit->InvalidateCacheRange(start_address, length);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ClearExclusiveState() {
|
||||
jit->ClearExclusiveState();
|
||||
}
|
||||
|
||||
std::shared_ptr<Memory::PageTable> ARM_Dynarmic::GetPageTable() const {
|
||||
return current_page_table;
|
||||
}
|
||||
@ -332,6 +355,11 @@ std::unique_ptr<Dynarmic::A32::Jit> ARM_Dynarmic::MakeJit() {
|
||||
config.page_table = ¤t_page_table->GetPointerArray();
|
||||
config.coprocessors[15] = std::make_shared<DynarmicCP15>(cp15_state);
|
||||
config.define_unpredictable_behaviour = true;
|
||||
|
||||
// Multi-process state
|
||||
config.processor_id = GetID();
|
||||
config.global_monitor = &exclusive_monitor.monitor;
|
||||
|
||||
return std::make_unique<Dynarmic::A32::Jit>(config);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <dynarmic/A32/a32.h>
|
||||
#include <dynarmic/interface/A32/a32.h>
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic_cp15.h"
|
||||
@ -17,15 +17,18 @@ class MemorySystem;
|
||||
} // namespace Memory
|
||||
|
||||
namespace Core {
|
||||
class DynarmicExclusiveMonitor;
|
||||
class ExclusiveMonitor;
|
||||
class System;
|
||||
}
|
||||
} // namespace Core
|
||||
|
||||
class DynarmicUserCallbacks;
|
||||
|
||||
class ARM_Dynarmic final : public ARM_Interface {
|
||||
public:
|
||||
ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u32 id,
|
||||
std::shared_ptr<Core::Timing::Timer> timer);
|
||||
explicit ARM_Dynarmic(Core::System* system_, Memory::MemorySystem& memory_, u32 core_id_,
|
||||
std::shared_ptr<Core::Timing::Timer> timer,
|
||||
Core::ExclusiveMonitor& exclusive_monitor_);
|
||||
~ARM_Dynarmic() override;
|
||||
|
||||
void Run() override;
|
||||
@ -52,6 +55,7 @@ public:
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void InvalidateCacheRange(u32 start_address, std::size_t length) override;
|
||||
void ClearExclusiveState() override;
|
||||
void SetPageTable(const std::shared_ptr<Memory::PageTable>& page_table) override;
|
||||
void PurgeState() override;
|
||||
|
||||
@ -69,6 +73,7 @@ private:
|
||||
|
||||
u32 fpexc = 0;
|
||||
CP15State cp15_state;
|
||||
Core::DynarmicExclusiveMonitor& exclusive_monitor;
|
||||
|
||||
Dynarmic::A32::Jit* jit = nullptr;
|
||||
std::shared_ptr<Memory::PageTable> current_page_table = nullptr;
|
||||
|
@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <dynarmic/A32/coprocessor.h>
|
||||
#include <dynarmic/interface/A32/coprocessor.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
struct CP15State {
|
||||
|
59
src/core/arm/dynarmic/arm_exclusive_monitor.cpp
Normal file
59
src/core/arm/dynarmic/arm_exclusive_monitor.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::MemorySystem& memory_,
|
||||
std::size_t core_count_)
|
||||
: monitor{core_count_}, memory{memory_} {}
|
||||
|
||||
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
|
||||
|
||||
u8 DynarmicExclusiveMonitor::ExclusiveRead8(std::size_t core_index, VAddr addr) {
|
||||
return monitor.ReadAndMark<u8>(core_index, addr, [&]() -> u8 { return memory.Read8(addr); });
|
||||
}
|
||||
|
||||
u16 DynarmicExclusiveMonitor::ExclusiveRead16(std::size_t core_index, VAddr addr) {
|
||||
return monitor.ReadAndMark<u16>(core_index, addr, [&]() -> u16 { return memory.Read16(addr); });
|
||||
}
|
||||
|
||||
u32 DynarmicExclusiveMonitor::ExclusiveRead32(std::size_t core_index, VAddr addr) {
|
||||
return monitor.ReadAndMark<u32>(core_index, addr, [&]() -> u32 { return memory.Read32(addr); });
|
||||
}
|
||||
|
||||
u64 DynarmicExclusiveMonitor::ExclusiveRead64(std::size_t core_index, VAddr addr) {
|
||||
return monitor.ReadAndMark<u64>(core_index, addr, [&]() -> u64 { return memory.Read64(addr); });
|
||||
}
|
||||
|
||||
void DynarmicExclusiveMonitor::ClearExclusive(std::size_t core_index) {
|
||||
monitor.ClearProcessor(core_index);
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
|
||||
return monitor.DoExclusiveOperation<u8>(core_index, vaddr, [&](u8 expected) -> bool {
|
||||
return memory.WriteExclusive8(vaddr, value, expected);
|
||||
});
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
|
||||
return monitor.DoExclusiveOperation<u16>(core_index, vaddr, [&](u16 expected) -> bool {
|
||||
return memory.WriteExclusive16(vaddr, value, expected);
|
||||
});
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
|
||||
return monitor.DoExclusiveOperation<u32>(core_index, vaddr, [&](u32 expected) -> bool {
|
||||
return memory.WriteExclusive32(vaddr, value, expected);
|
||||
});
|
||||
}
|
||||
|
||||
bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
|
||||
return monitor.DoExclusiveOperation<u64>(core_index, vaddr, [&](u64 expected) -> bool {
|
||||
return memory.WriteExclusive64(vaddr, value, expected);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace Core
|
40
src/core/arm/dynarmic/arm_exclusive_monitor.h
Normal file
40
src/core/arm/dynarmic/arm_exclusive_monitor.h
Normal file
@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <dynarmic/interface/exclusive_monitor.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
|
||||
namespace Memory {
|
||||
class MemorySystem;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class DynarmicExclusiveMonitor final : public ExclusiveMonitor {
|
||||
public:
|
||||
explicit DynarmicExclusiveMonitor(Memory::MemorySystem& memory_, std::size_t core_count_);
|
||||
~DynarmicExclusiveMonitor() override;
|
||||
|
||||
u8 ExclusiveRead8(std::size_t core_index, VAddr addr) override;
|
||||
u16 ExclusiveRead16(std::size_t core_index, VAddr addr) override;
|
||||
u32 ExclusiveRead32(std::size_t core_index, VAddr addr) override;
|
||||
u64 ExclusiveRead64(std::size_t core_index, VAddr addr) override;
|
||||
void ClearExclusive(std::size_t core_index) override;
|
||||
|
||||
bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) override;
|
||||
bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) override;
|
||||
bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) override;
|
||||
bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) override;
|
||||
|
||||
private:
|
||||
friend class ::ARM_Dynarmic;
|
||||
Dynarmic::ExclusiveMonitor monitor;
|
||||
Memory::MemorySystem& memory;
|
||||
};
|
||||
|
||||
} // namespace Core
|
@ -30,6 +30,7 @@ public:
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void InvalidateCacheRange(u32 start_address, std::size_t length) override;
|
||||
void ClearExclusiveState() override{};
|
||||
|
||||
void SetPC(u32 pc) override;
|
||||
u32 GetPC() const override;
|
||||
|
26
src/core/arm/exclusive_monitor.cpp
Normal file
26
src/core/arm/exclusive_monitor.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
|
||||
#endif
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace Core {
|
||||
|
||||
ExclusiveMonitor::~ExclusiveMonitor() = default;
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::MemorySystem& memory,
|
||||
std::size_t num_cores) {
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
return std::make_unique<Core::DynarmicExclusiveMonitor>(memory, num_cores);
|
||||
}
|
||||
#endif
|
||||
// TODO(merry): Passthrough exclusive monitor
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace Core
|
35
src/core/arm/exclusive_monitor.h
Normal file
35
src/core/arm/exclusive_monitor.h
Normal file
@ -0,0 +1,35 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Memory {
|
||||
class MemorySystem;
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ExclusiveMonitor {
|
||||
public:
|
||||
virtual ~ExclusiveMonitor();
|
||||
|
||||
virtual u8 ExclusiveRead8(std::size_t core_index, VAddr addr) = 0;
|
||||
virtual u16 ExclusiveRead16(std::size_t core_index, VAddr addr) = 0;
|
||||
virtual u32 ExclusiveRead32(std::size_t core_index, VAddr addr) = 0;
|
||||
virtual u64 ExclusiveRead64(std::size_t core_index, VAddr addr) = 0;
|
||||
virtual void ClearExclusive(std::size_t core_index) = 0;
|
||||
|
||||
virtual bool ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) = 0;
|
||||
virtual bool ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) = 0;
|
||||
virtual bool ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) = 0;
|
||||
virtual bool ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> MakeExclusiveMonitor(Memory::MemorySystem& memory,
|
||||
std::size_t num_cores);
|
||||
|
||||
} // namespace Core
|
@ -64,12 +64,12 @@ void CheatEngine::UpdateCheat(int index, const std::shared_ptr<CheatBase>& new_c
|
||||
}
|
||||
|
||||
void CheatEngine::SaveCheatFile() const {
|
||||
const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir);
|
||||
const std::string cheat_dir = Common::FS::GetUserPath(Common::FS::UserPath::CheatsDir);
|
||||
const std::string filepath = fmt::format(
|
||||
"{}{:016X}.txt", cheat_dir, system.Kernel().GetCurrentProcess()->codeset->program_id);
|
||||
|
||||
if (!FileUtil::IsDirectory(cheat_dir)) {
|
||||
FileUtil::CreateDir(cheat_dir);
|
||||
if (!Common::FS::IsDirectory(cheat_dir)) {
|
||||
Common::FS::CreateDir(cheat_dir);
|
||||
}
|
||||
|
||||
std::ofstream file;
|
||||
@ -84,15 +84,15 @@ void CheatEngine::SaveCheatFile() const {
|
||||
}
|
||||
|
||||
void CheatEngine::LoadCheatFile() {
|
||||
const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir);
|
||||
const std::string cheat_dir = Common::FS::GetUserPath(Common::FS::UserPath::CheatsDir);
|
||||
const std::string filepath = fmt::format(
|
||||
"{}{:016X}.txt", cheat_dir, system.Kernel().GetCurrentProcess()->codeset->program_id);
|
||||
|
||||
if (!FileUtil::IsDirectory(cheat_dir)) {
|
||||
FileUtil::CreateDir(cheat_dir);
|
||||
if (!Common::FS::IsDirectory(cheat_dir)) {
|
||||
Common::FS::CreateDir(cheat_dir);
|
||||
}
|
||||
|
||||
if (!FileUtil::Exists(filepath))
|
||||
if (!Common::FS::Exists(filepath))
|
||||
return;
|
||||
|
||||
auto gateway_cheats = GatewayCheat::LoadFile(filepath);
|
||||
|
@ -13,7 +13,8 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/texture.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_ARM64)
|
||||
#include "core/arm/exclusive_monitor.h"
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#endif
|
||||
#include "core/arm/dyncom/arm_dyncom.h"
|
||||
@ -247,7 +248,7 @@ System::ResultStatus System::SingleStep() {
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
|
||||
FileUtil::SetCurrentRomPath(filepath);
|
||||
Common::FS::SetCurrentRomPath(filepath);
|
||||
app_loader = Loader::GetLoader(filepath);
|
||||
if (!app_loader) {
|
||||
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
|
||||
@ -313,8 +314,8 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||
|
||||
if (Settings::values.custom_textures) {
|
||||
const u64 program_id = Kernel().GetCurrentProcess()->codeset->program_id;
|
||||
FileUtil::CreateFullPath(fmt::format(
|
||||
"{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), program_id));
|
||||
Common::FS::CreateFullPath(fmt::format(
|
||||
"{}textures/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir), program_id));
|
||||
custom_tex_cache->FindCustomTextures(program_id);
|
||||
}
|
||||
if (Settings::values.preload_textures) {
|
||||
@ -364,11 +365,12 @@ System::ResultStatus System::Init(Frontend::EmuWindow& emu_window, u32 system_mo
|
||||
kernel = std::make_unique<Kernel::KernelSystem>(
|
||||
*memory, *timing, [this] { PrepareReschedule(); }, system_mode, num_cores, n3ds_mode);
|
||||
|
||||
exclusive_monitor = MakeExclusiveMonitor(*memory, num_cores);
|
||||
if (Settings::values.use_cpu_jit) {
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_ARM64)
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(ARCHITECTURE_arm64)
|
||||
for (u32 i = 0; i < num_cores; ++i) {
|
||||
cpu_cores.push_back(
|
||||
std::make_shared<ARM_Dynarmic>(this, *memory, i, timing->GetTimer(i)));
|
||||
cpu_cores.push_back(std::make_shared<ARM_Dynarmic>(
|
||||
this, *memory, i, timing->GetTimer(i), *exclusive_monitor));
|
||||
}
|
||||
#else
|
||||
for (u32 i = 0; i < num_cores; ++i) {
|
||||
@ -541,6 +543,7 @@ void System::Shutdown(bool is_deserializing) {
|
||||
dsp_core.reset();
|
||||
kernel.reset();
|
||||
cpu_cores.clear();
|
||||
exclusive_monitor.reset();
|
||||
timing.reset();
|
||||
|
||||
if (video_dumper && video_dumper->IsDumping()) {
|
||||
|
@ -61,6 +61,7 @@ class RendererBase;
|
||||
|
||||
namespace Core {
|
||||
|
||||
class ExclusiveMonitor;
|
||||
class Timing;
|
||||
|
||||
class System {
|
||||
@ -361,6 +362,8 @@ private:
|
||||
std::unique_ptr<Kernel::KernelSystem> kernel;
|
||||
std::unique_ptr<Timing> timing;
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
|
||||
|
||||
private:
|
||||
static System s_instance;
|
||||
|
||||
|
@ -174,6 +174,22 @@ void Timing::Timer::MoveEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
u32 Timing::Timer::StartAdjust() {
|
||||
ASSERT((adjust_value_curr_handle & 1) == 0); // Should always be even
|
||||
adjust_value_last = std::chrono::steady_clock::now();
|
||||
return ++adjust_value_curr_handle;
|
||||
}
|
||||
|
||||
void Timing::Timer::EndAdjust(u32 start_adjust_handle) {
|
||||
std::chrono::time_point<std::chrono::steady_clock> new_timer = std::chrono::steady_clock::now();
|
||||
ASSERT(new_timer >= adjust_value_last && start_adjust_handle == adjust_value_curr_handle);
|
||||
AddTicks(nsToCycles(static_cast<float>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(new_timer - adjust_value_last)
|
||||
.count() /
|
||||
cpu_clock_scale)));
|
||||
++adjust_value_curr_handle;
|
||||
}
|
||||
|
||||
s64 Timing::Timer::GetMaxSliceLength() const {
|
||||
const auto& next_event = event_queue.begin();
|
||||
if (next_event != event_queue.end()) {
|
||||
|
@ -203,6 +203,11 @@ public:
|
||||
|
||||
void MoveEvents();
|
||||
|
||||
// Use these two functions to adjust the guest system tick on host blocking operations, so
|
||||
// that the guest can tell how much time passed during the host call.
|
||||
u32 StartAdjust();
|
||||
void EndAdjust(u32 start_adjust_handle);
|
||||
|
||||
private:
|
||||
friend class Timing;
|
||||
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
||||
@ -227,6 +232,9 @@ public:
|
||||
s64 downcount = MAX_SLICE_LENGTH;
|
||||
s64 executed_ticks = 0;
|
||||
u64 idled_cycles = 0;
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> adjust_value_last;
|
||||
u32 adjust_value_curr_handle = 0;
|
||||
// Stores a scaling for the internal clockspeed. Changing this number results in
|
||||
// under/overclocking the guest cpu
|
||||
double cpu_clock_scale = 1.0;
|
||||
|
@ -45,14 +45,14 @@ void CustomTexCache::FindCustomTextures(u64 program_id) {
|
||||
// [TitleID]/tex1_[width]x[height]_[64-bit hash]_[format].png
|
||||
|
||||
const std::string load_path = fmt::format(
|
||||
"{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), program_id);
|
||||
"{}textures/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir), program_id);
|
||||
|
||||
if (FileUtil::Exists(load_path)) {
|
||||
FileUtil::FSTEntry texture_dir;
|
||||
std::vector<FileUtil::FSTEntry> textures;
|
||||
if (Common::FS::Exists(load_path)) {
|
||||
Common::FS::FSTEntry texture_dir;
|
||||
std::vector<Common::FS::FSTEntry> textures;
|
||||
// 64 nested folders should be plenty for most cases
|
||||
FileUtil::ScanDirectoryTree(load_path, texture_dir, 64);
|
||||
FileUtil::GetAllFilesFromNestedEntries(texture_dir, textures);
|
||||
Common::FS::ScanDirectoryTree(load_path, texture_dir, 64);
|
||||
Common::FS::GetAllFilesFromNestedEntries(texture_dir, textures);
|
||||
|
||||
for (const auto& file : textures) {
|
||||
if (file.isDirectory)
|
||||
|
@ -412,7 +412,7 @@ bool FFmpegMuxer::Init(const std::string& path, const Layout::FramebufferLayout&
|
||||
|
||||
InitializeFFmpegLibraries();
|
||||
|
||||
if (!FileUtil::CreateFullPath(path)) {
|
||||
if (!Common::FS::CreateFullPath(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ namespace FileSys {
|
||||
*/
|
||||
class FixSizeDiskFile : public DiskFile {
|
||||
public:
|
||||
FixSizeDiskFile(FileUtil::IOFile&& file, const Mode& mode,
|
||||
FixSizeDiskFile(Common::FS::IOFile&& file, const Mode& mode,
|
||||
std::unique_ptr<DelayGenerator> delay_generator_)
|
||||
: DiskFile(std::move(file), mode, std::move(delay_generator_)) {
|
||||
size = GetSize();
|
||||
@ -144,7 +144,7 @@ public:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(full_path, "r+b");
|
||||
Common::FS::IOFile file(full_path, "r+b");
|
||||
if (!file.IsOpen()) {
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening {}", full_path);
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
@ -248,7 +248,7 @@ Path ArchiveFactory_ExtSaveData::GetCorrectedPath(const Path& path) {
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(const Path& path,
|
||||
u64 program_id) {
|
||||
std::string fullpath = GetExtSaveDataPath(mount_point, GetCorrectedPath(path)) + "user/";
|
||||
if (!FileUtil::Exists(fullpath)) {
|
||||
if (!Common::FS::Exists(fullpath)) {
|
||||
// TODO(Subv): Verify the archive behavior of SharedExtSaveData compared to ExtSaveData.
|
||||
// ExtSaveData seems to return FS_NotFound (120) when the archive doesn't exist.
|
||||
if (!shared) {
|
||||
@ -270,12 +270,12 @@ ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path,
|
||||
// These folders are always created with the ExtSaveData
|
||||
std::string user_path = GetExtSaveDataPath(mount_point, corrected_path) + "user/";
|
||||
std::string boss_path = GetExtSaveDataPath(mount_point, corrected_path) + "boss/";
|
||||
FileUtil::CreateFullPath(user_path);
|
||||
FileUtil::CreateFullPath(boss_path);
|
||||
Common::FS::CreateFullPath(user_path);
|
||||
Common::FS::CreateFullPath(boss_path);
|
||||
|
||||
// Write the format metadata
|
||||
std::string metadata_path = GetExtSaveDataPath(mount_point, corrected_path) + "metadata";
|
||||
FileUtil::IOFile file(metadata_path, "wb");
|
||||
Common::FS::IOFile file(metadata_path, "wb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
// TODO(Subv): Find the correct error code
|
||||
@ -289,7 +289,7 @@ ResultCode ArchiveFactory_ExtSaveData::Format(const Path& path,
|
||||
ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Path& path,
|
||||
u64 program_id) const {
|
||||
std::string metadata_path = GetExtSaveDataPath(mount_point, path) + "metadata";
|
||||
FileUtil::IOFile file(metadata_path, "rb");
|
||||
Common::FS::IOFile file(metadata_path, "rb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_FS, "Could not open metadata information for archive");
|
||||
@ -305,7 +305,7 @@ ResultVal<ArchiveFormatInfo> ArchiveFactory_ExtSaveData::GetFormatInfo(const Pat
|
||||
void ArchiveFactory_ExtSaveData::WriteIcon(const Path& path, const u8* icon_data,
|
||||
std::size_t icon_size) {
|
||||
std::string game_path = FileSys::GetExtSaveDataPath(GetMountPoint(), path);
|
||||
FileUtil::IOFile icon_file(game_path + "icon", "wb");
|
||||
Common::FS::IOFile icon_file(game_path + "icon", "wb");
|
||||
icon_file.WriteBytes(icon_data, icon_size);
|
||||
}
|
||||
|
||||
|
@ -97,14 +97,14 @@ ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& pa
|
||||
return ERROR_NOT_FOUND;
|
||||
} else {
|
||||
// Create the file
|
||||
FileUtil::CreateEmptyFile(full_path);
|
||||
Common::FS::CreateEmptyFile(full_path);
|
||||
}
|
||||
break;
|
||||
case PathParser::FileFound:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
|
||||
Common::FS::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
|
||||
if (!file.IsOpen()) {
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening {}", full_path);
|
||||
return ERROR_NOT_FOUND;
|
||||
@ -141,7 +141,7 @@ ResultCode SDMCArchive::DeleteFile(const Path& path) const {
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
if (FileUtil::Delete(full_path)) {
|
||||
if (Common::FS::Delete(full_path)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -168,7 +168,7 @@ ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path)
|
||||
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
|
||||
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
|
||||
|
||||
if (FileUtil::Rename(src_path_full, dest_path_full)) {
|
||||
if (Common::FS::Rename(src_path_full, dest_path_full)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -218,12 +218,12 @@ static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mou
|
||||
}
|
||||
|
||||
ResultCode SDMCArchive::DeleteDirectory(const Path& path) const {
|
||||
return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
|
||||
return DeleteDirectoryHelper(path, mount_point, Common::FS::Delete);
|
||||
}
|
||||
|
||||
ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
return DeleteDirectoryHelper(
|
||||
path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
|
||||
path, mount_point, [](const std::string& p) { return Common::FS::DeleteDirRecursively(p); });
|
||||
}
|
||||
|
||||
ResultCode SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
|
||||
@ -255,11 +255,11 @@ ResultCode SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const {
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
FileUtil::CreateEmptyFile(full_path);
|
||||
Common::FS::CreateEmptyFile(full_path);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(full_path, "wb");
|
||||
Common::FS::IOFile file(full_path, "wb");
|
||||
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
|
||||
// We do this by seeking to the right size, then writing a single null byte.
|
||||
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
|
||||
@ -297,7 +297,7 @@ ResultCode SDMCArchive::CreateDirectory(const Path& path) const {
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
if (FileUtil::CreateDir(mount_point + path.AsString())) {
|
||||
if (Common::FS::CreateDir(mount_point + path.AsString())) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -325,7 +325,7 @@ ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_p
|
||||
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
|
||||
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
|
||||
|
||||
if (FileUtil::Rename(src_path_full, dest_path_full)) {
|
||||
if (Common::FS::Rename(src_path_full, dest_path_full)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -382,7 +382,7 @@ bool ArchiveFactory_SDMC::Initialize() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FileUtil::CreateFullPath(sdmc_directory)) {
|
||||
if (!Common::FS::CreateFullPath(sdmc_directory)) {
|
||||
LOG_ERROR(Service_FS, "Unable to create SDMC path.");
|
||||
return false;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ bool ArchiveFactory_SDMCWriteOnly::Initialize() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FileUtil::CreateFullPath(sdmc_directory)) {
|
||||
if (!Common::FS::CreateFullPath(sdmc_directory)) {
|
||||
LOG_ERROR(Service_FS, "Unable to create SDMC path.");
|
||||
return false;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ ArchiveSource_SDSaveData::ArchiveSource_SDSaveData(const std::string& sdmc_direc
|
||||
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 program_id) {
|
||||
std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
|
||||
if (!FileUtil::Exists(concrete_mount_point)) {
|
||||
if (!Common::FS::Exists(concrete_mount_point)) {
|
||||
// When a SaveData archive is created for the first time, it is not yet formatted and the
|
||||
// save file/directory structure expected by the game has not yet been initialized.
|
||||
// Returning the NotFormatted error code will signal the game to provision the SaveData
|
||||
@ -60,12 +60,12 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveSource_SDSaveData::Open(u64 pr
|
||||
ResultCode ArchiveSource_SDSaveData::Format(u64 program_id,
|
||||
const FileSys::ArchiveFormatInfo& format_info) {
|
||||
std::string concrete_mount_point = GetSaveDataPath(mount_point, program_id);
|
||||
FileUtil::DeleteDirRecursively(concrete_mount_point);
|
||||
FileUtil::CreateFullPath(concrete_mount_point);
|
||||
Common::FS::DeleteDirRecursively(concrete_mount_point);
|
||||
Common::FS::CreateFullPath(concrete_mount_point);
|
||||
|
||||
// Write the format metadata
|
||||
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
|
||||
FileUtil::IOFile file(metadata_path, "wb");
|
||||
Common::FS::IOFile file(metadata_path, "wb");
|
||||
|
||||
if (file.IsOpen()) {
|
||||
file.WriteBytes(&format_info, sizeof(format_info));
|
||||
@ -76,7 +76,7 @@ ResultCode ArchiveSource_SDSaveData::Format(u64 program_id,
|
||||
|
||||
ResultVal<ArchiveFormatInfo> ArchiveSource_SDSaveData::GetFormatInfo(u64 program_id) const {
|
||||
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
|
||||
FileUtil::IOFile file(metadata_path, "rb");
|
||||
Common::FS::IOFile file(metadata_path, "rb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_FS, "Could not open metadata information for archive");
|
||||
|
@ -58,7 +58,7 @@ ArchiveFactory_SystemSaveData::ArchiveFactory_SystemSaveData(const std::string&
|
||||
ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(const Path& path,
|
||||
u64 program_id) {
|
||||
std::string fullpath = GetSystemSaveDataPath(base_path, path);
|
||||
if (!FileUtil::Exists(fullpath)) {
|
||||
if (!Common::FS::Exists(fullpath)) {
|
||||
// TODO(Subv): Check error code, this one is probably wrong
|
||||
return ERR_NOT_FORMATTED;
|
||||
}
|
||||
@ -70,8 +70,8 @@ ResultCode ArchiveFactory_SystemSaveData::Format(const Path& path,
|
||||
const FileSys::ArchiveFormatInfo& format_info,
|
||||
u64 program_id) {
|
||||
std::string fullpath = GetSystemSaveDataPath(base_path, path);
|
||||
FileUtil::DeleteDirRecursively(fullpath);
|
||||
FileUtil::CreateFullPath(fullpath);
|
||||
Common::FS::DeleteDirRecursively(fullpath);
|
||||
Common::FS::CreateFullPath(fullpath);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -66,7 +66,7 @@ Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) {
|
||||
}
|
||||
|
||||
Loader::ResultStatus CIAContainer::Load(const std::string& filepath) {
|
||||
FileUtil::IOFile file(filepath, "rb");
|
||||
Common::FS::IOFile file(filepath, "rb");
|
||||
if (!file.IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
|
@ -58,7 +58,7 @@ bool DiskFile::Close() const {
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DiskDirectory::DiskDirectory(const std::string& path) {
|
||||
directory.size = FileUtil::ScanDirectoryTree(path, directory);
|
||||
directory.size = Common::FS::ScanDirectoryTree(path, directory);
|
||||
directory.isDirectory = true;
|
||||
children_iterator = directory.children.begin();
|
||||
}
|
||||
@ -67,7 +67,7 @@ u32 DiskDirectory::Read(const u32 count, Entry* entries) {
|
||||
u32 entries_read = 0;
|
||||
|
||||
while (entries_read < count && children_iterator != directory.children.cend()) {
|
||||
const FileUtil::FSTEntry& file = *children_iterator;
|
||||
const Common::FS::FSTEntry& file = *children_iterator;
|
||||
const std::string& filename = file.virtualName;
|
||||
Entry& entry = entries[entries_read];
|
||||
|
||||
@ -80,7 +80,7 @@ u32 DiskDirectory::Read(const u32 count, Entry* entries) {
|
||||
break;
|
||||
}
|
||||
|
||||
FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
|
||||
Common::FS::SplitFilename83(filename, entry.short_name, entry.extension);
|
||||
|
||||
entry.is_directory = file.isDirectory;
|
||||
entry.is_hidden = (filename[0] == '.');
|
||||
|
@ -25,9 +25,9 @@ namespace FileSys {
|
||||
|
||||
class DiskFile : public FileBackend {
|
||||
public:
|
||||
DiskFile(FileUtil::IOFile&& file_, const Mode& mode_,
|
||||
DiskFile(Common::FS::IOFile&& file_, const Mode& mode_,
|
||||
std::unique_ptr<DelayGenerator> delay_generator_)
|
||||
: file(new FileUtil::IOFile(std::move(file_))) {
|
||||
: file(new Common::FS::IOFile(std::move(file_))) {
|
||||
delay_generator = std::move(delay_generator_);
|
||||
mode.hex = mode_.hex;
|
||||
}
|
||||
@ -45,7 +45,7 @@ public:
|
||||
|
||||
protected:
|
||||
Mode mode;
|
||||
std::unique_ptr<FileUtil::IOFile> file;
|
||||
std::unique_ptr<Common::FS::IOFile> file;
|
||||
|
||||
private:
|
||||
DiskFile() = default;
|
||||
@ -74,11 +74,11 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
FileUtil::FSTEntry directory{};
|
||||
Common::FS::FSTEntry directory{};
|
||||
|
||||
// We need to remember the last entry we returned, so a subsequent call to Read will continue
|
||||
// from the next one. This iterator will always point to the next unread entry.
|
||||
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
|
||||
std::vector<Common::FS::FSTEntry>::iterator children_iterator;
|
||||
|
||||
private:
|
||||
DiskDirectory() = default;
|
||||
|
@ -140,17 +140,17 @@ std::string LayeredFS::ReadName(u32 offset, u32 name_length) {
|
||||
}
|
||||
|
||||
void LayeredFS::LoadRelocations() {
|
||||
if (!FileUtil::Exists(patch_path)) {
|
||||
if (!Common::FS::Exists(patch_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const FileUtil::DirectoryEntryCallable callback = [this,
|
||||
const Common::FS::DirectoryEntryCallable callback = [this,
|
||||
&callback](u64* /*num_entries_out*/,
|
||||
const std::string& directory,
|
||||
const std::string& virtual_name) {
|
||||
auto* parent = directory_path_map.at(directory.substr(patch_path.size() - 1));
|
||||
|
||||
if (FileUtil::IsDirectory(directory + virtual_name + DIR_SEP)) {
|
||||
if (Common::FS::IsDirectory(directory + virtual_name + DIR_SEP)) {
|
||||
const auto path = (directory + virtual_name + DIR_SEP).substr(patch_path.size() - 1);
|
||||
if (!directory_path_map.count(path)) { // Add this directory
|
||||
auto directory = std::make_unique<Directory>();
|
||||
@ -161,7 +161,7 @@ void LayeredFS::LoadRelocations() {
|
||||
parent->directories.emplace_back(std::move(directory));
|
||||
LOG_INFO(Service_FS, "LayeredFS created directory {}", path);
|
||||
}
|
||||
return FileUtil::ForeachDirectoryEntry(nullptr, directory + virtual_name + DIR_SEP,
|
||||
return Common::FS::ForeachDirectoryEntry(nullptr, directory + virtual_name + DIR_SEP,
|
||||
callback);
|
||||
}
|
||||
|
||||
@ -179,16 +179,16 @@ void LayeredFS::LoadRelocations() {
|
||||
auto* file = file_path_map.at(path);
|
||||
file->relocation.type = 1;
|
||||
file->relocation.replace_file_path = directory + virtual_name;
|
||||
file->relocation.size = FileUtil::GetSize(directory + virtual_name);
|
||||
file->relocation.size = Common::FS::GetSize(directory + virtual_name);
|
||||
LOG_INFO(Service_FS, "LayeredFS replacement file in use for {}", path);
|
||||
return true;
|
||||
};
|
||||
|
||||
FileUtil::ForeachDirectoryEntry(nullptr, patch_path, callback);
|
||||
Common::FS::ForeachDirectoryEntry(nullptr, patch_path, callback);
|
||||
}
|
||||
|
||||
void LayeredFS::LoadExtRelocations() {
|
||||
if (!FileUtil::Exists(patch_ext_path)) {
|
||||
if (!Common::FS::Exists(patch_ext_path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -197,11 +197,11 @@ void LayeredFS::LoadExtRelocations() {
|
||||
patch_ext_path.erase(patch_ext_path.size() - 1, 1);
|
||||
}
|
||||
|
||||
FileUtil::FSTEntry result;
|
||||
FileUtil::ScanDirectoryTree(patch_ext_path, result, 256);
|
||||
Common::FS::FSTEntry result;
|
||||
Common::FS::ScanDirectoryTree(patch_ext_path, result, 256);
|
||||
|
||||
for (const auto& entry : result.children) {
|
||||
if (FileUtil::IsDirectory(entry.physicalName)) {
|
||||
if (Common::FS::IsDirectory(entry.physicalName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -230,7 +230,7 @@ void LayeredFS::LoadExtRelocations() {
|
||||
continue;
|
||||
}
|
||||
|
||||
FileUtil::IOFile patch_file(entry.physicalName, "rb");
|
||||
Common::FS::IOFile patch_file(entry.physicalName, "rb");
|
||||
if (!patch_file) {
|
||||
LOG_ERROR(Service_FS, "LayeredFS Could not open file {}", entry.physicalName);
|
||||
continue;
|
||||
@ -522,7 +522,7 @@ std::size_t LayeredFS::ReadFile(std::size_t offset, std::size_t length, u8* buff
|
||||
romfs->ReadFile(relocation.original_offset + relative_offset, to_read,
|
||||
buffer + read_size);
|
||||
} else if (relocation.type == 1) { // replace
|
||||
FileUtil::IOFile replace_file(relocation.replace_file_path, "rb");
|
||||
Common::FS::IOFile replace_file(relocation.replace_file_path, "rb");
|
||||
if (replace_file) {
|
||||
replace_file.Seek(relative_offset, SEEK_SET);
|
||||
replace_file.ReadBytes(buffer + read_size, to_read);
|
||||
@ -548,7 +548,7 @@ std::size_t LayeredFS::ReadFile(std::size_t offset, std::size_t length, u8* buff
|
||||
}
|
||||
|
||||
bool LayeredFS::ExtractDirectory(Directory& current, const std::string& target_path) {
|
||||
if (!FileUtil::CreateFullPath(target_path + current.path)) {
|
||||
if (!Common::FS::CreateFullPath(target_path + current.path)) {
|
||||
LOG_ERROR(Service_FS, "Could not create path {}", target_path + current.path);
|
||||
return false;
|
||||
}
|
||||
@ -560,7 +560,7 @@ bool LayeredFS::ExtractDirectory(Directory& current, const std::string& target_p
|
||||
const auto path = target_path + file->path;
|
||||
LOG_INFO(Service_FS, "Extracting {} to {}", file->path, path);
|
||||
|
||||
FileUtil::IOFile target_file(path, "wb");
|
||||
Common::FS::IOFile target_file(path, "wb");
|
||||
if (!target_file) {
|
||||
LOG_ERROR(Service_FS, "Could not open file {}", path);
|
||||
return false;
|
||||
|
@ -116,7 +116,7 @@ static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decom
|
||||
|
||||
NCCHContainer::NCCHContainer(const std::string& filepath, u32 ncch_offset, u32 partition)
|
||||
: ncch_offset(ncch_offset), partition(partition), filepath(filepath) {
|
||||
file = FileUtil::IOFile(filepath, "rb");
|
||||
file = Common::FS::IOFile(filepath, "rb");
|
||||
}
|
||||
|
||||
Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath, u32 ncch_offset,
|
||||
@ -124,7 +124,7 @@ Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath, u32 nc
|
||||
this->filepath = filepath;
|
||||
this->ncch_offset = ncch_offset;
|
||||
this->partition = partition;
|
||||
file = FileUtil::IOFile(filepath, "rb");
|
||||
file = Common::FS::IOFile(filepath, "rb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
LOG_WARNING(Service_FS, "Failed to open {}", filepath);
|
||||
@ -330,7 +330,7 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
|
||||
// System archives and DLC don't have an extended header but have RomFS
|
||||
if (ncch_header.extended_header_size) {
|
||||
auto read_exheader = [this](FileUtil::IOFile& file) {
|
||||
auto read_exheader = [this](Common::FS::IOFile& file) {
|
||||
const std::size_t size = sizeof(exheader_header);
|
||||
return file && file.ReadBytes(&exheader_header, size) == size;
|
||||
};
|
||||
@ -360,7 +360,7 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
}
|
||||
|
||||
const auto mods_path =
|
||||
fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
fmt::format("{}mods/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
|
||||
GetModId(ncch_header.program_id));
|
||||
const std::array<std::string, 2> exheader_override_paths{{
|
||||
mods_path + "exheader.bin",
|
||||
@ -369,7 +369,7 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
|
||||
bool has_exheader_override = false;
|
||||
for (const auto& path : exheader_override_paths) {
|
||||
FileUtil::IOFile exheader_override_file{path, "rb"};
|
||||
Common::FS::IOFile exheader_override_file{path, "rb"};
|
||||
if (read_exheader(exheader_override_file)) {
|
||||
has_exheader_override = true;
|
||||
break;
|
||||
@ -430,7 +430,7 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
.ProcessData(data, data, sizeof(exefs_header));
|
||||
}
|
||||
|
||||
exefs_file = FileUtil::IOFile(filepath, "rb");
|
||||
exefs_file = Common::FS::IOFile(filepath, "rb");
|
||||
has_exefs = true;
|
||||
}
|
||||
|
||||
@ -451,15 +451,15 @@ Loader::ResultStatus NCCHContainer::Load() {
|
||||
Loader::ResultStatus NCCHContainer::LoadOverrides() {
|
||||
// Check for split-off files, mark the archive as tainted if we will use them
|
||||
std::string romfs_override = filepath + ".romfs";
|
||||
if (FileUtil::Exists(romfs_override)) {
|
||||
if (Common::FS::Exists(romfs_override)) {
|
||||
is_tainted = true;
|
||||
}
|
||||
|
||||
// If we have a split-off exefs file/folder, it takes priority
|
||||
std::string exefs_override = filepath + ".exefs";
|
||||
std::string exefsdir_override = filepath + ".exefsdir/";
|
||||
if (FileUtil::Exists(exefs_override)) {
|
||||
exefs_file = FileUtil::IOFile(exefs_override, "rb");
|
||||
if (Common::FS::Exists(exefs_override)) {
|
||||
exefs_file = Common::FS::IOFile(exefs_override, "rb");
|
||||
|
||||
if (exefs_file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) == sizeof(ExeFs_Header)) {
|
||||
LOG_DEBUG(Service_FS, "Loading ExeFS section from {}", exefs_override);
|
||||
@ -467,9 +467,9 @@ Loader::ResultStatus NCCHContainer::LoadOverrides() {
|
||||
is_tainted = true;
|
||||
has_exefs = true;
|
||||
} else {
|
||||
exefs_file = FileUtil::IOFile(filepath, "rb");
|
||||
exefs_file = Common::FS::IOFile(filepath, "rb");
|
||||
}
|
||||
} else if (FileUtil::Exists(exefsdir_override) && FileUtil::IsDirectory(exefsdir_override)) {
|
||||
} else if (Common::FS::Exists(exefsdir_override) && Common::FS::IsDirectory(exefsdir_override)) {
|
||||
is_tainted = true;
|
||||
}
|
||||
|
||||
@ -585,7 +585,7 @@ Loader::ResultStatus NCCHContainer::ApplyCodePatch(std::vector<u8>& code) const
|
||||
};
|
||||
|
||||
const auto mods_path =
|
||||
fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
fmt::format("{}mods/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
|
||||
GetModId(ncch_header.program_id));
|
||||
const std::array<PatchLocation, 6> patch_paths{{
|
||||
{mods_path + "exefs/code.ips", Patch::ApplyIpsPatch},
|
||||
@ -597,7 +597,7 @@ Loader::ResultStatus NCCHContainer::ApplyCodePatch(std::vector<u8>& code) const
|
||||
}};
|
||||
|
||||
for (const PatchLocation& info : patch_paths) {
|
||||
FileUtil::IOFile file{info.path, "rb"};
|
||||
Common::FS::IOFile file{info.path, "rb"};
|
||||
if (!file)
|
||||
continue;
|
||||
|
||||
@ -631,7 +631,7 @@ Loader::ResultStatus NCCHContainer::LoadOverrideExeFSSection(const char* name,
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
const auto mods_path =
|
||||
fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
fmt::format("{}mods/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
|
||||
GetModId(ncch_header.program_id));
|
||||
const std::array<std::string, 3> override_paths{{
|
||||
mods_path + "exefs/" + override_name,
|
||||
@ -640,7 +640,7 @@ Loader::ResultStatus NCCHContainer::LoadOverrideExeFSSection(const char* name,
|
||||
}};
|
||||
|
||||
for (const auto& path : override_paths) {
|
||||
FileUtil::IOFile section_file(path, "rb");
|
||||
Common::FS::IOFile section_file(path, "rb");
|
||||
|
||||
if (section_file.IsOpen()) {
|
||||
auto section_size = section_file.GetSize();
|
||||
@ -683,7 +683,7 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<RomFSReader>& romf
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
// We reopen the file, to allow its position to be independent from file's
|
||||
FileUtil::IOFile romfs_file_inner(filepath, "rb");
|
||||
Common::FS::IOFile romfs_file_inner(filepath, "rb");
|
||||
if (!romfs_file_inner.IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
@ -698,10 +698,10 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr<RomFSReader>& romf
|
||||
}
|
||||
|
||||
const auto path =
|
||||
fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
|
||||
fmt::format("{}mods/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
|
||||
GetModId(ncch_header.program_id));
|
||||
if (use_layered_fs &&
|
||||
(FileUtil::Exists(path + "romfs/") || FileUtil::Exists(path + "romfs_ext/"))) {
|
||||
(Common::FS::Exists(path + "romfs/") || Common::FS::Exists(path + "romfs_ext/"))) {
|
||||
|
||||
romfs_file = std::make_shared<LayeredFS>(std::move(direct_romfs), path + "romfs/",
|
||||
path + "romfs_ext/");
|
||||
@ -730,8 +730,8 @@ Loader::ResultStatus NCCHContainer::DumpRomFS(const std::string& target_path) {
|
||||
Loader::ResultStatus NCCHContainer::ReadOverrideRomFS(std::shared_ptr<RomFSReader>& romfs_file) {
|
||||
// Check for RomFS overrides
|
||||
std::string split_filepath = filepath + ".romfs";
|
||||
if (FileUtil::Exists(split_filepath)) {
|
||||
FileUtil::IOFile romfs_file_inner(split_filepath, "rb");
|
||||
if (Common::FS::Exists(split_filepath)) {
|
||||
Common::FS::IOFile romfs_file_inner(split_filepath, "rb");
|
||||
if (romfs_file_inner.IsOpen()) {
|
||||
LOG_WARNING(Service_FS, "File {} overriding built-in RomFS; LayeredFS not enabled",
|
||||
split_filepath);
|
||||
|
@ -368,8 +368,8 @@ private:
|
||||
u32 partition = 0;
|
||||
|
||||
std::string filepath;
|
||||
FileUtil::IOFile file;
|
||||
FileUtil::IOFile exefs_file;
|
||||
Common::FS::IOFile file;
|
||||
Common::FS::IOFile exefs_file;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include "common/file_util.h"
|
||||
#include "common/fs/fs.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/path_parser.h"
|
||||
|
||||
@ -59,7 +59,7 @@ PathParser::PathParser(const Path& path) {
|
||||
|
||||
PathParser::HostStatus PathParser::GetHostStatus(std::string_view mount_point) const {
|
||||
std::string path{mount_point};
|
||||
if (!FileUtil::IsDirectory(path))
|
||||
if (!Common::FS::IsDir(path))
|
||||
return InvalidMountPoint;
|
||||
if (path_sequence.empty()) {
|
||||
return DirectoryFound;
|
||||
@ -70,17 +70,17 @@ PathParser::HostStatus PathParser::GetHostStatus(std::string_view mount_point) c
|
||||
path += '/';
|
||||
path += *iter;
|
||||
|
||||
if (!FileUtil::Exists(path))
|
||||
if (!Common::FS::Exists(path))
|
||||
return PathNotFound;
|
||||
if (FileUtil::IsDirectory(path))
|
||||
if (Common::FS::IsDir(path))
|
||||
continue;
|
||||
return FileInPath;
|
||||
}
|
||||
|
||||
path += "/" + path_sequence.back();
|
||||
if (!FileUtil::Exists(path))
|
||||
if (!Common::FS::Exists(path))
|
||||
return NotFound;
|
||||
if (FileUtil::IsDirectory(path))
|
||||
if (Common::FS::IsDir(path))
|
||||
return DirectoryFound;
|
||||
return FileFound;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
|
||||
|
@ -30,11 +30,11 @@ private:
|
||||
*/
|
||||
class DirectRomFSReader : public RomFSReader {
|
||||
public:
|
||||
DirectRomFSReader(FileUtil::IOFile&& file, std::size_t file_offset, std::size_t data_size)
|
||||
DirectRomFSReader(Common::FS::IOFile&& file, std::size_t file_offset, std::size_t data_size)
|
||||
: is_encrypted(false), file(std::move(file)), file_offset(file_offset),
|
||||
data_size(data_size) {}
|
||||
|
||||
DirectRomFSReader(FileUtil::IOFile&& file, std::size_t file_offset, std::size_t data_size,
|
||||
DirectRomFSReader(Common::FS::IOFile&& file, std::size_t file_offset, std::size_t data_size,
|
||||
const std::array<u8, 16>& key, const std::array<u8, 16>& ctr,
|
||||
std::size_t crypto_offset)
|
||||
: is_encrypted(true), file(std::move(file)), key(key), ctr(ctr), file_offset(file_offset),
|
||||
@ -50,7 +50,7 @@ public:
|
||||
|
||||
private:
|
||||
bool is_encrypted;
|
||||
FileUtil::IOFile file;
|
||||
Common::FS::IOFile file;
|
||||
std::array<u8, 16> key;
|
||||
std::array<u8, 16> ctr;
|
||||
u64 file_offset;
|
||||
|
@ -79,14 +79,14 @@ ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& pa
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
} else {
|
||||
// Create the file
|
||||
FileUtil::CreateEmptyFile(full_path);
|
||||
Common::FS::CreateEmptyFile(full_path);
|
||||
}
|
||||
break;
|
||||
case PathParser::FileFound:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
|
||||
Common::FS::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
|
||||
if (!file.IsOpen()) {
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening {}", full_path);
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
@ -123,7 +123,7 @@ ResultCode SaveDataArchive::DeleteFile(const Path& path) const {
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
if (FileUtil::Delete(full_path)) {
|
||||
if (Common::FS::Delete(full_path)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_pa
|
||||
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
|
||||
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
|
||||
|
||||
if (FileUtil::Rename(src_path_full, dest_path_full)) {
|
||||
if (Common::FS::Rename(src_path_full, dest_path_full)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -200,12 +200,12 @@ static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mou
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const {
|
||||
return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
|
||||
return DeleteDirectoryHelper(path, mount_point, Common::FS::Delete);
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
return DeleteDirectoryHelper(
|
||||
path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
|
||||
path, mount_point, [](const std::string& p) { return Common::FS::DeleteDirRecursively(p); });
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
|
||||
@ -237,11 +237,11 @@ ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) cons
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
FileUtil::CreateEmptyFile(full_path);
|
||||
Common::FS::CreateEmptyFile(full_path);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(full_path, "wb");
|
||||
Common::FS::IOFile file(full_path, "wb");
|
||||
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
|
||||
// We do this by seeking to the right size, then writing a single null byte.
|
||||
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
|
||||
@ -281,7 +281,7 @@ ResultCode SaveDataArchive::CreateDirectory(const Path& path) const {
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
if (FileUtil::CreateDir(mount_point + path.AsString())) {
|
||||
if (Common::FS::CreateDir(mount_point + path.AsString())) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -309,7 +309,7 @@ ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& de
|
||||
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
|
||||
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
|
||||
|
||||
if (FileUtil::Rename(src_path_full, dest_path_full)) {
|
||||
if (Common::FS::Rename(src_path_full, dest_path_full)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
@ -12,9 +12,9 @@ namespace FileSys {
|
||||
bool SeedDB::Load() {
|
||||
seeds.clear();
|
||||
const std::string path{
|
||||
fmt::format("{}/seeddb.bin", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir))};
|
||||
if (!FileUtil::Exists(path)) {
|
||||
if (!FileUtil::CreateFullPath(path)) {
|
||||
fmt::format("{}/seeddb.bin", Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir))};
|
||||
if (!Common::FS::Exists(path)) {
|
||||
if (!Common::FS::CreateFullPath(path)) {
|
||||
LOG_ERROR(Service_FS, "Failed to create seed database");
|
||||
return false;
|
||||
}
|
||||
@ -24,7 +24,7 @@ bool SeedDB::Load() {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
FileUtil::IOFile file{path, "rb"};
|
||||
Common::FS::IOFile file{path, "rb"};
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_FS, "Failed to open seed database");
|
||||
return false;
|
||||
@ -59,12 +59,12 @@ bool SeedDB::Load() {
|
||||
|
||||
bool SeedDB::Save() {
|
||||
const std::string path{
|
||||
fmt::format("{}/seeddb.bin", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir))};
|
||||
if (!FileUtil::CreateFullPath(path)) {
|
||||
fmt::format("{}/seeddb.bin", Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir))};
|
||||
if (!Common::FS::CreateFullPath(path)) {
|
||||
LOG_ERROR(Service_FS, "Failed to create seed database");
|
||||
return false;
|
||||
}
|
||||
FileUtil::IOFile file{path, "wb"};
|
||||
Common::FS::IOFile file{path, "wb"};
|
||||
if (!file.IsOpen()) {
|
||||
LOG_ERROR(Service_FS, "Failed to open seed database");
|
||||
return false;
|
||||
|
@ -17,7 +17,7 @@
|
||||
namespace FileSys {
|
||||
|
||||
Loader::ResultStatus TitleMetadata::Load(const std::string& file_path) {
|
||||
FileUtil::IOFile file(file_path, "rb");
|
||||
Common::FS::IOFile file(file_path, "rb");
|
||||
if (!file.IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
@ -78,7 +78,7 @@ Loader::ResultStatus TitleMetadata::Load(const std::vector<u8>& file_data, std::
|
||||
}
|
||||
|
||||
Loader::ResultStatus TitleMetadata::Save(const std::string& file_path) {
|
||||
FileUtil::IOFile file(file_path, "wb");
|
||||
Common::FS::IOFile file(file_path, "wb");
|
||||
if (!file.IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
|
@ -18,7 +18,7 @@ void MiiSelector::Finalize(u32 return_code, HLE::Applets::MiiData mii) {
|
||||
std::vector<HLE::Applets::MiiData> LoadMiis() {
|
||||
std::vector<HLE::Applets::MiiData> miis;
|
||||
|
||||
std::string nand_directory{FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)};
|
||||
std::string nand_directory{Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)};
|
||||
FileSys::ArchiveFactory_ExtSaveData extdata_archive_factory(nand_directory, true);
|
||||
|
||||
auto archive_result = extdata_archive_factory.Open(Service::PTM::ptm_shared_extdata_id, 0);
|
||||
|
@ -849,14 +849,14 @@ static void ReadMemory() {
|
||||
SendReply("E01");
|
||||
}
|
||||
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (!memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(),
|
||||
addr)) {
|
||||
if (!Memory::IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(),
|
||||
addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
std::vector<u8> data(len);
|
||||
memory.ReadBlock(addr, data.data(), len);
|
||||
Core::System::GetInstance().Memory().ReadBlock(
|
||||
*Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len);
|
||||
|
||||
MemToGdbHex(reply, data.data(), len);
|
||||
reply[len * 2] = '\0';
|
||||
@ -873,16 +873,16 @@ static void WriteMemory() {
|
||||
auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
|
||||
u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset));
|
||||
|
||||
auto& memory = Core::System::GetInstance().Memory();
|
||||
if (!memory.IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(),
|
||||
addr)) {
|
||||
if (!Memory::IsValidVirtualAddress(*Core::System::GetInstance().Kernel().GetCurrentProcess(),
|
||||
addr)) {
|
||||
return SendReply("E00");
|
||||
}
|
||||
|
||||
std::vector<u8> data(len);
|
||||
|
||||
GdbHexToMem(data.data(), len_pos + 1, len);
|
||||
memory.WriteBlock(addr, data.data(), len);
|
||||
Core::System::GetInstance().Memory().WriteBlock(
|
||||
*Core::System::GetInstance().Kernel().GetCurrentProcess(), addr, data.data(), len);
|
||||
Core::GetRunningCore().ClearInstructionCache();
|
||||
SendReply("OK");
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "common/scm_rev.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "core/arm/arm_interface.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
@ -38,6 +39,7 @@
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
#include "core/hle/lock.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -372,7 +374,7 @@ ResultCode SVC::UnmapMemoryBlock(Handle handle, u32 addr) {
|
||||
|
||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||
ResultCode SVC::ConnectToPort(Handle* out_handle, VAddr port_name_address) {
|
||||
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), port_name_address))
|
||||
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), port_name_address))
|
||||
return ERR_NOT_FOUND;
|
||||
|
||||
static constexpr std::size_t PortNameMaxLength = 11;
|
||||
@ -539,7 +541,7 @@ ResultCode SVC::WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle
|
||||
bool wait_all, s64 nano_seconds) {
|
||||
Thread* thread = kernel.GetCurrentThreadManager().GetCurrentThread();
|
||||
|
||||
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
return ERR_INVALID_POINTER;
|
||||
|
||||
// NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If
|
||||
@ -685,7 +687,7 @@ static ResultCode ReceiveIPCRequest(Kernel::KernelSystem& kernel, Memory::Memory
|
||||
/// In a single operation, sends a IPC reply and waits for a new request.
|
||||
ResultCode SVC::ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count,
|
||||
Handle reply_target) {
|
||||
if (!memory.IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
if (!Memory::IsValidVirtualAddress(*kernel.GetCurrentProcess(), handles_address))
|
||||
return ERR_INVALID_POINTER;
|
||||
|
||||
// Check if 'handle_count' is invalid
|
||||
|
@ -337,7 +337,8 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
|
||||
}
|
||||
|
||||
// TODO(yuriks): Other checks, returning 0xD9001BEA
|
||||
if (!memory.IsValidVirtualAddress(*owner_process, entry_point)) {
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) {
|
||||
LOG_ERROR(Kernel_SVC, "(name={}): invalid entry {:08x}", name, entry_point);
|
||||
// TODO: Verify error
|
||||
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel,
|
||||
|
@ -107,7 +107,7 @@ ResultCode CIAFile::WriteTitleMetadata() {
|
||||
// If a TMD already exists for this app (ie 00000000.tmd), the incoming TMD
|
||||
// will be the same plus one, (ie 00000001.tmd), both will be kept until
|
||||
// the install is finalized and old contents can be discarded.
|
||||
if (FileUtil::Exists(GetTitleMetadataPath(media_type, tmd.GetTitleID())))
|
||||
if (Common::FS::Exists(GetTitleMetadataPath(media_type, tmd.GetTitleID())))
|
||||
is_update = true;
|
||||
|
||||
std::string tmd_path = GetTitleMetadataPath(media_type, tmd.GetTitleID(), is_update);
|
||||
@ -115,7 +115,7 @@ ResultCode CIAFile::WriteTitleMetadata() {
|
||||
// Create content/ folder if it doesn't exist
|
||||
std::string tmd_folder;
|
||||
Common::SplitPath(tmd_path, &tmd_folder, nullptr, nullptr);
|
||||
FileUtil::CreateFullPath(tmd_folder);
|
||||
Common::FS::CreateFullPath(tmd_folder);
|
||||
|
||||
// Save TMD so that we can start getting new .app paths
|
||||
if (tmd.Save(tmd_path) != Loader::ResultStatus::Success)
|
||||
@ -126,7 +126,7 @@ ResultCode CIAFile::WriteTitleMetadata() {
|
||||
Common::SplitPath(GetTitleContentPath(media_type, tmd.GetTitleID(),
|
||||
FileSys::TMDContentIndex::Main, is_update),
|
||||
&app_folder, nullptr, nullptr);
|
||||
FileUtil::CreateFullPath(app_folder);
|
||||
Common::FS::CreateFullPath(app_folder);
|
||||
|
||||
auto content_count = container.GetTitleMetadata().GetContentCount();
|
||||
content_written.resize(content_count);
|
||||
@ -169,7 +169,7 @@ ResultVal<std::size_t> CIAFile::WriteContentData(u64 offset, std::size_t length,
|
||||
// Since the incoming TMD has already been written, we can use GetTitleContentPath
|
||||
// to get the content paths to write to.
|
||||
FileSys::TitleMetadata tmd = container.GetTitleMetadata();
|
||||
FileUtil::IOFile file(GetTitleContentPath(media_type, tmd.GetTitleID(), i, is_update),
|
||||
Common::FS::IOFile file(GetTitleContentPath(media_type, tmd.GetTitleID(), i, is_update),
|
||||
content_written[i] ? "ab" : "wb");
|
||||
|
||||
if (!file.IsOpen()) {
|
||||
@ -285,7 +285,7 @@ bool CIAFile::Close() const {
|
||||
// Install aborted
|
||||
if (!complete) {
|
||||
LOG_ERROR(Service_AM, "CIAFile closed prematurely, aborting install...");
|
||||
FileUtil::DeleteDir(GetTitlePath(media_type, container.GetTitleMetadata().GetTitleID()));
|
||||
Common::FS::Delete(GetTitlePath(media_type, container.GetTitleMetadata().GetTitleID()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -294,7 +294,7 @@ bool CIAFile::Close() const {
|
||||
GetTitleMetadataPath(media_type, container.GetTitleMetadata().GetTitleID(), false);
|
||||
std::string new_tmd_path =
|
||||
GetTitleMetadataPath(media_type, container.GetTitleMetadata().GetTitleID(), true);
|
||||
if (FileUtil::Exists(new_tmd_path) && old_tmd_path != new_tmd_path) {
|
||||
if (Common::FS::Exists(new_tmd_path) && old_tmd_path != new_tmd_path) {
|
||||
FileSys::TitleMetadata old_tmd;
|
||||
FileSys::TitleMetadata new_tmd;
|
||||
|
||||
@ -315,10 +315,10 @@ bool CIAFile::Close() const {
|
||||
if (abort)
|
||||
break;
|
||||
|
||||
FileUtil::Delete(GetTitleContentPath(media_type, old_tmd.GetTitleID(), old_index));
|
||||
Common::FS::Delete(GetTitleContentPath(media_type, old_tmd.GetTitleID(), old_index));
|
||||
}
|
||||
|
||||
FileUtil::Delete(old_tmd_path);
|
||||
Common::FS::Delete(old_tmd_path);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -329,7 +329,7 @@ InstallStatus InstallCIA(const std::string& path,
|
||||
std::function<ProgressCallback>&& update_callback) {
|
||||
LOG_INFO(Service_AM, "Installing {}...", path);
|
||||
|
||||
if (!FileUtil::Exists(path)) {
|
||||
if (!Common::FS::Exists(path)) {
|
||||
LOG_ERROR(Service_AM, "File {} does not exist!", path);
|
||||
return InstallStatus::ErrorFileNotFound;
|
||||
}
|
||||
@ -350,7 +350,7 @@ InstallStatus InstallCIA(const std::string& path,
|
||||
}
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(path, "rb");
|
||||
Common::FS::IOFile file(path, "rb");
|
||||
if (!file.IsOpen())
|
||||
return InstallStatus::ErrorFailedToOpenFile;
|
||||
|
||||
@ -374,11 +374,11 @@ InstallStatus InstallCIA(const std::string& path,
|
||||
|
||||
LOG_INFO(Service_AM, "Installed {} successfully.", path);
|
||||
|
||||
const FileUtil::DirectoryEntryCallable callback =
|
||||
const Common::FS::DirectoryEntryCallable callback =
|
||||
[&callback](u64* num_entries_out, const std::string& directory,
|
||||
const std::string& virtual_name) -> bool {
|
||||
const std::string physical_name = directory + DIR_SEP + virtual_name;
|
||||
const bool is_dir = FileUtil::IsDirectory(physical_name);
|
||||
const bool is_dir = Common::FS::IsDirectory(physical_name);
|
||||
if (!is_dir) {
|
||||
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
|
||||
if (!loader) {
|
||||
@ -392,10 +392,10 @@ InstallStatus InstallCIA(const std::string& path,
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return FileUtil::ForeachDirectoryEntry(nullptr, physical_name, callback);
|
||||
return Common::FS::ForeachDirectoryEntry(nullptr, physical_name, callback);
|
||||
}
|
||||
};
|
||||
if (!FileUtil::ForeachDirectoryEntry(
|
||||
if (!Common::FS::ForeachDirectoryEntry(
|
||||
nullptr,
|
||||
GetTitlePath(
|
||||
Service::AM::GetTitleMediaType(container.GetTitleMetadata().GetTitleID()),
|
||||
@ -439,9 +439,9 @@ std::string GetTitleMetadataPath(Service::FS::MediaType media_type, u64 tid, boo
|
||||
constexpr u32 MAX_TMD_ID = 0xFFFFFFFF;
|
||||
u32 base_id = MAX_TMD_ID;
|
||||
u32 update_id = 0;
|
||||
FileUtil::FSTEntry entries;
|
||||
FileUtil::ScanDirectoryTree(content_path, entries);
|
||||
for (const FileUtil::FSTEntry& entry : entries.children) {
|
||||
Common::FS::FSTEntry entries;
|
||||
Common::FS::ScanDirectoryTree(content_path, entries);
|
||||
for (const Common::FS::FSTEntry& entry : entries.children) {
|
||||
std::string filename_filename, filename_extension;
|
||||
Common::SplitPath(entry.virtualName, nullptr, &filename_filename, &filename_extension);
|
||||
|
||||
@ -522,12 +522,12 @@ std::string GetTitlePath(Service::FS::MediaType media_type, u64 tid) {
|
||||
|
||||
std::string GetMediaTitlePath(Service::FS::MediaType media_type) {
|
||||
if (media_type == Service::FS::MediaType::NAND)
|
||||
return fmt::format("{}{}/title/", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
|
||||
return fmt::format("{}{}/title/", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
|
||||
SYSTEM_ID);
|
||||
|
||||
if (media_type == Service::FS::MediaType::SDMC)
|
||||
return fmt::format("{}Nintendo 3DS/{}/{}/title/",
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), SYSTEM_ID,
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir), SYSTEM_ID,
|
||||
SDCARD_ID);
|
||||
|
||||
if (media_type == Service::FS::MediaType::GameCard) {
|
||||
@ -546,10 +546,10 @@ void Module::ScanForTitles(Service::FS::MediaType media_type) {
|
||||
|
||||
std::string title_path = GetMediaTitlePath(media_type);
|
||||
|
||||
FileUtil::FSTEntry entries;
|
||||
FileUtil::ScanDirectoryTree(title_path, entries, 1);
|
||||
for (const FileUtil::FSTEntry& tid_high : entries.children) {
|
||||
for (const FileUtil::FSTEntry& tid_low : tid_high.children) {
|
||||
Common::FS::FSTEntry entries;
|
||||
Common::FS::ScanDirectoryTree(title_path, entries, 1);
|
||||
for (const Common::FS::FSTEntry& tid_high : entries.children) {
|
||||
for (const Common::FS::FSTEntry& tid_low : tid_high.children) {
|
||||
std::string tid_string = tid_high.virtualName + tid_low.virtualName;
|
||||
|
||||
if (tid_string.length() == TITLE_ID_VALID_LENGTH) {
|
||||
@ -632,7 +632,7 @@ void Module::Interface::FindDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
||||
content_info.ownership =
|
||||
OWNERSHIP_OWNED; // TODO(Steveice10): Pull this from the ticket.
|
||||
|
||||
if (FileUtil::Exists(GetTitleContentPath(media_type, title_id, content_requested[i]))) {
|
||||
if (Common::FS::Exists(GetTitleContentPath(media_type, title_id, content_requested[i]))) {
|
||||
content_info.ownership |= OWNERSHIP_DOWNLOADED;
|
||||
}
|
||||
|
||||
@ -684,7 +684,7 @@ void Module::Interface::ListDLCContentInfos(Kernel::HLERequestContext& ctx) {
|
||||
content_info.ownership =
|
||||
OWNERSHIP_OWNED; // TODO(Steveice10): Pull this from the ticket.
|
||||
|
||||
if (FileUtil::Exists(GetTitleContentPath(media_type, title_id, i))) {
|
||||
if (Common::FS::Exists(GetTitleContentPath(media_type, title_id, i))) {
|
||||
content_info.ownership |= OWNERSHIP_DOWNLOADED;
|
||||
}
|
||||
|
||||
@ -802,17 +802,17 @@ void Module::Interface::DeleteUserProgram(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
LOG_INFO(Service_AM, "Deleting title 0x{:016x}", title_id);
|
||||
std::string path = GetTitlePath(media_type, title_id);
|
||||
if (!FileUtil::Exists(path)) {
|
||||
if (!Common::FS::Exists(path)) {
|
||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||
ErrorLevel::Permanent));
|
||||
LOG_ERROR(Service_AM, "Title not found");
|
||||
return;
|
||||
}
|
||||
bool success = FileUtil::DeleteDirRecursively(path);
|
||||
bool success = Common::FS::DeleteDirRecursively(path);
|
||||
am->ScanForAllTitles();
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
if (!success)
|
||||
LOG_ERROR(Service_AM, "FileUtil::DeleteDirRecursively unexpectedly failed");
|
||||
LOG_ERROR(Service_AM, "Common::FS::DeleteDirRecursively unexpectedly failed");
|
||||
}
|
||||
|
||||
void Module::Interface::GetProductCode(Kernel::HLERequestContext& ctx) {
|
||||
@ -821,7 +821,7 @@ void Module::Interface::GetProductCode(Kernel::HLERequestContext& ctx) {
|
||||
u64 title_id = rp.Pop<u64>();
|
||||
std::string path = GetTitleContentPath(media_type, title_id);
|
||||
|
||||
if (!FileUtil::Exists(path)) {
|
||||
if (!Common::FS::Exists(path)) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||
ErrorLevel::Permanent));
|
||||
@ -1016,7 +1016,7 @@ void Module::Interface::CheckContentRights(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
// TODO(shinyquagsire23): Read tickets for this instead?
|
||||
bool has_rights =
|
||||
FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index));
|
||||
Common::FS::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index));
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS); // No error
|
||||
@ -1032,7 +1032,7 @@ void Module::Interface::CheckContentRightsIgnorePlatform(Kernel::HLERequestConte
|
||||
|
||||
// TODO(shinyquagsire23): Read tickets for this instead?
|
||||
bool has_rights =
|
||||
FileUtil::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index));
|
||||
Common::FS::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index));
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
rb.Push(RESULT_SUCCESS); // No error
|
||||
@ -1396,17 +1396,17 @@ void Module::Interface::DeleteProgram(Kernel::HLERequestContext& ctx) {
|
||||
LOG_INFO(Service_AM, "Deleting title 0x{:016x}", title_id);
|
||||
std::string path = GetTitlePath(media_type, title_id);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
if (!FileUtil::Exists(path)) {
|
||||
if (!Common::FS::Exists(path)) {
|
||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::AM, ErrorSummary::InvalidState,
|
||||
ErrorLevel::Permanent));
|
||||
LOG_ERROR(Service_AM, "Title not found");
|
||||
return;
|
||||
}
|
||||
bool success = FileUtil::DeleteDirRecursively(path);
|
||||
bool success = Common::FS::DeleteDirRecursively(path);
|
||||
am->ScanForAllTitles();
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
if (!success)
|
||||
LOG_ERROR(Service_AM, "FileUtil::DeleteDirRecursively unexpectedly failed");
|
||||
LOG_ERROR(Service_AM, "Common::FS::DeleteDirRecursively unexpectedly failed");
|
||||
}
|
||||
|
||||
void Module::Interface::GetSystemUpdaterMutex(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -213,10 +213,10 @@ bool Module::LoadLegacySharedFont() {
|
||||
// generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided
|
||||
// a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file
|
||||
// "shared_font.bin" in the Citra "sysdata" directory.
|
||||
std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SHARED_FONT;
|
||||
std::string filepath = Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + SHARED_FONT;
|
||||
|
||||
FileUtil::CreateFullPath(filepath); // Create path if not already created
|
||||
FileUtil::IOFile file(filepath, "rb");
|
||||
Common::FS::CreateFullPath(filepath); // Create path if not already created
|
||||
Common::FS::IOFile file(filepath, "rb");
|
||||
if (file.IsOpen()) {
|
||||
file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
|
||||
return true;
|
||||
|
@ -1380,7 +1380,7 @@ Module::Module(Core::System& system) : system(system) {
|
||||
change_state_event =
|
||||
system.Kernel().CreateEvent(Kernel::ResetType::OneShot, "CECD::change_state_event");
|
||||
|
||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);
|
||||
|
||||
// Open the SystemSaveData archive 0x00010026
|
||||
|
@ -560,7 +560,7 @@ ResultCode Module::FormatConfig() {
|
||||
} // namespace Service::CFG
|
||||
|
||||
ResultCode Module::LoadConfigNANDSaveFile() {
|
||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);
|
||||
|
||||
// Open the SystemSaveData archive 0x00010017
|
||||
|
@ -32,10 +32,10 @@
|
||||
namespace Service::FS {
|
||||
|
||||
MediaType GetMediaTypeFromPath(std::string_view path) {
|
||||
if (path.rfind(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 0) == 0) {
|
||||
if (path.rfind(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir), 0) == 0) {
|
||||
return MediaType::NAND;
|
||||
}
|
||||
if (path.rfind(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 0) == 0) {
|
||||
if (path.rfind(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir), 0) == 0) {
|
||||
return MediaType::SDMC;
|
||||
}
|
||||
return MediaType::GameCard;
|
||||
@ -263,9 +263,9 @@ ResultCode ArchiveManager::DeleteExtSaveData(MediaType media_type, u32 high, u32
|
||||
|
||||
std::string media_type_directory;
|
||||
if (media_type == MediaType::NAND) {
|
||||
media_type_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
media_type_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
} else if (media_type == MediaType::SDMC) {
|
||||
media_type_directory = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||
media_type_directory = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Unsupported media type {}", media_type);
|
||||
return ResultCode(-1); // TODO(Subv): Find the right error code
|
||||
@ -275,7 +275,7 @@ ResultCode ArchiveManager::DeleteExtSaveData(MediaType media_type, u32 high, u32
|
||||
std::string base_path =
|
||||
FileSys::GetExtDataContainerPath(media_type_directory, media_type == MediaType::NAND);
|
||||
std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path);
|
||||
if (FileUtil::Exists(extsavedata_path) && !FileUtil::DeleteDirRecursively(extsavedata_path))
|
||||
if (Common::FS::Exists(extsavedata_path) && !Common::FS::DeleteDirRecursively(extsavedata_path))
|
||||
return ResultCode(-1); // TODO(Subv): Find the right error code
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
@ -284,10 +284,10 @@ ResultCode ArchiveManager::DeleteSystemSaveData(u32 high, u32 low) {
|
||||
// Construct the binary path to the archive first
|
||||
const FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low);
|
||||
|
||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
const std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
|
||||
const std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
|
||||
if (!FileUtil::DeleteDirRecursively(systemsavedata_path)) {
|
||||
if (!Common::FS::DeleteDirRecursively(systemsavedata_path)) {
|
||||
return ResultCode(-1); // TODO(Subv): Find the right error code
|
||||
}
|
||||
|
||||
@ -298,10 +298,10 @@ ResultCode ArchiveManager::CreateSystemSaveData(u32 high, u32 low) {
|
||||
// Construct the binary path to the archive first
|
||||
const FileSys::Path path = FileSys::ConstructSystemSaveDataBinaryPath(high, low);
|
||||
|
||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
const std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
|
||||
const std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
|
||||
if (!FileUtil::CreateFullPath(systemsavedata_path)) {
|
||||
if (!Common::FS::CreateFullPath(systemsavedata_path)) {
|
||||
return ResultCode(-1); // TODO(Subv): Find the right error code
|
||||
}
|
||||
|
||||
@ -322,8 +322,8 @@ void ArchiveManager::RegisterArchiveTypes() {
|
||||
// TODO(Subv): Add the other archive types (see here for the known types:
|
||||
// http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes).
|
||||
|
||||
std::string sdmc_directory = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
|
||||
std::string nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
std::string sdmc_directory = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
|
||||
std::string nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
auto sdmc_factory = std::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory);
|
||||
if (sdmc_factory->Initialize())
|
||||
RegisterArchiveType(std::move(sdmc_factory), ArchiveIdCode::SDMC);
|
||||
|
@ -492,7 +492,10 @@ void NWM_UDS::HandleDeauthenticationFrame(const Network::WifiPacket& packet) {
|
||||
auto node_it = std::find_if(node_info.begin(), node_info.end(), [&node](const NodeInfo& info) {
|
||||
return info.network_node_id == node.node_id;
|
||||
});
|
||||
ASSERT(node_it != node_info.end());
|
||||
if (node_it == node_info.end()) {
|
||||
LOG_ERROR(Service_NWM, "node_it is last node of node_info");
|
||||
return;
|
||||
}
|
||||
|
||||
connection_status.node_bitmask &= ~(1 << (node.node_id - 1));
|
||||
connection_status.changed_nodes |= 1 << (node.node_id - 1);
|
||||
@ -1097,9 +1100,6 @@ void NWM_UDS::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
u32 data_size = rp.Pop<u32>();
|
||||
u8 flags = rp.Pop<u8>();
|
||||
|
||||
// There should never be a dest_node_id of 0
|
||||
ASSERT(dest_node_id != 0);
|
||||
|
||||
std::vector<u8> input_buffer = rp.PopStaticBuffer();
|
||||
ASSERT(input_buffer.size() >= data_size);
|
||||
input_buffer.resize(data_size);
|
||||
@ -1114,6 +1114,14 @@ void NWM_UDS::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There should never be a dest_node_id of 0
|
||||
if (dest_node_id == 0) {
|
||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
|
||||
ErrorSummary::WrongArgument, ErrorLevel::Status));
|
||||
LOG_ERROR(Service_NWM, "dest_node_id is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dest_node_id == connection_status.network_node_id) {
|
||||
LOG_ERROR(Service_NWM, "tried to send packet to itself");
|
||||
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS,
|
||||
|
@ -134,7 +134,7 @@ void Module::Interface::CheckNew3DS(Kernel::HLERequestContext& ctx) {
|
||||
}
|
||||
|
||||
static void WriteGameCoinData(GameCoin gamecoin_data) {
|
||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
FileSys::ArchiveFactory_ExtSaveData extdata_archive_factory(nand_directory, true);
|
||||
|
||||
FileSys::Path archive_path(ptm_shared_extdata_id);
|
||||
@ -167,7 +167,7 @@ static void WriteGameCoinData(GameCoin gamecoin_data) {
|
||||
}
|
||||
|
||||
static GameCoin ReadGameCoinData() {
|
||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
FileSys::ArchiveFactory_ExtSaveData extdata_archive_factory(nand_directory, true);
|
||||
|
||||
FileSys::Path archive_path(ptm_shared_extdata_id);
|
||||
@ -197,7 +197,7 @@ static GameCoin ReadGameCoinData() {
|
||||
Module::Module() {
|
||||
// Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't
|
||||
// exist
|
||||
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
|
||||
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
|
||||
FileSys::ArchiveFactory_ExtSaveData extdata_archive_factory(nand_directory, true);
|
||||
const FileSys::Path archive_path(ptm_shared_extdata_id);
|
||||
const auto archive_result = extdata_archive_factory.Open(archive_path, 0);
|
||||
|
@ -212,19 +212,25 @@ struct CTRPollFD {
|
||||
|
||||
/// Translates the resulting events of a Poll operation from 3ds specific to platform
|
||||
/// specific
|
||||
static u32 TranslateToPlatform(Events input_event) {
|
||||
static u32 TranslateToPlatform(Events input_event, bool isOutput) {
|
||||
#if _WIN32
|
||||
constexpr bool isWin = true;
|
||||
#else
|
||||
constexpr bool isWin = false;
|
||||
#endif
|
||||
|
||||
u32 ret = 0;
|
||||
if (input_event.pollin)
|
||||
ret |= POLLIN;
|
||||
if (input_event.pollpri)
|
||||
if (input_event.pollpri && !isWin)
|
||||
ret |= POLLPRI;
|
||||
if (input_event.pollhup)
|
||||
if (input_event.pollhup && (!isWin || isOutput))
|
||||
ret |= POLLHUP;
|
||||
if (input_event.pollerr)
|
||||
if (input_event.pollerr && (!isWin || isOutput))
|
||||
ret |= POLLERR;
|
||||
if (input_event.pollout)
|
||||
ret |= POLLOUT;
|
||||
if (input_event.pollnval)
|
||||
if (input_event.pollnval && (isWin && isOutput))
|
||||
ret |= POLLNVAL;
|
||||
return ret;
|
||||
}
|
||||
@ -233,20 +239,26 @@ struct CTRPollFD {
|
||||
Events revents; ///< Events received (output)
|
||||
|
||||
/// Converts a platform-specific pollfd to a 3ds specific structure
|
||||
static CTRPollFD FromPlatform(pollfd const& fd) {
|
||||
static CTRPollFD FromPlatform(SOC::SOC_U& socu, pollfd const& fd) {
|
||||
CTRPollFD result;
|
||||
result.events.hex = Events::TranslateTo3DS(fd.events).hex;
|
||||
result.revents.hex = Events::TranslateTo3DS(fd.revents).hex;
|
||||
result.fd = static_cast<u32>(fd.fd);
|
||||
for (auto iter = socu.open_sockets.begin(); iter != socu.open_sockets.end(); ++iter) {
|
||||
if (iter->second.socket_fd == fd.fd) {
|
||||
result.fd = iter->first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Converts a 3ds specific pollfd to a platform-specific structure
|
||||
static pollfd ToPlatform(CTRPollFD const& fd) {
|
||||
static pollfd ToPlatform(SOC::SOC_U& socu, CTRPollFD const& fd) {
|
||||
pollfd result;
|
||||
result.events = Events::TranslateToPlatform(fd.events);
|
||||
result.revents = Events::TranslateToPlatform(fd.revents);
|
||||
result.fd = fd.fd;
|
||||
result.events = Events::TranslateToPlatform(fd.events, false);
|
||||
result.revents = Events::TranslateToPlatform(fd.revents, true);
|
||||
auto iter = socu.open_sockets.find(fd.fd);
|
||||
result.fd = (iter != socu.open_sockets.end()) ? iter->second.socket_fd : 0;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@ -342,6 +354,14 @@ struct CTRAddrInfo {
|
||||
|
||||
static_assert(sizeof(CTRAddrInfo) == 0x130, "Size of CTRAddrInfo is not correct");
|
||||
|
||||
void SOC_U::PreTimerAdjust() {
|
||||
timer_adjust_handle = Core::System::GetInstance().GetRunningCore().GetTimer().StartAdjust();
|
||||
}
|
||||
|
||||
void SOC_U::PostTimerAdjust() {
|
||||
Core::System::GetInstance().GetRunningCore().GetTimer().EndAdjust(timer_adjust_handle);
|
||||
}
|
||||
|
||||
void SOC_U::CleanupSockets() {
|
||||
for (auto sock : open_sockets)
|
||||
closesocket(sock.second.socket_fd);
|
||||
@ -376,21 +396,28 @@ void SOC_U::Socket(Kernel::HLERequestContext& ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
u32 ret = static_cast<u32>(::socket(domain, type, protocol));
|
||||
u64 ret = static_cast<u64>(::socket(domain, type, protocol));
|
||||
u32 socketHandle = GetNextSocketID();
|
||||
|
||||
if ((s32)ret != SOCKET_ERROR_VALUE)
|
||||
open_sockets[ret] = {ret, true};
|
||||
if ((s64)ret != SOCKET_ERROR_VALUE)
|
||||
open_sockets[socketHandle] = {static_cast<decltype(SocketHolder::socket_fd)>(ret), true};
|
||||
|
||||
if ((s32)ret == SOCKET_ERROR_VALUE)
|
||||
if ((s64)ret == SOCKET_ERROR_VALUE)
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(ret);
|
||||
rb.Push(socketHandle);
|
||||
}
|
||||
|
||||
void SOC_U::Bind(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x05, 2, 4);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
u32 len = rp.Pop<u32>();
|
||||
rp.PopPID();
|
||||
auto sock_addr_buf = rp.PopStaticBuffer();
|
||||
@ -400,7 +427,7 @@ void SOC_U::Bind(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr);
|
||||
|
||||
s32 ret = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
|
||||
s32 ret = ::bind(fd_info->second.socket_fd, &sock_addr, std::max<u32>(sizeof(sock_addr), len));
|
||||
|
||||
if (ret != 0)
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
@ -413,6 +440,12 @@ void SOC_U::Bind(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x13, 3, 2);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
u32 ctr_cmd = rp.Pop<u32>();
|
||||
u32 ctr_arg = rp.Pop<u32>();
|
||||
rp.PopPID();
|
||||
@ -427,11 +460,10 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
|
||||
if (ctr_cmd == 3) { // F_GETFL
|
||||
#ifdef _WIN32
|
||||
posix_ret = 0;
|
||||
auto iter = open_sockets.find(socket_handle);
|
||||
if (iter != open_sockets.end() && iter->second.blocking == false)
|
||||
if (fd_info->second.blocking == false)
|
||||
posix_ret |= 4; // O_NONBLOCK
|
||||
#else
|
||||
int ret = ::fcntl(socket_handle, F_GETFL, 0);
|
||||
int ret = ::fcntl(fd_info->second.socket_fd, F_GETFL, 0);
|
||||
if (ret == SOCKET_ERROR_VALUE) {
|
||||
posix_ret = TranslateError(GET_ERRNO);
|
||||
return;
|
||||
@ -443,7 +475,7 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
|
||||
} else if (ctr_cmd == 4) { // F_SETFL
|
||||
#ifdef _WIN32
|
||||
unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0;
|
||||
int ret = ioctlsocket(socket_handle, FIONBIO, &tmp);
|
||||
int ret = ioctlsocket(fd_info->second.socket_fd, FIONBIO, &tmp);
|
||||
if (ret == SOCKET_ERROR_VALUE) {
|
||||
posix_ret = TranslateError(GET_ERRNO);
|
||||
return;
|
||||
@ -452,7 +484,7 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
|
||||
if (iter != open_sockets.end())
|
||||
iter->second.blocking = (tmp == 0);
|
||||
#else
|
||||
int flags = ::fcntl(socket_handle, F_GETFL, 0);
|
||||
int flags = ::fcntl(fd_info->second.socket_fd, F_GETFL, 0);
|
||||
if (flags == SOCKET_ERROR_VALUE) {
|
||||
posix_ret = TranslateError(GET_ERRNO);
|
||||
return;
|
||||
@ -462,7 +494,7 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
|
||||
if (ctr_arg & 4) // O_NONBLOCK
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
int ret = ::fcntl(socket_handle, F_SETFL, flags);
|
||||
int ret = ::fcntl(fd_info->second.socket_fd, F_SETFL, flags);
|
||||
if (ret == SOCKET_ERROR_VALUE) {
|
||||
posix_ret = TranslateError(GET_ERRNO);
|
||||
return;
|
||||
@ -478,10 +510,16 @@ void SOC_U::Fcntl(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::Listen(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x03, 2, 2);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
u32 backlog = rp.Pop<u32>();
|
||||
rp.PopPID();
|
||||
|
||||
s32 ret = ::listen(socket_handle, backlog);
|
||||
s32 ret = ::listen(fd_info->second.socket_fd, backlog);
|
||||
if (ret != 0)
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
|
||||
@ -496,11 +534,19 @@ void SOC_U::Accept(Kernel::HLERequestContext& ctx) {
|
||||
// performing nonblocking operations and spinlock until the data is available
|
||||
IPC::RequestParser rp(ctx, 0x04, 2, 2);
|
||||
const auto socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
[[maybe_unused]] const auto max_addr_len = static_cast<socklen_t>(rp.Pop<u32>());
|
||||
rp.PopPID();
|
||||
sockaddr addr;
|
||||
socklen_t addr_len = sizeof(addr);
|
||||
u32 ret = static_cast<u32>(::accept(socket_handle, &addr, &addr_len));
|
||||
PreTimerAdjust();
|
||||
u32 ret = static_cast<u32>(::accept(fd_info->second.socket_fd, &addr, &addr_len));
|
||||
PostTimerAdjust();
|
||||
|
||||
if (static_cast<s32>(ret) != SOCKET_ERROR_VALUE) {
|
||||
open_sockets[ret] = {ret, true};
|
||||
@ -543,12 +589,21 @@ void SOC_U::GetHostId(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::Close(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x0B, 1, 2);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
rp.PopPID();
|
||||
|
||||
s32 ret = 0;
|
||||
open_sockets.erase(socket_handle);
|
||||
|
||||
ret = closesocket(socket_handle);
|
||||
PreTimerAdjust();
|
||||
ret = closesocket(fd_info->second.socket_fd);
|
||||
PostTimerAdjust();
|
||||
|
||||
open_sockets.erase(socket_handle);
|
||||
|
||||
if (ret != 0)
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
@ -561,6 +616,12 @@ void SOC_U::Close(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x0A, 4, 6);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
u32 len = rp.Pop<u32>();
|
||||
u32 flags = rp.Pop<u32>();
|
||||
u32 addr_len = rp.Pop<u32>();
|
||||
@ -569,16 +630,18 @@ void SOC_U::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
auto dest_addr_buff = rp.PopStaticBuffer();
|
||||
|
||||
s32 ret = -1;
|
||||
PreTimerAdjust();
|
||||
if (addr_len > 0) {
|
||||
CTRSockAddr ctr_dest_addr;
|
||||
std::memcpy(&ctr_dest_addr, dest_addr_buff.data(), sizeof(ctr_dest_addr));
|
||||
sockaddr dest_addr = CTRSockAddr::ToPlatform(ctr_dest_addr);
|
||||
ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags,
|
||||
&dest_addr, sizeof(dest_addr));
|
||||
ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()),
|
||||
len, flags, &dest_addr, sizeof(dest_addr));
|
||||
} else {
|
||||
ret = ::sendto(socket_handle, reinterpret_cast<const char*>(input_buff.data()), len, flags,
|
||||
nullptr, 0);
|
||||
ret = ::sendto(fd_info->second.socket_fd, reinterpret_cast<const char*>(input_buff.data()),
|
||||
len, flags, nullptr, 0);
|
||||
}
|
||||
PostTimerAdjust();
|
||||
|
||||
if (ret == SOCKET_ERROR_VALUE)
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
@ -591,6 +654,12 @@ void SOC_U::SendTo(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x7, 4, 4);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
u32 len = rp.Pop<u32>();
|
||||
u32 flags = rp.Pop<u32>();
|
||||
u32 addr_len = rp.Pop<u32>();
|
||||
@ -604,19 +673,20 @@ void SOC_U::RecvFromOther(Kernel::HLERequestContext& ctx) {
|
||||
socklen_t src_addr_len = sizeof(src_addr);
|
||||
|
||||
s32 ret = -1;
|
||||
PreTimerAdjust();
|
||||
if (addr_len > 0) {
|
||||
ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags,
|
||||
&src_addr, &src_addr_len);
|
||||
ret = ::recvfrom(fd_info->second.socket_fd, reinterpret_cast<char*>(output_buff.data()),
|
||||
len, flags, &src_addr, &src_addr_len);
|
||||
if (ret >= 0 && src_addr_len > 0) {
|
||||
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
||||
std::memcpy(addr_buff.data(), &ctr_src_addr, sizeof(ctr_src_addr));
|
||||
}
|
||||
} else {
|
||||
ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags,
|
||||
NULL, 0);
|
||||
ret = ::recvfrom(fd_info->second.socket_fd, reinterpret_cast<char*>(output_buff.data()),
|
||||
len, flags, NULL, 0);
|
||||
addr_buff.resize(0);
|
||||
}
|
||||
|
||||
PostTimerAdjust();
|
||||
if (ret == SOCKET_ERROR_VALUE) {
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
} else {
|
||||
@ -636,6 +706,12 @@ void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) {
|
||||
// performing nonblocking operations and spinlock until the data is available
|
||||
IPC::RequestParser rp(ctx, 0x08, 4, 2);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
u32 len = rp.Pop<u32>();
|
||||
u32 flags = rp.Pop<u32>();
|
||||
u32 addr_len = rp.Pop<u32>();
|
||||
@ -648,19 +724,21 @@ void SOC_U::RecvFrom(Kernel::HLERequestContext& ctx) {
|
||||
socklen_t src_addr_len = sizeof(src_addr);
|
||||
|
||||
s32 ret = -1;
|
||||
PreTimerAdjust();
|
||||
if (addr_len > 0) {
|
||||
// Only get src adr if input adr available
|
||||
ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags,
|
||||
&src_addr, &src_addr_len);
|
||||
ret = ::recvfrom(fd_info->second.socket_fd, reinterpret_cast<char*>(output_buff.data()),
|
||||
len, flags, &src_addr, &src_addr_len);
|
||||
if (ret >= 0 && src_addr_len > 0) {
|
||||
ctr_src_addr = CTRSockAddr::FromPlatform(src_addr);
|
||||
std::memcpy(addr_buff.data(), &ctr_src_addr, sizeof(ctr_src_addr));
|
||||
}
|
||||
} else {
|
||||
ret = ::recvfrom(socket_handle, reinterpret_cast<char*>(output_buff.data()), len, flags,
|
||||
NULL, 0);
|
||||
ret = ::recvfrom(fd_info->second.socket_fd, reinterpret_cast<char*>(output_buff.data()),
|
||||
len, flags, NULL, 0);
|
||||
addr_buff.resize(0);
|
||||
}
|
||||
PostTimerAdjust();
|
||||
|
||||
s32 total_received = ret;
|
||||
if (ret == SOCKET_ERROR_VALUE) {
|
||||
@ -691,21 +769,32 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
// The 3ds_pollfd and the pollfd structures may be different (Windows/Linux have different
|
||||
// sizes)
|
||||
// so we have to copy the data
|
||||
// so we have to copy the data in order
|
||||
std::vector<pollfd> platform_pollfd(nfds);
|
||||
std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform);
|
||||
for (u32 i = 0; i < nfds; i++) {
|
||||
platform_pollfd[i] = CTRPollFD::ToPlatform(*this, ctr_fds[i]);
|
||||
}
|
||||
|
||||
PreTimerAdjust();
|
||||
s32 ret = ::poll(platform_pollfd.data(), nfds, timeout);
|
||||
PostTimerAdjust();
|
||||
|
||||
// Now update the output pollfd structure
|
||||
std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(),
|
||||
CTRPollFD::FromPlatform);
|
||||
// Now update the output 3ds_pollfd structure
|
||||
for (u32 i = 0; i < nfds; i++) {
|
||||
ctr_fds[i] = CTRPollFD::FromPlatform(*this, platform_pollfd[i]);
|
||||
}
|
||||
|
||||
std::vector<u8> output_fds(nfds * sizeof(CTRPollFD));
|
||||
std::memcpy(output_fds.data(), ctr_fds.data(), nfds * sizeof(CTRPollFD));
|
||||
|
||||
if (ret == SOCKET_ERROR_VALUE)
|
||||
if (ret == SOCKET_ERROR_VALUE) {
|
||||
int err = GET_ERRNO;
|
||||
LOG_ERROR(Service_SOC, "Socket error: {}", err);
|
||||
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
}
|
||||
|
||||
size_t test = platform_pollfd.size();
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 2);
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@ -716,12 +805,18 @@ void SOC_U::Poll(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x17, 2, 2);
|
||||
const auto socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
[[maybe_unused]] const auto max_addr_len = rp.Pop<u32>();
|
||||
rp.PopPID();
|
||||
|
||||
sockaddr dest_addr;
|
||||
socklen_t dest_addr_len = sizeof(dest_addr);
|
||||
s32 ret = ::getsockname(socket_handle, &dest_addr, &dest_addr_len);
|
||||
s32 ret = ::getsockname(fd_info->second.socket_fd, &dest_addr, &dest_addr_len);
|
||||
|
||||
CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
|
||||
std::vector<u8> dest_addr_buff(sizeof(ctr_dest_addr));
|
||||
@ -739,10 +834,16 @@ void SOC_U::GetSockName(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::Shutdown(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x0C, 2, 2);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
s32 how = rp.Pop<s32>();
|
||||
rp.PopPID();
|
||||
|
||||
s32 ret = ::shutdown(socket_handle, how);
|
||||
s32 ret = ::shutdown(fd_info->second.socket_fd, how);
|
||||
if (ret != 0)
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
|
||||
@ -753,12 +854,18 @@ void SOC_U::Shutdown(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::GetPeerName(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x18, 2, 2);
|
||||
const auto socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
[[maybe_unused]] const auto max_addr_len = rp.Pop<u32>();
|
||||
rp.PopPID();
|
||||
|
||||
sockaddr dest_addr;
|
||||
socklen_t dest_addr_len = sizeof(dest_addr);
|
||||
const int ret = ::getpeername(socket_handle, &dest_addr, &dest_addr_len);
|
||||
const int ret = ::getpeername(fd_info->second.socket_fd, &dest_addr, &dest_addr_len);
|
||||
|
||||
CTRSockAddr ctr_dest_addr = CTRSockAddr::FromPlatform(dest_addr);
|
||||
std::vector<u8> dest_addr_buff(sizeof(ctr_dest_addr));
|
||||
@ -781,6 +888,12 @@ void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
|
||||
// performing nonblocking operations and spinlock until the data is available
|
||||
IPC::RequestParser rp(ctx, 0x06, 2, 4);
|
||||
const auto socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
[[maybe_unused]] const auto input_addr_len = rp.Pop<u32>();
|
||||
rp.PopPID();
|
||||
auto input_addr_buf = rp.PopStaticBuffer();
|
||||
@ -789,7 +902,9 @@ void SOC_U::Connect(Kernel::HLERequestContext& ctx) {
|
||||
std::memcpy(&ctr_input_addr, input_addr_buf.data(), sizeof(ctr_input_addr));
|
||||
|
||||
sockaddr input_addr = CTRSockAddr::ToPlatform(ctr_input_addr);
|
||||
s32 ret = ::connect(socket_handle, &input_addr, sizeof(input_addr));
|
||||
PreTimerAdjust();
|
||||
s32 ret = ::connect(fd_info->second.socket_fd, &input_addr, sizeof(input_addr));
|
||||
PostTimerAdjust();
|
||||
if (ret != 0)
|
||||
ret = TranslateError(GET_ERRNO);
|
||||
|
||||
@ -821,6 +936,12 @@ void SOC_U::ShutdownSockets(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x11, 4, 2);
|
||||
u32 socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
u32 level = rp.Pop<u32>();
|
||||
s32 optname = rp.Pop<s32>();
|
||||
socklen_t optlen = static_cast<socklen_t>(rp.Pop<u32>());
|
||||
@ -838,7 +959,7 @@ void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) {
|
||||
#endif
|
||||
} else {
|
||||
char* optval_data = reinterpret_cast<char*>(optval.data());
|
||||
err = ::getsockopt(socket_handle, level, optname, optval_data, &optlen);
|
||||
err = ::getsockopt(fd_info->second.socket_fd, level, optname, optval_data, &optlen);
|
||||
if (err == SOCKET_ERROR_VALUE) {
|
||||
err = TranslateError(GET_ERRNO);
|
||||
}
|
||||
@ -854,6 +975,12 @@ void SOC_U::GetSockOpt(Kernel::HLERequestContext& ctx) {
|
||||
void SOC_U::SetSockOpt(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx, 0x12, 4, 4);
|
||||
const auto socket_handle = rp.Pop<u32>();
|
||||
auto fd_info = open_sockets.find(socket_handle);
|
||||
if (fd_info == open_sockets.end()) {
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
rb.Push(ERR_INVALID_HANDLE);
|
||||
return;
|
||||
}
|
||||
const auto level = rp.Pop<u32>();
|
||||
const auto optname = rp.Pop<s32>();
|
||||
[[maybe_unused]] const auto optlen = static_cast<socklen_t>(rp.Pop<u32>());
|
||||
@ -870,7 +997,7 @@ void SOC_U::SetSockOpt(Kernel::HLERequestContext& ctx) {
|
||||
#endif
|
||||
} else {
|
||||
const char* optval_data = reinterpret_cast<const char*>(optval.data());
|
||||
err = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval_data,
|
||||
err = static_cast<u32>(::setsockopt(fd_info->second.socket_fd, level, optname, optval_data,
|
||||
static_cast<socklen_t>(optval.size())));
|
||||
if (err == SOCKET_ERROR_VALUE) {
|
||||
err = TranslateError(GET_ERRNO);
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <unordered_map>
|
||||
#include <boost/serialization/unordered_map.hpp>
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
@ -16,7 +17,13 @@ namespace Service::SOC {
|
||||
|
||||
/// Holds information about a particular socket
|
||||
struct SocketHolder {
|
||||
#ifdef _WIN32
|
||||
using SOCKET = unsigned long long;
|
||||
SOCKET socket_fd; ///< The socket descriptor
|
||||
#else
|
||||
u32 socket_fd; ///< The socket descriptor
|
||||
#endif // _WIN32
|
||||
|
||||
bool blocking; ///< Whether the socket is blocking or not, it is only read on Windows.
|
||||
|
||||
private:
|
||||
@ -34,6 +41,10 @@ public:
|
||||
~SOC_U();
|
||||
|
||||
private:
|
||||
static constexpr ResultCode ERR_INVALID_HANDLE =
|
||||
ResultCode(ErrorDescription::InvalidHandle, ErrorModule::SOC, ErrorSummary::InvalidArgument,
|
||||
ErrorLevel::Permanent);
|
||||
|
||||
void Socket(Kernel::HLERequestContext& ctx);
|
||||
void Bind(Kernel::HLERequestContext& ctx);
|
||||
void Fcntl(Kernel::HLERequestContext& ctx);
|
||||
@ -59,16 +70,29 @@ private:
|
||||
void GetAddrInfoImpl(Kernel::HLERequestContext& ctx);
|
||||
void GetNameInfoImpl(Kernel::HLERequestContext& ctx);
|
||||
|
||||
// Socked ids
|
||||
u32 next_socket_id = 3;
|
||||
u32 GetNextSocketID() {
|
||||
return next_socket_id++;
|
||||
}
|
||||
|
||||
// System timer adjust
|
||||
u32 timer_adjust_handle;
|
||||
void PreTimerAdjust();
|
||||
void PostTimerAdjust();
|
||||
|
||||
/// Close all open sockets
|
||||
void CleanupSockets();
|
||||
|
||||
/// Holds info about the currently open sockets
|
||||
friend struct CTRPollFD;
|
||||
std::unordered_map<u32, SocketHolder> open_sockets;
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar, const unsigned int) {
|
||||
ar& boost::serialization::base_object<Kernel::SessionRequestHandler>(*this);
|
||||
ar& open_sockets;
|
||||
ar& timer_adjust_handle;
|
||||
}
|
||||
friend class boost::serialization::access;
|
||||
};
|
||||
|
@ -159,8 +159,8 @@ void LoadBootromKeys() {
|
||||
// by other applications e.g. process9. These normal keys thus aren't used by any application
|
||||
// and have no value for emulation
|
||||
|
||||
const std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + BOOTROM9;
|
||||
auto file = FileUtil::IOFile(filepath, "rb");
|
||||
const std::string filepath = Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + BOOTROM9;
|
||||
auto file = Common::FS::IOFile(filepath, "rb");
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
@ -310,8 +310,8 @@ void LoadNativeFirmKeysNew3DS() {
|
||||
// The first 0x10 bytes of the secret_sector are used as a key to decrypt a KeyX from the
|
||||
// native_firm
|
||||
const std::string filepath =
|
||||
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SECRET_SECTOR;
|
||||
auto secret = FileUtil::IOFile(filepath, "rb");
|
||||
Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + SECRET_SECTOR;
|
||||
auto secret = Common::FS::IOFile(filepath, "rb");
|
||||
if (!secret) {
|
||||
return;
|
||||
}
|
||||
@ -426,8 +426,8 @@ void LoadNativeFirmKeysNew3DS() {
|
||||
}
|
||||
|
||||
void LoadPresetKeys() {
|
||||
const std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + AES_KEYS;
|
||||
FileUtil::CreateFullPath(filepath); // Create path if not already created
|
||||
const std::string filepath = Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + AES_KEYS;
|
||||
Common::FS::CreateFullPath(filepath); // Create path if not already created
|
||||
std::ifstream file;
|
||||
OpenFStream(file, filepath, std::ios_base::in);
|
||||
if (!file) {
|
||||
|
@ -52,8 +52,8 @@ void InitSlots() {
|
||||
return;
|
||||
initialized = true;
|
||||
|
||||
const std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + BOOTROM9;
|
||||
FileUtil::IOFile file(filepath, "rb");
|
||||
const std::string filepath = Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + BOOTROM9;
|
||||
Common::FS::IOFile file(filepath, "rb");
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
@ -94,7 +94,7 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo* loadinfo, u32* offsets)
|
||||
|
||||
using Kernel::CodeSet;
|
||||
|
||||
static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr,
|
||||
static THREEDSX_Error Load3DSXFile(Common::FS::IOFile& file, u32 base_addr,
|
||||
std::shared_ptr<CodeSet>* out_codeset) {
|
||||
if (!file.IsOpen())
|
||||
return ERROR_FILE;
|
||||
@ -248,7 +248,7 @@ static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr,
|
||||
return ERROR_NONE;
|
||||
}
|
||||
|
||||
FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) {
|
||||
FileType AppLoader_THREEDSX::IdentifyType(Common::FS::IOFile& file) {
|
||||
u32 magic;
|
||||
file.Seek(0, SEEK_SET);
|
||||
if (1 != file.ReadArray<u32>(&magic, 1))
|
||||
@ -315,7 +315,7 @@ ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileSys::RomFSReader>
|
||||
LOG_DEBUG(Loader, "RomFS size: {:#010X}", romfs_size);
|
||||
|
||||
// We reopen the file, to allow its position to be independent from file's
|
||||
FileUtil::IOFile romfs_file_inner(filepath, "rb");
|
||||
Common::FS::IOFile romfs_file_inner(filepath, "rb");
|
||||
if (!romfs_file_inner.IsOpen())
|
||||
return ResultStatus::Error;
|
||||
|
||||
|
@ -17,16 +17,16 @@ namespace Loader {
|
||||
/// Loads an 3DSX file
|
||||
class AppLoader_THREEDSX final : public AppLoader {
|
||||
public:
|
||||
AppLoader_THREEDSX(FileUtil::IOFile&& file, const std::string& filename,
|
||||
AppLoader_THREEDSX(Common::FS::IOFile&& file, const std::string& filename,
|
||||
const std::string& filepath)
|
||||
: AppLoader(std::move(file)), filename(std::move(filename)), filepath(filepath) {}
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
* @param file FileUtil::IOFile open file
|
||||
* @param file Common::FS::IOFile open file
|
||||
* @return FileType found, or FileType::Error if this loader doesn't know it
|
||||
*/
|
||||
static FileType IdentifyType(FileUtil::IOFile& file);
|
||||
static FileType IdentifyType(Common::FS::IOFile& file);
|
||||
|
||||
FileType GetFileType() override {
|
||||
return IdentifyType(file);
|
||||
|
@ -364,7 +364,7 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
|
||||
|
||||
namespace Loader {
|
||||
|
||||
FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) {
|
||||
FileType AppLoader_ELF::IdentifyType(Common::FS::IOFile& file) {
|
||||
u32 magic;
|
||||
file.Seek(0, SEEK_SET);
|
||||
if (1 != file.ReadArray<u32>(&magic, 1))
|
||||
|
@ -17,15 +17,15 @@ namespace Loader {
|
||||
/// Loads an ELF/AXF file
|
||||
class AppLoader_ELF final : public AppLoader {
|
||||
public:
|
||||
AppLoader_ELF(FileUtil::IOFile&& file, std::string filename)
|
||||
AppLoader_ELF(Common::FS::IOFile&& file, std::string filename)
|
||||
: AppLoader(std::move(file)), filename(std::move(filename)) {}
|
||||
|
||||
/**
|
||||
* Returns the type of the file
|
||||
* @param file FileUtil::IOFile open file
|
||||
* @param file Common::FS::IOFile open file
|
||||
* @return FileType found, or FileType::Error if this loader doesn't know it
|
||||
*/
|
||||
static FileType IdentifyType(FileUtil::IOFile& file);
|
||||
static FileType IdentifyType(Common::FS::IOFile& file);
|
||||
|
||||
FileType GetFileType() override {
|
||||
return IdentifyType(file);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user