1 // Qt Desktop UI: works on Linux, Windows and Mac OSX
2 #include "ppsspp_config.h"
3 #include "mainwindow.h"
4
5 #include <QApplication>
6 #include <QDesktopServices>
7 #include <QDesktopWidget>
8 #include <QFileDialog>
9 #include <QMessageBox>
10
11 #include "Common/System/Display.h"
12 #include "Common/System/NativeApp.h"
13 #include "Common/System/System.h"
14 #include "Common/File/Path.h"
15 #include "Core/MIPS/MIPSDebugInterface.h"
16 #include "Core/Debugger/SymbolMap.h"
17 #include "Core/HLE/sceUmd.h"
18 #include "Core/SaveState.h"
19 #include "Core/System.h"
20 #include "GPU/GPUInterface.h"
21 #include "UI/GamepadEmu.h"
22
MainWindow(QWidget * parent,bool fullscreen)23 MainWindow::MainWindow(QWidget *parent, bool fullscreen) :
24 QMainWindow(parent),
25 currentLanguage("en"),
26 nextState(CORE_POWERDOWN),
27 lastUIState(UISTATE_MENU)
28 {
29 setWindowIcon(QIcon(qApp->applicationDirPath() + "/assets/icon_regular_72.png"));
30
31 SetGameTitle("");
32 emugl = new MainUI(this);
33
34 setCentralWidget(emugl);
35 createMenus();
36 updateMenus();
37
38 SetFullScreen(fullscreen);
39
40 QObject::connect(emugl, SIGNAL(doubleClick()), this, SLOT(fullscrAct()));
41 QObject::connect(emugl, SIGNAL(newFrame()), this, SLOT(newFrame()));
42 }
43
clamp1(float x)44 inline float clamp1(float x) {
45 if (x > 1.0f) return 1.0f;
46 if (x < -1.0f) return -1.0f;
47 return x;
48 }
49
newFrame()50 void MainWindow::newFrame()
51 {
52 if (lastUIState != GetUIState()) {
53 lastUIState = GetUIState();
54 if (lastUIState == UISTATE_INGAME && g_Config.bFullScreen && !QApplication::overrideCursor() && !g_Config.bShowTouchControls)
55 QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
56 if (lastUIState != UISTATE_INGAME && g_Config.bFullScreen && QApplication::overrideCursor())
57 QApplication::restoreOverrideCursor();
58
59 updateMenus();
60 }
61
62 if (g_Config.bFullScreen != isFullScreen())
63 SetFullScreen(g_Config.bFullScreen);
64
65 std::unique_lock<std::mutex> lock(msgMutex_);
66 while (!msgQueue_.empty()) {
67 MainWindowMsg msg = msgQueue_.front();
68 msgQueue_.pop();
69 switch (msg) {
70 case MainWindowMsg::BOOT_DONE:
71 bootDone();
72 break;
73 case MainWindowMsg::WINDOW_TITLE_CHANGED:
74 std::unique_lock<std::mutex> lock(titleMutex_);
75 setWindowTitle(QString::fromUtf8(newWindowTitle_.c_str()));
76 break;
77 }
78 }
79 }
80
updateMenuGroupInt(QActionGroup * group,int value)81 void MainWindow::updateMenuGroupInt(QActionGroup *group, int value) {
82 foreach (QAction *action, group->actions()) {
83 action->setChecked(action->data().toInt() == value);
84 }
85 }
86
updateMenus()87 void MainWindow::updateMenus()
88 {
89 updateMenuGroupInt(saveStateGroup, g_Config.iCurrentStateSlot);
90 updateMenuGroupInt(displayRotationGroup, g_Config.iInternalScreenRotation);
91 updateMenuGroupInt(renderingResolutionGroup, g_Config.iInternalResolution);
92 updateMenuGroupInt(renderingModeGroup, g_Config.iRenderingMode);
93 updateMenuGroupInt(frameSkippingGroup, g_Config.iFrameSkip);
94 updateMenuGroupInt(frameSkippingTypeGroup, g_Config.iFrameSkipType);
95 updateMenuGroupInt(textureFilteringGroup, g_Config.iTexFiltering);
96 updateMenuGroupInt(screenScalingFilterGroup, g_Config.iBufFilter);
97 updateMenuGroupInt(textureScalingLevelGroup, g_Config.iTexScalingLevel);
98 updateMenuGroupInt(textureScalingTypeGroup, g_Config.iTexScalingType);
99
100 foreach(QAction * action, windowGroup->actions()) {
101 int width = (g_Config.IsPortrait() ? 272 : 480) * action->data().toInt();
102 int height = (g_Config.IsPortrait() ? 480 : 272) * action->data().toInt();
103 if (g_Config.iWindowWidth == width && g_Config.iWindowHeight == height) {
104 action->setChecked(true);
105 break;
106 }
107 }
108 emit updateMenu();
109 }
110
bootDone()111 void MainWindow::bootDone()
112 {
113 if (nextState == CORE_RUNNING)
114 runAct();
115 updateMenus();
116 }
117
118 /* SIGNALS */
loadAct()119 void MainWindow::loadAct()
120 {
121 QString filename = QFileDialog::getOpenFileName(NULL, "Load File", g_Config.currentDirectory.c_str(), "PSP ROMs (*.pbp *.elf *.iso *.cso *.prx)");
122 if (QFile::exists(filename))
123 {
124 QFileInfo info(filename);
125 g_Config.currentDirectory = Path(info.absolutePath().toStdString());
126 NativeMessageReceived("boot", filename.toStdString().c_str());
127 }
128 }
129
closeAct()130 void MainWindow::closeAct()
131 {
132 updateMenus();
133
134 NativeMessageReceived("stop", "");
135 SetGameTitle("");
136 }
137
openmsAct()138 void MainWindow::openmsAct()
139 {
140 QString confighome = getenv("XDG_CONFIG_HOME");
141 QString memorystick = confighome + "/ppsspp";
142 QDesktopServices::openUrl(QUrl(memorystick));
143 }
144
SaveStateActionFinished(SaveState::Status status,const std::string & message,void * userdata)145 void SaveStateActionFinished(SaveState::Status status, const std::string &message, void *userdata)
146 {
147 // TODO: Improve messaging?
148 if (status == SaveState::Status::FAILURE)
149 {
150 QMessageBox msgBox;
151 msgBox.setWindowTitle("Load Save State");
152 msgBox.setText("Savestate failure. Please try again later");
153 msgBox.exec();
154 return;
155 }
156 }
157
qlstateAct()158 void MainWindow::qlstateAct()
159 {
160 Path gamePath = PSP_CoreParameter().fileToStart;
161 SaveState::LoadSlot(gamePath, 0, SaveStateActionFinished, this);
162 }
163
qsstateAct()164 void MainWindow::qsstateAct()
165 {
166 Path gamePath = PSP_CoreParameter().fileToStart;
167 SaveState::SaveSlot(gamePath, 0, SaveStateActionFinished, this);
168 }
169
lstateAct()170 void MainWindow::lstateAct()
171 {
172 QFileDialog dialog(0,"Load state");
173 dialog.setFileMode(QFileDialog::ExistingFile);
174 QStringList filters;
175 filters << "Save States (*.ppst)" << "|All files (*.*)";
176 dialog.setNameFilters(filters);
177 dialog.setAcceptMode(QFileDialog::AcceptOpen);
178 if (dialog.exec())
179 {
180 QStringList fileNames = dialog.selectedFiles();
181 SaveState::Load(Path(fileNames[0].toStdString()), -1, SaveStateActionFinished, this);
182 }
183 }
184
sstateAct()185 void MainWindow::sstateAct()
186 {
187 QFileDialog dialog(0,"Save state");
188 dialog.setFileMode(QFileDialog::AnyFile);
189 dialog.setAcceptMode(QFileDialog::AcceptSave);
190 QStringList filters;
191 filters << "Save States (*.ppst)" << "|All files (*.*)";
192 dialog.setNameFilters(filters);
193 if (dialog.exec())
194 {
195 QStringList fileNames = dialog.selectedFiles();
196 SaveState::Save(Path(fileNames[0].toStdString()), -1, SaveStateActionFinished, this);
197 }
198 }
199
recordDisplayAct()200 void MainWindow::recordDisplayAct()
201 {
202 g_Config.bDumpFrames = !g_Config.bDumpFrames;
203 }
204
useLosslessVideoCodecAct()205 void MainWindow::useLosslessVideoCodecAct()
206 {
207 g_Config.bUseFFV1 = !g_Config.bUseFFV1;
208 }
209
useOutputBufferAct()210 void MainWindow::useOutputBufferAct()
211 {
212 g_Config.bDumpVideoOutput = !g_Config.bDumpVideoOutput;
213 }
214
recordAudioAct()215 void MainWindow::recordAudioAct()
216 {
217 g_Config.bDumpAudio = !g_Config.bDumpAudio;
218 }
219
exitAct()220 void MainWindow::exitAct()
221 {
222 closeAct();
223 QApplication::exit(0);
224 }
225
runAct()226 void MainWindow::runAct()
227 {
228 NativeMessageReceived("run", "");
229 }
230
pauseAct()231 void MainWindow::pauseAct()
232 {
233 NativeMessageReceived("pause", "");
234 }
235
stopAct()236 void MainWindow::stopAct()
237 {
238 Core_Stop();
239 NativeMessageReceived("stop", "");
240 }
241
resetAct()242 void MainWindow::resetAct()
243 {
244 updateMenus();
245
246 NativeMessageReceived("reset", "");
247 }
248
switchUMDAct()249 void MainWindow::switchUMDAct()
250 {
251 QString filename = QFileDialog::getOpenFileName(NULL, "Switch UMD", g_Config.currentDirectory.c_str(), "PSP ROMs (*.pbp *.elf *.iso *.cso *.prx)");
252 if (QFile::exists(filename))
253 {
254 QFileInfo info(filename);
255 g_Config.currentDirectory = Path(info.absolutePath().toStdString());
256 __UmdReplace(Path(filename.toStdString()));
257 }
258 }
259
breakonloadAct()260 void MainWindow::breakonloadAct()
261 {
262 g_Config.bAutoRun = !g_Config.bAutoRun;
263 }
264
lmapAct()265 void MainWindow::lmapAct()
266 {
267 QFileDialog dialog(0,"Load .MAP");
268 dialog.setFileMode(QFileDialog::ExistingFile);
269 QStringList filters;
270 filters << "Maps (*.map)" << "|All files (*.*)";
271 dialog.setNameFilters(filters);
272 dialog.setAcceptMode(QFileDialog::AcceptOpen);
273 QStringList fileNames;
274 if (dialog.exec())
275 fileNames = dialog.selectedFiles();
276
277 if (fileNames.count() > 0)
278 {
279 QString fileName = QFileInfo(fileNames[0]).absoluteFilePath();
280 g_symbolMap->LoadSymbolMap(Path(fileName.toStdString()));
281 }
282 }
283
smapAct()284 void MainWindow::smapAct()
285 {
286 QFileDialog dialog(0,"Save .MAP");
287 dialog.setFileMode(QFileDialog::AnyFile);
288 dialog.setAcceptMode(QFileDialog::AcceptSave);
289 QStringList filters;
290 filters << "Save .MAP (*.map)" << "|All files (*.*)";
291 dialog.setNameFilters(filters);
292 QStringList fileNames;
293 if (dialog.exec())
294 {
295 fileNames = dialog.selectedFiles();
296 g_symbolMap->SaveSymbolMap(Path(fileNames[0].toStdString()));
297 }
298 }
299
lsymAct()300 void MainWindow::lsymAct()
301 {
302 QFileDialog dialog(0,"Load .SYM");
303 dialog.setFileMode(QFileDialog::ExistingFile);
304 QStringList filters;
305 filters << "Symbols (*.sym)" << "|All files (*.*)";
306 dialog.setNameFilters(filters);
307 dialog.setAcceptMode(QFileDialog::AcceptOpen);
308 QStringList fileNames;
309 if (dialog.exec())
310 fileNames = dialog.selectedFiles();
311
312 if (fileNames.count() > 0)
313 {
314 QString fileName = QFileInfo(fileNames[0]).absoluteFilePath();
315 g_symbolMap->LoadNocashSym(Path(fileName.toStdString()));
316 }
317 }
318
ssymAct()319 void MainWindow::ssymAct()
320 {
321 QFileDialog dialog(0,"Save .SYM");
322 dialog.setFileMode(QFileDialog::AnyFile);
323 dialog.setAcceptMode(QFileDialog::AcceptSave);
324 QStringList filters;
325 filters << "Save .SYM (*.sym)" << "|All files (*.*)";
326 dialog.setNameFilters(filters);
327 QStringList fileNames;
328 if (dialog.exec())
329 {
330 fileNames = dialog.selectedFiles();
331 g_symbolMap->SaveNocashSym(Path(fileNames[0].toStdString()));
332 }
333 }
334
resetTableAct()335 void MainWindow::resetTableAct()
336 {
337 g_symbolMap->Clear();
338 }
339
dumpNextAct()340 void MainWindow::dumpNextAct()
341 {
342 gpu->DumpNextFrame();
343 }
344
consoleAct()345 void MainWindow::consoleAct()
346 {
347 LogManager::GetInstance()->GetConsoleListener()->Show(LogManager::GetInstance()->GetConsoleListener()->Hidden());
348 }
349
raiseTopMost()350 void MainWindow::raiseTopMost()
351 {
352 setWindowState( (windowState() & ~Qt::WindowMinimized) | Qt::WindowActive);
353 raise();
354 activateWindow();
355 }
356
SetFullScreen(bool fullscreen)357 void MainWindow::SetFullScreen(bool fullscreen) {
358 if (fullscreen) {
359 #if !PPSSPP_PLATFORM(MAC)
360 menuBar()->hide();
361
362 emugl->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
363 // TODO: Shouldn't this be physicalSize()?
364 emugl->resizeGL(emugl->size().width(), emugl->size().height());
365 // TODO: Won't showFullScreen do this for us?
366 setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
367 setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
368 #endif
369
370 showFullScreen();
371 InitPadLayout(dp_xres, dp_yres);
372
373 if (GetUIState() == UISTATE_INGAME && !g_Config.bShowTouchControls)
374 QApplication::setOverrideCursor(QCursor(Qt::BlankCursor));
375 } else {
376 #if !PPSSPP_PLATFORM(MAC)
377 menuBar()->show();
378 updateMenus();
379 #endif
380
381 showNormal();
382 SetWindowScale(-1);
383 InitPadLayout(dp_xres, dp_yres);
384
385 if (GetUIState() == UISTATE_INGAME && QApplication::overrideCursor())
386 QApplication::restoreOverrideCursor();
387
388 QDesktopWidget *desktop = QApplication::desktop();
389 int screenNum = QProcessEnvironment::systemEnvironment().value("SDL_VIDEO_FULLSCREEN_HEAD", "0").toInt();
390
391 // Move window to the center of selected screen
392 QRect rect = desktop->screenGeometry(screenNum);
393 move((rect.width() - frameGeometry().width()) / 4, (rect.height() - frameGeometry().height()) / 4);
394 }
395 }
396
fullscrAct()397 void MainWindow::fullscrAct()
398 {
399 // Toggle the current state.
400 g_Config.bFullScreen = !isFullScreen();
401 SetFullScreen(g_Config.bFullScreen);
402
403 QTimer::singleShot(1000, this, SLOT(raiseTopMost()));
404 }
405
websiteAct()406 void MainWindow::websiteAct()
407 {
408 QDesktopServices::openUrl(QUrl("https://www.ppsspp.org/"));
409 }
410
forumAct()411 void MainWindow::forumAct()
412 {
413 QDesktopServices::openUrl(QUrl("https://forums.ppsspp.org/"));
414 }
415
goldAct()416 void MainWindow::goldAct()
417 {
418 QDesktopServices::openUrl(QUrl("https://central.ppsspp.org/buygold"));
419 }
420
gitAct()421 void MainWindow::gitAct()
422 {
423 QDesktopServices::openUrl(QUrl("https://github.com/hrydgard/ppsspp/"));
424 }
425
discordAct()426 void MainWindow::discordAct()
427 {
428 QDesktopServices::openUrl(QUrl("https://discord.gg/5NJB6dD"));
429 }
430
aboutAct()431 void MainWindow::aboutAct()
432 {
433 QMessageBox::about(this, "About", QString("PPSSPP Qt %1\n\n"
434 "PSP emulator and debugger\n\n"
435 "Copyright (c) by Henrik Rydg\xc3\xa5rd and the PPSSPP Project 2012-\n"
436 "Qt port maintained by xSacha\n\n"
437 "Additional credits:\n"
438 " PSPSDK by #pspdev (freenode)\n"
439 " CISO decompression code by BOOSTER\n"
440 " zlib by Jean-loup Gailly (compression) and Mark Adler (decompression)\n"
441 " Qt project by Digia\n\n"
442 "All trademarks are property of their respective owners.\n"
443 "The emulator is for educational and development purposes only and it may not be used to play games you do not legally own.").arg(PPSSPP_GIT_VERSION));
444 }
445
446 /* Private functions */
SetWindowScale(int zoom)447 void MainWindow::SetWindowScale(int zoom) {
448 if (isFullScreen())
449 fullscrAct();
450
451 int width, height;
452 if (zoom == -1 && (g_Config.iWindowWidth <= 0 || g_Config.iWindowHeight <= 0)) {
453 // Default to zoom level 2.
454 zoom = 2;
455 }
456 if (zoom == -1) {
457 // Take the last setting.
458 width = g_Config.iWindowWidth;
459 height = g_Config.iWindowHeight;
460 } else {
461 // Update to the specified factor. Let's clamp first.
462 if (zoom < 1)
463 zoom = 1;
464 if (zoom > 10)
465 zoom = 10;
466
467 width = (g_Config.IsPortrait() ? 272 : 480) * zoom;
468 height = (g_Config.IsPortrait() ? 480 : 272) * zoom;
469 }
470
471 g_Config.iWindowWidth = width;
472 g_Config.iWindowHeight = height;
473
474 #if !PPSSPP_PLATFORM(MAC)
475 emugl->setFixedSize(g_Config.iWindowWidth, g_Config.iWindowHeight);
476 // TODO: Shouldn't this be scaled size?
477 emugl->resizeGL(g_Config.iWindowWidth, g_Config.iWindowHeight);
478 setFixedSize(sizeHint());
479 #else
480 resize(g_Config.iWindowWidth, g_Config.iWindowHeight);
481 #endif
482 updateMenus();
483 }
484
SetGameTitle(QString text)485 void MainWindow::SetGameTitle(QString text)
486 {
487 QString title = QString("PPSSPP %1").arg(PPSSPP_GIT_VERSION);
488 if (text != "")
489 title += QString(" - %1").arg(text);
490
491 setWindowTitle(title);
492 }
493
loadLanguage(const QString & language,bool translate)494 void MainWindow::loadLanguage(const QString& language, bool translate)
495 {
496 if (currentLanguage != language)
497 {
498 QLocale::setDefault(QLocale(language));
499 QApplication::removeTranslator(&translator);
500
501 currentLanguage = language;
502 if (translator.load(QString(":/languages/ppsspp_%1.qm").arg(language))) {
503 QApplication::installTranslator(&translator);
504 }
505 if (translate)
506 emit retranslate();
507 }
508 }
509
createMenus()510 void MainWindow::createMenus()
511 {
512 // File
513 MenuTree* fileMenu = new MenuTree(this, menuBar(), QT_TR_NOOP("&File"));
514 fileMenu->add(new MenuAction(this, SLOT(loadAct()), QT_TR_NOOP("&Load..."), QKeySequence::Open))
515 ->addEnableState(UISTATE_MENU);
516 fileMenu->addSeparator();
517 fileMenu->add(new MenuAction(this, SLOT(openmsAct()), QT_TR_NOOP("Open &Memory Stick")))
518 ->addEnableState(UISTATE_MENU);
519 fileMenu->addSeparator();
520 MenuTree* savestateMenu = new MenuTree(this, fileMenu, QT_TR_NOOP("Saves&tate slot"));
521 saveStateGroup = new MenuActionGroup(this, savestateMenu, SLOT(saveStateGroup_triggered(QAction *)),
522 QStringList() << "1" << "2" << "3" << "4" << "5",
523 QList<int>() << 0 << 1 << 2 << 3 << 4);
524 fileMenu->add(new MenuAction(this, SLOT(qlstateAct()), QT_TR_NOOP("L&oad state"), Qt::Key_F4))
525 ->addDisableState(UISTATE_MENU);
526 fileMenu->add(new MenuAction(this, SLOT(qsstateAct()), QT_TR_NOOP("S&ave state"), Qt::Key_F2))
527 ->addDisableState(UISTATE_MENU);
528 fileMenu->add(new MenuAction(this, SLOT(lstateAct()), QT_TR_NOOP("&Load state file...")))
529 ->addDisableState(UISTATE_MENU);
530 fileMenu->add(new MenuAction(this, SLOT(sstateAct()), QT_TR_NOOP("&Save state file...")))
531 ->addDisableState(UISTATE_MENU);
532 MenuTree* recordMenu = new MenuTree(this, fileMenu, QT_TR_NOOP("&Record"));
533 recordMenu->add(new MenuAction(this, SLOT(recordDisplayAct()), QT_TR_NOOP("Record &display")))
534 ->addEventChecked(&g_Config.bDumpFrames);
535 recordMenu->add(new MenuAction(this, SLOT(useLosslessVideoCodecAct()), QT_TR_NOOP("&Use lossless video codec (FFV1)")))
536 ->addEventChecked(&g_Config.bUseFFV1);
537 recordMenu->add(new MenuAction(this, SLOT(useOutputBufferAct()), QT_TR_NOOP("Use output buffer for video")))
538 ->addEventChecked(&g_Config.bDumpVideoOutput);
539 recordMenu->addSeparator();
540 recordMenu->add(new MenuAction(this, SLOT(recordAudioAct()), QT_TR_NOOP("Record &audio")))
541 ->addEventChecked(&g_Config.bDumpAudio);
542 fileMenu->addSeparator();
543 fileMenu->add(new MenuAction(this, SLOT(exitAct()), QT_TR_NOOP("E&xit"), QKeySequence::Quit));
544
545 // Emulation
546 MenuTree* emuMenu = new MenuTree(this, menuBar(), QT_TR_NOOP("&Emulation"));
547 emuMenu->add(new MenuAction(this, SLOT(pauseAct()), QT_TR_NOOP("&Pause")))
548 ->addEnableState(UISTATE_INGAME);
549 emuMenu->add(new MenuAction(this, SLOT(stopAct()), QT_TR_NOOP("&Stop"), Qt::CTRL + Qt::Key_W))
550 ->addEnableState(UISTATE_INGAME);
551 emuMenu->add(new MenuAction(this, SLOT(resetAct()), QT_TR_NOOP("R&eset"), Qt::CTRL + Qt::Key_B))
552 ->addEnableState(UISTATE_INGAME);
553 emuMenu->add(new MenuAction(this, SLOT(switchUMDAct()), QT_TR_NOOP("Switch UMD"), Qt::CTRL + Qt::Key_U))
554 ->addEnableState(UISTATE_INGAME);
555 MenuTree* displayRotationMenu = new MenuTree(this, emuMenu, QT_TR_NOOP("Display rotation"));
556 displayRotationGroup = new MenuActionGroup(this, displayRotationMenu, SLOT(displayRotationGroup_triggered(QAction *)),
557 QStringList() << "Landscape" << "Portrait" << "Landscape reversed" << "Portrait reversed",
558 QList<int>() << 1 << 2 << 3 << 4);
559
560 // Debug
561 MenuTree* debugMenu = new MenuTree(this, menuBar(), QT_TR_NOOP("&Debug"));
562 debugMenu->add(new MenuAction(this, SLOT(breakonloadAct()), QT_TR_NOOP("Break on load")))
563 ->addEventUnchecked(&g_Config.bAutoRun);
564 debugMenu->add(new MenuAction(this, SLOT(ignoreIllegalAct()), QT_TR_NOOP("&Ignore illegal reads/writes")))
565 ->addEventChecked(&g_Config.bIgnoreBadMemAccess);
566 debugMenu->addSeparator();
567 debugMenu->add(new MenuAction(this, SLOT(lmapAct()), QT_TR_NOOP("&Load MAP file...")))
568 ->addDisableState(UISTATE_MENU);
569 debugMenu->add(new MenuAction(this, SLOT(smapAct()), QT_TR_NOOP("&Save MAP file...")))
570 ->addDisableState(UISTATE_MENU);
571 debugMenu->add(new MenuAction(this, SLOT(lsymAct()), QT_TR_NOOP("Lo&ad SYM file...")))
572 ->addDisableState(UISTATE_MENU);
573 debugMenu->add(new MenuAction(this, SLOT(ssymAct()), QT_TR_NOOP("Sav&e SYM file...")))
574 ->addDisableState(UISTATE_MENU);
575 debugMenu->add(new MenuAction(this, SLOT(resetTableAct()),QT_TR_NOOP("Reset s&ymbol table")))
576 ->addDisableState(UISTATE_MENU);
577 debugMenu->addSeparator();
578 debugMenu->add(new MenuAction(this, SLOT(takeScreen()), QT_TR_NOOP("&Take screenshot"), Qt::Key_F12))
579 ->addDisableState(UISTATE_MENU);
580 debugMenu->add(new MenuAction(this, SLOT(dumpNextAct()), QT_TR_NOOP("D&ump next frame to log")))
581 ->addDisableState(UISTATE_MENU);
582 debugMenu->add(new MenuAction(this, SLOT(statsAct()), QT_TR_NOOP("Show debu&g statistics")))
583 ->addEventChecked(&g_Config.bShowDebugStats);
584 debugMenu->addSeparator();
585 debugMenu->add(new MenuAction(this, SLOT(consoleAct()), QT_TR_NOOP("&Log console"), Qt::CTRL + Qt::Key_L))
586 ->addDisableState(UISTATE_MENU);
587
588 // Game settings
589 MenuTree* gameSettingsMenu = new MenuTree(this, menuBar(), QT_TR_NOOP("&Game settings"));
590 gameSettingsMenu->add(new MenuAction(this, SLOT(languageAct()), QT_TR_NOOP("La&nguage...")));
591 gameSettingsMenu->add(new MenuAction(this, SLOT(controlMappingAct()), QT_TR_NOOP("C&ontrol mapping...")));
592 gameSettingsMenu->add(new MenuAction(this, SLOT(displayLayoutEditorAct()), QT_TR_NOOP("Display layout editor...")));
593 gameSettingsMenu->add(new MenuAction(this, SLOT(moreSettingsAct()), QT_TR_NOOP("&More settings...")));
594 gameSettingsMenu->addSeparator();
595 #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0)
596 gameSettingsMenu->add(new MenuAction(this, SLOT(fullscrAct()), QT_TR_NOOP("&Fullscreen"), Qt::Key_F11))
597 #else
598 gameSettingsMenu->add(new MenuAction(this, SLOT(fullscrAct()), QT_TR_NOOP("Fu&llscreen"), QKeySequence::FullScreen))
599 #endif
600 ->addEventChecked(&g_Config.bFullScreen);
601 MenuTree* renderingResolutionMenu = new MenuTree(this, gameSettingsMenu, QT_TR_NOOP("&Rendering resolution"));
602 renderingResolutionGroup = new MenuActionGroup(this, renderingResolutionMenu, SLOT(renderingResolutionGroup_triggered(QAction *)),
603 QStringList() << "&Auto" << "&1x" << "&2x" << "&3x" << "&4x" << "&5x" << "&6x" << "&7x" << "&8x" << "&9x" << "1&0x",
604 QList<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10);
605 // - Window Size
606 MenuTree* windowMenu = new MenuTree(this, gameSettingsMenu, QT_TR_NOOP("&Window size"));
607 windowGroup = new MenuActionGroup(this, windowMenu, SLOT(windowGroup_triggered(QAction *)),
608 QStringList() << "&1x" << "&2x" << "&3x" << "&4x" << "&5x" << "&6x" << "&7x" << "&8x" << "&9x" << "1&0x",
609 QList<int>() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10);
610
611 MenuTree* renderingModeMenu = new MenuTree(this, gameSettingsMenu, QT_TR_NOOP("Rendering m&ode"));
612 renderingModeGroup = new MenuActionGroup(this, renderingModeMenu, SLOT(renderingModeGroup_triggered(QAction *)),
613 QStringList() << "&Skip buffered effects (non-buffered, faster)" << "&Buffered rendering",
614 QList<int>() << 0 << 1);
615 MenuTree* frameSkippingMenu = new MenuTree(this, gameSettingsMenu, QT_TR_NOOP("&Frame skipping"));
616 frameSkippingMenu->add(new MenuAction(this, SLOT(autoframeskipAct()), QT_TR_NOOP("&Auto")))
617 ->addEventChecked(&g_Config.bAutoFrameSkip);
618 frameSkippingMenu->addSeparator();
619 frameSkippingGroup = new MenuActionGroup(this, frameSkippingMenu, SLOT(frameSkippinGroup_triggered(QAction *)),
620 QStringList() << "&Off" << "&1" << "&2" << "&3" << "&4" << "&5" << "&6" << "&7" << "&8",
621 QList<int>() << 0 << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 8);
622 MenuTree* frameSkippingTypeMenu = new MenuTree(this, gameSettingsMenu, QT_TR_NOOP("Frame skipping type"));
623 frameSkippingTypeGroup = new MenuActionGroup(this, frameSkippingTypeMenu, SLOT(frameSkippingTypeGroup_triggered(QAction *)),
624 QStringList() << "Skip number of frames" << "Skip percent of FPS",
625 QList<int>() << 0 << 1);
626 MenuTree* textureFilteringMenu = new MenuTree(this, gameSettingsMenu, QT_TR_NOOP("Te&xture filtering"));
627 textureFilteringGroup = new MenuActionGroup(this, textureFilteringMenu, SLOT(textureFilteringGroup_triggered(QAction *)),
628 QStringList() << "&Auto" << "&Nearest" << "&Linear" << "Auto Max &Quality",
629 QList<int>() << 1 << 2 << 3 << 4);
630 MenuTree* screenScalingFilterMenu = new MenuTree(this, gameSettingsMenu, QT_TR_NOOP("Scr&een scaling filter"));
631 screenScalingFilterGroup = new MenuActionGroup(this, screenScalingFilterMenu, SLOT(screenScalingFilterGroup_triggered(QAction *)),
632 QStringList() << "&Linear" << "&Nearest",
633 QList<int>() << 0 << 1);
634
635 MenuTree* textureScalingMenu = new MenuTree(this, gameSettingsMenu, QT_TR_NOOP("&Texture scaling"));
636 textureScalingLevelGroup = new MenuActionGroup(this, textureScalingMenu, SLOT(textureScalingLevelGroup_triggered(QAction *)),
637 QStringList() << "&Off" << "&2x" << "&3x" << "&4x" << "&5x",
638 QList<int>() << 1 << 2 << 3 << 4 << 5);
639 textureScalingMenu->addSeparator();
640 textureScalingTypeGroup = new MenuActionGroup(this, textureScalingMenu, SLOT(textureScalingTypeGroup_triggered(QAction *)),
641 QStringList() << "&xBRZ" << "&Hybrid" << "&Bicubic" << "H&ybrid + bicubic",
642 QList<int>() << 0 << 1 << 2 << 3);
643 textureScalingMenu->addSeparator();
644 textureScalingMenu->add(new MenuAction(this, SLOT(deposterizeAct()), QT_TR_NOOP("&Deposterize")))
645 ->addEventChecked(&g_Config.bTexDeposterize);
646
647 gameSettingsMenu->add(new MenuAction(this, SLOT(transformAct()), QT_TR_NOOP("&Hardware transform")))
648 ->addEventChecked(&g_Config.bHardwareTransform);
649 gameSettingsMenu->add(new MenuAction(this, SLOT(vertexCacheAct()), QT_TR_NOOP("&Vertex cache")))
650 ->addEventChecked(&g_Config.bVertexCache);
651 gameSettingsMenu->add(new MenuAction(this, SLOT(showFPSAct()), QT_TR_NOOP("&Show FPS counter")))
652 ->addEventChecked(&g_Config.iShowFPSCounter);
653 gameSettingsMenu->addSeparator();
654 gameSettingsMenu->add(new MenuAction(this, SLOT(audioAct()), QT_TR_NOOP("Enable s&ound")))
655 ->addEventChecked(&g_Config.bEnableSound);
656 gameSettingsMenu->addSeparator();
657 gameSettingsMenu->add(new MenuAction(this, SLOT(cheatsAct()), QT_TR_NOOP("Enable &cheats"), Qt::CTRL + Qt::Key_T))
658 ->addEventChecked(&g_Config.bEnableCheats);
659 gameSettingsMenu->addSeparator();
660 gameSettingsMenu->add(new MenuAction(this, SLOT(chatAct()), QT_TR_NOOP("Open chat"), Qt::CTRL + Qt::Key_C))
661 ->SetEnabledFunc([=]() {
662 return g_Config.bEnableNetworkChat && GetUIState() == UISTATE_INGAME;
663 });
664
665 // Help
666 MenuTree* helpMenu = new MenuTree(this, menuBar(), QT_TR_NOOP("&Help"));
667 helpMenu->add(new MenuAction(this, SLOT(websiteAct()), QT_TR_NOOP("Visit www.&ppsspp.org")));
668 helpMenu->add(new MenuAction(this, SLOT(forumAct()), QT_TR_NOOP("PPSSPP &forums")));
669 helpMenu->add(new MenuAction(this, SLOT(goldAct()), QT_TR_NOOP("Buy &Gold")));
670 helpMenu->add(new MenuAction(this, SLOT(gitAct()), QT_TR_NOOP("Git&Hub")));
671 helpMenu->add(new MenuAction(this, SLOT(discordAct()), QT_TR_NOOP("Discord")));
672 helpMenu->addSeparator();
673 helpMenu->add(new MenuAction(this, SLOT(aboutAct()), QT_TR_NOOP("&About PPSSPP...")));
674
675 retranslate();
676 }
677