From 2e8076d909ce9fbfa0b804a3618497f43d38b98c Mon Sep 17 00:00:00 2001 From: Tyler Wilding Date: Sun, 3 Apr 2022 20:06:59 -0400 Subject: [PATCH] input-rec: begin achieving parity in Qt --- pcsx2-qt/EmuThread.cpp | 17 ++ pcsx2-qt/MainWindow.cpp | 92 ++++++++++ pcsx2-qt/MainWindow.h | 6 + pcsx2-qt/MainWindow.ui | 55 ++++++ pcsx2-qt/QtHost.cpp | 4 + pcsx2-qt/input-rec/NewInputRecordingDlg.cpp | 113 ++++++++++++ .../NewInputRecordingDlg.h} | 37 ++-- pcsx2-qt/input-rec/NewInputRecordingDlg.ui | 163 ++++++++++++++++++ pcsx2-qt/pcsx2-qt.vcxproj | 8 +- pcsx2-qt/pcsx2-qt.vcxproj.filters | 20 +++ pcsx2/Pcsx2Config.cpp | 1 + pcsx2/pcsx2core.vcxproj | 12 +- pcsx2/pcsx2core.vcxproj.filters | 36 ++++ 13 files changed, 547 insertions(+), 17 deletions(-) create mode 100644 pcsx2-qt/input-rec/NewInputRecordingDlg.cpp rename pcsx2-qt/{Settings/CreateMemoryCardDialog.h => input-rec/NewInputRecordingDlg.h} (54%) create mode 100644 pcsx2-qt/input-rec/NewInputRecordingDlg.ui diff --git a/pcsx2-qt/EmuThread.cpp b/pcsx2-qt/EmuThread.cpp index bd814ad752..27753c17ea 100644 --- a/pcsx2-qt/EmuThread.cpp +++ b/pcsx2-qt/EmuThread.cpp @@ -35,6 +35,7 @@ #include "pcsx2/HostDisplay.h" #include "pcsx2/PAD/Host/PAD.h" #include "pcsx2/PerformanceMetrics.h" +#include "pcsx2/Recording/InputRecordingControls.h" #include "pcsx2/VMManager.h" #include "DisplayWidget.h" @@ -968,4 +969,20 @@ DEFINE_HOTKEY("ToggleFullscreen", "General", "Toggle Fullscreen", [](bool presse if (!pressed) g_emu_thread->toggleFullscreen(); }) +// Input Recording Hot Keys +DEFINE_HOTKEY("InputRecToggleMode", "Input Recording", "Toggle Recording Mode", [](bool pressed) { + if (!pressed) // ?? - not pressed so it is on key up? + { + g_InputRecordingControls.RecordModeToggle(); + } +}) +// TODO - Vaser - the way the pause/resuming used to work is broken on Qt, needs a rewrite. +// - Currently if you frame advance you can't get out of frame advancing! +DEFINE_HOTKEY("InputRecFrameAdvance", "Input Recording", "Frame Advance", [](bool pressed) { + if (!pressed) // ?? - not pressed so it is on key up? + { + g_InputRecordingControls.FrameAdvance(); + g_InputRecordingControls.ResumeCoreThreadIfStarted(); + } +}) END_HOTKEY_LIST() diff --git a/pcsx2-qt/MainWindow.cpp b/pcsx2-qt/MainWindow.cpp index 8e0202b06b..d57e68309f 100644 --- a/pcsx2-qt/MainWindow.cpp +++ b/pcsx2-qt/MainWindow.cpp @@ -31,6 +31,7 @@ #include "pcsx2/GSDumpReplayer.h" #include "pcsx2/HostDisplay.h" #include "pcsx2/PerformanceMetrics.h" +#include "pcsx2/Recording/InputRecording.h" #include "AboutDialog.h" #include "AutoUpdaterDialog.h" @@ -38,6 +39,7 @@ #include "EmuThread.h" #include "GameList/GameListRefreshThread.h" #include "GameList/GameListWidget.h" +#include "input-rec/NewInputRecordingDlg.h" #include "MainWindow.h" #include "QtHost.h" #include "QtUtils.h" @@ -45,6 +47,8 @@ #include "Settings/GameListSettingsWidget.h" #include "Settings/InterfaceSettingsWidget.h" #include "SettingWidgetBinder.h" +#include "svnrev.h" + static constexpr char DISC_IMAGE_FILTER[] = QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.gz *.elf *.irx *.m3u *.gs *.gs.xz *.gs.zst);;" @@ -213,6 +217,15 @@ void MainWindow::connectSignals() connect(m_ui.actionSaveGSDump, &QAction::triggered, this, &MainWindow::onSaveGSDumpActionTriggered); + // Input Recording + connect(m_ui.actionInputRecNew, &QAction::triggered, this, &MainWindow::onInputRecNewActionTriggered); + connect(m_ui.actionInputRecPlay, &QAction::triggered, this, &MainWindow::onInputRecPlayActionTriggered); + connect(m_ui.actionInputRecStop, &QAction::triggered, this, &MainWindow::onInputRecStopActionTriggered); + SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionInputRecConsoleLogs, "Logging", "EnableInputRecordingLogs", false); + connect(m_ui.actionInputRecConsoleLogs, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); + SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.actionInputRecControllerLogs, "Logging", "EnableControllerLogs", false); + connect(m_ui.actionInputRecControllerLogs, &QAction::triggered, this, &MainWindow::onLoggingOptionChanged); + // These need to be queued connections to stop crashing due to menus opening/closing and switching focus. connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress); connect(m_game_list_widget, &GameListWidget::refreshComplete, this, &MainWindow::onGameListRefreshComplete); @@ -1179,6 +1192,85 @@ void MainWindow::onLoggingOptionChanged() QtHost::UpdateLogging(); } +void MainWindow::onInputRecNewActionTriggered() +{ + const bool wasPaused = VMManager::GetState() == VMState::Paused; + const bool wasRunning = VMManager::GetState() == VMState::Running; + if (wasRunning && !wasPaused) + { + VMManager::SetPaused(true); + } + + NewInputRecordingDlg dlg(this); + const auto result = dlg.exec(); + + if (result == QDialog::Accepted) + { + if (g_InputRecording.Create( + dlg.getFilePath(), + dlg.getInputRecType() == InputRecording::Type::FROM_SAVESTATE, + dlg.getAuthorName())) + { + return; + } + } + + if (wasRunning && !wasPaused) + { + VMManager::SetPaused(false); + } +} + +#include "pcsx2/Recording/InputRecordingControls.h" + +void MainWindow::onInputRecPlayActionTriggered() +{ + const bool wasPaused = VMManager::GetState() == VMState::Paused; + + if (!wasPaused) + g_InputRecordingControls.PauseImmediately(); + + QFileDialog dialog(this); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setWindowTitle("Select a File"); + dialog.setNameFilter(tr("Input Recording Files (*.p2m2)")); + QStringList fileNames; + if (dialog.exec()) + { + fileNames = dialog.selectedFiles(); + } + + if (fileNames.length() > 0) + { + if (g_InputRecording.IsActive()) + { + g_InputRecording.Stop(); + } + if (g_InputRecording.Play(fs::path(fileNames.first().toStdString()))) + { + return; + } + } + + if (!wasPaused) + { + g_InputRecordingControls.Resume(); + } +} + +void MainWindow::onInputRecStopActionTriggered() +{ + if (g_InputRecording.IsActive()) + { + g_InputRecording.Stop(); + } +} + +void MainWindow::onInputRecOpenSettingsTriggered() +{ + // TODO - Vaser - Implement +} + void MainWindow::onVMStarting() { m_vm_valid = true; diff --git a/pcsx2-qt/MainWindow.h b/pcsx2-qt/MainWindow.h index aceaa1692d..aba501f121 100644 --- a/pcsx2-qt/MainWindow.h +++ b/pcsx2-qt/MainWindow.h @@ -137,6 +137,12 @@ private Q_SLOTS: void onScreenshotActionTriggered(); void onSaveGSDumpActionTriggered(); + // Input Recording + void onInputRecNewActionTriggered(); + void onInputRecPlayActionTriggered(); + void onInputRecStopActionTriggered(); + void onInputRecOpenSettingsTriggered(); + void onVMStarting(); void onVMStarted(); void onVMPaused(); diff --git a/pcsx2-qt/MainWindow.ui b/pcsx2-qt/MainWindow.ui index 1e9a708584..65f9629dab 100644 --- a/pcsx2-qt/MainWindow.ui +++ b/pcsx2-qt/MainWindow.ui @@ -182,7 +182,21 @@ &Tools + + + Input Recording + + + + + + + + + + + @@ -745,6 +759,47 @@ Save Single Frame GS Dump + + + New + + + + + Play + + + + + Stop + + + + + Settings + + + + + Input Recording Logs + + + + + true + + + Controller Logs + + + + + true + + + Input Recording Logs + + diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 7a1e55fd1a..1cb640a640 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -770,6 +770,10 @@ void QtHost::UpdateLogging() DevConWriterEnabled = any_logging_sinks && QtHost::GetBaseBoolSettingValue("Logging", "EnableVerbose", false); SysConsole.eeConsole.Enabled = any_logging_sinks && QtHost::GetBaseBoolSettingValue("Logging", "EnableEEConsole", true); SysConsole.iopConsole.Enabled = any_logging_sinks && QtHost::GetBaseBoolSettingValue("Logging", "EnableIOPConsole", true); + + // Input Recording Logs + SysConsole.recordingConsole.Enabled = system_console_enabled && QtHost::GetBaseBoolSettingValue("Logging", "EnableInputRecordingLogs", true); + SysConsole.controlInfo.Enabled = system_console_enabled && QtHost::GetBaseBoolSettingValue("Logging", "EnableControllerLogs", true); SetSystemConsoleEnabled(system_console_enabled); } diff --git a/pcsx2-qt/input-rec/NewInputRecordingDlg.cpp b/pcsx2-qt/input-rec/NewInputRecordingDlg.cpp new file mode 100644 index 0000000000..2a4919ec34 --- /dev/null +++ b/pcsx2-qt/input-rec/NewInputRecordingDlg.cpp @@ -0,0 +1,113 @@ +/* PCSX2 - PS2 Emulator for PCs + * Copyright (C) 2002-2022 PCSX2 Dev Team + * + * PCSX2 is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * PCSX2 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with PCSX2. + * If not, see . + */ + +#include "PrecompiledHeader.h" + +#include "NewInputRecordingDlg.h" + +#include "QtUtils.h" +#include +#include +#include + +NewInputRecordingDlg::NewInputRecordingDlg(QWidget* parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setFixedSize(geometry().width(), geometry().height()); + + // Default State + m_ui.m_recTypeWarning->hide(); + m_ui.m_dlgBtns->button(QDialogButtonBox::Ok)->setEnabled(false); + m_ui.m_filePathInput->setDisabled(true); + + connect(m_ui.m_recTypePowerOn, &QRadioButton::clicked, this, &NewInputRecordingDlg::onRecordingTypePowerOnChecked); + connect(m_ui.m_recTypeSaveState, &QRadioButton::clicked, this, &NewInputRecordingDlg::onRecordingTypeSaveStateChecked); + + connect(m_ui.m_filePathBrowseBtn, &QPushButton::clicked, this, &NewInputRecordingDlg::onBrowseForPathClicked); + connect(m_ui.m_authorInput, &QLineEdit::textEdited, this, &NewInputRecordingDlg::onAuthorNameChanged); +} + +NewInputRecordingDlg::~NewInputRecordingDlg() = default; + +InputRecording::Type NewInputRecordingDlg::getInputRecType() +{ + return m_recType; +} + +std::string NewInputRecordingDlg::getFilePath() +{ + return m_filePath.toStdString(); +} + +std::string NewInputRecordingDlg::getAuthorName() +{ + return m_authorName.toStdString(); +} + +void NewInputRecordingDlg::onRecordingTypePowerOnChecked(bool checked) +{ + if (checked) + { + m_recType = InputRecording::Type::POWER_ON; + m_ui.m_recTypeWarning->hide(); + } +} + +void NewInputRecordingDlg::onRecordingTypeSaveStateChecked(bool checked) +{ + if (checked) + { + m_recType = InputRecording::Type::FROM_SAVESTATE; + m_ui.m_recTypeWarning->show(); + } +} + +void NewInputRecordingDlg::onBrowseForPathClicked() +{ + QFileDialog dialog(this); + dialog.setFileMode(QFileDialog::AnyFile); + dialog.setWindowTitle("Select a File"); + dialog.setNameFilter(tr("Input Recording Files (*.p2m2)")); + QStringList fileNames; + if (dialog.exec()) + { + fileNames = dialog.selectedFiles(); + } + if (fileNames.length() > 0) + { + m_filePath = fileNames.first(); + m_ui.m_filePathInput->setText(m_filePath); + updateFormStatus(); + } +} + +void NewInputRecordingDlg::onAuthorNameChanged(const QString& text) +{ + m_authorName = text; + updateFormStatus(); +} + +bool NewInputRecordingDlg::isFormValid() +{ + return !m_filePath.isEmpty() && !m_authorName.isEmpty(); +} + +void NewInputRecordingDlg::updateFormStatus() +{ + m_ui.m_dlgBtns->button(QDialogButtonBox::Ok)->setEnabled(isFormValid()); +} diff --git a/pcsx2-qt/Settings/CreateMemoryCardDialog.h b/pcsx2-qt/input-rec/NewInputRecordingDlg.h similarity index 54% rename from pcsx2-qt/Settings/CreateMemoryCardDialog.h rename to pcsx2-qt/input-rec/NewInputRecordingDlg.h index d7900e3c2d..49b8d74377 100644 --- a/pcsx2-qt/Settings/CreateMemoryCardDialog.h +++ b/pcsx2-qt/input-rec/NewInputRecordingDlg.h @@ -15,31 +15,38 @@ #pragma once +#include "ui_NewInputRecordingDlg.h" + +#include "pcsx2/Recording/InputRecording.h" + #include -#include "ui_CreateMemoryCardDialog.h" - -#include "pcsx2/Config.h" - -class CreateMemoryCardDialog final : public QDialog +class NewInputRecordingDlg final : public QDialog { Q_OBJECT public: - explicit CreateMemoryCardDialog(QWidget* parent = nullptr); - ~CreateMemoryCardDialog(); + explicit NewInputRecordingDlg(QWidget* parent = nullptr); + ~NewInputRecordingDlg(); + + InputRecording::Type getInputRecType(); + std::string getFilePath(); + std::string getAuthorName(); private Q_SLOTS: - void nameTextChanged(); - void createCard(); + void onRecordingTypePowerOnChecked(bool checked); + void onRecordingTypeSaveStateChecked(bool checked); + + void onBrowseForPathClicked(); + void onAuthorNameChanged(const QString& text); private: - void setType(MemoryCardType type, MemoryCardFileType fileType); - void restoreDefaults(); - void updateState(); + Ui::NewInputRecordingDlg m_ui; - Ui::CreateMemoryCardDialog m_ui; + InputRecording::Type m_recType = InputRecording::Type::POWER_ON; + QString m_filePath = ""; + QString m_authorName = ""; - MemoryCardType m_type = MemoryCardType::File; - MemoryCardFileType m_fileType = MemoryCardFileType::PS2_8MB; + bool isFormValid(); + void updateFormStatus(); }; diff --git a/pcsx2-qt/input-rec/NewInputRecordingDlg.ui b/pcsx2-qt/input-rec/NewInputRecordingDlg.ui new file mode 100644 index 0000000000..e1ab82d6af --- /dev/null +++ b/pcsx2-qt/input-rec/NewInputRecordingDlg.ui @@ -0,0 +1,163 @@ + + + NewInputRecordingDlg + + + + 0 + 0 + 424 + 305 + + + + New Input Recording + + + + 12 + + + + + 12 + + + + + Select Recording Type + + + false + + + + + + + + Power On + + + true + + + + + + + Save State + + + + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p align="center"><span style=" color:#ff0000;">Be Warned! Making an input recording that starts from a save-state will fail to work on future versions due to save-state versioning.</span></p></body></html> + + + true + + + + + + + Select File Path + + + + + + + + + + + + Browse + + + + + + + + + Enter Author Name + + + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + true + + + + + + + + + + + m_dlgBtns + accepted() + NewInputRecordingDlg + accept() + + + 248 + 254 + + + 157 + 274 + + + + + m_dlgBtns + rejected() + NewInputRecordingDlg + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/pcsx2-qt/pcsx2-qt.vcxproj b/pcsx2-qt/pcsx2-qt.vcxproj index bbd70116e8..d36e529322 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj +++ b/pcsx2-qt/pcsx2-qt.vcxproj @@ -45,7 +45,7 @@ $(SolutionDir)3rdparty\lzma\include;%(AdditionalIncludeDirectories) $(ProjectDir);$(SolutionDir)pcsx2;%(AdditionalIncludeDirectories) - %(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList + %(AdditionalIncludeDirectories);$(ProjectDir)\Settings;$(ProjectDir)\GameList;$(ProjectDir)\input-rec Async Use PrecompiledHeader.h @@ -134,6 +134,7 @@ + @@ -194,6 +195,7 @@ + @@ -240,6 +242,7 @@ + NotUsing @@ -312,6 +315,9 @@ Document + + + diff --git a/pcsx2-qt/pcsx2-qt.vcxproj.filters b/pcsx2-qt/pcsx2-qt.vcxproj.filters index 155560ce8d..7b6c0d9841 100644 --- a/pcsx2-qt/pcsx2-qt.vcxproj.filters +++ b/pcsx2-qt/pcsx2-qt.vcxproj.filters @@ -13,6 +13,12 @@ {aff229ec-39ea-40d4-b1c9-b5c4a932a2e0} + + {9ec02647-68e6-4894-9c97-d3347997cae6} + + + {d5c14016-9690-4e49-bcb9-a634a937951a} + @@ -192,6 +198,12 @@ moc + + moc + + + Tools\Input Recording + @@ -282,6 +294,9 @@ Settings + + Tools\Input Recording + @@ -352,4 +367,9 @@ + + + Tools\Input Recording + + diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 95754f2241..6dab8c6a7e 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1005,6 +1005,7 @@ Pcsx2Config::Pcsx2Config() McdEnableEjection = true; McdFolderAutoManage = true; EnablePatches = true; + EnableRecordingTools = true; #ifdef PCSX2_CORE EnableGameFixes = true; #endif diff --git a/pcsx2/pcsx2core.vcxproj b/pcsx2/pcsx2core.vcxproj index fa5b556517..d27074ff32 100644 --- a/pcsx2/pcsx2core.vcxproj +++ b/pcsx2/pcsx2core.vcxproj @@ -54,7 +54,7 @@ PrecompiledHeader.h;%(ForcedIncludeFiles) NoExtensions /Zc:externConstexpr %(AdditionalOptions) - WIN32_LEAN_AND_MEAN;ZIP_STATIC;LZMA_API_STATIC;BUILD_DX=1;ENABLE_OPENGL;ENABLE_VULKAN;SPU2X_CUBEB;SDL_BUILD;PCSX2_CORE;DISABLE_RECORDING;%(PreprocessorDefinitions) + WIN32_LEAN_AND_MEAN;ZIP_STATIC;LZMA_API_STATIC;BUILD_DX=1;ENABLE_OPENGL;ENABLE_VULKAN;SPU2X_CUBEB;SDL_BUILD;PCSX2_CORE;%(PreprocessorDefinitions) PCSX2_DEBUG;PCSX2_DEVBUILD;_SECURE_SCL_=1;%(PreprocessorDefinitions) PCSX2_DEVEL;PCSX2_DEVBUILD;NDEBUG;_SECURE_SCL_=1;%(PreprocessorDefinitions) NDEBUG;_SECURE_SCL_=0;%(PreprocessorDefinitions) @@ -223,6 +223,11 @@ + + + + + @@ -536,6 +541,11 @@ + + + + + diff --git a/pcsx2/pcsx2core.vcxproj.filters b/pcsx2/pcsx2core.vcxproj.filters index e45a8f36e9..a1585b0fda 100644 --- a/pcsx2/pcsx2core.vcxproj.filters +++ b/pcsx2/pcsx2core.vcxproj.filters @@ -232,6 +232,12 @@ {eb697f5b-85f5-424a-a7e4-8d8b73d3426e} + + {9153e32b-e1e3-49ac-b490-b56adfd1692f} + + + {03ba2aa7-2cd9-48cb-93c6-fc93d5bdc938} + @@ -1248,6 +1254,21 @@ Host + + Tools\Input Recording + + + Tools\Input Recording + + + Tools\Input Recording + + + Tools\Input Recording + + + Tools\Input Recording + @@ -2071,6 +2092,21 @@ Host + + Tools\Input Recording + + + Tools\Input Recording + + + Tools\Input Recording + + + Tools\Input Recording + + + Tools\Input Recording +