gambattemenuhandler.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895
  1. //
  2. // Copyright (C) 2007 by sinamas <sinamas at users.sourceforge.net>
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License version 2 as
  6. // published by the Free Software Foundation.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License version 2 for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // version 2 along with this program; if not, write to the
  15. // Free Software Foundation, Inc.,
  16. // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
  17. //
  18. #include "gambattemenuhandler.h"
  19. #include "blitterconf.h"
  20. #include "cheatdialog.h"
  21. #include "gambattesource.h"
  22. #include "mainwindow.h"
  23. #include "miscdialog.h"
  24. #include "palettedialog.h"
  25. #include "sounddialog.h"
  26. #include "videodialog.h"
  27. #include <QActionGroup>
  28. #include <QFileInfo>
  29. #include <QSettings>
  30. #include <QtGui>
  31. #include <iostream>
  32. namespace {
  33. static QString const strippedName(QString const &fullFileName) {
  34. return QFileInfo(fullFileName).fileName();
  35. }
  36. struct TmpPauser : private Uncopyable {
  37. MainWindow &mw;
  38. int const inc;
  39. explicit TmpPauser(MainWindow &mw, int inc = 4)
  40. : mw(mw), inc(inc)
  41. {
  42. mw.incPause(inc);
  43. }
  44. ~TmpPauser() {
  45. mw.decPause(inc);
  46. }
  47. };
  48. }
  49. FrameRateAdjuster::FrameTime::FrameTime(unsigned baseNum, unsigned baseDenom)
  50. : index_(num_steps)
  51. {
  52. setBaseFrameTime(baseNum, baseDenom);
  53. }
  54. void FrameRateAdjuster::FrameTime::setBaseFrameTime(unsigned const baseNum, unsigned const baseDenom) {
  55. frameTimes_[num_steps] = Rational(baseNum, baseDenom);
  56. unsigned const bnum = baseNum * 0x10000ul / baseDenom;
  57. for (std::size_t i = num_steps, num = bnum; i < num_steps * 2; ++i)
  58. frameTimes_[i + 1] = Rational(num = num * 11 / 10, 0x10000);
  59. for (std::size_t i = num_steps, num = bnum; i; --i)
  60. frameTimes_[i - 1] = Rational(num = num * 10 / 11, 0x10000);
  61. }
  62. FrameRateAdjuster::FrameRateAdjuster(MiscDialog const &miscDialog, MainWindow &mw, QObject *parent)
  63. : QObject(parent)
  64. , frameTime_(miscDialog.baseFps().height(), miscDialog.baseFps().width())
  65. , miscDialog_(miscDialog)
  66. , mw_(mw)
  67. , decFrameRateAction_(new QAction(tr("&Decrease Frame Rate"), &mw))
  68. , incFrameRateAction_(new QAction(tr("&Increase Frame Rate"), &mw))
  69. , resetFrameRateAction_(new QAction(tr("&Reset Frame Rate"), &mw))
  70. , enabled_(true)
  71. {
  72. decFrameRateAction_->setShortcut(QString("Ctrl+D"));
  73. incFrameRateAction_->setShortcut(QString("Ctrl+I"));
  74. resetFrameRateAction_->setShortcut(QString("Ctrl+U"));
  75. connect(decFrameRateAction_, SIGNAL(triggered()), this, SLOT(decFrameRate()));
  76. connect(incFrameRateAction_, SIGNAL(triggered()), this, SLOT(incFrameRate()));
  77. connect(resetFrameRateAction_, SIGNAL(triggered()), this, SLOT(resetFrameRate()));
  78. connect(&miscDialog, SIGNAL(accepted()), this, SLOT(miscDialogChange()));
  79. changed();
  80. }
  81. QList<QAction *> const FrameRateAdjuster::actions() {
  82. QList<QAction *> l;
  83. l.append(decFrameRateAction_);
  84. l.append(incFrameRateAction_);
  85. l.append(resetFrameRateAction_);
  86. return l;
  87. }
  88. void FrameRateAdjuster::miscDialogChange() {
  89. QSize const &baseFps = miscDialog_.baseFps();
  90. frameTime_.setBaseFrameTime(baseFps.height(), baseFps.width());
  91. changed();
  92. }
  93. void FrameRateAdjuster::setDisabled(bool disabled) {
  94. enabled_ = !disabled;
  95. changed();
  96. }
  97. void FrameRateAdjuster::decFrameRate() {
  98. if (enabled_) {
  99. frameTime_.inc();
  100. changed();
  101. }
  102. }
  103. void FrameRateAdjuster::incFrameRate() {
  104. if (enabled_) {
  105. frameTime_.dec();
  106. changed();
  107. }
  108. }
  109. void FrameRateAdjuster::resetFrameRate() {
  110. if (enabled_) {
  111. frameTime_.reset();
  112. changed();
  113. }
  114. }
  115. void FrameRateAdjuster::changed() {
  116. incFrameRateAction_->setEnabled(enabled_ && frameTime_.decPossible());
  117. decFrameRateAction_->setEnabled(enabled_ && frameTime_.incPossible());
  118. resetFrameRateAction_->setEnabled(enabled_ && frameTime_.resetPossible());
  119. FrameTime::Rational const &ft = enabled_ ? frameTime_.get() : frameTime_.base();
  120. mw_.setFrameTime(ft.num, ft.denom);
  121. }
  122. WindowSizeMenu::WindowSizeMenu(MainWindow &mw, VideoDialog const &vd)
  123. : mw_(mw)
  124. , menu_(new QMenu(tr("&Window Size"), &mw))
  125. , group_(new QActionGroup(menu_))
  126. , maxSize_(QApplication::desktop()->screen()->size())
  127. {
  128. fillMenu(vd.sourceSize(), vd.scalingMethod());
  129. setCheckedSize(QSettings().value("video/windowSize", QSize()).toSize());
  130. connect(group_, SIGNAL(triggered(QAction *)), this, SLOT(triggered()));
  131. QSize const &size = checkedSize();
  132. mw_.setWindowSize(size);
  133. if (size.isEmpty())
  134. mw_.resize(QSettings().value("mainwindow/size", QSize(160, 144)).toSize());
  135. }
  136. WindowSizeMenu::~WindowSizeMenu() {
  137. QSettings settings;
  138. settings.setValue("video/windowSize", checkedSize());
  139. }
  140. void WindowSizeMenu::videoDialogChange(VideoDialog const &vd) {
  141. QSize const &oldSize = checkedSize();
  142. disconnect(group_, SIGNAL(triggered(QAction *)), this, SLOT(triggered()));
  143. menu_->clear();
  144. delete group_;
  145. group_ = new QActionGroup(menu_);
  146. fillMenu(vd.sourceSize(), vd.scalingMethod());
  147. setCheckedSize(oldSize);
  148. connect(group_, SIGNAL(triggered(QAction *)), this, SLOT(triggered()));
  149. QSize const &newSize = checkedSize();
  150. if (newSize != oldSize)
  151. mw_.setWindowSize(newSize);
  152. }
  153. void WindowSizeMenu::triggered() {
  154. mw_.setWindowSize(checkedSize());
  155. }
  156. void WindowSizeMenu::fillMenu(QSize const &sourceSize, ScalingMethod const scalingMethod) {
  157. QSize const aspectRatio(160, 144);
  158. QSize const basesz(scalingMethod == scaling_integer ? sourceSize : aspectRatio);
  159. QSize sz(basesz);
  160. while (sz.width() <= maxSize_.width() && sz.height() <= maxSize_.height()) {
  161. if (sz.width() >= sourceSize.width() && sz.height() >= sourceSize.height()) {
  162. QAction *a = menu_->addAction(
  163. '&' + QString::number(sz.width()) + 'x' + QString::number(sz.height()));
  164. a->setData(sz);
  165. a->setCheckable(true);
  166. group_->addAction(a);
  167. }
  168. sz += basesz;
  169. }
  170. QAction *a = menu_->addAction(tr("&Variable"));
  171. a->setData(QSize());
  172. a->setCheckable(true);
  173. group_->addAction(a);
  174. }
  175. void WindowSizeMenu::setCheckedSize(QSize const &size) {
  176. QList<QAction *> const &actions = group_->actions();
  177. foreach (QAction *a, actions) {
  178. if (a->data() == size) {
  179. a->setChecked(true);
  180. return;
  181. }
  182. }
  183. if (!group_->checkedAction())
  184. actions.front()->setChecked(true);
  185. }
  186. QSize const WindowSizeMenu::checkedSize() const {
  187. return group_->checkedAction() ? group_->checkedAction()->data().toSize() : QSize();
  188. }
  189. static QString const settingsPath() {
  190. QString path = QSettings(QSettings::IniFormat, QSettings::UserScope,
  191. QCoreApplication::organizationName(), QCoreApplication::applicationName()).fileName();
  192. path.truncate(path.lastIndexOf('/'));
  193. return path;
  194. }
  195. static QString const toCmdString(QAction const *a) {
  196. QString text = a->text().toLower();
  197. text.replace('&', QString());
  198. text.replace(' ', '-');
  199. return text;
  200. }
  201. static char toCmdChar(QAction const *a) {
  202. return a->shortcut().count() == 1
  203. ? (a->shortcut()[0] - Qt::Key_A + 'a') & 0xff
  204. : 0;
  205. }
  206. static QAction * findCmdStringAction(QList<QAction *> const &l, QString const &cmdstr) {
  207. foreach (QAction *a, l) {
  208. if (cmdstr == toCmdString(a))
  209. return a;
  210. }
  211. return 0;
  212. }
  213. static QAction * findCmdCharAction(QList<QAction *> const &l, char const c) {
  214. foreach (QAction *a, l) {
  215. if (c == toCmdChar(a))
  216. return a;
  217. }
  218. return 0;
  219. }
  220. static void printUsage(char const *const arg0, QList<QAction *> const &actions) {
  221. std::cout << "Usage: " << arg0 << " [OPTION]... [romfile]\n";
  222. foreach (QAction const *const a, actions) {
  223. if (a->isCheckable() && a->isEnabled()) {
  224. std::string const &text = toCmdString(a).toStdString();
  225. char const c = toCmdChar(a);
  226. std::cout << " "
  227. << (c ? '-' + std::string(1, c) + ", " : std::string(" "))
  228. << "--" << text << "[=0]\n";
  229. }
  230. }
  231. }
  232. GambatteMenuHandler::GambatteMenuHandler(MainWindow &mw,
  233. GambatteSource &source,
  234. int const argc,
  235. char const *const argv[])
  236. : mw_(mw)
  237. , source_(source)
  238. , soundDialog_(new SoundDialog(mw, &mw))
  239. , videoDialog_(new VideoDialog(mw, source.generateVideoSourceInfos(),
  240. QString("Video filter:"), &mw))
  241. , miscDialog_(new MiscDialog(settingsPath() + "/saves", &mw))
  242. , cheatDialog_(new CheatDialog(settingsPath() + "/cheats.ini", &mw))
  243. , recentFileActs_()
  244. , pauseAction_()
  245. , syncFrameRateAction_()
  246. , gbaCgbAction_()
  247. , forceDmgAction_()
  248. , fsAct_()
  249. , recentMenu_()
  250. , globalPaletteDialog_()
  251. , romPaletteDialog_()
  252. , stateSlotGroup_(new QActionGroup(&mw))
  253. , windowSizeMenu_(mw, *videoDialog_)
  254. , pauseInc_(4)
  255. {
  256. mw.setWindowTitle("Gambatte");
  257. source.inputDialog()->setParent(&mw, source.inputDialog()->windowFlags());
  258. {
  259. QString const &settingspath = settingsPath();
  260. QString const &palpath = settingspath + "/palettes";
  261. QDir::root().mkpath(settingspath + "/saves");
  262. QDir::root().mkpath(palpath);
  263. globalPaletteDialog_ = new PaletteDialog(palpath, 0, &mw);
  264. romPaletteDialog_ = new PaletteDialog(palpath, globalPaletteDialog_, &mw);
  265. connect(globalPaletteDialog_, SIGNAL(accepted()), this, SLOT(globalPaletteChange()));
  266. connect(romPaletteDialog_, SIGNAL(accepted()), this, SLOT(romPaletteChange()));
  267. }
  268. QActionGroup *const romLoadedActions = new QActionGroup(&mw);
  269. romLoadedActions->setExclusive(false);
  270. {
  271. for (int i = 0; i < max_recent_files; ++i) {
  272. recentFileActs_[i] = new QAction(&mw);
  273. recentFileActs_[i]->setVisible(false);
  274. connect(recentFileActs_[i], SIGNAL(triggered()), this, SLOT(openRecentFile()));
  275. }
  276. QMenu *fileMenu = mw.menuBar()->addMenu(tr("&File"));
  277. fileMenu->addAction(tr("&Open..."), this, SLOT(open()), tr("Ctrl+O"));
  278. recentMenu_ = fileMenu->addMenu(tr("Open Re&cent"));
  279. for (int i = 0; i < max_recent_files; ++i)
  280. recentMenu_->addAction(recentFileActs_[i]);
  281. fileMenu->addSeparator();
  282. romLoadedActions->addAction(fileMenu->addAction(tr("&Reset"), this, SLOT(reset()), tr("Ctrl+R")));
  283. fileMenu->addSeparator();
  284. romLoadedActions->addAction(fileMenu->addAction(tr("Save State &As..."), this, SLOT(saveStateAs())));
  285. romLoadedActions->addAction(fileMenu->addAction(tr("Load State &From..."), this, SLOT(loadStateFrom())));
  286. fileMenu->addSeparator();
  287. romLoadedActions->addAction(fileMenu->addAction(tr("&Save State"),
  288. this, SLOT(saveState()), QString("Ctrl+S")));
  289. romLoadedActions->addAction(fileMenu->addAction(tr("&Load State"),
  290. this, SLOT(loadState()), QString("Ctrl+L")));
  291. {
  292. QMenu *const stateSlotMenu = fileMenu->addMenu(tr("S&elect State Slot"));
  293. stateSlotMenu->setEnabled(false);
  294. stateSlotMenu->addAction(tr("&Previous"), this, SLOT(prevStateSlot()), QString("Ctrl+Z"));
  295. stateSlotMenu->addAction(tr("&Next"), this, SLOT(nextStateSlot()), QString("Ctrl+X"));
  296. stateSlotMenu->addSeparator();
  297. for (int i = 0; i < 10; ++i) {
  298. int const no = i == 9 ? 0 : i + 1;
  299. QString const &strno = QString::number(no);
  300. QAction *action = stateSlotMenu->addAction(
  301. "Slot &" + strno, this, SLOT(selectStateSlot()), strno);
  302. action->setCheckable(true);
  303. action->setData(no);
  304. stateSlotGroup_->addAction(action);
  305. }
  306. connect(this, SIGNAL(romLoaded(bool)), stateSlotMenu, SLOT(setEnabled(bool)));
  307. }
  308. fileMenu->addSeparator();
  309. fileMenu->addAction(tr("&Quit"), qApp, SLOT(closeAllWindows()), tr("Ctrl+Q"));
  310. updateRecentFileActions();
  311. }
  312. FrameRateAdjuster *const frameRateAdjuster = new FrameRateAdjuster(*miscDialog_, mw, this);
  313. QList<QAction *> cmdactions;
  314. {
  315. QMenu *const playm = mw.menuBar()->addMenu(tr("&Play"));
  316. romLoadedActions->addAction(pauseAction_ = playm->addAction(
  317. tr("&Pause"), this, SLOT(pauseChange()), QString("Ctrl+P")));
  318. pauseAction_->setCheckable(true);
  319. romLoadedActions->addAction(playm->addAction(tr("Frame &Step"),
  320. this, SLOT(frameStep()), QString("Ctrl+.")));
  321. playm->addSeparator();
  322. syncFrameRateAction_ = playm->addAction(tr("&Sync Frame Rate to Refresh Rate"));
  323. syncFrameRateAction_->setCheckable(true);
  324. connect(syncFrameRateAction_, SIGNAL(triggered(bool)),
  325. frameRateAdjuster, SLOT(setDisabled(bool)));
  326. connect(syncFrameRateAction_, SIGNAL(triggered(bool)),
  327. &mw, SLOT(setSyncToRefreshRate(bool)));
  328. foreach (QAction *action, frameRateAdjuster->actions())
  329. playm->addAction(romLoadedActions->addAction(action));
  330. cmdactions += playm->actions();
  331. }
  332. QMenu *const settingsm = mw.menuBar()->addMenu(tr("&Settings"));
  333. settingsm->addAction(tr("&Input..."), this, SLOT(execInputDialog()));
  334. settingsm->addAction(tr("&Miscellaneous..."), this, SLOT(execMiscDialog()));
  335. settingsm->addAction(tr("&Sound..."), this, SLOT(execSoundDialog()));
  336. settingsm->addAction(tr("&Video..."), this, SLOT(execVideoDialog()));
  337. settingsm->addSeparator();
  338. settingsm->addMenu(windowSizeMenu_.menu());
  339. settingsm->addSeparator();
  340. gbaCgbAction_ = settingsm->addAction(tr("GB&A CGB Mode"));
  341. gbaCgbAction_->setCheckable(true);
  342. gbaCgbAction_->setChecked(QSettings().value("gbacgb", false).toBool());
  343. forceDmgAction_ = settingsm->addAction(tr("Force &DMG Mode"));
  344. forceDmgAction_->setCheckable(true);
  345. {
  346. QMenu *const palm = settingsm->addMenu(tr("DMG &Palette"));
  347. palm->addAction(tr("&Global..."), this, SLOT(execGlobalPaletteDialog()));
  348. QAction *romPaletteAct = palm->addAction(tr("Current &ROM..."), this, SLOT(execRomPaletteDialog()));
  349. romPaletteAct->setEnabled(false);
  350. connect(this, SIGNAL(dmgRomLoaded(bool)), romPaletteAct, SLOT(setEnabled(bool)));
  351. }
  352. settingsm->addSeparator();
  353. fsAct_ = settingsm->addAction(tr("&Full Screen"), this, SLOT(toggleFullScreen()), tr("Ctrl+F"));
  354. fsAct_->setCheckable(true);
  355. cmdactions += settingsm->actions();
  356. romLoadedActions->addAction(mw.menuBar()->addMenu(tr("&Tools"))->addAction(tr("&Cheats..."),
  357. cheatDialog_, SLOT(exec())));
  358. romLoadedActions->setEnabled(false);
  359. mw.menuBar()->addSeparator();
  360. QMenu *const helpMenu = mw.menuBar()->addMenu(tr("&Help"));
  361. helpMenu->addAction(tr("&About"), this, SLOT(about()));
  362. mw.addActions(mw.menuBar()->actions());
  363. {
  364. QAction *escAct = new QAction(&mw);
  365. escAct->setShortcut(tr("Esc"));
  366. connect(escAct, SIGNAL(triggered()), this, SLOT(escPressed()));
  367. mw.addAction(escAct);
  368. }
  369. mw.setSamplesPerFrame(35112);
  370. connect(&source, SIGNAL(setTurbo(bool)), &mw, SLOT(setFastForward(bool)));
  371. connect(&source, SIGNAL(togglePause()), pauseAction_, SLOT(trigger()));
  372. connect(&source, SIGNAL(frameStep()), this, SLOT(frameStep()));
  373. connect(&source, SIGNAL(decFrameRate()), frameRateAdjuster, SLOT(decFrameRate()));
  374. connect(&source, SIGNAL(incFrameRate()), frameRateAdjuster, SLOT(incFrameRate()));
  375. connect(&source, SIGNAL(resetFrameRate()), frameRateAdjuster, SLOT(resetFrameRate()));
  376. connect(&source, SIGNAL(prevStateSlot()), this, SLOT(prevStateSlot()));
  377. connect(&source, SIGNAL(nextStateSlot()), this, SLOT(nextStateSlot()));
  378. connect(&source, SIGNAL(saveStateSignal()), this, SLOT(saveState()));
  379. connect(&source, SIGNAL(loadStateSignal()), this, SLOT(loadState()));
  380. connect(&source, SIGNAL(quit()), qApp, SLOT(closeAllWindows()));
  381. connect(videoDialog_, SIGNAL(accepted()), this, SLOT(videoDialogChange()));
  382. connect(soundDialog_, SIGNAL(accepted()), this, SLOT(soundDialogChange()));
  383. connect(miscDialog_, SIGNAL(accepted()), this, SLOT(miscDialogChange()));
  384. connect(cheatDialog_, SIGNAL(accepted()), this, SLOT(cheatDialogChange()));
  385. connect(&mw, SIGNAL(videoBlitterFailure()), this, SLOT(videoBlitterFailure()));
  386. connect(&mw, SIGNAL(audioEngineFailure()), this, SLOT(audioEngineFailure()));
  387. connect(&mw, SIGNAL(closing()), this, SLOT(saveWindowSizeIfNotFullScreen()));
  388. connect(&mw, SIGNAL(dwmCompositionChange()), this, SLOT(reconsiderSyncFrameRateActionEnable()));
  389. connect(this, SIGNAL(romLoaded(bool)), romLoadedActions, SLOT(setEnabled(bool)));
  390. connect(this, SIGNAL(romLoaded(bool)), stateSlotGroup_->actions().at(0), SLOT(setChecked(bool)));
  391. mw.setAspectRatio(QSize(160, 144));
  392. videoDialogChange();
  393. soundDialogChange();
  394. miscDialogChange();
  395. bool unknownCmd = false;
  396. for (int i = 1; i < argc; ++i) {
  397. if (argv[i][0] == '-' && argv[i][1]) {
  398. QString const argstr(argv[i] + 2);
  399. if (QAction *a = argv[i][1] == '-'
  400. ? findCmdStringAction(cmdactions, argstr.left(argstr.lastIndexOf('=')))
  401. : findCmdCharAction(cmdactions, argv[i][1])) {
  402. if (argstr.endsWith("=0") == a->isChecked() && a->isEnabled())
  403. a->trigger();
  404. } else
  405. unknownCmd = true;
  406. }
  407. }
  408. if (unknownCmd)
  409. printUsage(argv[0], cmdactions);
  410. for (int i = 1; i < argc; ++i) {
  411. if (argv[i][0] != '-') {
  412. if (fsAct_->isChecked())
  413. mw.menuBar()->hide();
  414. loadFile(QFileInfo(QString(argv[i])).absoluteFilePath());
  415. break;
  416. }
  417. }
  418. }
  419. GambatteMenuHandler::~GambatteMenuHandler() {
  420. QSettings settings;
  421. settings.setValue("gbacgb", gbaCgbAction_->isChecked());
  422. }
  423. void GambatteMenuHandler::updateRecentFileActions() {
  424. QSettings settings;
  425. QStringList files = settings.value("recentFileList").toStringList();
  426. int const numRecentFiles = qMin(files.size(), static_cast<int>(max_recent_files));
  427. for (int i = 0; i < numRecentFiles; ++i) {
  428. QString const &text = tr("&%1 %2").arg(i + 1).arg(strippedName(files[i]));
  429. recentFileActs_[i]->setText(text);
  430. recentFileActs_[i]->setData(files[i]);
  431. recentFileActs_[i]->setVisible(true);
  432. }
  433. for (int j = numRecentFiles; j < max_recent_files; ++j)
  434. recentFileActs_[j]->setVisible(false);
  435. recentMenu_->setEnabled(numRecentFiles > 0);
  436. }
  437. void GambatteMenuHandler::setCurrentFile(QString const &fileName) {
  438. QSettings settings;
  439. QStringList files = settings.value("recentFileList").toStringList();
  440. files.removeAll(fileName);
  441. files.prepend(fileName);
  442. while (files.size() > max_recent_files)
  443. files.removeLast();
  444. settings.setValue("recentFileList", files);
  445. updateRecentFileActions();
  446. }
  447. void GambatteMenuHandler::loadFile(QString const &fileName) {
  448. TmpPauser tmpPauser(mw_, 4);
  449. pauseAction_->setChecked(false);
  450. pauseChange();
  451. mw_.waitUntilPaused();
  452. if (gambatte::LoadRes const error =
  453. source_.load(fileName.toLocal8Bit().constData(),
  454. gbaCgbAction_->isChecked() * gambatte::GB::GBA_CGB
  455. + forceDmgAction_->isChecked() * gambatte::GB::FORCE_DMG
  456. + miscDialog_->multicartCompat() * gambatte::GB::MULTICART_COMPAT)) {
  457. mw_.stop();
  458. emit dmgRomLoaded(false);
  459. emit romLoaded(false);
  460. QMessageBox::critical(
  461. &mw_,
  462. tr("File Load Error"),
  463. (tr("Failed to load file\n")
  464. + fileName + ".\n\n"
  465. + gambatte::to_string(error).c_str() + '.'));
  466. return;
  467. }
  468. QString const &romTitle = QString::fromStdString(source_.romTitle()).trimmed();
  469. cheatDialog_->setGameName(romTitle.isEmpty() ? QFileInfo(fileName).completeBaseName() : romTitle);
  470. cheatDialogChange();
  471. if (!source_.isCgb()) {
  472. romPaletteDialog_->setSettingsFile(
  473. QFileInfo(fileName).completeBaseName() + ".pal",
  474. romTitle);
  475. setDmgPaletteColors();
  476. }
  477. gambatte::PakInfo const &pak = source_.pakInfo();
  478. std::cout << romTitle.toStdString() << '\n'
  479. << "GamePak type: " << pak.mbc()
  480. << " rambanks: " << pak.rambanks()
  481. << " rombanks: " << pak.rombanks() << '\n'
  482. << "header checksum: " << (pak.headerChecksumOk() ? "ok" : "bad") << '\n'
  483. << "cgb: " << source_.isCgb() << std::endl;
  484. mw_.setWindowTitle(strippedName(fileName) + (pak.headerChecksumOk() ? "" : " [bad]") + " - Gambatte");
  485. setCurrentFile(fileName);
  486. emit romLoaded(true);
  487. emit dmgRomLoaded(!source_.isCgb());
  488. mw_.resetAudio();
  489. mw_.run();
  490. }
  491. void GambatteMenuHandler::open() {
  492. TmpPauser tmpPauser(mw_, 4);
  493. mw_.waitUntilPaused();
  494. QString const &fileName = QFileDialog::getOpenFileName(
  495. &mw_, tr("Open"), recentFileActs_[0]->data().toString(),
  496. tr("Game Boy ROM Images (*.dmg *.gb *.gbc *.sgb *.zip *.gz);;All Files (*)"));
  497. if (!fileName.isEmpty())
  498. loadFile(fileName);
  499. // giving back focus after getOpenFileName seems to fail at times, which
  500. // can be problematic with current exclusive mode handling.
  501. mw_.setFocus();
  502. }
  503. void GambatteMenuHandler::openRecentFile() {
  504. if (QAction const *action = qobject_cast<QAction *>(sender()))
  505. loadFile(action->data().toString());
  506. }
  507. void GambatteMenuHandler::about() {
  508. TmpPauser tmpPauser(mw_, pauseInc_);
  509. QMessageBox::about(
  510. &mw_,
  511. "About Gambatte",
  512. "<h3>Gambatte Qt"
  513. #ifdef GAMBATTE_QT_VERSION_STR
  514. " (" GAMBATTE_QT_VERSION_STR ")"
  515. #endif
  516. "</h3>"
  517. "<p>"
  518. "<b>Homepage:</b> "
  519. "<a href=\"https://github.com/sinamas/gambatte\">"
  520. "https://github.com/sinamas/gambatte"
  521. "</a>"
  522. "</p>"
  523. "<p>"
  524. "A portable, open-source Game Boy Color emulator."
  525. "</p>"
  526. );
  527. }
  528. void GambatteMenuHandler::globalPaletteChange() {
  529. romPaletteDialog_->externalChange();
  530. setDmgPaletteColors();
  531. }
  532. void GambatteMenuHandler::romPaletteChange() {
  533. globalPaletteDialog_->externalChange();
  534. setDmgPaletteColors();
  535. }
  536. namespace {
  537. struct SetDmgPaletteColorFun {
  538. GambatteSource &source; int palnum; int colornum; unsigned long rgb32;
  539. void operator()() const { source.setDmgPaletteColor(palnum, colornum, rgb32); }
  540. };
  541. struct SetVideoSourceFun {
  542. GambatteSource &source; std::size_t sourceIndex;
  543. void operator()() const { source.setVideoSource(sourceIndex); }
  544. };
  545. struct SetSaveDirFun {
  546. GambatteSource &source; QString path;
  547. void operator()() const { source.setSavedir(path.toLocal8Bit().constData()); }
  548. };
  549. } // anon ns
  550. void GambatteMenuHandler::setDmgPaletteColors() {
  551. for (int palnum = 0; palnum < 3; ++palnum)
  552. for (int colornum = 0; colornum < 4; ++colornum) {
  553. SetDmgPaletteColorFun fun = { source_, palnum, colornum,
  554. romPaletteDialog_->color(palnum, colornum) };
  555. mw_.callInWorkerThread(fun);
  556. }
  557. }
  558. void GambatteMenuHandler::videoDialogChange() {
  559. {
  560. SetVideoSourceFun fun = { source_, videoDialog_->sourceIndex() };
  561. mw_.callInWorkerThread(fun);
  562. }
  563. applySettings(mw_, *videoDialog_);
  564. windowSizeMenu_.videoDialogChange(*videoDialog_);
  565. reconsiderSyncFrameRateActionEnable();
  566. }
  567. void GambatteMenuHandler::soundDialogChange() {
  568. applySettings(mw_, *soundDialog_);
  569. }
  570. void GambatteMenuHandler::miscDialogChange() {
  571. SetSaveDirFun const setSaveDirFun = { source_, miscDialog_->savePath() };
  572. mw_.callInWorkerThread(setSaveDirFun);
  573. mw_.setDwmTripleBuffer(miscDialog_->dwmTripleBuf());
  574. mw_.setFastForwardSpeed(miscDialog_->turboSpeed());
  575. mw_.setPauseOnFocusOut(miscDialog_->pauseOnFocusOut() ? 2 : 0);
  576. pauseInc_ = miscDialog_->pauseOnDialogs() ? 4 : 0;
  577. }
  578. void GambatteMenuHandler::cheatDialogChange() {
  579. std::string gameGenieCodes;
  580. std::string gameSharkCodes;
  581. foreach (QString const &s, cheatDialog_->cheats().split(';', QString::SkipEmptyParts)) {
  582. if (s.contains('-')) {
  583. gameGenieCodes += s.toStdString() + ';';
  584. } else
  585. gameSharkCodes += s.toStdString() + ';';
  586. }
  587. source_.setGameGenie(gameGenieCodes);
  588. source_.setGameShark(gameSharkCodes);
  589. }
  590. void GambatteMenuHandler::reconsiderSyncFrameRateActionEnable() {
  591. if (mw_.blitterConf(videoDialog_->blitterNo()).maxSwapInterval()
  592. && !MainWindow::isDwmCompositionEnabled()) {
  593. syncFrameRateAction_->setEnabled(true);
  594. } else {
  595. if (syncFrameRateAction_->isChecked())
  596. syncFrameRateAction_->trigger();
  597. syncFrameRateAction_->setEnabled(false);
  598. }
  599. }
  600. void GambatteMenuHandler::execGlobalPaletteDialog() {
  601. TmpPauser tmpPauser(mw_, pauseInc_);
  602. globalPaletteDialog_->exec();
  603. }
  604. void GambatteMenuHandler::execRomPaletteDialog() {
  605. TmpPauser tmpPauser(mw_, pauseInc_);
  606. romPaletteDialog_->exec();
  607. }
  608. void GambatteMenuHandler::execInputDialog() {
  609. TmpPauser tmpPauser(mw_, pauseInc_);
  610. source_.inputDialog()->exec();
  611. }
  612. void GambatteMenuHandler::execSoundDialog() {
  613. TmpPauser tmpPauser(mw_, pauseInc_);
  614. soundDialog_->exec();
  615. }
  616. void GambatteMenuHandler::execVideoDialog() {
  617. TmpPauser tmpPauser(mw_, pauseInc_);
  618. videoDialog_->exec();
  619. }
  620. void GambatteMenuHandler::execMiscDialog() {
  621. TmpPauser tmpPauser(mw_, pauseInc_);
  622. miscDialog_->exec();
  623. }
  624. void GambatteMenuHandler::prevStateSlot() {
  625. stateSlotGroup_->actions().at(source_.currentState() < 2
  626. ? source_.currentState() + 8
  627. : source_.currentState() - 2)->trigger();
  628. }
  629. void GambatteMenuHandler::nextStateSlot() {
  630. stateSlotGroup_->actions().at(source_.currentState())->trigger();
  631. }
  632. namespace {
  633. struct SelectStateFun {
  634. GambatteSource &source; int i;
  635. void operator()() const { source.selectState(i); }
  636. };
  637. struct SaveStateFun {
  638. GambatteSource &source;
  639. MainWindow::FrameBuffer fb;
  640. void operator()() const {
  641. source.saveState(MainWindow::FrameBuffer::Locked(fb).get());
  642. }
  643. };
  644. struct LoadStateFun {
  645. GambatteSource &source;
  646. void operator()() const { source.loadState(); }
  647. };
  648. struct SaveStateAsFun {
  649. GambatteSource &source;
  650. MainWindow::FrameBuffer fb;
  651. QString fileName;
  652. void operator()() const {
  653. source.saveState(MainWindow::FrameBuffer::Locked(fb).get(),
  654. fileName.toLocal8Bit().constData());
  655. }
  656. };
  657. struct LoadStateFromFun {
  658. GambatteSource &source;
  659. QString fileName;
  660. void operator()() const {
  661. source.loadState(fileName.toLocal8Bit().constData());
  662. }
  663. };
  664. struct ResetFun {
  665. GambatteSource &source;
  666. void operator()() const { source.reset(); }
  667. };
  668. } // anon ns
  669. void GambatteMenuHandler::selectStateSlot() {
  670. if (QAction *action = stateSlotGroup_->checkedAction()) {
  671. SelectStateFun fun = { source_, action->data().toInt() };
  672. mw_.callInWorkerThread(fun);
  673. }
  674. }
  675. void GambatteMenuHandler::saveState() {
  676. SaveStateFun fun = { source_, MainWindow::FrameBuffer(mw_) };
  677. mw_.callInWorkerThread(fun);
  678. }
  679. void GambatteMenuHandler::loadState() {
  680. LoadStateFun fun = { source_ };
  681. mw_.callInWorkerThread(fun);
  682. }
  683. void GambatteMenuHandler::saveStateAs() {
  684. TmpPauser tmpPauser(mw_, 4);
  685. mw_.waitUntilPaused();
  686. QString const &fileName = QFileDialog::getSaveFileName(
  687. &mw_, tr("Save State"), QString(),
  688. tr("Gambatte Quick Save Files (*.gqs);;All Files (*)"));
  689. if (!fileName.isEmpty()) {
  690. SaveStateAsFun fun = { source_, MainWindow::FrameBuffer(mw_), fileName };
  691. mw_.callInWorkerThread(fun);
  692. }
  693. }
  694. void GambatteMenuHandler::loadStateFrom() {
  695. TmpPauser tmpPauser(mw_, 4);
  696. mw_.waitUntilPaused();
  697. QString const &fileName = QFileDialog::getOpenFileName(
  698. &mw_, tr("Load State"), QString(),
  699. tr("Gambatte Quick Save Files (*.gqs);;All Files (*)"));
  700. if (!fileName.isEmpty()) {
  701. LoadStateFromFun fun = { source_, fileName };
  702. mw_.callInWorkerThread(fun);
  703. }
  704. }
  705. void GambatteMenuHandler::reset() {
  706. ResetFun fun = { source_ };
  707. mw_.callInWorkerThread(fun);
  708. }
  709. void GambatteMenuHandler::pauseChange() {
  710. if (pauseAction_->isChecked())
  711. mw_.pause();
  712. else
  713. mw_.unpause();
  714. }
  715. void GambatteMenuHandler::frameStep() {
  716. if (pauseAction_->isChecked())
  717. mw_.frameStep();
  718. else
  719. pauseAction_->trigger();
  720. }
  721. void GambatteMenuHandler::escPressed() {
  722. #ifdef Q_WS_MAC
  723. if (fsAct_->isChecked())
  724. fsAct_->trigger();
  725. #else
  726. mw_.menuBar()->setVisible(!mw_.menuBar()->isVisible());
  727. if (!mw_.menuBar()->isVisible())
  728. mw_.hideCursor();
  729. #endif
  730. }
  731. void GambatteMenuHandler::videoBlitterFailure() {
  732. TmpPauser tmpPauser(mw_, pauseInc_);
  733. QMessageBox::critical(&mw_, tr("Video engine failure"),
  734. tr("Failed to update video output. This may be fixed by changing the video engine settings."));
  735. videoDialog_->exec();
  736. }
  737. void GambatteMenuHandler::audioEngineFailure() {
  738. TmpPauser tmpPauser(mw_, pauseInc_);
  739. QMessageBox::critical(&mw_, tr("Sound engine failure"),
  740. tr("Failed to output audio. This may be fixed by changing the sound settings."));
  741. soundDialog_->exec();
  742. }
  743. void GambatteMenuHandler::toggleFullScreen() {
  744. saveWindowSizeIfNotFullScreen();
  745. mw_.toggleFullScreen();
  746. }
  747. void GambatteMenuHandler::saveWindowSizeIfNotFullScreen() {
  748. if (!mw_.isFullScreen()) {
  749. QSettings settings;
  750. settings.setValue("mainwindow/size", mw_.size());
  751. }
  752. }