Compare commits

..

19 Commits

Author SHA1 Message Date
GPUCode
797d0e45ce blit screen temp 2022-11-16 21:54:09 +02:00
GPUCode
37ce0540ed vk_texture_runtime: HACK skip texture flushes 2022-11-15 13:59:03 +02:00
GPUCode
e978931574 renderer_vulkan: Fix LMDM crashes
* Cache vertex format lookups to avoid crashing the driver when the game does too many lookups

* Increase watcher count. For some reason the game uses way too many watchers

* Fix other misc SPIRV bugs and don't crash when shadow is requested
2022-11-13 15:29:16 +02:00
GPUCode
facddbfc8c vk_shader_gen_spv: Implement alpha testing
* Should fix all current graphical bugs
2022-11-13 10:38:54 +02:00
GPUCode
68a3217d1e vk_instance: Make DynamicLoader static
* That way we don't construct/destroy it all the time
2022-11-12 21:36:25 +02:00
GPUCode
ddfae0025c citra_qt: Add SPIR-V shader option 2022-11-07 22:33:53 +02:00
GPUCode
75b6c5b4c5 externals: Update sirit 2022-11-06 22:13:07 +02:00
GPUCode
32c6e76ab9 renderer_vulkan: Move fragment shader microprofile to a better place 2022-11-06 21:57:30 +02:00
GPUCode
479c7ed162 shader_cache: Fix type deduction 2022-11-06 21:41:57 +02:00
GPUCode
009dfa5265 cmake: Fork sirit
* Upstream is missing some required instructions
2022-11-06 21:30:57 +02:00
GPUCode
b225239e1f renderer_vulkan: Begin new fragment shader SPIR-V emitter 2022-11-06 20:34:40 +02:00
GPUCode
f887621921 renderer_vulkan: Drop requirement for VK_EXT_depth_clip_control 2022-11-03 15:32:15 +02:00
GPUCode
407ae3972a Revert "code: Use std::numbers::pi"
* Not available on Android

This reverts commit d6e545932a.
2022-11-03 14:01:24 +02:00
GPUCode
0f0ca03551 Revert "CI: dont upload macos artifacts (#6121)"
This reverts commit 30831e6367.
2022-11-03 07:12:54 +02:00
GPUCode
9ae84fb6e8 renderer_vulkan: Integrate MacOS wsi 2022-11-02 23:19:50 +02:00
GPUCode
215255d415 video_core: Fix build issues on macos 2022-11-02 23:10:31 +02:00
GPUCode
f5f88101e1 renderer_vulkan: Emulate 3-component vertex formats when unsupported
* This fixes the crashes on AMD
2022-11-02 21:46:58 +02:00
GPUCode
e511e51491 renderer_vulkan: Emulate logic op when unsupported
* Similar to GLES this is done to prepare for the android port
2022-11-01 14:24:45 +02:00
GPUCode
242047744e gl_rasterizer: Cleanup and fix bugs 2022-10-31 21:58:14 +02:00
130 changed files with 4712 additions and 3833 deletions

View File

@@ -95,6 +95,13 @@ jobs:
env:
MACOSX_DEPLOYMENT_TARGET: "10.13"
ENABLE_COMPATIBILITY_REPORTING: "ON"
- name: Pack
run: ./.ci/macos/upload.sh
- name: Upload
uses: actions/upload-artifact@v3
with:
name: macos
path: artifacts/
windows:
runs-on: windows-latest
steps:

3
.gitmodules vendored
View File

@@ -67,3 +67,6 @@
[submodule "glm"]
path = externals/glm
url = https://github.com/g-truc/glm
[submodule "sirit"]
path = externals/sirit
url = https://github.com/GPUCode/sirit

View File

@@ -67,6 +67,9 @@ set(ENABLE_SPVREMAPPER OFF)
set(ENABLE_CTEST OFF)
add_subdirectory(glslang)
# Sirit
add_subdirectory(sirit)
# glm
add_subdirectory(glm)

1
externals/sirit vendored Submodule

Submodule externals/sirit added at 297d820eeb

View File

@@ -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 = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + "config.ini";
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::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);
Common::FS::CreateFullPath(location);
Common::FS::WriteStringToFile(true, location, default_contents);
FileUtil::CreateFullPath(location);
FileUtil::WriteStringToFile(true, location, default_contents);
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
return LoadINI(default_contents, false);

View File

@@ -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 (!Common::FS::Exists(update_path))
if (!FileUtil::Exists(update_path))
return original_smdh;
std::unique_ptr<Loader::AppLoader> update_loader = Loader::GetLoader(update_path);

View File

@@ -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>());
Common::FS::CreateFullPath(Common::FS::GetUserPath(Common::FS::UserPath::LogDir));
FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
Log::AddBackend(std::make_unique<Log::FileBackend>(
Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + LOG_FILE));
FileUtil::GetUserPath(FileUtil::UserPath::LogDir) + LOG_FILE));
LOG_INFO(Frontend, "Logging backend initialised");
// Initialize misc classes

View File

@@ -154,7 +154,7 @@ static Core::System::ResultStatus RunCitra(const std::string& filepath) {
Config{};
// Replace with game-specific settings
u64 program_id{};
Common::FS::SetCurrentRomPath(filepath);
FileUtil::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) {
Common::FS::SetCurrentDir(GetJString(env, j_directory));
FileUtil::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 Common::FS::DirectoryEntryCallable ScanDir =
const FileUtil::DirectoryEntryCallable ScanDir =
[&games, &ScanDir](u64*, const std::string& directory, const std::string& virtual_name) {
std::string path = directory + virtual_name;
if (Common::FS::IsDirectory(path)) {
if (FileUtil::IsDirectory(path)) {
path += '/';
Common::FS::ForeachDirectoryEntry(nullptr, path, ScanDir);
FileUtil::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, "",
Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
"Nintendo "
"3DS/00000000000000000000000000000000/"
"00000000000000000000000000000000/title/00040000");
ScanDir(nullptr, "",
Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"00000000000000000000000000000000/title/00040010");
jobjectArray jgames = env->NewObjectArray(static_cast<jsize>(games.size()),
env->FindClass("java/lang/String"), nullptr);

View File

@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.2.0'
classpath 'com.android.tools.build:gradle:7.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@@ -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 = Common::FS::GetUserPath(Common::FS::UserPath::DLLDir);
Common::FS::CreateDir(dll_path);
std::string dll_path = FileUtil::GetUserPath(FileUtil::UserPath::DLLDir);
FileUtil::CreateDir(dll_path);
std::wstring w_dll_path = Common::UTF8ToUTF16W(dll_path);
SetDllDirectoryW(w_dll_path.c_str());

View File

@@ -170,8 +170,8 @@ static void InitializeLogging() {
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
Common::FS::CreateFullPath(log_dir);
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
#ifdef _WIN32
Log::AddBackend(std::make_unique<Log::DebuggerBackend>());

View File

@@ -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 = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + "sdl2-config.ini";
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::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);
Common::FS::CreateFullPath(location);
Common::FS::WriteStringToFile(true, location, default_contents);
FileUtil::CreateFullPath(location);
FileUtil::WriteStringToFile(true, location, default_contents);
sdl2_config = std::make_unique<INIReader>(location); // Reopen file
return LoadINI(default_contents, false);
@@ -208,9 +208,9 @@ void Config::ReadValues() {
sdl2_config->GetBoolean("Data Storage", "use_custom_storage", false);
if (Settings::values.use_custom_storage) {
Common::FS::UpdateUserPath(Common::FS::UserPath::NANDDir,
FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir,
sdl2_config->GetString("Data Storage", "nand_directory", ""));
Common::FS::UpdateUserPath(Common::FS::UserPath::SDMCDir,
FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir,
sdl2_config->GetString("Data Storage", "sdmc_directory", ""));
}

View File

@@ -320,6 +320,8 @@ static Frontend::WindowSystemType GetWindowSystemType() {
return Frontend::WindowSystemType::X11;
else if (platform_name == QStringLiteral("wayland"))
return Frontend::WindowSystemType::Wayland;
else if (platform_name == QStringLiteral("cocoa"))
return Frontend::WindowSystemType::MacOS;
LOG_CRITICAL(Frontend, "Unknown Qt platform!");
return Frontend::WindowSystemType::Windows;

View File

@@ -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 = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + "qt-config.ini";
Common::FS::CreateFullPath(qt_config_loc);
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
FileUtil::CreateFullPath(qt_config_loc);
qt_config =
std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
Reload();
@@ -312,8 +312,8 @@ void Config::ReadDataStorageValues() {
ReadSetting(QStringLiteral("sdmc_directory"), QStringLiteral("")).toString().toStdString();
if (Settings::values.use_custom_storage) {
Common::FS::UpdateUserPath(Common::FS::UserPath::NANDDir, nand_dir);
Common::FS::UpdateUserPath(Common::FS::UserPath::SDMCDir, sdmc_dir);
FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir, nand_dir);
FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir, sdmc_dir);
}
qt_config->endGroup();
@@ -486,6 +486,7 @@ void Config::ReadRendererValues() {
.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.spirv_shader_gen = ReadSetting(QStringLiteral("spirv_shader_gen"), false).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();
@@ -875,10 +876,10 @@ void Config::SaveDataStorageValues() {
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(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)),
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
QStringLiteral(""));
WriteSetting(QStringLiteral("sdmc_directory"),
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)),
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
QStringLiteral(""));
qt_config->endGroup();
@@ -1006,6 +1007,7 @@ void Config::SaveRendererValues() {
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("spirv_shader_gen"), Settings::values.spirv_shader_gen, false);
WriteSetting(QStringLiteral("use_hw_renderer"), Settings::values.use_hw_renderer, true);
WriteSetting(QStringLiteral("use_hw_shader"), Settings::values.use_hw_shader, true);
#ifdef __APPLE__

View File

@@ -22,7 +22,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent)
SetConfiguration();
connect(ui->open_log_button, &QPushButton::clicked, []() {
QString path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LogDir));
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
});

View File

@@ -117,9 +117,9 @@ void ConfigureGeneral::SetConfiguration() {
QString screenshot_path = UISettings::values.screenshot_path;
if (screenshot_path.isEmpty()) {
screenshot_path =
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir));
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir));
screenshot_path.append(QStringLiteral("screenshots/"));
Common::FS::CreateFullPath(screenshot_path.toStdString());
FileUtil::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;
Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + "qt-config.ini");
FileUtil::Delete(FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini");
std::exit(0);
}

View File

@@ -85,6 +85,7 @@ void ConfigureGraphics::SetConfiguration() {
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);
ui->spirv_shader_gen->setChecked(Settings::values.spirv_shader_gen);
}
void ConfigureGraphics::ApplyConfiguration() {
@@ -99,6 +100,7 @@ void ConfigureGraphics::ApplyConfiguration() {
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();
Settings::values.spirv_shader_gen = ui->spirv_shader_gen->isChecked();
}
void ConfigureGraphics::RetranslateUI() {
@@ -121,4 +123,5 @@ void ConfigureGraphics::SetPhysicalDeviceComboVisibility(int index) {
const bool is_visible = graphics_api == Settings::GraphicsAPI::Vulkan;
ui->physical_device_label->setVisible(is_visible);
ui->physical_device_combo->setVisible(is_visible);
ui->spirv_shader_gen->setVisible(is_visible);
}

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>400</width>
<height>430</height>
<height>513</height>
</rect>
</property>
<property name="minimumSize">
@@ -70,6 +70,13 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="spirv_shader_gen">
<property name="text">
<string>SPIR-V Shader Generation</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -16,33 +16,33 @@ ConfigureStorage::ConfigureStorage(QWidget* parent)
SetConfiguration();
connect(ui->open_nand_dir, &QPushButton::clicked, []() {
QString path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::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(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)),
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)),
QFileDialog::ShowDirsOnly);
if (!dir_path.isEmpty()) {
Common::FS::UpdateUserPath(Common::FS::UserPath::NANDDir, dir_path.toStdString());
FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir, dir_path.toStdString());
SetConfiguration();
}
});
connect(ui->open_sdmc_dir, &QPushButton::clicked, []() {
QString path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::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(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)),
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
QFileDialog::ShowDirsOnly);
if (!dir_path.isEmpty()) {
Common::FS::UpdateUserPath(Common::FS::UserPath::SDMCDir, dir_path.toStdString());
FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir, dir_path.toStdString());
SetConfiguration();
}
});
@@ -61,13 +61,13 @@ ConfigureStorage::~ConfigureStorage() = default;
void ConfigureStorage::SetConfiguration() {
ui->nand_group->setVisible(Settings::values.use_custom_storage);
QString nand_path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
QString nand_path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::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 &&
Settings::values.use_custom_storage);
QString sdmc_path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
QString sdmc_path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
ui->sdmc_dir_path->setText(sdmc_path);
ui->open_sdmc_dir->setEnabled(!sdmc_path.isEmpty());
@@ -82,10 +82,10 @@ void ConfigureStorage::ApplyConfiguration() {
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));
FileUtil::UpdateUserPath(FileUtil::UserPath::NANDDir,
GetDefaultUserPath(FileUtil::UserPath::NANDDir));
FileUtil::UpdateUserPath(FileUtil::UserPath::SDMCDir,
GetDefaultUserPath(FileUtil::UserPath::SDMCDir));
}
}

View File

@@ -493,15 +493,15 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
const bool is_application =
0x0004000000000000 <= program_id && program_id <= 0x00040000FFFFFFFF;
std::string sdmc_dir = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
open_save_location->setEnabled(
is_application && Common::FS::Exists(FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(
is_application && FileUtil::Exists(FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(
sdmc_dir, program_id)));
if (extdata_id) {
open_extdata_location->setEnabled(
is_application &&
Common::FS::Exists(FileSys::GetExtDataPathFromId(sdmc_dir, extdata_id)));
FileUtil::Exists(FileSys::GetExtDataPathFromId(sdmc_dir, extdata_id)));
} else {
open_extdata_location->setVisible(false);
}
@@ -510,7 +510,7 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
open_application_location->setEnabled(path.toStdString() ==
Service::AM::GetTitleContentPath(media_type, program_id));
open_update_location->setEnabled(
is_application && Common::FS::Exists(Service::AM::GetTitlePath(Service::FS::MediaType::SDMC,
is_application && FileUtil::Exists(Service::AM::GetTitlePath(Service::FS::MediaType::SDMC,
program_id + 0xe00000000) +
"content/"));
auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
@@ -535,29 +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 (Common::FS::CreateFullPath(fmt::format("{}textures/{:016X}/",
Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/",
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
program_id))) {
emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_DUMP);
}
});
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),
if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/",
FileUtil::GetUserPath(FileUtil::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),
if (FileUtil::CreateFullPath(fmt::format("{}textures/{:016X}/",
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
program_id))) {
emit OpenFolderRequested(program_id, GameListOpenTarget::TEXTURE_LOAD);
}
});
connect(open_mods_location, &QAction::triggered, this, [this, program_id] {
if (Common::FS::CreateFullPath(fmt::format("{}mods/{:016X}/",
Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
if (FileUtil::CreateFullPath(fmt::format("{}mods/{:016X}/",
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
program_id))) {
emit OpenFolderRequested(program_id, GameListOpenTarget::MODS);
}
@@ -568,7 +568,7 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
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))) {
if (FileUtil::CreateFullPath(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir))) {
emit OpenFolderRequested(program_id, GameListOpenTarget::SHADER_CACHE);
}
});
@@ -576,14 +576,14 @@ void GameList::AddGamePopup(QMenu& context_menu, const QString& path, u64 progra
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),
FileUtil::GetUserPath(FileUtil::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));
fmt::format("{}vulkan", FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir));
QDir dir{QString::fromStdString(path)};
dir.removeRecursively();
});

View File

@@ -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 = Common::FS::IsDirectory(physical_name);
const bool is_dir = FileUtil::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 (Common::FS::Exists(update_path)) {
if (FileUtil::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(Common::FS::GetSize(physical_name)),
new GameListItemSize(FileUtil::GetSize(physical_name)),
},
parent_dir);
@@ -113,7 +113,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
return true;
};
Common::FS::ForeachDirectoryEntry(nullptr, dir_path, callback);
FileUtil::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(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)) +
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)) +
QStringLiteral("Nintendo "
"3DS/00000000000000000000000000000000/"
"00000000000000000000000000000000/title/00040000");
QString demos_path =
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)) +
QString::fromStdString(FileUtil::GetUserPath(FileUtil::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(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)) +
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)) +
QStringLiteral("00000000000000000000000000000000/title/00040010");
watch_list.append(path);
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SystemDir);

View File

@@ -138,8 +138,8 @@ static void InitializeLogging() {
log_filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(log_filter);
const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
Common::FS::CreateFullPath(log_dir);
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::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 = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
path = FileSys::ArchiveSource_SDSaveData::GetSaveDataPathFor(sdmc_dir, data_id);
break;
}
case GameListOpenTarget::EXT_DATA: {
open_target = "Extra Data";
std::string sdmc_dir = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
path = FileSys::GetExtDataPathFromId(sdmc_dir, data_id);
break;
}
@@ -1349,24 +1349,24 @@ void GMainWindow::OnGameListOpenFolder(u64 data_id, GameListOpenTarget target) {
case GameListOpenTarget::TEXTURE_DUMP: {
open_target = "Dumped Textures";
path = fmt::format("{}textures/{:016X}/",
Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), data_id);
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), data_id);
break;
}
case GameListOpenTarget::TEXTURE_LOAD: {
open_target = "Custom Textures";
path = fmt::format("{}textures/{:016X}/",
Common::FS::GetUserPath(Common::FS::UserPath::LoadDir), data_id);
FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), data_id);
break;
}
case GameListOpenTarget::MODS: {
open_target = "Mods";
path = fmt::format("{}mods/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
path = fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
data_id);
break;
}
case GameListOpenTarget::SHADER_CACHE: {
open_target = "Shader Cache";
path = Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir);
path = FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir);
break;
}
default:
@@ -1410,9 +1410,9 @@ void GMainWindow::OnGameListDumpRomFS(QString game_path, u64 program_id) {
dialog->setValue(0);
const auto base_path = fmt::format(
"{}romfs/{:016X}", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), program_id);
"{}romfs/{:016X}", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
const auto update_path =
fmt::format("{}romfs/{:016X}", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir),
fmt::format("{}romfs/{:016X}", FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
program_id | 0x0004000e00000000);
using FutureWatcher = QFutureWatcher<std::pair<Loader::ResultStatus, Loader::ResultStatus>>;
auto* future_watcher = new FutureWatcher(this);
@@ -1443,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(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) +
path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir) +
"Nintendo "
"3DS/00000000000000000000000000000000/"
"00000000000000000000000000000000/title/00040000");
} else if (directory == QStringLiteral("SYSTEM")) {
path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) +
path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
"00000000000000000000000000000000/title/00040010");
} else {
path = directory;
@@ -1873,7 +1873,7 @@ void GMainWindow::OnRemoveAmiibo() {
void GMainWindow::OnOpenCitraFolder() {
QDesktopServices::openUrl(QUrl::fromLocalFile(
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir))));
QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
}
void GMainWindow::OnToggleFilterBar() {
@@ -1973,12 +1973,12 @@ void GMainWindow::OnSaveMovie() {
void GMainWindow::OnCaptureScreenshot() {
OnPauseGame();
QString path = UISettings::values.screenshot_path;
if (!Common::FS::IsDirectory(path.toStdString())) {
if (!Common::FS::CreateFullPath(path.toStdString())) {
if (!FileUtil::IsDirectory(path.toStdString())) {
if (!FileUtil::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(Common::FS::GetUserPath(Common::FS::UserPath::UserDir));
path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir));
path.append(QStringLiteral("screenshots/"));
UISettings::values.screenshot_path = path;
};
@@ -2482,7 +2482,7 @@ int main(int argc, char* argv[]) {
QCoreApplication::setApplicationName(QStringLiteral("Citra"));
#ifdef __APPLE__
std::string bin_path = Common::FS::GetBundleDirectory() + DIR_SEP + "..";
std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + "..";
chdir(bin_path.c_str());
#endif
QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);

View File

@@ -69,20 +69,9 @@ add_library(common STATIC
common_funcs.h
common_paths.h
common_types.h
concepts.h
construct.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
file_util.cpp
file_util.h
hash.h
linear_disk_cache.h
logging/backend.cpp

View File

@@ -1,35 +0,0 @@
// 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

1073
src/common/file_util.cpp Normal file

File diff suppressed because it is too large Load Diff

396
src/common/file_util.h Normal file
View File

@@ -0,0 +1,396 @@
// 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
}

View File

@@ -1,415 +0,0 @@
// 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

View File

@@ -1,459 +0,0 @@
// 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

View File

@@ -1,624 +0,0 @@
// 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

View File

@@ -1,583 +0,0 @@
// 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

View File

@@ -1,25 +0,0 @@
// 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"

View File

@@ -1,6 +0,0 @@
#include "fs_serialize.h"
fs_serialize::fs_serialize()
{
}

View File

@@ -1,43 +0,0 @@
// 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

View File

@@ -1,71 +0,0 @@
// 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

View File

@@ -1,38 +0,0 @@
// 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

View File

@@ -1,85 +0,0 @@
// 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

View File

@@ -1,428 +0,0 @@
// 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

View File

@@ -1,302 +0,0 @@
// 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

View File

@@ -12,10 +12,12 @@
#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"
@@ -142,16 +144,17 @@ void LogcatBackend::Write(const Entry& entry) {
PrintMessageToLogcat(entry);
}
FileBackend::FileBackend(const std::filesystem::path& filename) : bytes_written(0) {
auto old_filename = filename;
old_filename += ".old.txt";
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");
}
// 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);
// _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);
}
void FileBackend::Write(const Entry& entry) {

View File

@@ -5,11 +5,11 @@
#pragma once
#include <chrono>
#include <filesystem>
#include <memory>
#include <string>
#include <string_view>
#include "common/fs/file.h"
#include "common/file_util.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
namespace Log {
@@ -101,7 +101,7 @@ public:
*/
class FileBackend : public Backend {
public:
explicit FileBackend(const std::filesystem::path& filename);
explicit FileBackend(const std::string& filename);
static const char* Name() {
return "file";
@@ -114,7 +114,7 @@ public:
void Write(const Entry& entry) override;
private:
Common::FS::IOFile file;
FileUtil::IOFile file;
std::size_t bytes_written;
};

View File

@@ -10,6 +10,8 @@
namespace Common {
constexpr float PI = 3.14159265f;
template <class T>
struct Rectangle {
T left{};

View File

@@ -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 = Common::FS::GetUserPath(Common::FS::UserPath::CheatsDir);
const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir);
const std::string filepath = fmt::format(
"{}{:016X}.txt", cheat_dir, system.Kernel().GetCurrentProcess()->codeset->program_id);
if (!Common::FS::IsDirectory(cheat_dir)) {
Common::FS::CreateDir(cheat_dir);
if (!FileUtil::IsDirectory(cheat_dir)) {
FileUtil::CreateDir(cheat_dir);
}
std::ofstream file;
@@ -84,15 +84,15 @@ void CheatEngine::SaveCheatFile() const {
}
void CheatEngine::LoadCheatFile() {
const std::string cheat_dir = Common::FS::GetUserPath(Common::FS::UserPath::CheatsDir);
const std::string cheat_dir = FileUtil::GetUserPath(FileUtil::UserPath::CheatsDir);
const std::string filepath = fmt::format(
"{}{:016X}.txt", cheat_dir, system.Kernel().GetCurrentProcess()->codeset->program_id);
if (!Common::FS::IsDirectory(cheat_dir)) {
Common::FS::CreateDir(cheat_dir);
if (!FileUtil::IsDirectory(cheat_dir)) {
FileUtil::CreateDir(cheat_dir);
}
if (!Common::FS::Exists(filepath))
if (!FileUtil::Exists(filepath))
return;
auto gateway_cheats = GatewayCheat::LoadFile(filepath);

View File

@@ -248,7 +248,7 @@ System::ResultStatus System::SingleStep() {
}
System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::string& filepath) {
Common::FS::SetCurrentRomPath(filepath);
FileUtil::SetCurrentRomPath(filepath);
app_loader = Loader::GetLoader(filepath);
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
@@ -314,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;
Common::FS::CreateFullPath(fmt::format(
"{}textures/{:016X}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir), program_id));
FileUtil::CreateFullPath(fmt::format(
"{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), program_id));
custom_tex_cache->FindCustomTextures(program_id);
}
if (Settings::values.preload_textures) {

View File

@@ -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}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir), program_id);
"{}textures/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir), program_id);
if (Common::FS::Exists(load_path)) {
Common::FS::FSTEntry texture_dir;
std::vector<Common::FS::FSTEntry> textures;
if (FileUtil::Exists(load_path)) {
FileUtil::FSTEntry texture_dir;
std::vector<FileUtil::FSTEntry> textures;
// 64 nested folders should be plenty for most cases
Common::FS::ScanDirectoryTree(load_path, texture_dir, 64);
Common::FS::GetAllFilesFromNestedEntries(texture_dir, textures);
FileUtil::ScanDirectoryTree(load_path, texture_dir, 64);
FileUtil::GetAllFilesFromNestedEntries(texture_dir, textures);
for (const auto& file : textures) {
if (file.isDirectory)

View File

@@ -412,7 +412,7 @@ bool FFmpegMuxer::Init(const std::string& path, const Layout::FramebufferLayout&
InitializeFFmpegLibraries();
if (!Common::FS::CreateFullPath(path)) {
if (!FileUtil::CreateFullPath(path)) {
return false;
}

View File

@@ -30,7 +30,7 @@ namespace FileSys {
*/
class FixSizeDiskFile : public DiskFile {
public:
FixSizeDiskFile(Common::FS::IOFile&& file, const Mode& mode,
FixSizeDiskFile(FileUtil::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
}
Common::FS::IOFile file(full_path, "r+b");
FileUtil::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 (!Common::FS::Exists(fullpath)) {
if (!FileUtil::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/";
Common::FS::CreateFullPath(user_path);
Common::FS::CreateFullPath(boss_path);
FileUtil::CreateFullPath(user_path);
FileUtil::CreateFullPath(boss_path);
// Write the format metadata
std::string metadata_path = GetExtSaveDataPath(mount_point, corrected_path) + "metadata";
Common::FS::IOFile file(metadata_path, "wb");
FileUtil::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";
Common::FS::IOFile file(metadata_path, "rb");
FileUtil::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);
Common::FS::IOFile icon_file(game_path + "icon", "wb");
FileUtil::IOFile icon_file(game_path + "icon", "wb");
icon_file.WriteBytes(icon_data, icon_size);
}

View File

@@ -97,14 +97,14 @@ ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& pa
return ERROR_NOT_FOUND;
} else {
// Create the file
Common::FS::CreateEmptyFile(full_path);
FileUtil::CreateEmptyFile(full_path);
}
break;
case PathParser::FileFound:
break; // Expected 'success' case
}
Common::FS::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
FileUtil::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 (Common::FS::Delete(full_path)) {
if (FileUtil::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 (Common::FS::Rename(src_path_full, dest_path_full)) {
if (FileUtil::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, Common::FS::Delete);
return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
}
ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const {
return DeleteDirectoryHelper(
path, mount_point, [](const std::string& p) { return Common::FS::DeleteDirRecursively(p); });
path, mount_point, [](const std::string& p) { return FileUtil::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) {
Common::FS::CreateEmptyFile(full_path);
FileUtil::CreateEmptyFile(full_path);
return RESULT_SUCCESS;
}
Common::FS::IOFile file(full_path, "wb");
FileUtil::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 (Common::FS::CreateDir(mount_point + path.AsString())) {
if (FileUtil::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 (Common::FS::Rename(src_path_full, dest_path_full)) {
if (FileUtil::Rename(src_path_full, dest_path_full)) {
return RESULT_SUCCESS;
}
@@ -382,7 +382,7 @@ bool ArchiveFactory_SDMC::Initialize() {
return false;
}
if (!Common::FS::CreateFullPath(sdmc_directory)) {
if (!FileUtil::CreateFullPath(sdmc_directory)) {
LOG_ERROR(Service_FS, "Unable to create SDMC path.");
return false;
}

View File

@@ -69,7 +69,7 @@ bool ArchiveFactory_SDMCWriteOnly::Initialize() {
return false;
}
if (!Common::FS::CreateFullPath(sdmc_directory)) {
if (!FileUtil::CreateFullPath(sdmc_directory)) {
LOG_ERROR(Service_FS, "Unable to create SDMC path.");
return false;
}

View File

@@ -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 (!Common::FS::Exists(concrete_mount_point)) {
if (!FileUtil::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);
Common::FS::DeleteDirRecursively(concrete_mount_point);
Common::FS::CreateFullPath(concrete_mount_point);
FileUtil::DeleteDirRecursively(concrete_mount_point);
FileUtil::CreateFullPath(concrete_mount_point);
// Write the format metadata
std::string metadata_path = GetSaveDataMetadataPath(mount_point, program_id);
Common::FS::IOFile file(metadata_path, "wb");
FileUtil::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);
Common::FS::IOFile file(metadata_path, "rb");
FileUtil::IOFile file(metadata_path, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Service_FS, "Could not open metadata information for archive");

View File

@@ -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 (!Common::FS::Exists(fullpath)) {
if (!FileUtil::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);
Common::FS::DeleteDirRecursively(fullpath);
Common::FS::CreateFullPath(fullpath);
FileUtil::DeleteDirRecursively(fullpath);
FileUtil::CreateFullPath(fullpath);
return RESULT_SUCCESS;
}

View File

@@ -66,7 +66,7 @@ Loader::ResultStatus CIAContainer::Load(const FileBackend& backend) {
}
Loader::ResultStatus CIAContainer::Load(const std::string& filepath) {
Common::FS::IOFile file(filepath, "rb");
FileUtil::IOFile file(filepath, "rb");
if (!file.IsOpen())
return Loader::ResultStatus::Error;

View File

@@ -58,7 +58,7 @@ bool DiskFile::Close() const {
////////////////////////////////////////////////////////////////////////////////////////////////////
DiskDirectory::DiskDirectory(const std::string& path) {
directory.size = Common::FS::ScanDirectoryTree(path, directory);
directory.size = FileUtil::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 Common::FS::FSTEntry& file = *children_iterator;
const FileUtil::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;
}
Common::FS::SplitFilename83(filename, entry.short_name, entry.extension);
FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
entry.is_directory = file.isDirectory;
entry.is_hidden = (filename[0] == '.');

View File

@@ -25,9 +25,9 @@ namespace FileSys {
class DiskFile : public FileBackend {
public:
DiskFile(Common::FS::IOFile&& file_, const Mode& mode_,
DiskFile(FileUtil::IOFile&& file_, const Mode& mode_,
std::unique_ptr<DelayGenerator> delay_generator_)
: file(new Common::FS::IOFile(std::move(file_))) {
: file(new FileUtil::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<Common::FS::IOFile> file;
std::unique_ptr<FileUtil::IOFile> file;
private:
DiskFile() = default;
@@ -74,11 +74,11 @@ public:
}
protected:
Common::FS::FSTEntry directory{};
FileUtil::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<Common::FS::FSTEntry>::iterator children_iterator;
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
private:
DiskDirectory() = default;

View File

@@ -140,17 +140,17 @@ std::string LayeredFS::ReadName(u32 offset, u32 name_length) {
}
void LayeredFS::LoadRelocations() {
if (!Common::FS::Exists(patch_path)) {
if (!FileUtil::Exists(patch_path)) {
return;
}
const Common::FS::DirectoryEntryCallable callback = [this,
const FileUtil::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 (Common::FS::IsDirectory(directory + virtual_name + DIR_SEP)) {
if (FileUtil::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 Common::FS::ForeachDirectoryEntry(nullptr, directory + virtual_name + DIR_SEP,
return FileUtil::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 = Common::FS::GetSize(directory + virtual_name);
file->relocation.size = FileUtil::GetSize(directory + virtual_name);
LOG_INFO(Service_FS, "LayeredFS replacement file in use for {}", path);
return true;
};
Common::FS::ForeachDirectoryEntry(nullptr, patch_path, callback);
FileUtil::ForeachDirectoryEntry(nullptr, patch_path, callback);
}
void LayeredFS::LoadExtRelocations() {
if (!Common::FS::Exists(patch_ext_path)) {
if (!FileUtil::Exists(patch_ext_path)) {
return;
}
@@ -197,11 +197,11 @@ void LayeredFS::LoadExtRelocations() {
patch_ext_path.erase(patch_ext_path.size() - 1, 1);
}
Common::FS::FSTEntry result;
Common::FS::ScanDirectoryTree(patch_ext_path, result, 256);
FileUtil::FSTEntry result;
FileUtil::ScanDirectoryTree(patch_ext_path, result, 256);
for (const auto& entry : result.children) {
if (Common::FS::IsDirectory(entry.physicalName)) {
if (FileUtil::IsDirectory(entry.physicalName)) {
continue;
}
@@ -230,7 +230,7 @@ void LayeredFS::LoadExtRelocations() {
continue;
}
Common::FS::IOFile patch_file(entry.physicalName, "rb");
FileUtil::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
Common::FS::IOFile replace_file(relocation.replace_file_path, "rb");
FileUtil::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 (!Common::FS::CreateFullPath(target_path + current.path)) {
if (!FileUtil::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);
Common::FS::IOFile target_file(path, "wb");
FileUtil::IOFile target_file(path, "wb");
if (!target_file) {
LOG_ERROR(Service_FS, "Could not open file {}", path);
return false;

View File

@@ -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 = Common::FS::IOFile(filepath, "rb");
file = FileUtil::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 = Common::FS::IOFile(filepath, "rb");
file = FileUtil::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](Common::FS::IOFile& file) {
auto read_exheader = [this](FileUtil::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}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::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) {
Common::FS::IOFile exheader_override_file{path, "rb"};
FileUtil::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 = Common::FS::IOFile(filepath, "rb");
exefs_file = FileUtil::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 (Common::FS::Exists(romfs_override)) {
if (FileUtil::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 (Common::FS::Exists(exefs_override)) {
exefs_file = Common::FS::IOFile(exefs_override, "rb");
if (FileUtil::Exists(exefs_override)) {
exefs_file = FileUtil::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 = Common::FS::IOFile(filepath, "rb");
exefs_file = FileUtil::IOFile(filepath, "rb");
}
} else if (Common::FS::Exists(exefsdir_override) && Common::FS::IsDirectory(exefsdir_override)) {
} else if (FileUtil::Exists(exefsdir_override) && FileUtil::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}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::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) {
Common::FS::IOFile file{info.path, "rb"};
FileUtil::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}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::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) {
Common::FS::IOFile section_file(path, "rb");
FileUtil::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
Common::FS::IOFile romfs_file_inner(filepath, "rb");
FileUtil::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}/", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir),
fmt::format("{}mods/{:016X}/", FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
GetModId(ncch_header.program_id));
if (use_layered_fs &&
(Common::FS::Exists(path + "romfs/") || Common::FS::Exists(path + "romfs_ext/"))) {
(FileUtil::Exists(path + "romfs/") || FileUtil::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 (Common::FS::Exists(split_filepath)) {
Common::FS::IOFile romfs_file_inner(split_filepath, "rb");
if (FileUtil::Exists(split_filepath)) {
FileUtil::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);

View File

@@ -368,8 +368,8 @@ private:
u32 partition = 0;
std::string filepath;
Common::FS::IOFile file;
Common::FS::IOFile exefs_file;
FileUtil::IOFile file;
FileUtil::IOFile exefs_file;
};
} // namespace FileSys

View File

@@ -4,7 +4,7 @@
#include <algorithm>
#include <set>
#include "common/fs/fs.h"
#include "common/file_util.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 (!Common::FS::IsDir(path))
if (!FileUtil::IsDirectory(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 (!Common::FS::Exists(path))
if (!FileUtil::Exists(path))
return PathNotFound;
if (Common::FS::IsDir(path))
if (FileUtil::IsDirectory(path))
continue;
return FileInPath;
}
path += "/" + path_sequence.back();
if (!Common::FS::Exists(path))
if (!FileUtil::Exists(path))
return NotFound;
if (Common::FS::IsDir(path))
if (FileUtil::IsDirectory(path))
return DirectoryFound;
return FileFound;
}

View File

@@ -5,7 +5,6 @@
#pragma once
#include <string>
#include <string_view>
#include <vector>
#include "core/file_sys/archive_backend.h"

View File

@@ -30,11 +30,11 @@ private:
*/
class DirectRomFSReader : public RomFSReader {
public:
DirectRomFSReader(Common::FS::IOFile&& file, std::size_t file_offset, std::size_t data_size)
DirectRomFSReader(FileUtil::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(Common::FS::IOFile&& file, std::size_t file_offset, std::size_t data_size,
DirectRomFSReader(FileUtil::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;
Common::FS::IOFile file;
FileUtil::IOFile file;
std::array<u8, 16> key;
std::array<u8, 16> ctr;
u64 file_offset;

View File

@@ -79,14 +79,14 @@ ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& pa
return ERROR_FILE_NOT_FOUND;
} else {
// Create the file
Common::FS::CreateEmptyFile(full_path);
FileUtil::CreateEmptyFile(full_path);
}
break;
case PathParser::FileFound:
break; // Expected 'success' case
}
Common::FS::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
FileUtil::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 (Common::FS::Delete(full_path)) {
if (FileUtil::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 (Common::FS::Rename(src_path_full, dest_path_full)) {
if (FileUtil::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, Common::FS::Delete);
return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
}
ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
return DeleteDirectoryHelper(
path, mount_point, [](const std::string& p) { return Common::FS::DeleteDirRecursively(p); });
path, mount_point, [](const std::string& p) { return FileUtil::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) {
Common::FS::CreateEmptyFile(full_path);
FileUtil::CreateEmptyFile(full_path);
return RESULT_SUCCESS;
}
Common::FS::IOFile file(full_path, "wb");
FileUtil::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 (Common::FS::CreateDir(mount_point + path.AsString())) {
if (FileUtil::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 (Common::FS::Rename(src_path_full, dest_path_full)) {
if (FileUtil::Rename(src_path_full, dest_path_full)) {
return RESULT_SUCCESS;
}

View File

@@ -12,9 +12,9 @@ namespace FileSys {
bool SeedDB::Load() {
seeds.clear();
const std::string path{
fmt::format("{}/seeddb.bin", Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir))};
if (!Common::FS::Exists(path)) {
if (!Common::FS::CreateFullPath(path)) {
fmt::format("{}/seeddb.bin", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir))};
if (!FileUtil::Exists(path)) {
if (!FileUtil::CreateFullPath(path)) {
LOG_ERROR(Service_FS, "Failed to create seed database");
return false;
}
@@ -24,7 +24,7 @@ bool SeedDB::Load() {
}
return true;
}
Common::FS::IOFile file{path, "rb"};
FileUtil::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", Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir))};
if (!Common::FS::CreateFullPath(path)) {
fmt::format("{}/seeddb.bin", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir))};
if (!FileUtil::CreateFullPath(path)) {
LOG_ERROR(Service_FS, "Failed to create seed database");
return false;
}
Common::FS::IOFile file{path, "wb"};
FileUtil::IOFile file{path, "wb"};
if (!file.IsOpen()) {
LOG_ERROR(Service_FS, "Failed to open seed database");
return false;

View File

@@ -17,7 +17,7 @@
namespace FileSys {
Loader::ResultStatus TitleMetadata::Load(const std::string& file_path) {
Common::FS::IOFile file(file_path, "rb");
FileUtil::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) {
Common::FS::IOFile file(file_path, "wb");
FileUtil::IOFile file(file_path, "wb");
if (!file.IsOpen())
return Loader::ResultStatus::Error;

View File

@@ -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{Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)};
std::string nand_directory{FileUtil::GetUserPath(FileUtil::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);

View File

@@ -16,7 +16,9 @@ namespace Frontend {
/// WindowInformation
enum class WindowSystemType : u8 {
Headless,
Android,
Windows,
MacOS,
X11,
Wayland,
};

View File

@@ -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 (Common::FS::Exists(GetTitleMetadataPath(media_type, tmd.GetTitleID())))
if (FileUtil::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);
Common::FS::CreateFullPath(tmd_folder);
FileUtil::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);
Common::FS::CreateFullPath(app_folder);
FileUtil::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();
Common::FS::IOFile file(GetTitleContentPath(media_type, tmd.GetTitleID(), i, is_update),
FileUtil::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...");
Common::FS::Delete(GetTitlePath(media_type, container.GetTitleMetadata().GetTitleID()));
FileUtil::DeleteDir(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 (Common::FS::Exists(new_tmd_path) && old_tmd_path != new_tmd_path) {
if (FileUtil::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;
Common::FS::Delete(GetTitleContentPath(media_type, old_tmd.GetTitleID(), old_index));
FileUtil::Delete(GetTitleContentPath(media_type, old_tmd.GetTitleID(), old_index));
}
Common::FS::Delete(old_tmd_path);
FileUtil::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 (!Common::FS::Exists(path)) {
if (!FileUtil::Exists(path)) {
LOG_ERROR(Service_AM, "File {} does not exist!", path);
return InstallStatus::ErrorFileNotFound;
}
@@ -350,7 +350,7 @@ InstallStatus InstallCIA(const std::string& path,
}
}
Common::FS::IOFile file(path, "rb");
FileUtil::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 Common::FS::DirectoryEntryCallable callback =
const FileUtil::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 = Common::FS::IsDirectory(physical_name);
const bool is_dir = FileUtil::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 Common::FS::ForeachDirectoryEntry(nullptr, physical_name, callback);
return FileUtil::ForeachDirectoryEntry(nullptr, physical_name, callback);
}
};
if (!Common::FS::ForeachDirectoryEntry(
if (!FileUtil::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;
Common::FS::FSTEntry entries;
Common::FS::ScanDirectoryTree(content_path, entries);
for (const Common::FS::FSTEntry& entry : entries.children) {
FileUtil::FSTEntry entries;
FileUtil::ScanDirectoryTree(content_path, entries);
for (const FileUtil::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/", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir),
return fmt::format("{}{}/title/", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir),
SYSTEM_ID);
if (media_type == Service::FS::MediaType::SDMC)
return fmt::format("{}Nintendo 3DS/{}/{}/title/",
Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir), SYSTEM_ID,
FileUtil::GetUserPath(FileUtil::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);
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) {
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) {
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 (Common::FS::Exists(GetTitleContentPath(media_type, title_id, content_requested[i]))) {
if (FileUtil::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 (Common::FS::Exists(GetTitleContentPath(media_type, title_id, i))) {
if (FileUtil::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 (!Common::FS::Exists(path)) {
if (!FileUtil::Exists(path)) {
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::AM, ErrorSummary::InvalidState,
ErrorLevel::Permanent));
LOG_ERROR(Service_AM, "Title not found");
return;
}
bool success = Common::FS::DeleteDirRecursively(path);
bool success = FileUtil::DeleteDirRecursively(path);
am->ScanForAllTitles();
rb.Push(RESULT_SUCCESS);
if (!success)
LOG_ERROR(Service_AM, "Common::FS::DeleteDirRecursively unexpectedly failed");
LOG_ERROR(Service_AM, "FileUtil::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 (!Common::FS::Exists(path)) {
if (!FileUtil::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 =
Common::FS::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index));
FileUtil::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 =
Common::FS::Exists(GetTitleContentPath(Service::FS::MediaType::SDMC, tid, content_index));
FileUtil::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 (!Common::FS::Exists(path)) {
if (!FileUtil::Exists(path)) {
rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::AM, ErrorSummary::InvalidState,
ErrorLevel::Permanent));
LOG_ERROR(Service_AM, "Title not found");
return;
}
bool success = Common::FS::DeleteDirRecursively(path);
bool success = FileUtil::DeleteDirRecursively(path);
am->ScanForAllTitles();
rb.Push(RESULT_SUCCESS);
if (!success)
LOG_ERROR(Service_AM, "Common::FS::DeleteDirRecursively unexpectedly failed");
LOG_ERROR(Service_AM, "FileUtil::DeleteDirRecursively unexpectedly failed");
}
void Module::Interface::GetSystemUpdaterMutex(Kernel::HLERequestContext& ctx) {

View File

@@ -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 = Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + SHARED_FONT;
std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SHARED_FONT;
Common::FS::CreateFullPath(filepath); // Create path if not already created
Common::FS::IOFile file(filepath, "rb");
FileUtil::CreateFullPath(filepath); // Create path if not already created
FileUtil::IOFile file(filepath, "rb");
if (file.IsOpen()) {
file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize());
return true;

View File

@@ -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 = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);
// Open the SystemSaveData archive 0x00010026

View File

@@ -560,7 +560,7 @@ ResultCode Module::FormatConfig() {
} // namespace Service::CFG
ResultCode Module::LoadConfigNANDSaveFile() {
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
FileSys::ArchiveFactory_SystemSaveData systemsavedata_factory(nand_directory);
// Open the SystemSaveData archive 0x00010017

View File

@@ -32,10 +32,10 @@
namespace Service::FS {
MediaType GetMediaTypeFromPath(std::string_view path) {
if (path.rfind(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir), 0) == 0) {
if (path.rfind(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 0) == 0) {
return MediaType::NAND;
}
if (path.rfind(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir), 0) == 0) {
if (path.rfind(FileUtil::GetUserPath(FileUtil::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 = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
media_type_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
} else if (media_type == MediaType::SDMC) {
media_type_directory = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
media_type_directory = FileUtil::GetUserPath(FileUtil::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 (Common::FS::Exists(extsavedata_path) && !Common::FS::DeleteDirRecursively(extsavedata_path))
if (FileUtil::Exists(extsavedata_path) && !FileUtil::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 = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
const std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
const std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
if (!Common::FS::DeleteDirRecursively(systemsavedata_path)) {
if (!FileUtil::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 = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
const std::string base_path = FileSys::GetSystemSaveDataContainerPath(nand_directory);
const std::string systemsavedata_path = FileSys::GetSystemSaveDataPath(base_path, path);
if (!Common::FS::CreateFullPath(systemsavedata_path)) {
if (!FileUtil::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 = Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir);
std::string nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
std::string sdmc_directory = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
std::string nand_directory = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
auto sdmc_factory = std::make_unique<FileSys::ArchiveFactory_SDMC>(sdmc_directory);
if (sdmc_factory->Initialize())
RegisterArchiveType(std::move(sdmc_factory), ArchiveIdCode::SDMC);

View File

@@ -134,7 +134,7 @@ void Module::Interface::CheckNew3DS(Kernel::HLERequestContext& ctx) {
}
static void WriteGameCoinData(GameCoin gamecoin_data) {
const std::string& nand_directory = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::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 = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::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 = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir);
const std::string& nand_directory = FileUtil::GetUserPath(FileUtil::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);

View File

@@ -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 = Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + BOOTROM9;
auto file = Common::FS::IOFile(filepath, "rb");
const std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + BOOTROM9;
auto file = FileUtil::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 =
Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + SECRET_SECTOR;
auto secret = Common::FS::IOFile(filepath, "rb");
FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SECRET_SECTOR;
auto secret = FileUtil::IOFile(filepath, "rb");
if (!secret) {
return;
}
@@ -426,8 +426,8 @@ void LoadNativeFirmKeysNew3DS() {
}
void LoadPresetKeys() {
const std::string filepath = Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + AES_KEYS;
Common::FS::CreateFullPath(filepath); // Create path if not already created
const std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + AES_KEYS;
FileUtil::CreateFullPath(filepath); // Create path if not already created
std::ifstream file;
OpenFStream(file, filepath, std::ios_base::in);
if (!file) {

View File

@@ -52,8 +52,8 @@ void InitSlots() {
return;
initialized = true;
const std::string filepath = Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir) + BOOTROM9;
Common::FS::IOFile file(filepath, "rb");
const std::string filepath = FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + BOOTROM9;
FileUtil::IOFile file(filepath, "rb");
if (!file) {
return;
}

View File

@@ -94,7 +94,7 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo* loadinfo, u32* offsets)
using Kernel::CodeSet;
static THREEDSX_Error Load3DSXFile(Common::FS::IOFile& file, u32 base_addr,
static THREEDSX_Error Load3DSXFile(FileUtil::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(Common::FS::IOFile& file, u32 base_addr,
return ERROR_NONE;
}
FileType AppLoader_THREEDSX::IdentifyType(Common::FS::IOFile& file) {
FileType AppLoader_THREEDSX::IdentifyType(FileUtil::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
Common::FS::IOFile romfs_file_inner(filepath, "rb");
FileUtil::IOFile romfs_file_inner(filepath, "rb");
if (!romfs_file_inner.IsOpen())
return ResultStatus::Error;

View File

@@ -17,16 +17,16 @@ namespace Loader {
/// Loads an 3DSX file
class AppLoader_THREEDSX final : public AppLoader {
public:
AppLoader_THREEDSX(Common::FS::IOFile&& file, const std::string& filename,
AppLoader_THREEDSX(FileUtil::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 Common::FS::IOFile open file
* @param file FileUtil::IOFile open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(Common::FS::IOFile& file);
static FileType IdentifyType(FileUtil::IOFile& file);
FileType GetFileType() override {
return IdentifyType(file);

View File

@@ -364,7 +364,7 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const
namespace Loader {
FileType AppLoader_ELF::IdentifyType(Common::FS::IOFile& file) {
FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) {
u32 magic;
file.Seek(0, SEEK_SET);
if (1 != file.ReadArray<u32>(&magic, 1))

View File

@@ -17,15 +17,15 @@ namespace Loader {
/// Loads an ELF/AXF file
class AppLoader_ELF final : public AppLoader {
public:
AppLoader_ELF(Common::FS::IOFile&& file, std::string filename)
AppLoader_ELF(FileUtil::IOFile&& file, std::string filename)
: AppLoader(std::move(file)), filename(std::move(filename)) {}
/**
* Returns the type of the file
* @param file Common::FS::IOFile open file
* @param file FileUtil::IOFile open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(Common::FS::IOFile& file);
static FileType IdentifyType(FileUtil::IOFile& file);
FileType GetFileType() override {
return IdentifyType(file);

View File

@@ -15,7 +15,7 @@
namespace Loader {
FileType IdentifyFile(Common::FS::IOFile& file) {
FileType IdentifyFile(FileUtil::IOFile& file) {
FileType type;
#define CHECK_TYPE(loader) \
@@ -33,7 +33,7 @@ FileType IdentifyFile(Common::FS::IOFile& file) {
}
FileType IdentifyFile(const std::string& file_name) {
Common::FS::IOFile file(file_name, "rb");
FileUtil::IOFile file(file_name, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Loader, "Failed to load file {}", file_name);
return FileType::Unknown;
@@ -91,7 +91,7 @@ const char* GetFileTypeString(FileType type) {
* @param filepath the file full path (with name)
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
*/
static std::unique_ptr<AppLoader> GetFileLoader(Common::FS::IOFile&& file, FileType type,
static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type,
const std::string& filename,
const std::string& filepath) {
switch (type) {
@@ -115,7 +115,7 @@ static std::unique_ptr<AppLoader> GetFileLoader(Common::FS::IOFile&& file, FileT
}
std::unique_ptr<AppLoader> GetLoader(const std::string& filename) {
Common::FS::IOFile file(filename, "rb");
FileUtil::IOFile file(filename, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Loader, "Failed to load file {}", filename);
return nullptr;

View File

@@ -41,7 +41,7 @@ enum class FileType {
* @param file open file
* @return FileType of file
*/
FileType IdentifyFile(Common::FS::IOFile& file);
FileType IdentifyFile(FileUtil::IOFile& file);
/**
* Identifies the type of a bootable file based on the magic value in its header.
@@ -84,7 +84,7 @@ constexpr u32 MakeMagic(char a, char b, char c, char d) {
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
explicit AppLoader(Common::FS::IOFile&& file) : file(std::move(file)) {}
explicit AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) {}
virtual ~AppLoader() {}
/**
@@ -232,7 +232,7 @@ public:
}
protected:
Common::FS::IOFile file;
FileUtil::IOFile file;
bool is_loaded = false;
};

View File

@@ -34,7 +34,7 @@ namespace Loader {
static const u64 UPDATE_MASK = 0x0000000e00000000;
FileType AppLoader_NCCH::IdentifyType(Common::FS::IOFile& file) {
FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) {
u32 magic;
file.Seek(0x100, SEEK_SET);
if (1 != file.ReadArray<u32>(&magic, 1))

View File

@@ -18,16 +18,16 @@ namespace Loader {
/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
class AppLoader_NCCH final : public AppLoader {
public:
AppLoader_NCCH(Common::FS::IOFile&& file, const std::string& filepath)
AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath)
: AppLoader(std::move(file)), base_ncch(filepath), overlay_ncch(&base_ncch),
filepath(filepath) {}
/**
* Returns the type of the file
* @param file Common::FS::IOFile open file
* @param file FileUtil::IOFile open file
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(Common::FS::IOFile& file);
static FileType IdentifyType(FileUtil::IOFile& file);
FileType GetFileType() override {
return IdentifyType(file);

View File

@@ -483,7 +483,7 @@ Movie::ValidationResult Movie::ValidateInput(const std::vector<u8>& input,
void Movie::SaveMovie() {
LOG_INFO(Movie, "Saving recorded movie to '{}'", record_movie_file);
Common::FS::IOFile save_record(record_movie_file, "wb");
FileUtil::IOFile save_record(record_movie_file, "wb");
if (!save_record.IsGood()) {
LOG_ERROR(Movie, "Unable to open file to save movie");
@@ -521,7 +521,7 @@ void Movie::SetPlaybackCompletionCallback(std::function<void()> completion_callb
void Movie::StartPlayback(const std::string& movie_file) {
LOG_INFO(Movie, "Loading Movie for playback");
Common::FS::IOFile save_record(movie_file, "rb");
FileUtil::IOFile save_record(movie_file, "rb");
const u64 size = save_record.GetSize();
if (save_record.IsGood() && size > sizeof(CTMHeader)) {
@@ -575,7 +575,7 @@ void Movie::SetReadOnly(bool read_only_) {
}
static boost::optional<CTMHeader> ReadHeader(const std::string& movie_file) {
Common::FS::IOFile save_record(movie_file, "rb");
FileUtil::IOFile save_record(movie_file, "rb");
const u64 size = save_record.GetSize();
if (!save_record || size <= sizeof(CTMHeader)) {
@@ -609,7 +609,7 @@ void Movie::PrepareForRecording() {
Movie::ValidationResult Movie::ValidateMovie(const std::string& movie_file) const {
LOG_INFO(Movie, "Validating Movie file '{}'", movie_file);
Common::FS::IOFile save_record(movie_file, "rb");
FileUtil::IOFile save_record(movie_file, "rb");
const u64 size = save_record.GetSize();
if (!save_record || size <= sizeof(CTMHeader)) {

View File

@@ -38,11 +38,11 @@ PerfStats::~PerfStats() {
std::ostringstream stream;
std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
std::ostream_iterator<double>(stream, "\n"));
const std::string& path = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
// %F Date format expanded is "%Y-%m-%d"
const std::string filename =
fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
Common::FS::IOFile file(filename, "w");
FileUtil::IOFile file(filename, "w");
file.WriteString(stream.str());
}

View File

@@ -41,11 +41,11 @@ std::string GetSaveStatePath(u64 program_id, u32 slot) {
const u64 movie_id = Movie::GetInstance().GetCurrentMovieID();
if (movie_id) {
return fmt::format("{}{:016X}.movie{:016X}.{:02d}.cst",
Common::FS::GetUserPath(Common::FS::UserPath::StatesDir), program_id,
FileUtil::GetUserPath(FileUtil::UserPath::StatesDir), program_id,
movie_id, slot);
} else {
return fmt::format("{}{:016X}.{:02d}.cst",
Common::FS::GetUserPath(Common::FS::UserPath::StatesDir), program_id, slot);
FileUtil::GetUserPath(FileUtil::UserPath::StatesDir), program_id, slot);
}
}
@@ -53,14 +53,14 @@ std::vector<SaveStateInfo> ListSaveStates(u64 program_id) {
std::vector<SaveStateInfo> result;
for (u32 slot = 1; slot <= SaveStateSlotCount; ++slot) {
const auto path = GetSaveStatePath(program_id, slot);
if (!Common::FS::Exists(path)) {
if (!FileUtil::Exists(path)) {
continue;
}
SaveStateInfo info;
info.slot = slot;
Common::FS::IOFile file(path, "rb");
FileUtil::IOFile file(path, "rb");
if (!file) {
LOG_ERROR(Core, "Could not open file {}", path);
continue;
@@ -108,11 +108,11 @@ void System::SaveState(u32 slot) const {
reinterpret_cast<const u8*>(str.data()), str.size());
const auto path = GetSaveStatePath(title_id, slot);
if (!Common::FS::CreateFullPath(path)) {
if (!FileUtil::CreateFullPath(path)) {
throw std::runtime_error("Could not create path " + path);
}
Common::FS::IOFile file(path, "wb");
FileUtil::IOFile file(path, "wb");
if (!file) {
throw std::runtime_error("Could not open file " + path);
}
@@ -143,9 +143,9 @@ void System::LoadState(u32 slot) {
std::vector<u8> decompressed;
{
std::vector<u8> buffer(Common::FS::GetSize(path) - sizeof(CSTHeader));
std::vector<u8> buffer(FileUtil::GetSize(path) - sizeof(CSTHeader));
Common::FS::IOFile file(path, "rb");
FileUtil::IOFile file(path, "rb");
file.Seek(sizeof(CSTHeader), SEEK_SET); // Skip header
if (file.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) {
throw std::runtime_error("Could not read from file at " + path);

View File

@@ -131,8 +131,8 @@ void LogSettings() {
LogSetting("DataStorage_UseVirtualSd", values.use_virtual_sd);
LogSetting("DataStorage_UseCustomStorage", values.use_custom_storage);
if (values.use_custom_storage) {
LogSetting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir));
LogSetting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir));
LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
}
LogSetting("System_IsNew3ds", values.is_new_3ds);
LogSetting("System_RegionValue", values.region_value);

View File

@@ -167,6 +167,7 @@ struct Values {
// Renderer
GraphicsAPI graphics_api;
u16 physical_device;
bool spirv_shader_gen;
bool renderer_debug;
bool dump_command_buffers;
bool async_command_recording;

View File

@@ -29,18 +29,18 @@ static u64 GenerateTelemetryId() {
u64 GetTelemetryId() {
u64 telemetry_id{};
const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
"telemetry_id"};
if (Common::FS::Exists(filename)) {
Common::FS::IOFile file(filename, "rb");
if (FileUtil::Exists(filename)) {
FileUtil::IOFile file(filename, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
return {};
}
file.ReadBytes(&telemetry_id, sizeof(u64));
} else {
Common::FS::IOFile file(filename, "wb");
FileUtil::IOFile file(filename, "wb");
if (!file.IsOpen()) {
LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
return {};
@@ -54,10 +54,10 @@ u64 GetTelemetryId() {
u64 RegenerateTelemetryId() {
const u64 new_telemetry_id{GenerateTelemetryId()};
const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) +
const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
"telemetry_id"};
Common::FS::IOFile file(filename, "wb");
FileUtil::IOFile file(filename, "wb");
if (!file.IsOpen()) {
LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
return {};

View File

@@ -75,7 +75,7 @@ void Recorder::Finish(const std::string& filename) {
try {
// Open file and write header
Common::FS::IOFile file(filename, "wb");
FileUtil::IOFile file(filename, "wb");
std::size_t written = file.WriteObject(header);
if (written != 1 || file.Tell() != initial.gpu_registers)
throw "Failed to write header";

View File

@@ -153,8 +153,8 @@ static void SaveBanList(const Network::Room::BanList& ban_list, const std::strin
static void InitializeLogging(const std::string& log_file) {
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
const std::string& log_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir);
Common::FS::CreateFullPath(log_dir);
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + log_file));
#ifdef _WIN32

View File

@@ -47,7 +47,7 @@ public:
} else {
tilt_direction = mouse_move.Cast<float>();
tilt_angle = std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f,
std::numbers::pi_v<float> * this->tilt_clamp / 180.0f);
Common::PI * this->tilt_clamp / 180.0f);
}
}
}
@@ -110,7 +110,7 @@ private:
// Find the angular rate vector in world space
auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
angular_rate *= 1000 / update_millisecond / std::numbers::pi_v<float> * 180;
angular_rate *= 1000 / update_millisecond / Common::PI * 180;
// Transform the two vectors from world space to 3DS space
gravity = QuaternionRotate(inv_q, gravity);

View File

@@ -16,6 +16,8 @@
#include <utility>
#include <vector>
#include <SDL.h>
#include "common/assert.h"
#include "common/math_util.h"
#include "common/logging/log.h"
#include "common/param_package.h"
#include "common/threadsafe_queue.h"
@@ -596,9 +598,9 @@ void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
event.csensor.data[2] / SDL_STANDARD_GRAVITY);
break;
case SDL_SENSOR_GYRO:
joystick->SetGyro(-event.csensor.data[0] * (180.0f / std::numbers::pi),
event.csensor.data[1] * (180.0f / std::numbers::pi),
-event.csensor.data[2] * (180.0f / std::numbers::pi));
joystick->SetGyro(-event.csensor.data[0] * (180.0f / Common::PI),
event.csensor.data[1] * (180.0f / Common::PI),
-event.csensor.data[2] * (180.0f / Common::PI));
break;
}
}

View File

@@ -22,9 +22,9 @@ TEST_CASE("PathParser", "[core][file_sys]") {
TEST_CASE("PathParser - Host file system", "[core][file_sys]") {
std::string test_dir = "./test";
Common::FS::CreateDir(test_dir);
Common::FS::CreateDir(test_dir + "/z");
Common::FS::CreateEmptyFile(test_dir + "/a");
FileUtil::CreateDir(test_dir);
FileUtil::CreateDir(test_dir + "/z");
FileUtil::CreateEmptyFile(test_dir + "/a");
REQUIRE(PathParser(Path("/a")).GetHostStatus(test_dir) == PathParser::FileFound);
REQUIRE(PathParser(Path("/b")).GetHostStatus(test_dir) == PathParser::NotFound);
@@ -32,7 +32,7 @@ TEST_CASE("PathParser - Host file system", "[core][file_sys]") {
REQUIRE(PathParser(Path("/a/c")).GetHostStatus(test_dir) == PathParser::FileInPath);
REQUIRE(PathParser(Path("/b/c")).GetHostStatus(test_dir) == PathParser::PathNotFound);
Common::FS::DeleteDirRecursively(test_dir);
FileUtil::DeleteDirRecursively(test_dir);
}
} // namespace FileSys

View File

@@ -86,6 +86,8 @@ add_library(video_core STATIC
renderer_vulkan/renderer_vulkan.h
renderer_vulkan/vk_blit_helper.cpp
renderer_vulkan/vk_blit_helper.h
renderer_vulkan/vk_blit_screen.cpp
renderer_vulkan/vk_blit_screen.h
renderer_vulkan/vk_common.cpp
renderer_vulkan/vk_common.h
renderer_vulkan/vk_descriptor_manager.cpp
@@ -111,6 +113,8 @@ add_library(video_core STATIC
renderer_vulkan/vk_renderpass_cache.h
renderer_vulkan/vk_shader_gen.cpp
renderer_vulkan/vk_shader_gen.h
renderer_vulkan/vk_shader_gen_spv.cpp
renderer_vulkan/vk_shader_gen_spv.h
renderer_vulkan/vk_shader_util.cpp
renderer_vulkan/vk_shader_util.h
renderer_vulkan/vk_stream_buffer.cpp
@@ -202,7 +206,8 @@ if (NOT MSVC)
endif()
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad vma vulkan-headers glm::glm SPIRV glslang nihstro-headers Boost::serialization)
target_link_libraries(video_core PRIVATE nihstro-headers Boost::serialization glm::glm)
target_link_libraries(video_core PRIVATE vulkan-headers vma sirit SPIRV glslang glad)
set_target_properties(video_core PROPERTIES INTERPROCEDURAL_OPTIMIZATION ${ENABLE_LTO})
if (ARCHITECTURE_x86_64)

View File

@@ -91,8 +91,7 @@ public:
u32 fill_size = 0;
public:
u32 watcher_count = 0;
std::array<std::weak_ptr<Watcher>, 8> watchers;
std::vector<std::weak_ptr<Watcher>> watchers;
};
template <class S>
@@ -190,7 +189,7 @@ template <class S>
auto SurfaceBase<S>::CreateWatcher() -> std::shared_ptr<Watcher> {
auto weak_ptr = reinterpret_cast<S*>(this)->weak_from_this();
auto watcher = std::make_shared<Watcher>(std::move(weak_ptr));
watchers[watcher_count++] = watcher;
watchers.push_back(watcher);
return watcher;
}
@@ -212,8 +211,7 @@ void SurfaceBase<S>::UnlinkAllWatcher() {
}
}
watchers = {};
watcher_count = 0;
watchers.clear();
}
} // namespace VideoCore

View File

@@ -10,7 +10,7 @@ namespace OpenGL {
enum class Vendor { Unknown = 0, AMD = 1, Nvidia = 2, Intel = 3, Generic = 4 };
enum class DriverBug {
// AMD drivers sometimes freeze when one shader stage is changed but not the others.
// AMD drivers sometimes freezes when one shader stage is changed but not the others.
ShaderStageChangeFreeze = 1 << 0,
// On AMD drivers there is a strange crash in indexed drawing. The crash happens when the buffer
// read position is near the end and is an out-of-bound access to the vertex buffer. This is

View File

@@ -20,19 +20,14 @@
namespace OpenGL {
static bool IsVendorAmd() {
const std::string_view gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
return gpu_vendor == "ATI Technologies Inc." || gpu_vendor == "Advanced Micro Devices, Inc.";
}
#ifdef __APPLE__
static bool IsVendorIntel() {
std::string gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))};
return gpu_vendor == "Intel Inc.";
}
#endif
constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
constexpr std::size_t INDEX_BUFFER_SIZE = 1 * 1024 * 1024;
constexpr std::size_t UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024;
constexpr std::size_t TEXTURE_BUFFER_SIZE = 1 * 1024 * 1024;
RasterizerOpenGL::RasterizerOpenGL(Frontend::EmuWindow& emu_window, Driver& driver)
: driver{driver}, runtime{driver}, res_cache{*this, runtime}, is_amd(IsVendorAmd()),
: driver{driver}, runtime{driver}, res_cache{*this, runtime},
shader_program_manager{emu_window, driver, !driver.IsOpenGLES()},
vertex_buffer{GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE},
uniform_buffer{GL_UNIFORM_BUFFER, UNIFORM_BUFFER_SIZE},
index_buffer{GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER_SIZE},
@@ -44,8 +39,7 @@ RasterizerOpenGL::RasterizerOpenGL(Frontend::EmuWindow& emu_window, Driver& driv
// Create a 1x1 clear texture to use in the NULL case,
// instead of OpenGL's default of solid black
glGenTextures(1, &default_texture);
glBindTexture(GL_TEXTURE_2D, default_texture);
default_texture.Create();
// For some reason alpha 0 wraps around to 1.0, so use 1/255 instead
u8 framebuffer_data[4] = {0, 0, 0, 1};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data);
@@ -128,17 +122,6 @@ RasterizerOpenGL::RasterizerOpenGL(Frontend::EmuWindow& emu_window, Driver& driv
state.Apply();
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer.GetHandle());
#ifdef __APPLE__
if (IsVendorIntel()) {
shader_program_manager = std::make_unique<ShaderProgramManager>(
emu_window, VideoCore::g_separable_shader_enabled, is_amd);
} else {
shader_program_manager = std::make_unique<ShaderProgramManager>(emu_window, true, is_amd);
}
#else
shader_program_manager = std::make_unique<ShaderProgramManager>(emu_window, !GLES, is_amd);
#endif
glEnable(GL_BLEND);
// Explicitly call the derived version to avoid warnings about calling virtual
@@ -150,7 +133,7 @@ RasterizerOpenGL::~RasterizerOpenGL() = default;
void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {
shader_program_manager->LoadDiskCache(stop_loading, callback);
shader_program_manager.LoadDiskCache(stop_loading, callback);
}
void RasterizerOpenGL::SyncEntireState() {
@@ -285,7 +268,7 @@ void RasterizerOpenGL::SetupVertexArray(u8* array_ptr, GLintptr buffer_offset,
MICROPROFILE_DEFINE(OpenGL_VS, "OpenGL", "Vertex Shader Setup", MP_RGB(192, 128, 128));
bool RasterizerOpenGL::SetupVertexShader() {
MICROPROFILE_SCOPE(OpenGL_VS);
return shader_program_manager->UseProgrammableVertexShader(Pica::g_state.regs,
return shader_program_manager.UseProgrammableVertexShader(Pica::g_state.regs,
Pica::g_state.vs);
}
@@ -299,7 +282,7 @@ bool RasterizerOpenGL::SetupGeometryShader() {
return false;
}
shader_program_manager->UseFixedGeometryShader(regs);
shader_program_manager.UseFixedGeometryShader(regs);
return true;
}
@@ -360,7 +343,7 @@ bool RasterizerOpenGL::AccelerateDrawBatchInternal(bool is_indexed) {
SetupVertexArray(buffer_ptr, buffer_offset, vs_input_index_min, vs_input_index_max);
vertex_buffer.Unmap(vs_input_size);
shader_program_manager->ApplyTo(state);
shader_program_manager.ApplyTo(state);
state.Apply();
if (is_indexed) {
@@ -623,7 +606,7 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
// the geometry in question.
// For example: a bug in Pokemon X/Y causes NULL-texture squares to be drawn
// on the male character's face, which in the OpenGL default appear black.
state.texture_units[texture_index].texture_2d = default_texture;
state.texture_units[texture_index].texture_2d = default_texture.handle;
}
} else {
state.texture_units[texture_index].texture_2d = 0;
@@ -687,9 +670,9 @@ bool RasterizerOpenGL::Draw(bool accelerate, bool is_indexed) {
} else {
state.draw.vertex_array = sw_vao.handle;
state.draw.vertex_buffer = vertex_buffer.GetHandle();
shader_program_manager->UseTrivialVertexShader();
shader_program_manager->UseTrivialGeometryShader();
shader_program_manager->ApplyTo(state);
shader_program_manager.UseTrivialVertexShader();
shader_program_manager.UseTrivialGeometryShader();
shader_program_manager.ApplyTo(state);
state.Apply();
std::size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex)));
@@ -784,7 +767,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
// Blending
case PICA_REG_INDEX(framebuffer.output_merger.alphablend_enable):
if (GLES) {
if (driver.IsOpenGLES()) {
// With GLES, we need this in the fragment shader to emulate logic operations
shader_dirty = true;
}
@@ -908,7 +891,7 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) {
// Logic op
case PICA_REG_INDEX(framebuffer.output_merger.logic_op):
if (GLES) {
if (driver.IsOpenGLES()) {
// With GLES, we need this in the fragment shader to emulate logic operations
shader_dirty = true;
}
@@ -1519,7 +1502,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(
}
void RasterizerOpenGL::SetShader() {
shader_program_manager->UseFragmentShader(Pica::g_state.regs);
shader_program_manager.UseFragmentShader(Pica::g_state.regs);
}
void RasterizerOpenGL::SyncClipEnabled() {
@@ -1595,7 +1578,7 @@ void RasterizerOpenGL::SyncLogicOp() {
const auto& regs = Pica::g_state.regs;
state.logic_op = PicaToGL::LogicOp(regs.framebuffer.output_merger.logic_op);
if (GLES) {
if (driver.IsOpenGLES()) {
if (!regs.framebuffer.output_merger.alphablend_enable) {
if (regs.framebuffer.output_merger.logic_op == Pica::FramebufferRegs::LogicOp::NoOp) {
// Color output is disabled by logic operation. We use color write mask to skip
@@ -1608,7 +1591,7 @@ void RasterizerOpenGL::SyncLogicOp() {
void RasterizerOpenGL::SyncColorWriteMask() {
const auto& regs = Pica::g_state.regs;
if (GLES) {
if (driver.IsOpenGLES()) {
if (!regs.framebuffer.output_merger.alphablend_enable) {
if (regs.framebuffer.output_merger.logic_op == Pica::FramebufferRegs::LogicOp::NoOp) {
// Color output is disabled by logic operation. We use color write mask to skip

View File

@@ -138,28 +138,15 @@ private:
private:
Driver& driver;
OpenGLState state;
GLuint default_texture;
TextureRuntime runtime;
RasterizerCache res_cache;
std::vector<HardwareVertex> vertex_batch;
bool is_amd;
bool shader_dirty = true;
std::unique_ptr<ShaderProgramManager> shader_program_manager;
// They shall be big enough for about one frame.
static constexpr std::size_t VERTEX_BUFFER_SIZE = 16 * 1024 * 1024;
static constexpr std::size_t INDEX_BUFFER_SIZE = 1 * 1024 * 1024;
static constexpr std::size_t UNIFORM_BUFFER_SIZE = 2 * 1024 * 1024;
static constexpr std::size_t TEXTURE_BUFFER_SIZE = 1 * 1024 * 1024;
ShaderProgramManager shader_program_manager;
OGLVertexArray sw_vao; // VAO for software shader draw
OGLVertexArray hw_vao; // VAO for hardware shader / accelerate draw
std::array<bool, 16> hw_vao_enabled_attributes{};
OGLTexture default_texture;
std::array<SamplerInfo, 3> texture_samplers;
OGLStreamBuffer vertex_buffer;
OGLStreamBuffer uniform_buffer;

View File

@@ -47,7 +47,7 @@ ShaderDiskCacheRaw::ShaderDiskCacheRaw(u64 unique_identifier, ProgramType progra
: unique_identifier{unique_identifier}, program_type{program_type}, config{config},
program_code{std::move(program_code)} {}
bool ShaderDiskCacheRaw::Load(Common::FS::IOFile& file) {
bool ShaderDiskCacheRaw::Load(FileUtil::IOFile& file) {
if (file.ReadBytes(&unique_identifier, sizeof(u64)) != sizeof(u64) ||
file.ReadBytes(&program_type, sizeof(u32)) != sizeof(u32)) {
return false;
@@ -77,7 +77,7 @@ bool ShaderDiskCacheRaw::Load(Common::FS::IOFile& file) {
return true;
}
bool ShaderDiskCacheRaw::Save(Common::FS::IOFile& file) const {
bool ShaderDiskCacheRaw::Save(FileUtil::IOFile& file) const {
if (file.WriteObject(unique_identifier) != 1 ||
file.WriteObject(static_cast<u32>(program_type)) != 1) {
return false;
@@ -114,7 +114,7 @@ std::optional<std::vector<ShaderDiskCacheRaw>> ShaderDiskCache::LoadTransferable
}
tried_to_load = true;
Common::FS::IOFile file(GetTransferablePath(), "rb");
FileUtil::IOFile file(GetTransferablePath(), "rb");
if (!file.IsOpen()) {
LOG_INFO(Render_OpenGL, "No transferable shader cache found for game with title id={}",
GetTitleID());
@@ -177,7 +177,7 @@ ShaderDiskCache::LoadPrecompiled(bool compressed) {
if (!IsUsable())
return {};
Common::FS::IOFile file(GetPrecompiledPath(), "rb");
FileUtil::IOFile file(GetPrecompiledPath(), "rb");
if (!file.IsOpen()) {
LOG_INFO(Render_OpenGL, "No precompiled shader cache found for game with title id={}",
GetTitleID());
@@ -197,7 +197,7 @@ ShaderDiskCache::LoadPrecompiled(bool compressed) {
}
std::optional<std::pair<std::unordered_map<u64, ShaderDiskCacheDecompiled>, ShaderDumpsMap>>
ShaderDiskCache::LoadPrecompiledFile(Common::FS::IOFile& file, bool compressed) {
ShaderDiskCache::LoadPrecompiledFile(FileUtil::IOFile& file, bool compressed) {
// Read compressed file from disk and decompress to virtual precompiled cache file
std::vector<u8> precompiled_file(file.GetSize());
file.ReadBytes(precompiled_file.data(), precompiled_file.size());
@@ -301,7 +301,7 @@ std::optional<ShaderDiskCacheDecompiled> ShaderDiskCache::LoadDecompiledEntry()
return entry;
}
void ShaderDiskCache::SaveDecompiledToFile(Common::FS::IOFile& file, u64 unique_identifier,
void ShaderDiskCache::SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier,
const ShaderDecompiler::ProgramResult& result,
bool sanitize_mul) {
if (!IsUsable())
@@ -331,7 +331,7 @@ bool ShaderDiskCache::SaveDecompiledToCache(u64 unique_identifier,
}
void ShaderDiskCache::InvalidateAll() {
if (!Common::FS::Delete(GetTransferablePath())) {
if (!FileUtil::Delete(GetTransferablePath())) {
LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}",
GetTransferablePath());
}
@@ -342,7 +342,7 @@ void ShaderDiskCache::InvalidatePrecompiled() {
// Clear virtual precompiled cache file
decompressed_precompiled_cache.resize(0);
if (!Common::FS::Delete(GetPrecompiledPath())) {
if (!FileUtil::Delete(GetPrecompiledPath())) {
LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath());
}
}
@@ -357,7 +357,7 @@ void ShaderDiskCache::SaveRaw(const ShaderDiskCacheRaw& entry) {
return;
}
Common::FS::IOFile file = AppendTransferableFile();
FileUtil::IOFile file = AppendTransferableFile();
if (!file.IsOpen())
return;
if (file.WriteObject(TransferableEntryKind::Raw) != 1 || !entry.Save(file)) {
@@ -413,7 +413,7 @@ void ShaderDiskCache::SaveDumpToFile(u64 unique_identifier, GLuint program, bool
if (!IsUsable())
return;
Common::FS::IOFile file = AppendPrecompiledFile();
FileUtil::IOFile file = AppendPrecompiledFile();
if (!file.IsOpen())
return;
@@ -444,14 +444,14 @@ bool ShaderDiskCache::IsUsable() const {
return tried_to_load && Settings::values.use_disk_shader_cache;
}
Common::FS::IOFile ShaderDiskCache::AppendTransferableFile() {
FileUtil::IOFile ShaderDiskCache::AppendTransferableFile() {
if (!EnsureDirectories())
return {};
const auto transferable_path{GetTransferablePath()};
const bool existed = Common::FS::Exists(transferable_path);
const bool existed = FileUtil::Exists(transferable_path);
Common::FS::IOFile file(transferable_path, "ab");
FileUtil::IOFile file(transferable_path, "ab");
if (!file.IsOpen()) {
LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path);
return {};
@@ -467,14 +467,14 @@ Common::FS::IOFile ShaderDiskCache::AppendTransferableFile() {
return file;
}
Common::FS::IOFile ShaderDiskCache::AppendPrecompiledFile() {
FileUtil::IOFile ShaderDiskCache::AppendPrecompiledFile() {
if (!EnsureDirectories())
return {};
const auto precompiled_path{GetPrecompiledPath()};
const bool existed = Common::FS::Exists(precompiled_path);
const bool existed = FileUtil::Exists(precompiled_path);
Common::FS::IOFile file(precompiled_path, "ab");
FileUtil::IOFile file(precompiled_path, "ab");
if (!file.IsOpen()) {
LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path);
return {};
@@ -506,7 +506,7 @@ void ShaderDiskCache::SaveVirtualPrecompiledFile() {
decompressed_precompiled_cache.data(), decompressed_precompiled_cache.size());
const auto precompiled_path{GetPrecompiledPath()};
Common::FS::IOFile file(precompiled_path, "wb");
FileUtil::IOFile file(precompiled_path, "wb");
if (!file.IsOpen()) {
LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path);
@@ -521,24 +521,24 @@ void ShaderDiskCache::SaveVirtualPrecompiledFile() {
bool ShaderDiskCache::EnsureDirectories() const {
const auto CreateDir = [](const std::string& dir) {
if (!Common::FS::CreateDir(dir)) {
if (!FileUtil::CreateDir(dir)) {
LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir);
return false;
}
return true;
};
return CreateDir(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)) &&
return CreateDir(FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir)) &&
CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) &&
CreateDir(GetPrecompiledDir()) && CreateDir(GetPrecompiledShaderDir());
}
std::string ShaderDiskCache::GetTransferablePath() {
return Common::FS::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
return FileUtil::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
}
std::string ShaderDiskCache::GetPrecompiledPath() {
return Common::FS::SanitizePath(GetPrecompiledShaderDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
return FileUtil::SanitizePath(GetPrecompiledShaderDir() + DIR_SEP_CHR + GetTitleID() + ".bin");
}
std::string ShaderDiskCache::GetTransferableDir() const {
@@ -557,7 +557,7 @@ std::string ShaderDiskCache::GetPrecompiledShaderDir() const {
}
std::string ShaderDiskCache::GetBaseDir() const {
return Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir) + DIR_SEP "opengl";
return FileUtil::GetUserPath(FileUtil::UserPath::ShaderDir) + DIR_SEP "opengl";
}
u64 ShaderDiskCache::GetProgramID() {

View File

@@ -27,7 +27,7 @@ namespace Core {
class System;
}
namespace Common::FS {
namespace FileUtil {
class IOFile;
}
@@ -49,9 +49,9 @@ public:
ShaderDiskCacheRaw() = default;
~ShaderDiskCacheRaw() = default;
bool Load(Common::FS::IOFile& file);
bool Load(FileUtil::IOFile& file);
bool Save(Common::FS::IOFile& file) const;
bool Save(FileUtil::IOFile& file) const;
u64 GetUniqueIdentifier() const {
return unique_identifier;
@@ -124,14 +124,14 @@ public:
private:
/// Loads the transferable cache. Returns empty on failure.
std::optional<std::pair<ShaderDecompiledMap, ShaderDumpsMap>> LoadPrecompiledFile(
Common::FS::IOFile& file, bool compressed);
FileUtil::IOFile& file, bool compressed);
/// Loads a decompiled cache entry from m_precompiled_cache_virtual_file. Returns empty on
/// failure.
std::optional<ShaderDiskCacheDecompiled> LoadDecompiledEntry();
/// Saves a decompiled entry to the passed file. Does not check for collisions.
void SaveDecompiledToFile(Common::FS::IOFile& file, u64 unique_identifier,
void SaveDecompiledToFile(FileUtil::IOFile& file, u64 unique_identifier,
const ShaderDecompiler::ProgramResult& code, bool sanitize_mul);
/// Saves a decompiled entry to the virtual precompiled cache. Does not check for collisions.
@@ -142,7 +142,7 @@ private:
bool IsUsable() const;
/// Opens current game's transferable file and write it's header if it doesn't exist
Common::FS::IOFile AppendTransferableFile();
FileUtil::IOFile AppendTransferableFile();
/// Save precompiled header to precompiled_cache_in_memory
void SavePrecompiledHeaderToVirtualPrecompiledCache();
@@ -223,7 +223,7 @@ private:
u64 program_id{};
std::string title_id;
Common::FS::IOFile AppendPrecompiledFile();
FileUtil::IOFile AppendPrecompiledFile();
};
} // namespace OpenGL

Some files were not shown because too many files have changed in this diff Show More