diff --git a/src/citra/config.cpp b/src/citra/config.cpp
index 88a36ed66..6f747fcf5 100644
--- a/src/citra/config.cpp
+++ b/src/citra/config.cpp
@@ -201,6 +201,10 @@ void Config::ReadValues() {
// Data Storage
Settings::values.use_virtual_sd =
sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true);
+ Settings::values.nand_dir = sdl2_config->GetString(
+ "Data Storage", "nand_directory", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
+ Settings::values.sdmc_dir = sdl2_config->GetString(
+ "Data Storage", "sdmc_directory", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
// System
Settings::values.is_new_3ds = sdl2_config->GetBoolean("System", "is_new_3ds", true);
diff --git a/src/citra/default_ini.h b/src/citra/default_ini.h
index 4646d5fd3..55d8da8f5 100644
--- a/src/citra/default_ini.h
+++ b/src/citra/default_ini.h
@@ -250,6 +250,14 @@ volume =
# 1 (default): Yes, 0: No
use_virtual_sd =
+# The path of the virtual SD card directory.
+# empty (default) will use the user_path
+sdmc_directory =
+
+# The path of NAND directory.
+# empty (default) will use the user_path
+nand_directory =
+
[System]
# The system model that Citra will try to emulate
# 0: Old 3DS, 1: New 3DS (default)
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt
index 24c2ca601..4f64f7be6 100644
--- a/src/citra_qt/CMakeLists.txt
+++ b/src/citra_qt/CMakeLists.txt
@@ -66,6 +66,9 @@ add_executable(citra-qt
configuration/configure_motion_touch.cpp
configuration/configure_motion_touch.h
configuration/configure_motion_touch.ui
+ configuration/configure_storage.cpp
+ configuration/configure_storage.h
+ configuration/configure_storage.ui
configuration/configure_system.cpp
configuration/configure_system.h
configuration/configure_system.ui
diff --git a/src/citra_qt/configuration/config.cpp b/src/citra_qt/configuration/config.cpp
index 8901c47c2..28612bbd0 100644
--- a/src/citra_qt/configuration/config.cpp
+++ b/src/citra_qt/configuration/config.cpp
@@ -302,6 +302,16 @@ void Config::ReadDataStorageValues() {
qt_config->beginGroup(QStringLiteral("Data Storage"));
Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool();
+ std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
+ Settings::values.nand_dir =
+ ReadSetting(QStringLiteral("nand_directory"), QString::fromStdString(nand_dir))
+ .toString()
+ .toStdString();
+ std::string sdmc_dir = FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir);
+ Settings::values.sdmc_dir =
+ ReadSetting(QStringLiteral("sdmc_directory"), QString::fromStdString(sdmc_dir))
+ .toString()
+ .toStdString();
qt_config->endGroup();
}
@@ -852,6 +862,12 @@ void Config::SaveDataStorageValues() {
qt_config->beginGroup(QStringLiteral("Data Storage"));
WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true);
+ WriteSetting(QStringLiteral("nand_directory"),
+ QString::fromStdString(Settings::values.nand_dir),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
+ WriteSetting(QStringLiteral("sdmc_directory"),
+ QString::fromStdString(Settings::values.sdmc_dir),
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
qt_config->endGroup();
}
diff --git a/src/citra_qt/configuration/configure.ui b/src/citra_qt/configuration/configure.ui
index efe53606e..1774ccbc1 100644
--- a/src/citra_qt/configuration/configure.ui
+++ b/src/citra_qt/configuration/configure.ui
@@ -68,6 +68,11 @@
Debug
+
+
+ Storage
+
+
Web
@@ -121,6 +126,12 @@
QWidget
configuration/configure_debug.h
1
+
+
+ ConfigureStorage
+ QWidget
+ configuration/configure_storage.h
+ 1
ConfigureInput
diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp
index 7e84f83ad..66eb3ec83 100644
--- a/src/citra_qt/configuration/configure_dialog.cpp
+++ b/src/citra_qt/configuration/configure_dialog.cpp
@@ -51,6 +51,7 @@ void ConfigureDialog::SetConfiguration() {
ui->debugTab->SetConfiguration();
ui->webTab->SetConfiguration();
ui->uiTab->SetConfiguration();
+ ui->storageTab->SetConfiguration();
}
void ConfigureDialog::ApplyConfiguration() {
@@ -66,6 +67,7 @@ void ConfigureDialog::ApplyConfiguration() {
ui->debugTab->ApplyConfiguration();
ui->webTab->ApplyConfiguration();
ui->uiTab->ApplyConfiguration();
+ ui->storageTab->ApplyConfiguration();
Settings::Apply();
Settings::LogSettings();
}
@@ -77,7 +79,7 @@ void ConfigureDialog::PopulateSelectionList() {
const std::array>, 5> items{
{{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
- {tr("System"), {ui->systemTab, ui->cameraTab}},
+ {tr("System"), {ui->systemTab, ui->cameraTab, ui->storageTab}},
{tr("Graphics"), {ui->enhancementsTab, ui->graphicsTab}},
{tr("Audio"), {ui->audioTab}},
{tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}};
@@ -118,6 +120,7 @@ void ConfigureDialog::RetranslateUI() {
ui->debugTab->RetranslateUI();
ui->webTab->RetranslateUI();
ui->uiTab->RetranslateUI();
+ ui->storageTab->RetranslateUI();
}
void ConfigureDialog::UpdateVisibleTabs() {
@@ -134,6 +137,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
{ui->audioTab, tr("Audio")},
{ui->cameraTab, tr("Camera")},
{ui->debugTab, tr("Debug")},
+ {ui->storageTab, tr("Storage")},
{ui->webTab, tr("Web")},
{ui->uiTab, tr("UI")}};
diff --git a/src/citra_qt/configuration/configure_storage.cpp b/src/citra_qt/configuration/configure_storage.cpp
new file mode 100644
index 000000000..62e299a90
--- /dev/null
+++ b/src/citra_qt/configuration/configure_storage.cpp
@@ -0,0 +1,78 @@
+// Copyright 2021 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include
+#include
+#include
+#include "citra_qt/configuration/configure_storage.h"
+#include "core/core.h"
+#include "core/settings.h"
+#include "ui_configure_storage.h"
+
+ConfigureStorage::ConfigureStorage(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique()) {
+ ui->setupUi(this);
+ SetConfiguration();
+
+ connect(ui->open_nand_dir, &QPushButton::clicked, []() {
+ QString path = QString::fromStdString(Settings::values.nand_dir);
+ 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(Settings::values.nand_dir),
+ QFileDialog::ShowDirsOnly);
+ if (!dir_path.isEmpty()) {
+ Settings::values.nand_dir = dir_path.toStdString();
+ SetConfiguration();
+ }
+ });
+
+ connect(ui->open_sdmc_dir, &QPushButton::clicked, []() {
+ QString path = QString::fromStdString(Settings::values.sdmc_dir);
+ 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(Settings::values.sdmc_dir),
+ QFileDialog::ShowDirsOnly);
+ if (!dir_path.isEmpty()) {
+ Settings::values.sdmc_dir = dir_path.toStdString();
+ SetConfiguration();
+ }
+ });
+
+ connect(ui->toggle_virtual_sd, &QCheckBox::clicked, this, [this]() {
+ ApplyConfiguration();
+ SetConfiguration();
+ });
+}
+
+ConfigureStorage::~ConfigureStorage() = default;
+
+void ConfigureStorage::SetConfiguration() {
+ ui->nand_group->setVisible(Settings::values.use_virtual_sd);
+ QString nand_path = QString::fromStdString(Settings::values.nand_dir);
+ ui->nand_dir_path->setText(nand_path);
+ ui->open_nand_dir->setEnabled(!Settings::values.nand_dir.empty());
+
+ ui->sdmc_group->setVisible(Settings::values.use_virtual_sd);
+ QString sdmc_path = QString::fromStdString(Settings::values.sdmc_dir);
+ ui->sdmc_dir_path->setText(sdmc_path);
+ ui->open_sdmc_dir->setEnabled(!Settings::values.sdmc_dir.empty());
+
+ ui->toggle_virtual_sd->setChecked(Settings::values.use_virtual_sd);
+
+ ui->storage_group->setEnabled(!Core::System::GetInstance().IsPoweredOn());
+}
+
+void ConfigureStorage::ApplyConfiguration() {
+ Settings::values.use_virtual_sd = ui->toggle_virtual_sd->isChecked();
+}
+
+void ConfigureStorage::RetranslateUI() {
+ ui->retranslateUi(this);
+}
diff --git a/src/citra_qt/configuration/configure_storage.h b/src/citra_qt/configuration/configure_storage.h
new file mode 100644
index 000000000..e79d3c008
--- /dev/null
+++ b/src/citra_qt/configuration/configure_storage.h
@@ -0,0 +1,26 @@
+// Copyright 2021 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include
+#include
+
+namespace Ui {
+class ConfigureStorage;
+}
+
+class ConfigureStorage : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureStorage(QWidget* parent = nullptr);
+ ~ConfigureStorage() override;
+
+ void ApplyConfiguration();
+ void RetranslateUI();
+ void SetConfiguration();
+
+ std::unique_ptr ui;
+};
diff --git a/src/citra_qt/configuration/configure_storage.ui b/src/citra_qt/configuration/configure_storage.ui
new file mode 100644
index 000000000..c6f72dd5d
--- /dev/null
+++ b/src/citra_qt/configuration/configure_storage.ui
@@ -0,0 +1,188 @@
+
+
+ ConfigureStorage
+
+
+
+ 0
+ 0
+ 681
+ 375
+
+
+
+ Form
+
+
+ -
+
+
-
+
+
+ Storage
+
+
+
-
+
+
-
+
+
+ Use Virtual SD
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+ NAND Directory
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ Open
+
+
+
+
+
+ -
+
+
-
+
+
+ NOTE: this does not move the contents of the previous directory to the new one
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Change
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
-
+
+
-
+
+
+ SDMC Directory
+
+
+
+ -
+
+
+ false
+
+
+
+ -
+
+
+ Open
+
+
+
+
+
+ -
+
+
-
+
+
+ NOTE: this does not move the contents of the previous directory to the new one
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Change
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 9901141b3..b7576c208 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -12,6 +12,7 @@
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "core/settings.h"
#ifdef _WIN32
#include
@@ -716,8 +717,13 @@ void SetUserPath(const std::string& path) {
}
#endif
}
- g_paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
- g_paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
+
+ g_paths.emplace(UserPath::SDMCDir, !Settings::values.sdmc_dir.empty()
+ ? Settings::values.sdmc_dir
+ : user_path + SDMC_DIR DIR_SEP);
+ g_paths.emplace(UserPath::NANDDir, !Settings::values.nand_dir.empty()
+ ? Settings::values.nand_dir
+ : user_path + NAND_DIR DIR_SEP);
g_paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
// TODO: Put the logs in a better location for each OS
g_paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
@@ -762,6 +768,11 @@ const std::string& GetUserPath(UserPath path) {
SetUserPath();
return g_paths[path];
}
+
+const void UpdateUserPath(UserPath path, const std::string& filename) {
+ g_paths[path] = filename + DIR_SEP;
+}
+
std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str) {
return IOFile(filename, text_file ? "w" : "wb").WriteString(str);
}
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 6fa14315c..0f0e862c3 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -186,6 +186,9 @@ void SetCurrentRomPath(const std::string& path);
// directory. To be used in "multi-user" mode (that is, installed).
[[nodiscard]] const std::string& GetUserPath(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();
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index ba2a170eb..855307ba5 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -118,6 +118,8 @@ void LogSettings() {
log_setting("Camera_OuterLeftConfig", values.camera_config[OuterLeftCamera]);
log_setting("Camera_OuterLeftFlip", values.camera_flip[OuterLeftCamera]);
log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd);
+ log_setting("DataStorage_SdmcDir", values.sdmc_dir);
+ log_setting("DataStorage_NandDir", values.nand_dir);
log_setting("System_IsNew3ds", values.is_new_3ds);
log_setting("System_RegionValue", values.region_value);
log_setting("Debugging_UseGdbstub", values.use_gdbstub);
diff --git a/src/core/settings.h b/src/core/settings.h
index 0876d312f..af41b9a02 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -140,6 +140,8 @@ struct Values {
// Data Storage
bool use_virtual_sd;
+ std::string nand_dir;
+ std::string sdmc_dir;
// System
int region_value;