summaryrefslogtreecommitdiff
path: root/src/yuzu/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/yuzu/main.cpp')
-rw-r--r--src/yuzu/main.cpp240
1 files changed, 187 insertions, 53 deletions
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index b5bfa6741..1d5a2b51a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -8,11 +8,15 @@
#include <thread>
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
+#include "applets/profile_select.h"
+#include "applets/software_keyboard.h"
+#include "configuration/configure_per_general.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
#include "core/hle/service/acc/profile_manager.h"
+#include "core/hle/service/am/applets/applets.h"
-// These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows
+// These are wrappers to avoid the calls to CreateDirectory and CreateFile because of the Windows
// defines.
static FileSys::VirtualDir VfsFilesystemCreateDirectoryWrapper(
const FileSys::VirtualFilesystem& vfs, const std::string& path, FileSys::Mode mode) {
@@ -59,6 +63,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "core/file_sys/romfs.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/submission_package.h"
+#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_ldr.h"
@@ -142,6 +147,9 @@ static void InitializeLogging() {
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>());
+#endif
}
GMainWindow::GMainWindow()
@@ -201,6 +209,49 @@ GMainWindow::~GMainWindow() {
delete render_window;
}
+void GMainWindow::ProfileSelectorSelectProfile() {
+ QtProfileSelectionDialog dialog(this);
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
+ Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
+ dialog.setWindowModality(Qt::WindowModal);
+ dialog.exec();
+
+ if (!dialog.GetStatus()) {
+ emit ProfileSelectorFinishedSelection(std::nullopt);
+ return;
+ }
+
+ Service::Account::ProfileManager manager;
+ const auto uuid = manager.GetUser(dialog.GetIndex());
+ if (!uuid.has_value()) {
+ emit ProfileSelectorFinishedSelection(std::nullopt);
+ return;
+ }
+
+ emit ProfileSelectorFinishedSelection(uuid);
+}
+
+void GMainWindow::SoftwareKeyboardGetText(
+ const Core::Frontend::SoftwareKeyboardParameters& parameters) {
+ QtSoftwareKeyboardDialog dialog(this, parameters);
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
+ Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
+ dialog.setWindowModality(Qt::WindowModal);
+ dialog.exec();
+
+ if (!dialog.GetStatus()) {
+ emit SoftwareKeyboardFinishedText(std::nullopt);
+ return;
+ }
+
+ emit SoftwareKeyboardFinishedText(dialog.GetText());
+}
+
+void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) {
+ QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message));
+ emit SoftwareKeyboardFinishedCheckDialog();
+}
+
void GMainWindow::InitializeWidgets() {
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
ui.action_Report_Compatibility->setVisible(true);
@@ -305,6 +356,11 @@ void GMainWindow::InitializeHotkeys() {
Qt::ApplicationShortcut);
hotkey_registry.RegisterHotkey("Main Window", "Decrease Speed Limit", QKeySequence("-"),
Qt::ApplicationShortcut);
+ hotkey_registry.RegisterHotkey("Main Window", "Load Amiibo", QKeySequence(Qt::Key_F2),
+ Qt::ApplicationShortcut);
+ hotkey_registry.RegisterHotkey("Main Window", "Capture Screenshot",
+ QKeySequence(QKeySequence::Print));
+
hotkey_registry.LoadHotkeys();
connect(hotkey_registry.GetHotkey("Main Window", "Load File", this), &QShortcut::activated,
@@ -358,6 +414,18 @@ void GMainWindow::InitializeHotkeys() {
UpdateStatusBar();
}
});
+ connect(hotkey_registry.GetHotkey("Main Window", "Load Amiibo", this), &QShortcut::activated,
+ this, [&] {
+ if (ui.action_Load_Amiibo->isEnabled()) {
+ OnLoadAmiibo();
+ }
+ });
+ connect(hotkey_registry.GetHotkey("Main Window", "Capture Screenshot", this),
+ &QShortcut::activated, this, [&] {
+ if (emu_thread->IsRunning()) {
+ OnCaptureScreenshot();
+ }
+ });
}
void GMainWindow::SetDefaultUIGeometry() {
@@ -406,6 +474,8 @@ void GMainWindow::ConnectWidgetEvents() {
connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID);
connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
&GMainWindow::OnGameListNavigateToGamedbEntry);
+ connect(game_list, &GameList::OpenPerGameGeneralRequested, this,
+ &GMainWindow::OnGameListOpenPerGameProperties);
connect(this, &GMainWindow::EmulationStarting, render_window,
&GRenderWindow::OnEmulationStarting);
@@ -453,7 +523,12 @@ void GMainWindow::ConnectMenuEvents() {
hotkey_registry.GetHotkey("Main Window", "Fullscreen", this)->key());
connect(ui.action_Fullscreen, &QAction::triggered, this, &GMainWindow::ToggleFullscreen);
+ // Movie
+ connect(ui.action_Capture_Screenshot, &QAction::triggered, this,
+ &GMainWindow::OnCaptureScreenshot);
+
// Help
+ connect(ui.action_Open_yuzu_Folder, &QAction::triggered, this, &GMainWindow::OnOpenYuzuFolder);
connect(ui.action_Rederive, &QAction::triggered, this,
std::bind(&GMainWindow::OnReinitializeKeys, this, ReinitializeKeyBehavior::Warning));
connect(ui.action_About, &QAction::triggered, this, &GMainWindow::OnAbout);
@@ -482,32 +557,20 @@ void GMainWindow::OnDisplayTitleBars(bool show) {
QStringList GMainWindow::GetUnsupportedGLExtensions() {
QStringList unsupported_ext;
- if (!GLAD_GL_ARB_program_interface_query)
- unsupported_ext.append("ARB_program_interface_query");
- if (!GLAD_GL_ARB_separate_shader_objects)
- unsupported_ext.append("ARB_separate_shader_objects");
- if (!GLAD_GL_ARB_vertex_attrib_binding)
- unsupported_ext.append("ARB_vertex_attrib_binding");
+ if (!GLAD_GL_ARB_direct_state_access)
+ unsupported_ext.append("ARB_direct_state_access");
if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
unsupported_ext.append("ARB_vertex_type_10f_11f_11f_rev");
if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
unsupported_ext.append("ARB_texture_mirror_clamp_to_edge");
- if (!GLAD_GL_ARB_base_instance)
- unsupported_ext.append("ARB_base_instance");
- if (!GLAD_GL_ARB_texture_storage)
- unsupported_ext.append("ARB_texture_storage");
if (!GLAD_GL_ARB_multi_bind)
unsupported_ext.append("ARB_multi_bind");
- if (!GLAD_GL_ARB_copy_image)
- unsupported_ext.append("ARB_copy_image");
// Extensions required to support some texture formats.
if (!GLAD_GL_EXT_texture_compression_s3tc)
unsupported_ext.append("EXT_texture_compression_s3tc");
if (!GLAD_GL_ARB_texture_compression_rgtc)
unsupported_ext.append("ARB_texture_compression_rgtc");
- if (!GLAD_GL_ARB_texture_compression_bptc)
- unsupported_ext.append("ARB_texture_compression_bptc");
if (!GLAD_GL_ARB_depth_buffer_float)
unsupported_ext.append("ARB_depth_buffer_float");
@@ -526,8 +589,8 @@ bool GMainWindow::LoadROM(const QString& filename) {
render_window->MakeCurrent();
if (!gladLoadGL()) {
- QMessageBox::critical(this, tr("Error while initializing OpenGL 3.3 Core!"),
- tr("Your GPU may not support OpenGL 3.3, or you do not "
+ QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
+ tr("Your GPU may not support OpenGL 4.3, or you do not "
"have the latest graphics driver."));
return false;
}
@@ -547,6 +610,9 @@ bool GMainWindow::LoadROM(const QString& filename) {
system.SetGPUDebugContext(debug_context);
+ system.SetProfileSelector(std::make_unique<QtProfileSelector>(*this));
+ system.SetSoftwareKeyboard(std::make_unique<QtSoftwareKeyboard>(*this));
+
const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
const auto drd_callout =
@@ -621,10 +687,26 @@ bool GMainWindow::LoadROM(const QString& filename) {
return true;
}
+void GMainWindow::SelectAndSetCurrentUser() {
+ QtProfileSelectionDialog dialog(this);
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
+ Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
+ dialog.setWindowModality(Qt::WindowModal);
+ dialog.exec();
+
+ if (dialog.GetStatus()) {
+ Settings::values.current_user = static_cast<s32>(dialog.GetIndex());
+ }
+}
+
void GMainWindow::BootGame(const QString& filename) {
LOG_INFO(Frontend, "yuzu starting...");
StoreRecentFile(filename); // Put the filename on top of the list
+ if (UISettings::values.select_user_on_boot) {
+ SelectAndSetCurrentUser();
+ }
+
if (!LoadROM(filename))
return;
@@ -698,6 +780,7 @@ void GMainWindow::ShutdownGame() {
ui.action_Restart->setEnabled(false);
ui.action_Report_Compatibility->setEnabled(false);
ui.action_Load_Amiibo->setEnabled(false);
+ ui.action_Capture_Screenshot->setEnabled(false);
render_window->hide();
game_list->show();
game_list->setFilterFocus();
@@ -760,33 +843,27 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir);
ASSERT(program_id != 0);
- Service::Account::ProfileManager manager{};
- const auto user_ids = manager.GetAllUsers();
- QStringList list;
- for (const auto& user_id : user_ids) {
- if (user_id == Service::Account::UUID{})
- continue;
- Service::Account::ProfileBase base;
- if (!manager.GetProfileBase(user_id, base))
- continue;
-
- list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer(
- reinterpret_cast<const char*>(base.username.data()), base.username.size())));
- }
+ const auto select_profile = [this]() -> s32 {
+ QtProfileSelectionDialog dialog(this);
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
+ Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
+ dialog.setWindowModality(Qt::WindowModal);
+ dialog.exec();
- bool ok = false;
- const auto index_string =
- QInputDialog::getItem(this, tr("Select User"),
- tr("Please select the user's save data you would like to open."),
- list, Settings::values.current_user, false, &ok);
- if (!ok)
- return;
+ if (!dialog.GetStatus()) {
+ return -1;
+ }
+
+ return dialog.GetIndex();
+ };
- const auto index = list.indexOf(index_string);
- ASSERT(index != -1 && index < 8);
+ const auto index = select_profile();
+ if (index == -1)
+ return;
+ Service::Account::ProfileManager manager;
const auto user_id = manager.GetUser(index);
- ASSERT(user_id != std::nullopt);
+ ASSERT(user_id);
path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser,
FileSys::SaveDataType::SaveData,
program_id, user_id->uuid, 0);
@@ -881,7 +958,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
}
const auto installed = Service::FileSystem::GetUnionContents();
- auto romfs_title_id = SelectRomFSDumpTarget(*installed, program_id);
+ const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
if (!romfs_title_id) {
failed();
@@ -896,7 +973,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
if (*romfs_title_id == program_id) {
romfs = file;
} else {
- romfs = installed->GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
+ romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
}
const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full);
@@ -929,7 +1006,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
const auto full = res == "Full";
const auto entry_size = CalculateRomFSEntrySize(extracted, full);
- QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this);
+ QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0,
+ static_cast<s32>(entry_size), this);
progress.setWindowModality(Qt::WindowModal);
progress.setMinimumDuration(100);
@@ -961,6 +1039,32 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id,
QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory));
}
+void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
+ u64 title_id{};
+ const auto v_file = Core::GetGameFileFromPath(vfs, file);
+ const auto loader = Loader::GetLoader(v_file);
+ if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
+ QMessageBox::information(this, tr("Properties"),
+ tr("The game properties could not be loaded."));
+ return;
+ }
+
+ ConfigurePerGameGeneral dialog(this, title_id);
+ dialog.loadFromFile(v_file);
+ auto result = dialog.exec();
+ if (result == QDialog::Accepted) {
+ dialog.applyConfiguration();
+
+ const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
+ if (reload) {
+ game_list->PopulateAsync(UISettings::values.gamedir,
+ UISettings::values.gamedir_deepscan);
+ }
+
+ config->Save();
+ }
+}
+
void GMainWindow::OnMenuLoadFile() {
const QString extensions =
QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main");
@@ -1080,14 +1184,14 @@ void GMainWindow::OnMenuInstallToNAND() {
return;
}
const auto res =
- Service::FileSystem::GetUserNANDContents()->InstallEntry(nsp, false, qt_raw_copy);
+ Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy);
if (res == FileSys::InstallResult::Success) {
success();
} else {
if (res == FileSys::InstallResult::ErrorAlreadyExists) {
if (overwrite()) {
const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- nsp, true, qt_raw_copy);
+ *nsp, true, qt_raw_copy);
if (res2 == FileSys::InstallResult::Success) {
success();
} else {
@@ -1142,10 +1246,10 @@ void GMainWindow::OnMenuInstallToNAND() {
FileSys::InstallResult res;
if (index >= static_cast<size_t>(FileSys::TitleType::Application)) {
res = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+ *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
} else {
res = Service::FileSystem::GetSystemNANDContents()->InstallEntry(
- nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+ *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
}
if (res == FileSys::InstallResult::Success) {
@@ -1153,7 +1257,7 @@ void GMainWindow::OnMenuInstallToNAND() {
} else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
if (overwrite()) {
const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
- nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
+ *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
if (res2 == FileSys::InstallResult::Success) {
success();
} else {
@@ -1215,8 +1319,13 @@ void GMainWindow::OnMenuRecentFile() {
void GMainWindow::OnStartGame() {
emu_thread->SetRunning(true);
+
+ qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
+ "Core::Frontend::SoftwareKeyboardParameters");
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
qRegisterMetaType<std::string>("std::string");
+ qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>");
+
connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
ui.action_Start->setEnabled(false);
@@ -1229,6 +1338,7 @@ void GMainWindow::OnStartGame() {
discord_rpc->Update();
ui.action_Load_Amiibo->setEnabled(true);
+ ui.action_Capture_Screenshot->setEnabled(true);
}
void GMainWindow::OnPauseGame() {
@@ -1237,6 +1347,7 @@ void GMainWindow::OnPauseGame() {
ui.action_Start->setEnabled(true);
ui.action_Pause->setEnabled(false);
ui.action_Stop->setEnabled(true);
+ ui.action_Capture_Screenshot->setEnabled(false);
}
void GMainWindow::OnStopGame() {
@@ -1328,7 +1439,13 @@ void GMainWindow::OnConfigure() {
UpdateUITheme();
if (UISettings::values.enable_discord_presence != old_discord_presence)
SetDiscordEnabled(UISettings::values.enable_discord_presence);
- game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
+
+ const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false);
+ if (reload) {
+ game_list->PopulateAsync(UISettings::values.gamedir,
+ UISettings::values.gamedir_deepscan);
+ }
+
config->Save();
}
}
@@ -1374,6 +1491,11 @@ void GMainWindow::OnLoadAmiibo() {
}
}
+void GMainWindow::OnOpenYuzuFolder() {
+ QDesktopServices::openUrl(QUrl::fromLocalFile(
+ QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::UserDir))));
+}
+
void GMainWindow::OnAbout() {
AboutDialog aboutDialog(this);
aboutDialog.exec();
@@ -1388,6 +1510,18 @@ void GMainWindow::OnToggleFilterBar() {
}
}
+void GMainWindow::OnCaptureScreenshot() {
+ OnPauseGame();
+ const QString path =
+ QFileDialog::getSaveFileName(this, tr("Capture Screenshot"),
+ UISettings::values.screenshot_path, tr("PNG Image (*.png)"));
+ if (!path.isEmpty()) {
+ UISettings::values.screenshot_path = QFileInfo(path).path();
+ render_window->CaptureScreenshot(UISettings::values.screenshot_resolution_factor, path);
+ }
+ OnStartGame();
+}
+
void GMainWindow::UpdateStatusBar() {
if (emu_thread == nullptr) {
status_bar_update_timer.stop();
@@ -1532,7 +1666,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
"derivation. It will be attempted but may not complete.<br><br>") +
errors +
tr("<br><br>You can get all of these and dump all of your games easily by "
- "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the "
+ "following <a href='https://yuzu-emu.org/help/quickstart/'>the "
"quickstart guide</a>. Alternatively, you can use another method of dumping "
"to obtain all of your keys."));
}
@@ -1560,7 +1694,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
}
}
-boost::optional<u64> GMainWindow::SelectRomFSDumpTarget(
+std::optional<u64> GMainWindow::SelectRomFSDumpTarget(
const FileSys::RegisteredCacheUnion& installed, u64 program_id) {
const auto dlc_entries =
installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
@@ -1587,7 +1721,7 @@ boost::optional<u64> GMainWindow::SelectRomFSDumpTarget(
this, tr("Select RomFS Dump Target"),
tr("Please select which RomFS you would like to dump."), list, 0, false, &ok);
if (!ok) {
- return boost::none;
+ return {};
}
return romfs_tids[list.indexOf(res)];
@@ -1612,7 +1746,7 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
return;
}
- if (ui.action_Fullscreen->isChecked()) {
+ if (!ui.action_Fullscreen->isChecked()) {
UISettings::values.geometry = saveGeometry();
UISettings::values.renderwindow_geometry = render_window->saveGeometry();
}