game list in progress

This commit is contained in:
2026-06-10 21:27:23 +02:00
parent d26417b83f
commit 2b59e5f461
7 changed files with 195 additions and 66 deletions
+69 -8
View File
@@ -6,6 +6,7 @@
#include <QStatusBar>
#include <QTimer>
#include <QMimeData>
#include <QHeaderView>
#include <resources/gamecontrollerdb.h>
#include <Options.hpp>
#include <Scheduler.hpp>
@@ -15,6 +16,42 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User
hide();
auto romsNotFoundLabel = new QLabel(R"(Kaizen could not find any ROMs. Set the path in "Settings -> General")");
romsList->verticalHeader()->hide();
romsList->setSelectionBehavior(QAbstractItemView::SelectRows);
romsList->setEditTriggers(QAbstractItemView::NoEditTriggers);
romsList->setSortingEnabled(true);
romsList->setColumnCount(3);
romsList->setHorizontalHeaderItem(0, romName);
romsList->setHorizontalHeaderItem(1, romLastPlayed);
romsList->setHorizontalHeaderItem(2, romTimePlayed);
auto romsPath = Options::GetRomsPath();
if (!romsPath.empty()) {
int i = 0;
centralWidget->addWidget(romsList);
for (const auto &file : fs::recursive_directory_iterator{romsPath}) {
if (file.is_regular_file() &&
std::ranges::any_of(std::array{".n64", ".z64", ".v64"},
[&](const std::string &ext) { return file.path().extension() == ext; })) {
romsList->insertRow(i);
romsList->setItem(i, 0, new QTableWidgetItem(file.path().stem().string().c_str()));
romsList->setItem(i, 1, new QTableWidgetItem("Never"));
romsList->setItem(i, 2, new QTableWidgetItem("0h 0m 0s"));
i++;
}
}
} else {
centralWidget->addWidget(romsNotFoundLabel);
}
connect(romsList, &QTableWidget::cellDoubleClicked, this, [&](int row, int) {
auto fileToLoad = fs::path(romsPath) / romsList->item(row, 0)->text().toStdString();
std::println("{}", fileToLoad.string());
LoadROM(fileToLoad.string());
});
installEventFilter(this);
restoreGeometry(settings.value("geometry").toByteArray());
@@ -23,6 +60,8 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User
vulkanWidget = new RenderWidget();
vulkanWidget->hide();
centralWidget->addWidget(vulkanWidget);
cpuTypeLabel = new QLabel("Interpreter");
if (Options::GetCpuType() == n64::CachedInterpreter)
cpuTypeLabel->setText("Cached Interpreter");
@@ -39,7 +78,7 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User
setWindowTitle("Kaizen " KAIZEN_VERSION_STR);
setMinimumSize(640, 480);
setCentralWidget(vulkanWidget);
setCentralWidget(centralWidget);
setAcceptDrops(true);
@@ -69,9 +108,10 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User
auto open = fileMenu->addAction("Open");
open->setShortcut(QKeyCombination(Qt::CTRL, Qt::Key_O));
connect(open, &QAction::triggered, this, [&] {
auto fileToLoad = QFileDialog::getOpenFileName(this, "Select a Nintendo 64 ROM", QDir::currentPath(),
"N64 ROM (*.z64 *.n64 *.v64)")
.toStdString();
auto originPath = romsPath.empty() ? QDir::currentPath() : romsPath.c_str();
auto fileToLoad =
QFileDialog::getOpenFileName(this, "Select a Nintendo 64 ROM", originPath, "N64 ROM (*.z64 *.n64 *.v64)")
.toStdString();
if (!fileToLoad.empty())
LoadROM(fileToLoad);
});
@@ -83,6 +123,26 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User
auto settingsMenu = emulationMenu->addAction("Settings");
settingsWindow = new SettingsWindow();
connect(settingsMenu, &QAction::triggered, settingsWindow, &SettingsWindow::show);
connect(settingsWindow->general, &GeneralSettings::romFolderSelected, this, [&] {
romsPath = Options::GetRomsPath();
if (!romsPath.empty()) {
int i = 0;
centralWidget->addWidget(romsList);
for (const auto &file : fs::recursive_directory_iterator{romsPath}) {
if (file.is_regular_file() &&
std::ranges::any_of(std::array{".n64", ".z64", ".v64"},
[&](const std::string &ext) { return file.path().extension() == ext; })) {
romsList->insertRow(i);
romsList->setItem(i, 0, new QTableWidgetItem(file.path().stem().string().c_str()));
romsList->setItem(i, 1, new QTableWidgetItem("Never"));
romsList->setItem(i, 2, new QTableWidgetItem("0h 0m 0s"));
i++;
}
}
} else {
centralWidget->addWidget(romsNotFoundLabel);
}
});
connect(settingsWindow->cpu, &CPUSettings::cpuTypeChanged, this, [&] {
core.cpuType = Options::GetCpuType();
@@ -102,8 +162,7 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User
auto unlockFramerate = emulationMenu->addAction("Unlock framerate");
unlockFramerate->setCheckable(true);
connect(unlockFramerate, &QAction::triggered, this,
[&](bool checked) { core.parallel.SetFramerateUnlocked(checked); });
connect(unlockFramerate, &QAction::triggered, this, [&](bool checked) { unlockFrameratePressed = checked; });
pause->setDisabled(true);
pause->setShortcut(QKeyCombination(Qt::CTRL, Qt::Key_P));
@@ -126,7 +185,7 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User
emulationMenu->addAction(stop);
connect(stop, &QAction::triggered, this, [&] {
Scheduler::GetInstance().EnqueueRelative(0, STOP);
vulkanWidget->show();
centralWidget->setCurrentWidget(romsList);
});
auto helpMenu = menuBar()->addMenu("Help");
@@ -154,6 +213,8 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User
updateKeys();
updateAxis();
core.parallel.SetFramerateUnlocked(unlockFrameratePressed);
auto timeStart = SDL_GetTicks();
core.Run();
core.parallel.UpdateScreen();
@@ -208,7 +269,7 @@ void KaizenGui::LoadROM(const std::string &path) noexcept {
pause->setEnabled(true);
reset->setEnabled(true);
stop->setEnabled(true);
vulkanWidget->show();
centralWidget->setCurrentWidget(vulkanWidget);
setWindowTitle(("Kaizen " KAIZEN_VERSION_STR " - " + core.mem->rom.gameNameDB).c_str());
}
+8
View File
@@ -2,10 +2,12 @@
#include <RenderWidget.hpp>
#include <SettingsWindow.hpp>
#include <SDL3/SDL_gamepad.h>
#include <QStackedWidget>
#include <QMainWindow>
#include <QFileDialog>
#include <QDropEvent>
#include <QDragEnterEvent>
#include <QTableWidget>
#include <Core.hpp>
#include <bitset>
@@ -30,6 +32,11 @@ class KaizenGui final : QMainWindow {
bool fastForward = false;
bool minimized = false;
QTableWidget *romsList = new QTableWidget();
QTableWidgetItem *romName = new QTableWidgetItem("Name");
QTableWidgetItem *romLastPlayed = new QTableWidgetItem("Last played");
QTableWidgetItem *romTimePlayed = new QTableWidgetItem("Time played");
QStackedWidget *centralWidget = new QStackedWidget();
SettingsWindow *settingsWindow;
RenderWidget *vulkanWidget;
QSettings settings;
@@ -53,4 +60,5 @@ class KaizenGui final : QMainWindow {
float elapsed = 0.f;
n64::Core &core = n64::Core::GetInstance();
std::bitset<18> pressedKeys{};
std::atomic_bool unlockFrameratePressed = false;
};
+32 -15
View File
@@ -5,28 +5,45 @@
#include <log.hpp>
GeneralSettings::GeneralSettings() : settings(QSettings::UserScope) {
description = new QLabel("Path:");
description->setToolTip("Path where game saves are stored.");
selectedFolderLabel = new QLabel(Options::GetSavesPath().c_str());
selectedFolderLabel->setDisabled(true);
folderSelectButton = new QToolButton();
selectedRomsFolderLabel = new QLabel(Options::GetRomsPath().c_str());
selectedRomsFolderLabel->setDisabled(true);
romsFolderSelectButton = new QToolButton();
romsFolderSelectButton->setToolTip("Path where ROMs are stored.");
connect(folderSelectButton, &QToolButton::clicked, this, [&] {
selectedSavesFolderLabel = new QLabel(Options::GetSavesPath().c_str());
selectedSavesFolderLabel->setDisabled(true);
savesFolderSelectButton = new QToolButton();
savesFolderSelectButton->setToolTip("Path where game saves are stored.");
connect(savesFolderSelectButton, &QToolButton::clicked, this, [&] {
auto dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), QCoreApplication::applicationDirPath(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
selectedFolderLabel->setText(dir);
selectedSavesFolderLabel->setText(dir);
Options::SetSavesPath(dir.toStdString());
settings.setValue("general/saves_path", dir);
settings.setValue("saves_path", dir);
settings.sync();
});
h = new QHBoxLayout();
connect(romsFolderSelectButton, &QToolButton::clicked, this, [&] {
auto dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), QCoreApplication::applicationDirPath(),
QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
h->addWidget(description);
h->addWidget(selectedFolderLabel);
h->addWidget(folderSelectButton);
selectedRomsFolderLabel->setText(dir);
Options::SetRomsPath(dir.toStdString());
settings.setValue("roms_path", dir);
settings.sync();
emit romFolderSelected();
});
v = new QVBoxLayout(this);
v->addLayout(h);
setLayout(v);
gl = new QGridLayout();
gl->addWidget(new QLabel("ROMs path:"), 0, 0);
gl->addWidget(selectedRomsFolderLabel, 0, 1);
gl->addWidget(romsFolderSelectButton, 0, 2);
gl->addWidget(new QLabel("Saves path:"), 1, 0);
gl->addWidget(selectedSavesFolderLabel, 1, 1);
gl->addWidget(savesFolderSelectButton, 1, 2);
setLayout(gl);
}
+8 -5
View File
@@ -7,13 +7,16 @@
class GeneralSettings final : public QWidget {
Q_OBJECT
QLabel *description;
QToolButton *folderSelectButton;
QLabel *selectedFolderLabel;
QVBoxLayout *v;
QHBoxLayout *h;
QToolButton *savesFolderSelectButton;
QLabel *selectedSavesFolderLabel;
QToolButton *romsFolderSelectButton;
QLabel *selectedRomsFolderLabel;
QGridLayout *gl;
QSettings settings;
public:
explicit GeneralSettings();
signals:
void romFolderSelected();
};
+72 -36
View File
@@ -1,24 +1,60 @@
#include <InputSettings.hpp>
InputSettings::InputSettings() : settings(QSettings::UserScope) {
mapping[0] = settings.value("input/mapping/Z", Qt::Key_Z).value<QKeySequence>();
mapping[1] = settings.value("input/mapping/A", Qt::Key_X).value<QKeySequence>();
mapping[2] = settings.value("input/mapping/B", Qt::Key_C).value<QKeySequence>();
mapping[3] = settings.value("input/mapping/L", Qt::Key_A).value<QKeySequence>();
mapping[4] = settings.value("input/mapping/R", Qt::Key_S).value<QKeySequence>();
mapping[5] = settings.value("input/mapping/S", Qt::Key_Return).value<QKeySequence>();
mapping[6] = settings.value("input/mapping/DU", Qt::Key_I).value<QKeySequence>();
mapping[7] = settings.value("input/mapping/DD", Qt::Key_K).value<QKeySequence>();
mapping[8] = settings.value("input/mapping/DL", Qt::Key_J).value<QKeySequence>();
mapping[9] = settings.value("input/mapping/DR", Qt::Key_L).value<QKeySequence>();
mapping[10] = settings.value("input/mapping/CU", Qt::Key_T).value<QKeySequence>();
mapping[11] = settings.value("input/mapping/CD", Qt::Key_G).value<QKeySequence>();
mapping[12] = settings.value("input/mapping/CL", Qt::Key_F).value<QKeySequence>();
mapping[13] = settings.value("input/mapping/CR", Qt::Key_H).value<QKeySequence>();
mapping[14] = settings.value("input/mapping/AU", Qt::Key_Up).value<QKeySequence>();
mapping[15] = settings.value("input/mapping/AD", Qt::Key_Down).value<QKeySequence>();
mapping[16] = settings.value("input/mapping/AL", Qt::Key_Left).value<QKeySequence>();
mapping[17] = settings.value("input/mapping/AR", Qt::Key_Right).value<QKeySequence>();
auto keyZ = settings.value("input/Z", Qt::Key_Z).value<QKeySequence>();
auto keyA = settings.value("input/A", Qt::Key_X).value<QKeySequence>();
auto keyB = settings.value("input/B", Qt::Key_C).value<QKeySequence>();
auto keyL = settings.value("input/L", Qt::Key_A).value<QKeySequence>();
auto keyR = settings.value("input/R", Qt::Key_S).value<QKeySequence>();
auto keyS = settings.value("input/S", Qt::Key_Return).value<QKeySequence>();
auto keyDU = settings.value("input/DU", Qt::Key_I).value<QKeySequence>();
auto keyDD = settings.value("input/DD", Qt::Key_K).value<QKeySequence>();
auto keyDL = settings.value("input/DL", Qt::Key_J).value<QKeySequence>();
auto keyDR = settings.value("input/DR", Qt::Key_L).value<QKeySequence>();
auto keyCU = settings.value("input/CU", Qt::Key_T).value<QKeySequence>();
auto keyCD = settings.value("input/CD", Qt::Key_G).value<QKeySequence>();
auto keyCL = settings.value("input/CL", Qt::Key_F).value<QKeySequence>();
auto keyCR = settings.value("input/CR", Qt::Key_H).value<QKeySequence>();
auto keyAU = settings.value("input/AU", Qt::Key_Up).value<QKeySequence>();
auto keyAD = settings.value("input/AD", Qt::Key_Down).value<QKeySequence>();
auto keyAL = settings.value("input/AL", Qt::Key_Left).value<QKeySequence>();
auto keyAR = settings.value("input/AR", Qt::Key_Right).value<QKeySequence>();
mapping[0] = keyZ;
mapping[1] = keyA;
mapping[2] = keyB;
mapping[3] = keyL;
mapping[4] = keyR;
mapping[5] = keyS;
mapping[6] = keyDU;
mapping[7] = keyDD;
mapping[8] = keyDL;
mapping[9] = keyDR;
mapping[10] = keyCU;
mapping[11] = keyCD;
mapping[12] = keyCL;
mapping[13] = keyCR;
mapping[14] = keyAU;
mapping[15] = keyAD;
mapping[16] = keyAL;
mapping[17] = keyAR;
btnZ->setKeySequence(keyZ);
btnA->setKeySequence(keyA);
btnB->setKeySequence(keyB);
btnL->setKeySequence(keyL);
btnR->setKeySequence(keyR);
btnS->setKeySequence(keyS);
btnDU->setKeySequence(keyDU);
btnDD->setKeySequence(keyDD);
btnDL->setKeySequence(keyDL);
btnDR->setKeySequence(keyDR);
btnCU->setKeySequence(keyCU);
btnCD->setKeySequence(keyCD);
btnCL->setKeySequence(keyCL);
btnCR->setKeySequence(keyCR);
btnAU->setKeySequence(keyAU);
btnAD->setKeySequence(keyAD);
btnAL->setKeySequence(keyAL);
btnAR->setKeySequence(keyAR);
form = new QFormLayout();
@@ -43,92 +79,92 @@ InputSettings::InputSettings() : settings(QSettings::UserScope) {
connect(btnZ, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[0] = btnZ->keySequence();
settings.setValue("input/mapping/Z", mapping[0]);
settings.setValue("input/Z", mapping[0]);
settings.sync();
});
connect(btnA, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[1] = btnA->keySequence();
settings.setValue("input/mapping/A", mapping[1]);
settings.setValue("input/A", mapping[1]);
settings.sync();
});
connect(btnB, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[2] = btnB->keySequence();
settings.setValue("input/mapping/B", mapping[2]);
settings.setValue("input/B", mapping[2]);
settings.sync();
});
connect(btnL, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[3] = btnL->keySequence();
settings.setValue("input/mapping/L", mapping[3]);
settings.setValue("input/L", mapping[3]);
settings.sync();
});
connect(btnR, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[4] = btnR->keySequence();
settings.setValue("input/mapping/R", mapping[4]);
settings.setValue("input/R", mapping[4]);
settings.sync();
});
connect(btnS, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[5] = btnS->keySequence();
settings.setValue("input/mapping/S", mapping[5]);
settings.setValue("input/S", mapping[5]);
settings.sync();
});
connect(btnDU, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[6] = btnDU->keySequence();
settings.setValue("input/mapping/DU", mapping[6]);
settings.setValue("input/DU", mapping[6]);
settings.sync();
});
connect(btnDD, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[7] = btnDD->keySequence();
settings.setValue("input/mapping/DD", mapping[7]);
settings.setValue("input/DD", mapping[7]);
settings.sync();
});
connect(btnDL, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[8] = btnDL->keySequence();
settings.setValue("input/mapping/DL", mapping[8]);
settings.setValue("input/DL", mapping[8]);
settings.sync();
});
connect(btnDR, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[9] = btnDR->keySequence();
settings.setValue("input/mapping/DR", mapping[9]);
settings.setValue("input/DR", mapping[9]);
settings.sync();
});
connect(btnCU, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[10] = btnCU->keySequence();
settings.setValue("input/mapping/CU", mapping[10]);
settings.setValue("input/CU", mapping[10]);
settings.sync();
});
connect(btnCD, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[11] = btnCD->keySequence();
settings.setValue("input/mapping/CD", mapping[11]);
settings.setValue("input/CD", mapping[11]);
settings.sync();
});
connect(btnCL, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[12] = btnCL->keySequence();
settings.setValue("input/mapping/CL", mapping[12]);
settings.setValue("input/CL", mapping[12]);
settings.sync();
});
connect(btnCR, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[13] = btnCR->keySequence();
settings.setValue("input/mapping/CR", mapping[13]);
settings.setValue("input/CR", mapping[13]);
settings.sync();
});
connect(btnAU, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[14] = btnAU->keySequence();
settings.setValue("input/mapping/AU", mapping[14]);
settings.setValue("input/AU", mapping[14]);
settings.sync();
});
connect(btnAD, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[15] = btnAD->keySequence();
settings.setValue("input/mapping/AD", mapping[15]);
settings.setValue("input/AD", mapping[15]);
settings.sync();
});
connect(btnAL, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[16] = btnAL->keySequence();
settings.setValue("input/mapping/AL", mapping[16]);
settings.setValue("input/AL", mapping[16]);
settings.sync();
});
connect(btnAR, &QKeySequenceEdit::keySequenceChanged, this, [&] {
mapping[17] = btnAR->keySequence();
settings.setValue("input/mapping/AR", mapping[17]);
settings.setValue("input/AR", mapping[17]);
settings.sync();
});
+2 -1
View File
@@ -6,5 +6,6 @@ Options::Options() {
volume = settings.value("audio/volume", 0.5f).toFloat();
cpuType = settings.value("cpu/type", n64::PlainInterpreter).value<n64::CPUType>();
idleSkip = settings.value("cpu/idle_skip", false).toBool();
savesPath = settings.value("general/saves_path", "saves").toString().toStdString();
savesPath = settings.value("saves_path", "").toString().toStdString();
romsPath = settings.value("roms_path", "").toString().toStdString();
}
+4 -1
View File
@@ -18,14 +18,17 @@ struct Options {
static float GetVolume() { return GetInstance().volume; }
static n64::CPUType GetCpuType() { return GetInstance().cpuType; }
static std::string GetSavesPath() { return GetInstance().savesPath; }
static std::string GetRomsPath() { return GetInstance().romsPath; }
static void SetIdleSkip(bool v) { GetInstance().idleSkip = v; }
static void SetVolume(float v) { GetInstance().volume = v; }
static void SetCpuType(n64::CPUType v) { GetInstance().cpuType = v; }
static void SetSavesPath(const std::string &v) { GetInstance().savesPath = v; }
static void SetRomsPath(const std::string &v) { GetInstance().romsPath = v; }
private:
bool idleSkip = false;
float volume = 0.5;
std::string savesPath = "saves";
std::string savesPath;
std::string romsPath;
n64::CPUType cpuType = n64::PlainInterpreter;
};