Compare commits
19 Commits
fs-fixes
...
blit-scree
Author | SHA1 | Date | |
---|---|---|---|
|
797d0e45ce | ||
|
37ce0540ed | ||
|
e978931574 | ||
|
facddbfc8c | ||
|
68a3217d1e | ||
|
ddfae0025c | ||
|
75b6c5b4c5 | ||
|
32c6e76ab9 | ||
|
479c7ed162 | ||
|
009dfa5265 | ||
|
b225239e1f | ||
|
f887621921 | ||
|
407ae3972a | ||
|
0f0ca03551 | ||
|
9ae84fb6e8 | ||
|
215255d415 | ||
|
f5f88101e1 | ||
|
e511e51491 | ||
|
242047744e |
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -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
3
.gitmodules
vendored
@@ -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
|
||||
|
3
externals/CMakeLists.txt
vendored
3
externals/CMakeLists.txt
vendored
@@ -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
1
externals/sirit
vendored
Submodule
Submodule externals/sirit added at 297d820eeb
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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());
|
||||
|
||||
|
@@ -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>());
|
||||
|
@@ -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", ""));
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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__
|
||||
|
@@ -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));
|
||||
});
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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();
|
||||
});
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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
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
396
src/common/file_util.h
Normal 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
|
||||
}
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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"
|
@@ -1,6 +0,0 @@
|
||||
#include "fs_serialize.h"
|
||||
|
||||
fs_serialize::fs_serialize()
|
||||
{
|
||||
|
||||
}
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -10,6 +10,8 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr float PI = 3.14159265f;
|
||||
|
||||
template <class T>
|
||||
struct Rectangle {
|
||||
T left{};
|
||||
|
@@ -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);
|
||||
|
@@ -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) {
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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");
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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] == '.');
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
|
||||
|
@@ -30,11 +30,11 @@ private:
|
||||
*/
|
||||
class DirectRomFSReader : public RomFSReader {
|
||||
public:
|
||||
DirectRomFSReader(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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -16,7 +16,9 @@ namespace Frontend {
|
||||
/// WindowInformation
|
||||
enum class WindowSystemType : u8 {
|
||||
Headless,
|
||||
Android,
|
||||
Windows,
|
||||
MacOS,
|
||||
X11,
|
||||
Wayland,
|
||||
};
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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) {
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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))
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
};
|
||||
|
||||
|
@@ -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))
|
||||
|
@@ -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);
|
||||
|
@@ -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)) {
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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 {};
|
||||
|
@@ -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";
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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)
|
||||
|
0
src/video_core/host_shaders/vulkan_present.frag
Normal file
0
src/video_core/host_shaders/vulkan_present.frag
Normal file
0
src/video_core/host_shaders/vulkan_present.vert
Normal file
0
src/video_core/host_shaders/vulkan_present.vert
Normal 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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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() {
|
||||
|
@@ -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
Reference in New Issue
Block a user