mirror of
https://github.com/rodamaral/lsnes.git
synced 2025-04-02 10:42:15 -04:00
896 lines
28 KiB
C++
896 lines
28 KiB
C++
/***************************************************************************
|
|
* Copyright (C) 2007 by Sindre Aamås *
|
|
* sinamas@users.sourceforge.net *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License version 2 as *
|
|
* published by the Free Software Foundation. *
|
|
* *
|
|
* This program 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 version 2 for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* version 2 along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
#include "gambattemenuhandler.h"
|
|
#include "blitterconf.h"
|
|
#include "cheatdialog.h"
|
|
#include "gambattesource.h"
|
|
#include "mainwindow.h"
|
|
#include "miscdialog.h"
|
|
#include "palettedialog.h"
|
|
#include "sounddialog.h"
|
|
#include "videodialog.h"
|
|
#include <QActionGroup>
|
|
#include <QFileInfo>
|
|
#include <QSettings>
|
|
#include <QtGui>
|
|
#include <iostream>
|
|
|
|
namespace {
|
|
|
|
static QString const strippedName(QString const &fullFileName) {
|
|
return QFileInfo(fullFileName).fileName();
|
|
}
|
|
|
|
struct TmpPauser : private Uncopyable {
|
|
MainWindow &mw;
|
|
int const inc;
|
|
|
|
explicit TmpPauser(MainWindow &mw, int inc = 4)
|
|
: mw(mw), inc(inc)
|
|
{
|
|
mw.incPause(inc);
|
|
}
|
|
|
|
~TmpPauser() {
|
|
mw.decPause(inc);
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
FrameRateAdjuster::FrameTime::FrameTime(unsigned baseNum, unsigned baseDenom)
|
|
: index_(num_steps)
|
|
{
|
|
setBaseFrameTime(baseNum, baseDenom);
|
|
}
|
|
|
|
void FrameRateAdjuster::FrameTime::setBaseFrameTime(unsigned const baseNum, unsigned const baseDenom) {
|
|
frameTimes_[num_steps] = Rational(baseNum, baseDenom);
|
|
|
|
unsigned const bnum = baseNum * 0x10000ul / baseDenom;
|
|
for (std::size_t i = num_steps, num = bnum; i < num_steps * 2; ++i)
|
|
frameTimes_[i + 1] = Rational(num = num * 11 / 10, 0x10000);
|
|
for (std::size_t i = num_steps, num = bnum; i; --i)
|
|
frameTimes_[i - 1] = Rational(num = num * 10 / 11, 0x10000);
|
|
}
|
|
|
|
FrameRateAdjuster::FrameRateAdjuster(MiscDialog const &miscDialog, MainWindow &mw, QObject *parent)
|
|
: QObject(parent)
|
|
, frameTime_(miscDialog.baseFps().height(), miscDialog.baseFps().width())
|
|
, miscDialog_(miscDialog)
|
|
, mw_(mw)
|
|
, decFrameRateAction_(new QAction(tr("&Decrease Frame Rate"), &mw))
|
|
, incFrameRateAction_(new QAction(tr("&Increase Frame Rate"), &mw))
|
|
, resetFrameRateAction_(new QAction(tr("&Reset Frame Rate"), &mw))
|
|
, enabled_(true)
|
|
{
|
|
decFrameRateAction_->setShortcut(QString("Ctrl+D"));
|
|
incFrameRateAction_->setShortcut(QString("Ctrl+I"));
|
|
resetFrameRateAction_->setShortcut(QString("Ctrl+U"));
|
|
|
|
connect(decFrameRateAction_, SIGNAL(triggered()), this, SLOT(decFrameRate()));
|
|
connect(incFrameRateAction_, SIGNAL(triggered()), this, SLOT(incFrameRate()));
|
|
connect(resetFrameRateAction_, SIGNAL(triggered()), this, SLOT(resetFrameRate()));
|
|
connect(&miscDialog, SIGNAL(accepted()), this, SLOT(miscDialogChange()));
|
|
changed();
|
|
}
|
|
|
|
QList<QAction *> const FrameRateAdjuster::actions() {
|
|
QList<QAction *> l;
|
|
l.append(decFrameRateAction_);
|
|
l.append(incFrameRateAction_);
|
|
l.append(resetFrameRateAction_);
|
|
return l;
|
|
}
|
|
|
|
void FrameRateAdjuster::miscDialogChange() {
|
|
QSize const &baseFps = miscDialog_.baseFps();
|
|
frameTime_.setBaseFrameTime(baseFps.height(), baseFps.width());
|
|
changed();
|
|
}
|
|
|
|
void FrameRateAdjuster::setDisabled(bool disabled) {
|
|
enabled_ = !disabled;
|
|
changed();
|
|
}
|
|
|
|
void FrameRateAdjuster::decFrameRate() {
|
|
if (enabled_) {
|
|
frameTime_.inc();
|
|
changed();
|
|
}
|
|
}
|
|
|
|
void FrameRateAdjuster::incFrameRate() {
|
|
if (enabled_) {
|
|
frameTime_.dec();
|
|
changed();
|
|
}
|
|
}
|
|
|
|
void FrameRateAdjuster::resetFrameRate() {
|
|
if (enabled_) {
|
|
frameTime_.reset();
|
|
changed();
|
|
}
|
|
}
|
|
|
|
void FrameRateAdjuster::changed() {
|
|
incFrameRateAction_->setEnabled(enabled_ && frameTime_.decPossible());
|
|
decFrameRateAction_->setEnabled(enabled_ && frameTime_.incPossible());
|
|
resetFrameRateAction_->setEnabled(enabled_ && frameTime_.resetPossible());
|
|
|
|
FrameTime::Rational const &ft = enabled_ ? frameTime_.get() : frameTime_.base();
|
|
mw_.setFrameTime(ft.num, ft.denom);
|
|
}
|
|
|
|
WindowSizeMenu::WindowSizeMenu(MainWindow &mw, VideoDialog const &vd)
|
|
: mw_(mw)
|
|
, menu_(new QMenu(tr("&Window Size"), &mw))
|
|
, group_(new QActionGroup(menu_))
|
|
, maxSize_(QApplication::desktop()->screen()->size())
|
|
{
|
|
fillMenu(vd.sourceSize(), vd.scalingMethod());
|
|
setCheckedSize(QSettings().value("video/windowSize", QSize()).toSize());
|
|
connect(group_, SIGNAL(triggered(QAction *)), this, SLOT(triggered()));
|
|
|
|
QSize const &size = checkedSize();
|
|
mw_.setWindowSize(size);
|
|
if (size.isEmpty())
|
|
mw_.resize(QSettings().value("mainwindow/size", QSize(160, 144)).toSize());
|
|
}
|
|
|
|
WindowSizeMenu::~WindowSizeMenu() {
|
|
QSettings settings;
|
|
settings.setValue("video/windowSize", checkedSize());
|
|
}
|
|
|
|
void WindowSizeMenu::videoDialogChange(VideoDialog const &vd) {
|
|
QSize const &oldSize = checkedSize();
|
|
disconnect(group_, SIGNAL(triggered(QAction *)), this, SLOT(triggered()));
|
|
menu_->clear();
|
|
delete group_;
|
|
group_ = new QActionGroup(menu_);
|
|
|
|
fillMenu(vd.sourceSize(), vd.scalingMethod());
|
|
setCheckedSize(oldSize);
|
|
connect(group_, SIGNAL(triggered(QAction *)), this, SLOT(triggered()));
|
|
|
|
QSize const &newSize = checkedSize();
|
|
if (newSize != oldSize)
|
|
mw_.setWindowSize(newSize);
|
|
}
|
|
|
|
void WindowSizeMenu::triggered() {
|
|
mw_.setWindowSize(checkedSize());
|
|
}
|
|
|
|
void WindowSizeMenu::fillMenu(QSize const &sourceSize, ScalingMethod const scalingMethod) {
|
|
QSize const aspectRatio(160, 144);
|
|
QSize const basesz(scalingMethod == scaling_integer ? sourceSize : aspectRatio);
|
|
QSize sz(basesz);
|
|
while (sz.width() <= maxSize_.width() && sz.height() <= maxSize_.height()) {
|
|
if (sz.width() >= sourceSize.width() && sz.height() >= sourceSize.height()) {
|
|
QAction *a = menu_->addAction(
|
|
'&' + QString::number(sz.width()) + 'x' + QString::number(sz.height()));
|
|
a->setData(sz);
|
|
a->setCheckable(true);
|
|
group_->addAction(a);
|
|
}
|
|
|
|
sz += basesz;
|
|
}
|
|
|
|
QAction *a = menu_->addAction(tr("&Variable"));
|
|
a->setData(QSize());
|
|
a->setCheckable(true);
|
|
group_->addAction(a);
|
|
}
|
|
|
|
void WindowSizeMenu::setCheckedSize(QSize const &size) {
|
|
QList<QAction *> const &actions = group_->actions();
|
|
foreach (QAction *a, actions) {
|
|
if (a->data() == size) {
|
|
a->setChecked(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!group_->checkedAction())
|
|
actions.front()->setChecked(true);
|
|
}
|
|
|
|
QSize const WindowSizeMenu::checkedSize() const {
|
|
return group_->checkedAction() ? group_->checkedAction()->data().toSize() : QSize();
|
|
}
|
|
|
|
static QString const settingsPath() {
|
|
QString path = QSettings(QSettings::IniFormat, QSettings::UserScope,
|
|
QCoreApplication::organizationName(), QCoreApplication::applicationName()).fileName();
|
|
path.truncate(path.lastIndexOf('/'));
|
|
return path;
|
|
}
|
|
|
|
static QString const toCmdString(QAction const *a) {
|
|
QString text = a->text().toLower();
|
|
text.replace('&', QString());
|
|
text.replace(' ', '-');
|
|
return text;
|
|
}
|
|
|
|
static char toCmdChar(QAction const *a) {
|
|
return a->shortcut().count() == 1
|
|
? (a->shortcut()[0] - Qt::Key_A + 'a') & 0xff
|
|
: 0;
|
|
}
|
|
|
|
static QAction * findCmdStringAction(QList<QAction *> const &l, QString const &cmdstr) {
|
|
foreach (QAction *a, l) {
|
|
if (cmdstr == toCmdString(a))
|
|
return a;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static QAction * findCmdCharAction(QList<QAction *> const &l, char const c) {
|
|
foreach (QAction *a, l) {
|
|
if (c == toCmdChar(a))
|
|
return a;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void printUsage(char const *const arg0, QList<QAction *> const &actions) {
|
|
std::cout << "Usage: " << arg0 << " [OPTION]... [romfile]\n";
|
|
|
|
foreach (QAction const *const a, actions) {
|
|
if (a->isCheckable() && a->isEnabled()) {
|
|
std::string const &text = toCmdString(a).toStdString();
|
|
char const c = toCmdChar(a);
|
|
std::cout << " "
|
|
<< (c ? '-' + std::string(1, c) + ", " : std::string(" "))
|
|
<< "--" << text << "[=0]\n";
|
|
}
|
|
}
|
|
}
|
|
|
|
GambatteMenuHandler::GambatteMenuHandler(MainWindow &mw,
|
|
GambatteSource &source,
|
|
int const argc,
|
|
char const *const argv[])
|
|
: mw_(mw)
|
|
, source_(source)
|
|
, soundDialog_(new SoundDialog(mw, &mw))
|
|
, videoDialog_(new VideoDialog(mw, source.generateVideoSourceInfos(),
|
|
QString("Video filter:"), &mw))
|
|
, miscDialog_(new MiscDialog(settingsPath() + "/saves", &mw))
|
|
, cheatDialog_(new CheatDialog(settingsPath() + "/cheats.ini", &mw))
|
|
, recentFileActs_()
|
|
, pauseAction_()
|
|
, syncFrameRateAction_()
|
|
, gbaCgbAction_()
|
|
, forceDmgAction_()
|
|
, fsAct_()
|
|
, recentMenu_()
|
|
, globalPaletteDialog_()
|
|
, romPaletteDialog_()
|
|
, stateSlotGroup_(new QActionGroup(&mw))
|
|
, windowSizeMenu_(mw, *videoDialog_)
|
|
, pauseInc_(4)
|
|
{
|
|
mw.setWindowTitle("Gambatte");
|
|
source.inputDialog()->setParent(&mw, source.inputDialog()->windowFlags());
|
|
|
|
{
|
|
QString const &settingspath = settingsPath();
|
|
QString const &palpath = settingspath + "/palettes";
|
|
QDir::root().mkpath(settingspath + "/saves");
|
|
QDir::root().mkpath(palpath);
|
|
globalPaletteDialog_ = new PaletteDialog(palpath, 0, &mw);
|
|
romPaletteDialog_ = new PaletteDialog(palpath, globalPaletteDialog_, &mw);
|
|
connect(globalPaletteDialog_, SIGNAL(accepted()), this, SLOT(globalPaletteChange()));
|
|
connect(romPaletteDialog_, SIGNAL(accepted()), this, SLOT(romPaletteChange()));
|
|
}
|
|
|
|
QActionGroup *const romLoadedActions = new QActionGroup(&mw);
|
|
romLoadedActions->setExclusive(false);
|
|
|
|
{
|
|
for (int i = 0; i < max_recent_files; ++i) {
|
|
recentFileActs_[i] = new QAction(&mw);
|
|
recentFileActs_[i]->setVisible(false);
|
|
connect(recentFileActs_[i], SIGNAL(triggered()), this, SLOT(openRecentFile()));
|
|
}
|
|
|
|
QMenu *fileMenu = mw.menuBar()->addMenu(tr("&File"));
|
|
fileMenu->addAction(tr("&Open..."), this, SLOT(open()), tr("Ctrl+O"));
|
|
|
|
recentMenu_ = fileMenu->addMenu(tr("Open Re¢"));
|
|
for (int i = 0; i < max_recent_files; ++i)
|
|
recentMenu_->addAction(recentFileActs_[i]);
|
|
|
|
fileMenu->addSeparator();
|
|
romLoadedActions->addAction(fileMenu->addAction(tr("&Reset"), this, SLOT(reset()), tr("Ctrl+R")));
|
|
fileMenu->addSeparator();
|
|
romLoadedActions->addAction(fileMenu->addAction(tr("Save State &As..."), this, SLOT(saveStateAs())));
|
|
romLoadedActions->addAction(fileMenu->addAction(tr("Load State &From..."), this, SLOT(loadStateFrom())));
|
|
fileMenu->addSeparator();
|
|
romLoadedActions->addAction(fileMenu->addAction(tr("&Save State"),
|
|
this, SLOT(saveState()), QString("Ctrl+S")));
|
|
romLoadedActions->addAction(fileMenu->addAction(tr("&Load State"),
|
|
this, SLOT(loadState()), QString("Ctrl+L")));
|
|
|
|
{
|
|
QMenu *const stateSlotMenu = fileMenu->addMenu(tr("S&elect State Slot"));
|
|
stateSlotMenu->setEnabled(false);
|
|
stateSlotMenu->addAction(tr("&Previous"), this, SLOT(prevStateSlot()), QString("Ctrl+Z"));
|
|
stateSlotMenu->addAction(tr("&Next"), this, SLOT(nextStateSlot()), QString("Ctrl+X"));
|
|
stateSlotMenu->addSeparator();
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
|
int const no = i == 9 ? 0 : i + 1;
|
|
QString const &strno = QString::number(no);
|
|
QAction *action = stateSlotMenu->addAction(
|
|
"Slot &" + strno, this, SLOT(selectStateSlot()), strno);
|
|
action->setCheckable(true);
|
|
action->setData(no);
|
|
stateSlotGroup_->addAction(action);
|
|
}
|
|
|
|
connect(this, SIGNAL(romLoaded(bool)), stateSlotMenu, SLOT(setEnabled(bool)));
|
|
}
|
|
|
|
fileMenu->addSeparator();
|
|
fileMenu->addAction(tr("&Quit"), qApp, SLOT(closeAllWindows()), tr("Ctrl+Q"));
|
|
updateRecentFileActions();
|
|
}
|
|
|
|
FrameRateAdjuster *const frameRateAdjuster = new FrameRateAdjuster(*miscDialog_, mw, this);
|
|
QList<QAction *> cmdactions;
|
|
|
|
{
|
|
QMenu *const playm = mw.menuBar()->addMenu(tr("&Play"));
|
|
romLoadedActions->addAction(pauseAction_ = playm->addAction(
|
|
tr("&Pause"), this, SLOT(pauseChange()), QString("Ctrl+P")));
|
|
pauseAction_->setCheckable(true);
|
|
romLoadedActions->addAction(playm->addAction(tr("Frame &Step"),
|
|
this, SLOT(frameStep()), QString("Ctrl+.")));
|
|
playm->addSeparator();
|
|
syncFrameRateAction_ = playm->addAction(tr("&Sync Frame Rate to Refresh Rate"));
|
|
syncFrameRateAction_->setCheckable(true);
|
|
connect(syncFrameRateAction_, SIGNAL(triggered(bool)),
|
|
frameRateAdjuster, SLOT(setDisabled(bool)));
|
|
connect(syncFrameRateAction_, SIGNAL(triggered(bool)),
|
|
&mw, SLOT(setSyncToRefreshRate(bool)));
|
|
|
|
foreach (QAction *action, frameRateAdjuster->actions())
|
|
playm->addAction(romLoadedActions->addAction(action));
|
|
|
|
cmdactions += playm->actions();
|
|
}
|
|
|
|
QMenu *const settingsm = mw.menuBar()->addMenu(tr("&Settings"));
|
|
settingsm->addAction(tr("&Input..."), this, SLOT(execInputDialog()));
|
|
settingsm->addAction(tr("&Miscellaneous..."), this, SLOT(execMiscDialog()));
|
|
settingsm->addAction(tr("&Sound..."), this, SLOT(execSoundDialog()));
|
|
settingsm->addAction(tr("&Video..."), this, SLOT(execVideoDialog()));
|
|
settingsm->addSeparator();
|
|
settingsm->addMenu(windowSizeMenu_.menu());
|
|
settingsm->addSeparator();
|
|
gbaCgbAction_ = settingsm->addAction(tr("GB&A CGB Mode"));
|
|
gbaCgbAction_->setCheckable(true);
|
|
gbaCgbAction_->setChecked(QSettings().value("gbacgb", false).toBool());
|
|
forceDmgAction_ = settingsm->addAction(tr("Force &DMG Mode"));
|
|
forceDmgAction_->setCheckable(true);
|
|
|
|
{
|
|
QMenu *const palm = settingsm->addMenu(tr("DMG &Palette"));
|
|
palm->addAction(tr("&Global..."), this, SLOT(execGlobalPaletteDialog()));
|
|
|
|
QAction *romPaletteAct = palm->addAction(tr("Current &ROM..."), this, SLOT(execRomPaletteDialog()));
|
|
romPaletteAct->setEnabled(false);
|
|
connect(this, SIGNAL(dmgRomLoaded(bool)), romPaletteAct, SLOT(setEnabled(bool)));
|
|
}
|
|
|
|
settingsm->addSeparator();
|
|
fsAct_ = settingsm->addAction(tr("&Full Screen"), this, SLOT(toggleFullScreen()), tr("Ctrl+F"));
|
|
fsAct_->setCheckable(true);
|
|
cmdactions += settingsm->actions();
|
|
|
|
romLoadedActions->addAction(mw.menuBar()->addMenu(tr("&Tools"))->addAction(tr("&Cheats..."),
|
|
cheatDialog_, SLOT(exec())));
|
|
romLoadedActions->setEnabled(false);
|
|
mw.menuBar()->addSeparator();
|
|
|
|
QMenu *const helpMenu = mw.menuBar()->addMenu(tr("&Help"));
|
|
helpMenu->addAction(tr("&About"), this, SLOT(about()));
|
|
|
|
mw.addActions(mw.menuBar()->actions());
|
|
|
|
{
|
|
QAction *escAct = new QAction(&mw);
|
|
escAct->setShortcut(tr("Esc"));
|
|
connect(escAct, SIGNAL(triggered()), this, SLOT(escPressed()));
|
|
mw.addAction(escAct);
|
|
}
|
|
|
|
mw.setSamplesPerFrame(35112);
|
|
connect(&source, SIGNAL(setTurbo(bool)), &mw, SLOT(setFastForward(bool)));
|
|
connect(&source, SIGNAL(togglePause()), pauseAction_, SLOT(trigger()));
|
|
connect(&source, SIGNAL(frameStep()), this, SLOT(frameStep()));
|
|
connect(&source, SIGNAL(decFrameRate()), frameRateAdjuster, SLOT(decFrameRate()));
|
|
connect(&source, SIGNAL(incFrameRate()), frameRateAdjuster, SLOT(incFrameRate()));
|
|
connect(&source, SIGNAL(resetFrameRate()), frameRateAdjuster, SLOT(resetFrameRate()));
|
|
connect(&source, SIGNAL(prevStateSlot()), this, SLOT(prevStateSlot()));
|
|
connect(&source, SIGNAL(nextStateSlot()), this, SLOT(nextStateSlot()));
|
|
connect(&source, SIGNAL(saveStateSignal()), this, SLOT(saveState()));
|
|
connect(&source, SIGNAL(loadStateSignal()), this, SLOT(loadState()));
|
|
connect(&source, SIGNAL(quit()), qApp, SLOT(closeAllWindows()));
|
|
connect(videoDialog_, SIGNAL(accepted()), this, SLOT(videoDialogChange()));
|
|
connect(soundDialog_, SIGNAL(accepted()), this, SLOT(soundDialogChange()));
|
|
connect(miscDialog_, SIGNAL(accepted()), this, SLOT(miscDialogChange()));
|
|
connect(cheatDialog_, SIGNAL(accepted()), this, SLOT(cheatDialogChange()));
|
|
connect(&mw, SIGNAL(videoBlitterFailure()), this, SLOT(videoBlitterFailure()));
|
|
connect(&mw, SIGNAL(audioEngineFailure()), this, SLOT(audioEngineFailure()));
|
|
connect(&mw, SIGNAL(closing()), this, SLOT(saveWindowSizeIfNotFullScreen()));
|
|
connect(&mw, SIGNAL(dwmCompositionChange()), this, SLOT(reconsiderSyncFrameRateActionEnable()));
|
|
connect(this, SIGNAL(romLoaded(bool)), romLoadedActions, SLOT(setEnabled(bool)));
|
|
connect(this, SIGNAL(romLoaded(bool)), stateSlotGroup_->actions().at(0), SLOT(setChecked(bool)));
|
|
|
|
mw.setAspectRatio(QSize(160, 144));
|
|
videoDialogChange();
|
|
soundDialogChange();
|
|
miscDialogChange();
|
|
|
|
bool unknownCmd = false;
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
if (argv[i][0] == '-' && argv[i][1]) {
|
|
QString const argstr(argv[i] + 2);
|
|
|
|
if (QAction *a = argv[i][1] == '-'
|
|
? findCmdStringAction(cmdactions, argstr.left(argstr.lastIndexOf('=')))
|
|
: findCmdCharAction(cmdactions, argv[i][1])) {
|
|
if (argstr.endsWith("=0") == a->isChecked() && a->isEnabled())
|
|
a->trigger();
|
|
} else
|
|
unknownCmd = true;
|
|
}
|
|
}
|
|
|
|
if (unknownCmd)
|
|
printUsage(argv[0], cmdactions);
|
|
|
|
for (int i = 1; i < argc; ++i) {
|
|
if (argv[i][0] != '-') {
|
|
if (fsAct_->isChecked())
|
|
mw.menuBar()->hide();
|
|
|
|
loadFile(QFileInfo(QString(argv[i])).absoluteFilePath());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
GambatteMenuHandler::~GambatteMenuHandler() {
|
|
QSettings settings;
|
|
settings.setValue("gbacgb", gbaCgbAction_->isChecked());
|
|
}
|
|
|
|
void GambatteMenuHandler::updateRecentFileActions() {
|
|
QSettings settings;
|
|
QStringList files = settings.value("recentFileList").toStringList();
|
|
|
|
int const numRecentFiles = qMin(files.size(), static_cast<int>(max_recent_files));
|
|
|
|
for (int i = 0; i < numRecentFiles; ++i) {
|
|
QString const &text = tr("&%1 %2").arg(i + 1).arg(strippedName(files[i]));
|
|
recentFileActs_[i]->setText(text);
|
|
recentFileActs_[i]->setData(files[i]);
|
|
recentFileActs_[i]->setVisible(true);
|
|
}
|
|
|
|
for (int j = numRecentFiles; j < max_recent_files; ++j)
|
|
recentFileActs_[j]->setVisible(false);
|
|
|
|
recentMenu_->setEnabled(numRecentFiles > 0);
|
|
}
|
|
|
|
void GambatteMenuHandler::setCurrentFile(QString const &fileName) {
|
|
QSettings settings;
|
|
QStringList files = settings.value("recentFileList").toStringList();
|
|
files.removeAll(fileName);
|
|
files.prepend(fileName);
|
|
while (files.size() > max_recent_files)
|
|
files.removeLast();
|
|
|
|
settings.setValue("recentFileList", files);
|
|
updateRecentFileActions();
|
|
}
|
|
|
|
void GambatteMenuHandler::loadFile(QString const &fileName) {
|
|
TmpPauser tmpPauser(mw_, 4);
|
|
pauseAction_->setChecked(false);
|
|
pauseChange();
|
|
mw_.waitUntilPaused();
|
|
|
|
if (gambatte::LoadRes const error =
|
|
source_.load(fileName.toLocal8Bit().constData(),
|
|
gbaCgbAction_->isChecked() * gambatte::GB::GBA_CGB
|
|
+ forceDmgAction_->isChecked() * gambatte::GB::FORCE_DMG
|
|
+ miscDialog_->multicartCompat() * gambatte::GB::MULTICART_COMPAT)) {
|
|
mw_.stop();
|
|
emit dmgRomLoaded(false);
|
|
emit romLoaded(false);
|
|
QMessageBox::critical(
|
|
&mw_,
|
|
tr("File Load Error"),
|
|
(tr("Failed to load file\n")
|
|
+ fileName + ".\n\n"
|
|
+ gambatte::to_string(error).c_str() + '.'));
|
|
return;
|
|
}
|
|
|
|
QString const &romTitle = QString::fromStdString(source_.romTitle()).trimmed();
|
|
cheatDialog_->setGameName(romTitle.isEmpty() ? QFileInfo(fileName).completeBaseName() : romTitle);
|
|
cheatDialogChange();
|
|
|
|
if (!source_.isCgb()) {
|
|
romPaletteDialog_->setSettingsFile(
|
|
QFileInfo(fileName).completeBaseName() + ".pal",
|
|
romTitle);
|
|
setDmgPaletteColors();
|
|
}
|
|
|
|
gambatte::PakInfo const &pak = source_.pakInfo();
|
|
std::cout << romTitle.toStdString() << '\n'
|
|
<< "GamePak type: " << pak.mbc()
|
|
<< " rambanks: " << pak.rambanks()
|
|
<< " rombanks: " << pak.rombanks() << '\n'
|
|
<< "header checksum: " << (pak.headerChecksumOk() ? "ok" : "bad") << '\n'
|
|
<< "cgb: " << source_.isCgb() << std::endl;
|
|
|
|
mw_.setWindowTitle(strippedName(fileName) + (pak.headerChecksumOk() ? "" : " [bad]") + " - Gambatte");
|
|
setCurrentFile(fileName);
|
|
|
|
emit romLoaded(true);
|
|
emit dmgRomLoaded(!source_.isCgb());
|
|
|
|
mw_.resetAudio();
|
|
mw_.run();
|
|
}
|
|
|
|
void GambatteMenuHandler::open() {
|
|
TmpPauser tmpPauser(mw_, 4);
|
|
mw_.waitUntilPaused();
|
|
|
|
QString const &fileName = QFileDialog::getOpenFileName(
|
|
&mw_, tr("Open"), recentFileActs_[0]->data().toString(),
|
|
tr("Game Boy ROM Images (*.dmg *.gb *.gbc *.sgb *.zip *.gz);;All Files (*)"));
|
|
if (!fileName.isEmpty())
|
|
loadFile(fileName);
|
|
|
|
// giving back focus after getOpenFileName seems to fail at times, which
|
|
// can be problematic with current exclusive mode handling.
|
|
mw_.setFocus();
|
|
}
|
|
|
|
void GambatteMenuHandler::openRecentFile() {
|
|
if (QAction const *action = qobject_cast<QAction *>(sender()))
|
|
loadFile(action->data().toString());
|
|
}
|
|
|
|
void GambatteMenuHandler::about() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
QMessageBox::about(
|
|
&mw_,
|
|
"About Gambatte",
|
|
"<h3>Gambatte Qt"
|
|
#ifdef GAMBATTE_QT_VERSION_STR
|
|
" (" GAMBATTE_QT_VERSION_STR ")"
|
|
#endif
|
|
"</h3>"
|
|
"<p>"
|
|
"<b>Homepage:</b> "
|
|
"<a href=\"https://github.com/sinamas/gambatte\">"
|
|
"https://github.com/sinamas/gambatte"
|
|
"</a>"
|
|
"</p>"
|
|
"<p>"
|
|
"Gambatte is an open-source Game Boy Color emulator written for fun "
|
|
"and made available in the hope that it will be useful. It is based "
|
|
"on some stupid amount of hardware accuracy tests."
|
|
"</p>"
|
|
);
|
|
}
|
|
|
|
void GambatteMenuHandler::globalPaletteChange() {
|
|
romPaletteDialog_->externalChange();
|
|
setDmgPaletteColors();
|
|
}
|
|
|
|
void GambatteMenuHandler::romPaletteChange() {
|
|
globalPaletteDialog_->externalChange();
|
|
setDmgPaletteColors();
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct SetDmgPaletteColorFun {
|
|
GambatteSource &source; int palnum; int colornum; unsigned long rgb32;
|
|
void operator()() const { source.setDmgPaletteColor(palnum, colornum, rgb32); }
|
|
};
|
|
|
|
struct SetVideoSourceFun {
|
|
GambatteSource &source; std::size_t sourceIndex;
|
|
void operator()() const { source.setVideoSource(sourceIndex); }
|
|
};
|
|
|
|
struct SetSaveDirFun {
|
|
GambatteSource &source; QString path;
|
|
void operator()() const { source.setSavedir(path.toLocal8Bit().constData()); }
|
|
};
|
|
|
|
} // anon ns
|
|
|
|
void GambatteMenuHandler::setDmgPaletteColors() {
|
|
for (int palnum = 0; palnum < 3; ++palnum)
|
|
for (int colornum = 0; colornum < 4; ++colornum) {
|
|
SetDmgPaletteColorFun fun = { source_, palnum, colornum,
|
|
romPaletteDialog_->color(palnum, colornum) };
|
|
mw_.callInWorkerThread(fun);
|
|
}
|
|
}
|
|
|
|
void GambatteMenuHandler::videoDialogChange() {
|
|
{
|
|
SetVideoSourceFun fun = { source_, videoDialog_->sourceIndex() };
|
|
mw_.callInWorkerThread(fun);
|
|
}
|
|
|
|
applySettings(mw_, *videoDialog_);
|
|
windowSizeMenu_.videoDialogChange(*videoDialog_);
|
|
reconsiderSyncFrameRateActionEnable();
|
|
}
|
|
|
|
void GambatteMenuHandler::soundDialogChange() {
|
|
applySettings(mw_, *soundDialog_);
|
|
}
|
|
|
|
void GambatteMenuHandler::miscDialogChange() {
|
|
SetSaveDirFun const setSaveDirFun = { source_, miscDialog_->savePath() };
|
|
mw_.callInWorkerThread(setSaveDirFun);
|
|
mw_.setDwmTripleBuffer(miscDialog_->dwmTripleBuf());
|
|
mw_.setFastForwardSpeed(miscDialog_->turboSpeed());
|
|
mw_.setPauseOnFocusOut(miscDialog_->pauseOnFocusOut() ? 2 : 0);
|
|
pauseInc_ = miscDialog_->pauseOnDialogs() ? 4 : 0;
|
|
}
|
|
|
|
void GambatteMenuHandler::cheatDialogChange() {
|
|
std::string gameGenieCodes;
|
|
std::string gameSharkCodes;
|
|
|
|
foreach (QString const &s, cheatDialog_->cheats().split(';', QString::SkipEmptyParts)) {
|
|
if (s.contains('-')) {
|
|
gameGenieCodes += s.toStdString() + ';';
|
|
} else
|
|
gameSharkCodes += s.toStdString() + ';';
|
|
}
|
|
|
|
source_.setGameGenie(gameGenieCodes);
|
|
source_.setGameShark(gameSharkCodes);
|
|
}
|
|
|
|
void GambatteMenuHandler::reconsiderSyncFrameRateActionEnable() {
|
|
if (mw_.blitterConf(videoDialog_->blitterNo()).maxSwapInterval()
|
|
&& !MainWindow::isDwmCompositionEnabled()) {
|
|
syncFrameRateAction_->setEnabled(true);
|
|
} else {
|
|
if (syncFrameRateAction_->isChecked())
|
|
syncFrameRateAction_->trigger();
|
|
|
|
syncFrameRateAction_->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
void GambatteMenuHandler::execGlobalPaletteDialog() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
globalPaletteDialog_->exec();
|
|
}
|
|
|
|
void GambatteMenuHandler::execRomPaletteDialog() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
romPaletteDialog_->exec();
|
|
}
|
|
|
|
void GambatteMenuHandler::execInputDialog() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
source_.inputDialog()->exec();
|
|
}
|
|
|
|
void GambatteMenuHandler::execSoundDialog() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
soundDialog_->exec();
|
|
}
|
|
|
|
void GambatteMenuHandler::execVideoDialog() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
videoDialog_->exec();
|
|
}
|
|
|
|
void GambatteMenuHandler::execMiscDialog() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
miscDialog_->exec();
|
|
}
|
|
|
|
void GambatteMenuHandler::prevStateSlot() {
|
|
stateSlotGroup_->actions().at(source_.currentState() < 2
|
|
? source_.currentState() + 8
|
|
: source_.currentState() - 2)->trigger();
|
|
}
|
|
|
|
void GambatteMenuHandler::nextStateSlot() {
|
|
stateSlotGroup_->actions().at(source_.currentState())->trigger();
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct SelectStateFun {
|
|
GambatteSource &source; int i;
|
|
void operator()() const { source.selectState(i); }
|
|
};
|
|
|
|
struct SaveStateFun {
|
|
GambatteSource &source;
|
|
MainWindow::FrameBuffer fb;
|
|
void operator()() const {
|
|
source.saveState(MainWindow::FrameBuffer::Locked(fb).get());
|
|
}
|
|
};
|
|
|
|
struct LoadStateFun {
|
|
GambatteSource &source;
|
|
void operator()() const { source.loadState(); }
|
|
};
|
|
|
|
struct SaveStateAsFun {
|
|
GambatteSource &source;
|
|
MainWindow::FrameBuffer fb;
|
|
QString fileName;
|
|
void operator()() const {
|
|
source.saveState(MainWindow::FrameBuffer::Locked(fb).get(),
|
|
fileName.toLocal8Bit().constData());
|
|
}
|
|
};
|
|
|
|
struct LoadStateFromFun {
|
|
GambatteSource &source;
|
|
QString fileName;
|
|
void operator()() const {
|
|
source.loadState(fileName.toLocal8Bit().constData());
|
|
}
|
|
};
|
|
|
|
struct ResetFun {
|
|
GambatteSource &source;
|
|
void operator()() const { source.reset(); }
|
|
};
|
|
|
|
} // anon ns
|
|
|
|
void GambatteMenuHandler::selectStateSlot() {
|
|
if (QAction *action = stateSlotGroup_->checkedAction()) {
|
|
SelectStateFun fun = { source_, action->data().toInt() };
|
|
mw_.callInWorkerThread(fun);
|
|
}
|
|
}
|
|
|
|
void GambatteMenuHandler::saveState() {
|
|
SaveStateFun fun = { source_, MainWindow::FrameBuffer(mw_) };
|
|
mw_.callInWorkerThread(fun);
|
|
}
|
|
|
|
void GambatteMenuHandler::loadState() {
|
|
LoadStateFun fun = { source_ };
|
|
mw_.callInWorkerThread(fun);
|
|
}
|
|
|
|
void GambatteMenuHandler::saveStateAs() {
|
|
TmpPauser tmpPauser(mw_, 4);
|
|
mw_.waitUntilPaused();
|
|
|
|
QString const &fileName = QFileDialog::getSaveFileName(
|
|
&mw_, tr("Save State"), QString(),
|
|
tr("Gambatte Quick Save Files (*.gqs);;All Files (*)"));
|
|
if (!fileName.isEmpty()) {
|
|
SaveStateAsFun fun = { source_, MainWindow::FrameBuffer(mw_), fileName };
|
|
mw_.callInWorkerThread(fun);
|
|
}
|
|
}
|
|
|
|
void GambatteMenuHandler::loadStateFrom() {
|
|
TmpPauser tmpPauser(mw_, 4);
|
|
mw_.waitUntilPaused();
|
|
|
|
QString const &fileName = QFileDialog::getOpenFileName(
|
|
&mw_, tr("Load State"), QString(),
|
|
tr("Gambatte Quick Save Files (*.gqs);;All Files (*)"));
|
|
if (!fileName.isEmpty()) {
|
|
LoadStateFromFun fun = { source_, fileName };
|
|
mw_.callInWorkerThread(fun);
|
|
}
|
|
}
|
|
|
|
void GambatteMenuHandler::reset() {
|
|
ResetFun fun = { source_ };
|
|
mw_.callInWorkerThread(fun);
|
|
}
|
|
|
|
void GambatteMenuHandler::pauseChange() {
|
|
if (pauseAction_->isChecked())
|
|
mw_.pause();
|
|
else
|
|
mw_.unpause();
|
|
}
|
|
|
|
void GambatteMenuHandler::frameStep() {
|
|
if (pauseAction_->isChecked())
|
|
mw_.frameStep();
|
|
else
|
|
pauseAction_->trigger();
|
|
}
|
|
|
|
void GambatteMenuHandler::escPressed() {
|
|
#ifdef Q_WS_MAC
|
|
if (fsAct_->isChecked())
|
|
fsAct_->trigger();
|
|
#else
|
|
mw_.menuBar()->setVisible(!mw_.menuBar()->isVisible());
|
|
if (!mw_.menuBar()->isVisible())
|
|
mw_.hideCursor();
|
|
#endif
|
|
}
|
|
|
|
void GambatteMenuHandler::videoBlitterFailure() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
QMessageBox::critical(&mw_, tr("Video engine failure"),
|
|
tr("Failed to update video output. This may be fixed by changing the video engine settings."));
|
|
videoDialog_->exec();
|
|
}
|
|
|
|
void GambatteMenuHandler::audioEngineFailure() {
|
|
TmpPauser tmpPauser(mw_, pauseInc_);
|
|
QMessageBox::critical(&mw_, tr("Sound engine failure"),
|
|
tr("Failed to output audio. This may be fixed by changing the sound settings."));
|
|
soundDialog_->exec();
|
|
}
|
|
|
|
void GambatteMenuHandler::toggleFullScreen() {
|
|
saveWindowSizeIfNotFullScreen();
|
|
mw_.toggleFullScreen();
|
|
}
|
|
|
|
void GambatteMenuHandler::saveWindowSizeIfNotFullScreen() {
|
|
if (!mw_.isFullScreen()) {
|
|
QSettings settings;
|
|
settings.setValue("mainwindow/size", mw_.size());
|
|
}
|
|
}
|