From 074468e536c03dd52b781f48b6d3b0f705271826 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 17 Jul 2023 11:23:46 +0300 Subject: [PATCH 1/5] Qt: Locate RPCS3.log.gz and exit toolbar action --- rpcs3/rpcs3qt/main_window.cpp | 49 +++++++++++++++++++++++++++++++++++ rpcs3/rpcs3qt/main_window.h | 2 ++ rpcs3/rpcs3qt/main_window.ui | 12 +++++++++ 3 files changed, 63 insertions(+) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 2920892fceae..695709f77c3c 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2347,6 +2347,48 @@ void main_window::CreateConnects() connect(ui->bootInstallPkgAct, &QAction::triggered, this, [this] {InstallPackages(); }); connect(ui->bootInstallPupAct, &QAction::triggered, this, [this] {InstallPup(); }); + + connect(this, &main_window::NotifyWindowCloseEvent, this, [this](bool closed) + { + if (!closed) + { + // Cancel the request + m_requested_show_logs_on_exit = false; + return; + } + + if (!m_requested_show_logs_on_exit) + { + // Not requested + return; + } + + const std::string archived_path = fs::get_cache_dir() + "RPCS3.log.gz"; + const std::string raw_file_path = fs::get_cache_dir() + "RPCS3.log"; + + fs::stat_t raw_stat{}; + fs::stat_t archived_stat{}; + + if ((!fs::stat(raw_file_path, raw_stat) || raw_stat.is_directory) || (!fs::stat(archived_path, archived_stat) || archived_stat.is_directory) || (raw_stat.size == 0 && archived_stat.size == 0)) + { + QMessageBox::warning(this, tr("Failed to locate log"), tr("Failed to locate log files.\nMake sure that RPCS3.log and RPCS3.log.gz are writable and can be created without permission issues.")); + return; + } + + if (archived_stat.size) + { + gui::utils::open_dir(archived_path); + return; + } + + gui::utils::open_dir(raw_file_path); + }); + + connect(ui->exitAndLocateLogAct, &QAction::triggered, this, [this]() + { + m_requested_show_logs_on_exit = true; + QWidget::close(); + }); connect(ui->exitAct, &QAction::triggered, this, &QWidget::close); connect(ui->batchCreatePPUCachesAct, &QAction::triggered, m_game_list_frame, &game_list_frame::BatchCreatePPUCaches); @@ -3212,6 +3254,7 @@ void main_window::closeEvent(QCloseEvent* closeEvent) { if (!m_gui_settings->GetBootConfirmation(this, gui::ib_confirm_exit)) { + Q_EMIT NotifyWindowCloseEvent(false); closeEvent->ignore(); return; } @@ -3223,6 +3266,12 @@ void main_window::closeEvent(QCloseEvent* closeEvent) } SaveWindowState(); + + // Flush logs here as well + logs::listener::sync_all(); + + Q_EMIT NotifyWindowCloseEvent(true); + Emu.Quit(true); } diff --git a/rpcs3/rpcs3qt/main_window.h b/rpcs3/rpcs3qt/main_window.h index ea41113efa74..e940fa03e13b 100644 --- a/rpcs3/rpcs3qt/main_window.h +++ b/rpcs3/rpcs3qt/main_window.h @@ -50,6 +50,7 @@ class main_window : public QMainWindow bool m_is_list_mode = true; bool m_save_slider_pos = false; + bool m_requested_show_logs_on_exit = false; int m_other_slider_pos = 0; QIcon m_app_icon; @@ -95,6 +96,7 @@ class main_window : public QMainWindow void RequestGlobalStylesheetChange(); void RequestTrophyManagerRepaint(); void NotifyEmuSettingsChange(); + void NotifyWindowCloseEvent(bool closed); public Q_SLOTS: void OnEmuStop(); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index b6f72c53661f..5a4adb992b64 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -213,6 +213,7 @@ + @@ -588,6 +589,17 @@ Configure Auto Pause + + + Exit And Locate Log + + + Exit RPCS3 and locate RPCS3.log.gz + + + Exit the application and locate the log + + Exit From 4fa7f466cf1c4af09f2ea2c5c25ff26d51083da1 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 17 Jul 2023 17:19:26 +0300 Subject: [PATCH 2/5] In addition, rename log --- rpcs3/rpcs3qt/main_window.cpp | 41 +++++++++++++++++++- rpcs3/rpcs3qt/main_window.ui | 8 ++-- rpcs3/util/logs.cpp | 72 ++++++++++++++++++++++++++++++++++- rpcs3/util/logs.hpp | 6 +++ 4 files changed, 121 insertions(+), 6 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 695709f77c3c..3976df3f71f9 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2375,16 +2375,55 @@ void main_window::CreateConnects() return; } + // Get new filename from title and title ID but simplified + std::string log_filename = Emu.GetTitleAndTitleID(); + log_filename.erase(std::remove_if(log_filename.begin(), log_filename.end(), [](u8 c){ return !std::isalnum(c) && c != ' ' && c != '[' && ']'; }), log_filename.end()); + fmt::trim_back(log_filename); + + auto rename_log = [](const std::string& from, const std::string& to) + { + // Test writablity here to avoid closing the log with no *chance* of success + if (fs::file test_writable{to, fs::write + fs::create}; !test_writable) + { + return false; + } + + // Close and flush log file handle (!) + // Cannot rename the file due to file management design + logs::listener::close_all_prematurely(); + + // Try to move it + return fs::rename(from, to, true); + }; + if (archived_stat.size) { + const std::string dest_archived_path = fs::get_cache_dir() + log_filename + ".log.gz"; + + if (!Emu.GetTitleID().empty() && rename_log(archived_path, dest_archived_path)) + { + gui_log.success("Moved log file to '%s'!", dest_archived_path); + gui::utils::open_dir(dest_archived_path); + return; + } + gui::utils::open_dir(archived_path); return; } + const std::string dest_raw_file_path = fs::get_cache_dir() + log_filename + ".log"; + + if (!Emu.GetTitleID().empty() && rename_log(raw_file_path, dest_raw_file_path)) + { + gui_log.success("Moved log file to '%s'!", dest_raw_file_path); + gui::utils::open_dir(dest_raw_file_path); + return; + } + gui::utils::open_dir(raw_file_path); }); - connect(ui->exitAndLocateLogAct, &QAction::triggered, this, [this]() + connect(ui->exitAnchorAndLocateLogAct, &QAction::triggered, this, [this]() { m_requested_show_logs_on_exit = true; QWidget::close(); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 5a4adb992b64..80f11d16f70a 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -213,7 +213,7 @@ - + @@ -589,12 +589,12 @@ Configure Auto Pause - + - Exit And Locate Log + Exit, Locate And Anchor Log - Exit RPCS3 and locate RPCS3.log.gz + Exit RPCS3, anchor the file log it and locate it Exit the application and locate the log diff --git a/rpcs3/util/logs.cpp b/rpcs3/util/logs.cpp index c900aa2745a0..8c1445987150 100644 --- a/rpcs3/util/logs.cpp +++ b/rpcs3/util/logs.cpp @@ -107,6 +107,9 @@ namespace logs // Ensure written to disk void sync(); + + // Close file handle after flushing to disk + void close_prematurely(); }; struct file_listener final : file_writer, public listener @@ -121,6 +124,11 @@ namespace logs { file_writer::sync(); } + + void close_prematurely() override + { + file_writer::close_prematurely(); + } }; struct root_listener final : public listener @@ -353,6 +361,10 @@ void logs::listener::sync() { } +void logs::listener::close_prematurely() +{ +} + void logs::listener::sync_all() { for (listener* lis = get_logger(); lis; lis = lis->m_next) @@ -361,6 +373,14 @@ void logs::listener::sync_all() } } +void logs::listener::close_all_prematurely() +{ + for (listener* lis = get_logger(); lis; lis = lis->m_next) + { + lis->close_prematurely(); + } +} + logs::registerer::registerer(channel& _ch) { std::lock_guard lock(g_mutex); @@ -535,7 +555,7 @@ logs::file_writer::~file_writer() } #ifdef _WIN32 - // Cancel compressed log file autodeletion + // Cancel compressed log file auto-deletion FILE_DISPOSITION_INFO disp; disp.DeleteFileW = false; SetFileInformationByHandle(m_fout2.get_handle(), FileDispositionInfo, &disp, sizeof(disp)); @@ -691,6 +711,56 @@ void logs::file_writer::sync() } } +void logs::file_writer::close_prematurely() +{ + if (!m_fptr) + { + return; + } + + // Ensure written to disk + sync(); + + std::lock_guard lock(m_m); + + if (m_fout2) + { + m_zs.avail_in = 0; + m_zs.next_in = nullptr; + + do + { + m_zs.avail_out = sizeof(m_zout); + m_zs.next_out = m_zout; + + if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out) + { + break; + } + } + while (m_zs.avail_out == 0); + + deflateEnd(&m_zs); + +#ifdef _WIN32 + // Cancel compressed log file auto-deletion + FILE_DISPOSITION_INFO disp; + disp.DeleteFileW = false; + SetFileInformationByHandle(m_fout2.get_handle(), FileDispositionInfo, &disp, sizeof(disp)); +#else + // Restore compressed log file permissions + ::fchmod(m_fout2.get_handle(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +#endif + + m_fout2.close(); + } + + if (m_fout) + { + m_fout.close(); + } +} + logs::file_listener::file_listener(const std::string& path, u64 max_size) : file_writer(path, max_size) , listener() diff --git a/rpcs3/util/logs.hpp b/rpcs3/util/logs.hpp index e174233ade05..21b00c4b352a 100644 --- a/rpcs3/util/logs.hpp +++ b/rpcs3/util/logs.hpp @@ -84,6 +84,9 @@ namespace logs // Flush contents (file writer) virtual void sync(); + // Close file handle after flushing to disk (hazardous) + virtual void close_prematurely(); + // Add new listener static void add(listener*); @@ -92,6 +95,9 @@ namespace logs // Flush log to disk static void sync_all(); + + // Close file handle after flushing to disk (hazardous) + static void close_all_prematurely(); }; struct alignas(16) channel : private message From 58da45e33077176e99bc865a0dc25eb041d87f66 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 17 Jul 2023 19:37:27 +0300 Subject: [PATCH 3/5] Fixes --- rpcs3/rpcs3qt/main_window.cpp | 36 +++++++++++++++++++++++++++-------- rpcs3/rpcs3qt/main_window.ui | 10 +++++----- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 3976df3f71f9..76292cbf263e 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2398,12 +2398,22 @@ void main_window::CreateConnects() if (archived_stat.size) { - const std::string dest_archived_path = fs::get_cache_dir() + log_filename + ".log.gz"; + std::string dest_archived_path = fs::get_cache_dir() + log_filename + ".log.gz"; - if (!Emu.GetTitleID().empty() && rename_log(archived_path, dest_archived_path)) + const QString file_path = QFileDialog::getOpenFileName(this, tr("Save RPCS3's log file"), qstr(dest_archived_path), tr("RPCS3 Archived Log File (*.log.gz);;All files (*.*)")); + + if (file_path.isEmpty()) + { + // Aborted - view the current location + gui::utils::open_dir(archived_path); + return; + } + + dest_archived_path = file_path.toStdString(); + + if (!Emu.GetTitleID().empty() && !dest_archived_path.empty() && rename_log(archived_path, dest_archived_path)) { gui_log.success("Moved log file to '%s'!", dest_archived_path); - gui::utils::open_dir(dest_archived_path); return; } @@ -2411,22 +2421,32 @@ void main_window::CreateConnects() return; } - const std::string dest_raw_file_path = fs::get_cache_dir() + log_filename + ".log"; + std::string dest_raw_file_path = fs::get_cache_dir() + log_filename + ".log"; + + const QString file_path = QFileDialog::getOpenFileName(this, tr("Save RPCS3's log file"), qstr(dest_raw_file_path), tr("RPCS3 Non-Archived Log File (*.log);;All files (*.*)")); + + if (file_path.isEmpty()) + { + // Aborted - view the current location + gui::utils::open_dir(raw_file_path); + return; + } + + dest_raw_file_path = file_path.toStdString(); - if (!Emu.GetTitleID().empty() && rename_log(raw_file_path, dest_raw_file_path)) + if (!Emu.GetTitleID().empty() && !dest_raw_file_path.empty() && rename_log(raw_file_path, dest_raw_file_path)) { gui_log.success("Moved log file to '%s'!", dest_raw_file_path); - gui::utils::open_dir(dest_raw_file_path); return; } gui::utils::open_dir(raw_file_path); }); - connect(ui->exitAnchorAndLocateLogAct, &QAction::triggered, this, [this]() + connect(ui->exitAndSaveLogAct, &QAction::triggered, this, [this]() { m_requested_show_logs_on_exit = true; - QWidget::close(); + close(); }); connect(ui->exitAct, &QAction::triggered, this, &QWidget::close); diff --git a/rpcs3/rpcs3qt/main_window.ui b/rpcs3/rpcs3qt/main_window.ui index 80f11d16f70a..9c93bf5c792d 100644 --- a/rpcs3/rpcs3qt/main_window.ui +++ b/rpcs3/rpcs3qt/main_window.ui @@ -213,7 +213,7 @@ - + @@ -589,15 +589,15 @@ Configure Auto Pause - + - Exit, Locate And Anchor Log + Exit And Save Log - Exit RPCS3, anchor the file log it and locate it + Exit RPCS3, move the log file to a custom location - Exit the application and locate the log + Exit the application and save the log to a user-defined location From 23d6cd72fc2dbc91dc30334bd7581575e0a401bb Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 17 Jul 2023 19:57:34 +0300 Subject: [PATCH 4/5] More fixes --- rpcs3/rpcs3qt/gui_settings.h | 1 + rpcs3/rpcs3qt/main_window.cpp | 47 ++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 15 deletions(-) diff --git a/rpcs3/rpcs3qt/gui_settings.h b/rpcs3/rpcs3qt/gui_settings.h index c1898d3fa737..1ff437790ba5 100644 --- a/rpcs3/rpcs3qt/gui_settings.h +++ b/rpcs3/rpcs3qt/gui_settings.h @@ -135,6 +135,7 @@ namespace gui const gui_save fd_insert_disc = gui_save(main_window, "lastExplorePathDISC", ""); const gui_save fd_cfg_check = gui_save(main_window, "lastExplorePathCfgChk", ""); const gui_save fd_save_elf = gui_save(main_window, "lastExplorePathSaveElf", ""); + const gui_save fd_save_log = gui_save(main_window, "lastExplorePathSaveLog", ""); const gui_save mw_debugger = gui_save(main_window, "debuggerVisible", false); const gui_save mw_logger = gui_save(main_window, "loggerVisible", true); diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 76292cbf263e..66080d963151 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -2376,12 +2376,19 @@ void main_window::CreateConnects() } // Get new filename from title and title ID but simplified - std::string log_filename = Emu.GetTitleAndTitleID(); + std::string log_filename = Emu.GetTitleID().empty() ? "RPCS3" : Emu.GetTitleAndTitleID(); log_filename.erase(std::remove_if(log_filename.begin(), log_filename.end(), [](u8 c){ return !std::isalnum(c) && c != ' ' && c != '[' && ']'; }), log_filename.end()); fmt::trim_back(log_filename); - auto rename_log = [](const std::string& from, const std::string& to) + QString path_last_log = m_gui_settings->GetValue(gui::fd_save_log).toString(); + + auto move_log = [](const std::string& from, const std::string& to) { + if (from == to) + { + return false; + } + // Test writablity here to avoid closing the log with no *chance* of success if (fs::file test_writable{to, fs::write + fs::create}; !test_writable) { @@ -2393,26 +2400,37 @@ void main_window::CreateConnects() logs::listener::close_all_prematurely(); // Try to move it - return fs::rename(from, to, true); + if (fs::rename(from, to, true)) + { + return true; + } + + // Try to copy it if fails + if (fs::copy_file(from, to, true)) + { + fs::remove_file(from); + return true; + } + + return false; }; if (archived_stat.size) { - std::string dest_archived_path = fs::get_cache_dir() + log_filename + ".log.gz"; + const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select RPCS3's log saving location (saving %0)").arg(qstr(log_filename + ".log.gz")), path_last_log, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); - const QString file_path = QFileDialog::getOpenFileName(this, tr("Save RPCS3's log file"), qstr(dest_archived_path), tr("RPCS3 Archived Log File (*.log.gz);;All files (*.*)")); - - if (file_path.isEmpty()) + if (dir_path.isEmpty()) { // Aborted - view the current location gui::utils::open_dir(archived_path); return; } - dest_archived_path = file_path.toStdString(); + const std::string dest_archived_path = dir_path.toStdString() + "/" + log_filename + ".log.gz"; - if (!Emu.GetTitleID().empty() && !dest_archived_path.empty() && rename_log(archived_path, dest_archived_path)) + if (!Emu.GetTitleID().empty() && !dest_archived_path.empty() && move_log(archived_path, dest_archived_path)) { + m_gui_settings->SetValue(gui::fd_save_log, dir_path); gui_log.success("Moved log file to '%s'!", dest_archived_path); return; } @@ -2421,21 +2439,20 @@ void main_window::CreateConnects() return; } - std::string dest_raw_file_path = fs::get_cache_dir() + log_filename + ".log"; - - const QString file_path = QFileDialog::getOpenFileName(this, tr("Save RPCS3's log file"), qstr(dest_raw_file_path), tr("RPCS3 Non-Archived Log File (*.log);;All files (*.*)")); + const QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select RPCS3's log saving location (saving %0)").arg(qstr(log_filename + ".log")), path_last_log, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); - if (file_path.isEmpty()) + if (dir_path.isEmpty()) { // Aborted - view the current location gui::utils::open_dir(raw_file_path); return; } - dest_raw_file_path = file_path.toStdString(); + const std::string dest_raw_file_path = dir_path.toStdString() + "/" + log_filename + ".log"; - if (!Emu.GetTitleID().empty() && !dest_raw_file_path.empty() && rename_log(raw_file_path, dest_raw_file_path)) + if (!Emu.GetTitleID().empty() && !dest_raw_file_path.empty() && move_log(raw_file_path, dest_raw_file_path)) { + m_gui_settings->SetValue(gui::fd_save_log, dir_path); gui_log.success("Moved log file to '%s'!", dest_raw_file_path); return; } From ab0c3649ee09c522b612d948e7d79f089e431fee Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 17 Jul 2023 22:27:16 +0300 Subject: [PATCH 5/5] Fixup main_window::IsValidFile --- rpcs3/rpcs3qt/main_window.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rpcs3/rpcs3qt/main_window.cpp b/rpcs3/rpcs3qt/main_window.cpp index 66080d963151..9aa243fb1046 100644 --- a/rpcs3/rpcs3qt/main_window.cpp +++ b/rpcs3/rpcs3qt/main_window.cpp @@ -3373,6 +3373,11 @@ Check data for valid file types and cache their paths if necessary */ main_window::drop_type main_window::IsValidFile(const QMimeData& md, QStringList* drop_paths) { + if (drop_paths) + { + drop_paths->clear(); + } + drop_type type = drop_type::drop_error; QList list = md.urls(); // get list of all the dropped file urls @@ -3380,6 +3385,14 @@ main_window::drop_type main_window::IsValidFile(const QMimeData& md, QStringList // Try to cache the data for half a second if (m_drop_file_timestamp != umax && m_drop_file_url_list == list && get_system_time() - m_drop_file_timestamp < 500'000) { + if (drop_paths) + { + for (auto&& url : m_drop_file_url_list) + { + drop_paths->append(url.toLocalFile()); + } + } + return m_drop_file_cached_drop_type; }