First Commit
This commit is contained in:
120
pcsx2-qt/Tools/InputRecording/InputRecordingViewer.cpp
Normal file
120
pcsx2-qt/Tools/InputRecording/InputRecordingViewer.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "InputRecordingViewer.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QString>
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/QMessageBox>
|
||||
#include <QtWidgets/qfiledialog.h>
|
||||
|
||||
// TODO - for now this uses a very naive implementation that fills the entire table
|
||||
// this needs to be replaced with a lazy-loading QTableView implementation
|
||||
//
|
||||
// For now, especially for just debugging input recording issues, its good enough!
|
||||
|
||||
InputRecordingViewer::InputRecordingViewer(QWidget* parent)
|
||||
: QMainWindow(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
m_ui.tableWidget->setSelectionMode(QAbstractItemView::NoSelection);
|
||||
|
||||
connect(m_ui.actionOpen, &QAction::triggered, this, &InputRecordingViewer::openFile);
|
||||
connect(m_ui.actionClose, &QAction::triggered, this, &InputRecordingViewer::closeFile);
|
||||
}
|
||||
|
||||
QTableWidgetItem* InputRecordingViewer::createRowItem(std::tuple<u8, u8> analog)
|
||||
{
|
||||
const auto [left, right] = analog;
|
||||
return new QTableWidgetItem(tr("%1 %2").arg(left).arg(right));
|
||||
}
|
||||
|
||||
QTableWidgetItem* InputRecordingViewer::createRowItem(bool pressed)
|
||||
{
|
||||
return new QTableWidgetItem(tr("%1").arg(pressed));
|
||||
}
|
||||
|
||||
QTableWidgetItem* InputRecordingViewer::createRowItem(std::tuple<bool, u8> buttonInfo)
|
||||
{
|
||||
const auto [isPressed, pressure] = buttonInfo;
|
||||
return new QTableWidgetItem(tr("%1 [%2]").arg(isPressed).arg(pressure));
|
||||
}
|
||||
|
||||
void InputRecordingViewer::loadTable()
|
||||
{
|
||||
static const auto headers = QStringList({tr("Left Analog"), tr("Right Analog"), tr("Cross"), tr("Square"), tr("Triangle"), tr("Circle"), tr("L1"), tr("R1"), tr("L2"), tr("R2"), tr("D-Pad Down"), tr("D-Pad Right"), tr("D-Pad Up"), tr("D-Pad Left"), tr("L3"), tr("R3"), tr("Select"), tr("Start")});
|
||||
m_ui.tableWidget->setColumnCount(headers.length());
|
||||
m_ui.tableWidget->setHorizontalHeaderLabels(headers);
|
||||
|
||||
// TODO - only port 1 for now
|
||||
auto dataColl = m_file.bulkReadPadData(0, m_file.getTotalFrames(), 0);
|
||||
m_ui.tableWidget->setRowCount(dataColl.size());
|
||||
|
||||
int frameNum = 0;
|
||||
for (const auto& frameData : dataColl)
|
||||
{
|
||||
m_ui.tableWidget->setItem(frameNum, 0, createRowItem(frameData.m_leftAnalog));
|
||||
m_ui.tableWidget->setItem(frameNum, 1, createRowItem(frameData.m_rightAnalog));
|
||||
m_ui.tableWidget->setItem(frameNum, 2, createRowItem(frameData.m_cross));
|
||||
m_ui.tableWidget->setItem(frameNum, 3, createRowItem(frameData.m_square));
|
||||
m_ui.tableWidget->setItem(frameNum, 4, createRowItem(frameData.m_triangle));
|
||||
m_ui.tableWidget->setItem(frameNum, 5, createRowItem(frameData.m_circle));
|
||||
m_ui.tableWidget->setItem(frameNum, 6, createRowItem(frameData.m_l1));
|
||||
m_ui.tableWidget->setItem(frameNum, 7, createRowItem(frameData.m_l2));
|
||||
m_ui.tableWidget->setItem(frameNum, 8, createRowItem(frameData.m_r1));
|
||||
m_ui.tableWidget->setItem(frameNum, 9, createRowItem(frameData.m_r2));
|
||||
m_ui.tableWidget->setItem(frameNum, 10, createRowItem(frameData.m_down));
|
||||
m_ui.tableWidget->setItem(frameNum, 11, createRowItem(frameData.m_right));
|
||||
m_ui.tableWidget->setItem(frameNum, 12, createRowItem(frameData.m_up));
|
||||
m_ui.tableWidget->setItem(frameNum, 13, createRowItem(frameData.m_left));
|
||||
m_ui.tableWidget->setItem(frameNum, 14, createRowItem(frameData.m_l3));
|
||||
m_ui.tableWidget->setItem(frameNum, 15, createRowItem(frameData.m_r3));
|
||||
m_ui.tableWidget->setItem(frameNum, 16, createRowItem(frameData.m_select));
|
||||
m_ui.tableWidget->setItem(frameNum, 17, createRowItem(frameData.m_select));
|
||||
frameNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecordingViewer::openFile()
|
||||
{
|
||||
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.isEmpty())
|
||||
{
|
||||
const std::string fileName = QDir::toNativeSeparators(fileNames.first()).toStdString();
|
||||
m_file_open = m_file.openExisting(fileName);
|
||||
m_ui.actionClose->setEnabled(m_file_open);
|
||||
if (m_file_open)
|
||||
{
|
||||
loadTable();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::critical(this, tr("Opening Recording Failed"), tr("Failed to open file: %1").arg(QString::fromUtf8(fileName.c_str())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputRecordingViewer::closeFile()
|
||||
{
|
||||
if (m_file_open)
|
||||
{
|
||||
m_file_open = !m_file.close();
|
||||
if (!m_file_open)
|
||||
{
|
||||
m_ui.tableWidget->clearContents();
|
||||
m_ui.tableWidget->setRowCount(0);
|
||||
}
|
||||
} // TODO else error
|
||||
m_ui.actionClose->setEnabled(m_file_open);
|
||||
}
|
||||
32
pcsx2-qt/Tools/InputRecording/InputRecordingViewer.h
Normal file
32
pcsx2-qt/Tools/InputRecording/InputRecordingViewer.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_InputRecordingViewer.h"
|
||||
|
||||
#include "pcsx2/Recording/InputRecordingFile.h"
|
||||
|
||||
class InputRecordingViewer final : public QMainWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InputRecordingViewer(QWidget* parent = nullptr);
|
||||
~InputRecordingViewer() = default;
|
||||
|
||||
private Q_SLOTS:
|
||||
void openFile();
|
||||
void closeFile();
|
||||
|
||||
private:
|
||||
Ui::InputRecordingViewer m_ui;
|
||||
|
||||
InputRecordingFile m_file;
|
||||
bool m_file_open;
|
||||
|
||||
void loadTable();
|
||||
QTableWidgetItem* createRowItem(std::tuple<u8, u8> analog);
|
||||
QTableWidgetItem* createRowItem(bool pressed);
|
||||
QTableWidgetItem* createRowItem(std::tuple<bool, u8> buttonInfo);
|
||||
};
|
||||
74
pcsx2-qt/Tools/InputRecording/InputRecordingViewer.ui
Normal file
74
pcsx2-qt/Tools/InputRecording/InputRecordingViewer.ui
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>InputRecordingViewer</class>
|
||||
<widget class="QMainWindow" name="InputRecordingViewer">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Input Recording Viewer</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QTableWidget" name="tableWidget"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>21</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
<string>File</string>
|
||||
</property>
|
||||
<addaction name="actionOpen"/>
|
||||
<addaction name="actionClose"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuTest">
|
||||
<property name="title">
|
||||
<string>Edit</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuView">
|
||||
<property name="title">
|
||||
<string>View</string>
|
||||
</property>
|
||||
</widget>
|
||||
<addaction name="menuFile"/>
|
||||
<addaction name="menuTest"/>
|
||||
<addaction name="menuView"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionOpen">
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionClose">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Close</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="9"/>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
94
pcsx2-qt/Tools/InputRecording/NewInputRecordingDlg.cpp
Normal file
94
pcsx2-qt/Tools/InputRecording/NewInputRecordingDlg.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#include "NewInputRecordingDlg.h"
|
||||
|
||||
#include "QtUtils.h"
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QString>
|
||||
#include <QtWidgets/QDialog>
|
||||
#include <QtWidgets/qfiledialog.h>
|
||||
|
||||
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()
|
||||
{
|
||||
QString filter = tr("Input Recording Files (*.p2m2)");
|
||||
QString filename = QDir::toNativeSeparators(QFileDialog::getSaveFileName(
|
||||
this, tr("Select a File"), QString(), filter, &filter));
|
||||
if (filename.isEmpty())
|
||||
return;
|
||||
|
||||
m_filePath = std::move(filename);
|
||||
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());
|
||||
}
|
||||
40
pcsx2-qt/Tools/InputRecording/NewInputRecordingDlg.h
Normal file
40
pcsx2-qt/Tools/InputRecording/NewInputRecordingDlg.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
|
||||
// SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ui_NewInputRecordingDlg.h"
|
||||
|
||||
#include "pcsx2/Recording/InputRecording.h"
|
||||
|
||||
#include <QtWidgets/QDialog>
|
||||
|
||||
class NewInputRecordingDlg final : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit NewInputRecordingDlg(QWidget* parent = nullptr);
|
||||
~NewInputRecordingDlg();
|
||||
|
||||
InputRecording::Type getInputRecType();
|
||||
std::string getFilePath();
|
||||
std::string getAuthorName();
|
||||
|
||||
private Q_SLOTS:
|
||||
void onRecordingTypePowerOnChecked(bool checked);
|
||||
void onRecordingTypeSaveStateChecked(bool checked);
|
||||
|
||||
void onBrowseForPathClicked();
|
||||
void onAuthorNameChanged(const QString& text);
|
||||
|
||||
private:
|
||||
Ui::NewInputRecordingDlg m_ui;
|
||||
|
||||
InputRecording::Type m_recType = InputRecording::Type::POWER_ON;
|
||||
QString m_filePath = "";
|
||||
QString m_authorName = "";
|
||||
|
||||
bool isFormValid();
|
||||
void updateFormStatus();
|
||||
};
|
||||
163
pcsx2-qt/Tools/InputRecording/NewInputRecordingDlg.ui
Normal file
163
pcsx2-qt/Tools/InputRecording/NewInputRecordingDlg.ui
Normal file
@@ -0,0 +1,163 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>NewInputRecordingDlg</class>
|
||||
<widget class="QDialog" name="NewInputRecordingDlg">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>424</width>
|
||||
<height>305</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>New Input Recording</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="m_mainLayout">
|
||||
<property name="spacing">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="m_recTypeGroup">
|
||||
<property name="title">
|
||||
<string>Select Recording Type</string>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="m_recTypeLayout">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="m_recTypePowerOn">
|
||||
<property name="text">
|
||||
<string extracomment="Indicates that the input recording that is about to be started will be recorded from the moment the emulation boots on/starts.">Power On</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="m_recTypeSaveState">
|
||||
<property name="text">
|
||||
<string extracomment="Indicates that the input recording that is about to be started will be recorded when an accompanying save state is saved.">Save State</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_recTypeWarning">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string><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></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_filePathLabel">
|
||||
<property name="text">
|
||||
<string>Select File Path</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="m_filePathLayout">
|
||||
<item>
|
||||
<widget class="QLineEdit" name="m_filePathInput"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="m_filePathBrowseBtn">
|
||||
<property name="text">
|
||||
<string>Browse</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="m_authorLabel">
|
||||
<property name="text">
|
||||
<string>Enter Author Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="m_authorInput"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="m_dlgBtns">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
<property name="centerButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>m_dlgBtns</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>NewInputRecordingDlg</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>m_dlgBtns</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>NewInputRecordingDlg</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
Reference in New Issue
Block a user