You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
5.7 KiB
217 lines
5.7 KiB
/*
|
|
Copyright 2020-2021 Brad Parker
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <QApplication>
|
|
#include <QMessageBox>
|
|
#include <QTimer>
|
|
#include <QCloseEvent>
|
|
|
|
#ifndef NO_SOUND
|
|
// only needed for Core::audioReady signal connection
|
|
#include <QAudioFormat>
|
|
#endif
|
|
|
|
#include "mainwindow.h"
|
|
#include "../core/core.h"
|
|
#include "../core/coreoptionsdialog.h"
|
|
|
|
static MainWindow *s_mainwindow = nullptr;
|
|
|
|
MainWindow::MainWindow(QWidget *parent) :
|
|
QOpenGLWidget(parent)
|
|
,m_core(nullptr)
|
|
#ifndef NO_SOUND
|
|
,m_audioOutput(nullptr)
|
|
#endif
|
|
,m_audioReady(false)
|
|
,m_audioStream(nullptr)
|
|
,m_corePath()
|
|
,m_romPath()
|
|
,m_coreOptionsDialog()
|
|
,m_settings(qApp->applicationName() + ".ini", QSettings::IniFormat, this)
|
|
{
|
|
s_mainwindow = this;
|
|
m_core = Core::instance();
|
|
setCursor(QCursor(Qt::BlankCursor));
|
|
setMouseTracking(true);
|
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
|
}
|
|
|
|
MainWindow::~MainWindow() {
|
|
//std::cout << "mainwindow dtor" << std::endl;
|
|
}
|
|
|
|
MainWindow* MainWindow::instance() {
|
|
return s_mainwindow;
|
|
}
|
|
|
|
void MainWindow::openCoreOptionsDialog() {
|
|
if(m_coreOptionsDialog)
|
|
delete m_coreOptionsDialog;
|
|
|
|
m_coreOptionsDialog = new CoreOptionsDialog(nullptr, m_core);
|
|
|
|
// layout is cleared after a single shot timer in the constructor, so we cannot build a new layout until after that
|
|
QTimer::singleShot(0, this, [this]() {
|
|
m_coreOptionsDialog->show();
|
|
m_coreOptionsDialog->reload();
|
|
});
|
|
}
|
|
|
|
void MainWindow::setCorePath(QString path) {
|
|
m_corePath = path;
|
|
}
|
|
|
|
void MainWindow::setROMPath(QString path) {
|
|
m_romPath = path;
|
|
}
|
|
|
|
void MainWindow::loadCore() {
|
|
std::cout << "Loading core." << std::endl;
|
|
|
|
connect(m_core, &Core::repaint, this, QOverload<>::of(&MainWindow::repaint));
|
|
connect(this, &MainWindow::frameSwapped, m_core, &Core::onGotFrameSwap);
|
|
|
|
if(m_coreOptionsDialog)
|
|
delete m_coreOptionsDialog;
|
|
|
|
#ifndef NO_SOUND
|
|
connect(m_core, &Core::audioReady, this, &MainWindow::startAudio);
|
|
#endif
|
|
connect(m_core, &Core::gotAudioSample, this, &MainWindow::onAudioSample);
|
|
connect(m_core, &Core::gotAudioSampleBatch, this, &MainWindow::onAudioSampleBatch);
|
|
connect(m_core, &Core::coreOptionsChanged, this, &MainWindow::onCoreOptionsChanged);
|
|
connect(this, &MainWindow::audioStreamReady, m_core, &Core::onAudioStreamReady);
|
|
|
|
m_core->load(m_corePath);
|
|
|
|
if(!m_core->isLoaded()) {
|
|
std::cout << "could not load core" << std::endl;
|
|
QMessageBox::critical(this, "Error", "Could not load core.");
|
|
QTimer::singleShot(0, qApp, &QApplication::quit);
|
|
return;
|
|
}
|
|
|
|
if(!m_core->isResolved()) {
|
|
std::cerr << "could not resolve required functions" << std::endl;
|
|
QMessageBox::critical(this, "Error", "Error loading core properly, the file may be corrupt.");
|
|
QTimer::singleShot(0, qApp, &QApplication::quit);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void MainWindow::loadContent() {
|
|
if(m_romPath.isEmpty() && !m_core->supportsNoGame()) {
|
|
std::cerr << "No ROM specified." << std::endl;
|
|
QTimer::singleShot(0, qApp, &QApplication::quit);
|
|
return;
|
|
}
|
|
|
|
if(!m_core->run(m_romPath)) {
|
|
QTimer::singleShot(0, qApp, &QApplication::quit);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *e) {
|
|
if(m_coreOptionsDialog && m_coreOptionsDialog->isVisible())
|
|
m_coreOptionsDialog->close();
|
|
|
|
// always try to save your SRAM and core options before even trying to stop the core, there is a chance it may crash
|
|
if(m_core) {
|
|
m_core->saveSRAM();
|
|
saveCoreOptions();
|
|
}
|
|
|
|
if(m_core && m_core->isLoaded())
|
|
m_core->unload();
|
|
|
|
QOpenGLWidget::closeEvent(e);
|
|
}
|
|
|
|
void MainWindow::saveCoreOptions() {
|
|
const QMap<QString, Core::CoreOption> &options = m_core->getCoreOptions();
|
|
const QStringList keys = options.keys();
|
|
|
|
m_settings.beginGroup("core_options");
|
|
|
|
for(int i = 0; i < options.size(); ++i) {
|
|
const Core::CoreOption &op = options.value(keys.at(i));
|
|
|
|
m_settings.setValue(op.key, op.val);
|
|
}
|
|
|
|
m_settings.endGroup();
|
|
m_settings.sync();
|
|
//std::cout << "saved settings to " << qUtf8Printable(m_settings.fileName()) << std::endl;
|
|
}
|
|
|
|
void MainWindow::saveState() {
|
|
if(m_core->saveState()) {
|
|
std::cout << "State saved." << std::endl;
|
|
}else{
|
|
std::cout << "State save failed." << std::endl;
|
|
}
|
|
}
|
|
|
|
void MainWindow::loadState() {
|
|
if(m_core->loadState()) {
|
|
std::cout << "State loaded." << std::endl;
|
|
}else{
|
|
std::cout << "State load failed." << std::endl;
|
|
}
|
|
}
|
|
|
|
void MainWindow::setCoreMuted(bool on) {
|
|
m_core->setMuted(on);
|
|
}
|
|
|
|
void MainWindow::onCoreOptionsChanged() {
|
|
std::cout << "core options changed, loading current values from settings." << std::endl;
|
|
|
|
QMap<QString, Core::CoreOption> &options = m_core->getCoreOptions();
|
|
|
|
m_settings.beginGroup("core_options");
|
|
|
|
QStringList keys = options.keys();
|
|
|
|
for(int i = 0; i < keys.size(); ++i) {
|
|
const QString &key = keys.at(i);
|
|
QString val = m_settings.value(key).toString();
|
|
|
|
if(val.isEmpty())
|
|
continue;
|
|
|
|
Core::CoreOption &op = options[key];
|
|
op.val = val;
|
|
op.valArray = val.toUtf8();
|
|
op.valData = op.valArray.constData();
|
|
|
|
//std::cout << "found key " << qUtf8Printable(key) << ", setting to " << qUtf8Printable(val) << std::endl;
|
|
}
|
|
|
|
m_settings.endGroup();
|
|
|
|
m_core->setVariablesChanged(true);
|
|
|
|
std::cout << "loaded core options from disk." << std::endl;
|
|
}
|
|
|
|
void MainWindow::go() {
|
|
loadCore();
|
|
loadContent();
|
|
}
|
|
|