1 #include "mainwindow.h"
2 #include "ui_mainwindow.h"
3 #include "sendinghandler.h"
4 #include "dockwidget.h"
5 #include "utils.h"
6 
7 #include "../../core/schedule.h"
8 #include "../../core/cpu.h"
9 #include "../../core/emu.h"
10 #include "../../core/link.h"
11 #include "../../core/debug/debug.h"
12 
13 #include <cmath>
14 #include <QtCore/QFileInfo>
15 #include <QtCore/QRegularExpression>
16 #include <QtNetwork/QNetworkAccessManager>
17 #include <QtWidgets/QDesktopWidget>
18 #include <QtWidgets/QApplication>
19 #include <QtWidgets/QMessageBox>
20 #include <QtWidgets/QInputDialog>
21 #include <QtWidgets/QScrollBar>
22 #include <QtNetwork/QNetworkReply>
23 
24 #ifdef _MSC_VER
25     #include <direct.h>
26     #define chdir _chdir
27 #else
28     #include <unistd.h>
29 #endif
30 
31 const QString MainWindow::SETTING_DEBUGGER_TEXT_SIZE        = QStringLiteral("Debugger/text_size");
32 const QString MainWindow::SETTING_DEBUGGER_RESTORE_ON_OPEN  = QStringLiteral("Debugger/restore_on_open");
33 const QString MainWindow::SETTING_DEBUGGER_SAVE_ON_CLOSE    = QStringLiteral("Debugger/save_on_close");
34 const QString MainWindow::SETTING_DEBUGGER_RESET_OPENS      = QStringLiteral("Debugger/open_on_reset");
35 const QString MainWindow::SETTING_DEBUGGER_ENABLE_SOFT      = QStringLiteral("Debugger/enable_soft_commands");
36 const QString MainWindow::SETTING_DEBUGGER_BOLD_SYMBOLS     = QStringLiteral("Debugger/disasm_bold_symbols");
37 const QString MainWindow::SETTING_DEBUGGER_DISASM_SPACE     = QStringLiteral("Debugger/disasm_add_space");
38 const QString MainWindow::SETTING_DEBUGGER_ADDR_COL         = QStringLiteral("Debugger/disasm_addr_column");
39 const QString MainWindow::SETTING_DEBUGGER_DATA_COL         = QStringLiteral("Debugger/disasm_data_column");
40 const QString MainWindow::SETTING_DEBUGGER_IMPLICT          = QStringLiteral("Debugger/disasm_implict");
41 const QString MainWindow::SETTING_DEBUGGER_UPPERCASE        = QStringLiteral("Debugger/disasm_uppercase");
42 const QString MainWindow::SETTING_DEBUGGER_IMAGE_PATH       = QStringLiteral("Debugger/image_path");
43 const QString MainWindow::SETTING_DEBUGGER_FLASH_BYTES      = QStringLiteral("Debugger/flash_bytes_per_line");
44 const QString MainWindow::SETTING_DEBUGGER_RAM_BYTES        = QStringLiteral("Debugger/ram_bytes_per_line");
45 const QString MainWindow::SETTING_DEBUGGER_FLASH_ASCII      = QStringLiteral("Debugger/flash_ascii");
46 const QString MainWindow::SETTING_DEBUGGER_RAM_ASCII        = QStringLiteral("Debugger/ram_ascii");
47 const QString MainWindow::SETTING_DEBUGGER_BREAK_IGNORE     = QStringLiteral("Debugger/ignore_breakpoints");
48 const QString MainWindow::SETTING_DEBUGGER_AUTO_EQUATES     = QStringLiteral("Debugger/auto_equates");
49 const QString MainWindow::SETTING_DEBUGGER_IGNORE_DMA       = QStringLiteral("Debugger/ignore_dma");
50 const QString MainWindow::SETTING_DEBUGGER_NORM_OS          = QStringLiteral("Debugger/norm_os");
51 const QString MainWindow::SETTING_DEBUGGER_PRE_I            = QStringLiteral("Debugger/pre_i");
52 const QString MainWindow::SETTING_SCREEN_FRAMESKIP          = QStringLiteral("Screen/frameskip");
53 const QString MainWindow::SETTING_SCREEN_SCALE              = QStringLiteral("Screen/scale");
54 const QString MainWindow::SETTING_SCREEN_SKIN               = QStringLiteral("Screen/skin");
55 const QString MainWindow::SETTING_SCREEN_SPI                = QStringLiteral("Screen/spi");
56 const QString MainWindow::SETTING_KEYPAD_KEYMAP             = QStringLiteral("Keypad/map");
57 const QString MainWindow::SETTING_KEYPAD_COLOR              = QStringLiteral("Keypad/color");
58 const QString MainWindow::SETTING_KEYPAD_CUSTOM_PATH        = QStringLiteral("Keypad/custom_path");
59 const QString MainWindow::SETTING_WINDOW_GROUP_DRAG         = QStringLiteral("Window/group_dock_drag");
60 const QString MainWindow::SETTING_WINDOW_FULLSCREEN         = QStringLiteral("Window/fullscreen");
61 const QString MainWindow::SETTING_WINDOW_STATE              = QStringLiteral("Window/state");
62 const QString MainWindow::SETTING_WINDOW_GEOMETRY           = QStringLiteral("Window/geometry");
63 const QString MainWindow::SETTING_WINDOW_SEPARATOR          = QStringLiteral("Window/boundaries");
64 const QString MainWindow::SETTING_WINDOW_MENUBAR            = QStringLiteral("Window/menubar");
65 const QString MainWindow::SETTING_WINDOW_STATUSBAR          = QStringLiteral("Window/statusbar");
66 const QString MainWindow::SETTING_WINDOW_POSITION           = QStringLiteral("Window/position");
67 const QString MainWindow::SETTING_WINDOW_MEMORY_DOCKS       = QStringLiteral("Window/memory_docks");
68 const QString MainWindow::SETTING_WINDOW_MEMORY_DOCK_BYTES  = QStringLiteral("Window/memory_docks_bytes");
69 const QString MainWindow::SETTING_WINDOW_MEMORY_DOCK_ASCII  = QStringLiteral("Window/memory_docks_ascii");
70 const QString MainWindow::SETTING_WINDOW_VISUALIZER_DOCKS   = QStringLiteral("Window/visualizer_docks");
71 const QString MainWindow::SETTING_WINDOW_VISUALIZER_CONFIG  = QStringLiteral("Window/visualizer_config");
72 const QString MainWindow::SETTING_WINDOW_KEYHISTORY_DOCKS   = QStringLiteral("Window/keyhistory_docks");
73 const QString MainWindow::SETTING_WINDOW_KEYHISTORY_CONFIG  = QStringLiteral("Window/keyhistory_config");
74 const QString MainWindow::SETTING_CAPTURE_FRAMESKIP         = QStringLiteral("Capture/frameskip");
75 const QString MainWindow::SETTING_CAPTURE_OPTIMIZE          = QStringLiteral("Capture/optimize");
76 const QString MainWindow::SETTING_SLOT_NAMES                = QStringLiteral("Slot/names");
77 const QString MainWindow::SETTING_SLOT_PATHS                = QStringLiteral("Slot/paths");
78 const QString MainWindow::SETTING_IMAGE_PATH                = QStringLiteral("image_path");
79 const QString MainWindow::SETTING_ROM_PATH                  = QStringLiteral("rom_path");
80 const QString MainWindow::SETTING_STATUS_INTERVAL           = QStringLiteral("status_interval");
81 const QString MainWindow::SETTING_FIRST_RUN                 = QStringLiteral("first_run");
82 const QString MainWindow::SETTING_UI_EDIT_MODE              = QStringLiteral("ui_edit_mode");
83 const QString MainWindow::SETTING_PAUSE_FOCUS               = QStringLiteral("pause_on_focus_change");
84 const QString MainWindow::SETTING_SAVE_ON_CLOSE             = QStringLiteral("save_on_close");
85 const QString MainWindow::SETTING_RESTORE_ON_OPEN           = QStringLiteral("restore_on_open");
86 const QString MainWindow::SETTING_EMUSPEED                  = QStringLiteral("emulated_speed");
87 const QString MainWindow::SETTING_AUTOUPDATE                = QStringLiteral("check_for_updates");
88 const QString MainWindow::SETTING_ALWAYS_ON_TOP             = QStringLiteral("always_on_top");
89 const QString MainWindow::SETTING_NATIVE_CONSOLE            = QStringLiteral("native_console");
90 const QString MainWindow::SETTING_CURRENT_DIR               = QStringLiteral("current_directory");
91 const QString MainWindow::SETTING_ENABLE_WIN_CONSOLE        = QStringLiteral("enable_windows_console");
92 const QString MainWindow::SETTING_RECENT_SAVE               = QStringLiteral("Recent/save_paths");
93 const QString MainWindow::SETTING_RECENT_PATHS              = QStringLiteral("Recent/paths");
94 const QString MainWindow::SETTING_RECENT_SELECT             = QStringLiteral("Recent/selected");
95 
96 const QString MainWindow::SETTING_KEYPAD_NATURAL            = QStringLiteral("natural");
97 const QString MainWindow::SETTING_KEYPAD_CEMU               = QStringLiteral("cemu");
98 const QString MainWindow::SETTING_KEYPAD_TILEM              = QStringLiteral("tilem");
99 const QString MainWindow::SETTING_KEYPAD_WABBITEMU          = QStringLiteral("wabbitemu");
100 const QString MainWindow::SETTING_KEYPAD_JSTIFIED           = QStringLiteral("jsTIfied");
101 const QString MainWindow::SETTING_KEYPAD_CUSTOM             = QStringLiteral("custom");
102 
103 const QString MainWindow::SETTING_PREFERRED_LANG            = QStringLiteral("preferred_lang");
104 const QString MainWindow::SETTING_VERSION                   = QStringLiteral("version");
105 
106 const QString MainWindow::SETTING_DEFAULT_CONFIG_FILE       = QStringLiteral("/cemu_config.ini");
107 const QString MainWindow::SETTING_DEFAULT_ROM_FILE          = QStringLiteral("/cemu_rom.rom");
108 const QString MainWindow::SETTING_DEFAULT_DEBUG_FILE        = QStringLiteral("/cemu_debug.ini");
109 const QString MainWindow::SETTING_DEFAULT_IMAGE_FILE        = QStringLiteral("/cemu_image.ce");
110 const QString MainWindow::TXT_YES                           = QStringLiteral("y");
111 const QString MainWindow::TXT_NO                            = QStringLiteral("n");
112 const QString MainWindow::TXT_NAN                           = QStringLiteral("NaN");
113 
114 // In all cases, all paths in memory should be absolute paths.
115 // But in the UI or config file, they can be relative (to appDir()) if portable mode is enabled, or absolute otherwise.
setPortable(bool state)116 void MainWindow::setPortable(bool state) {
117     ui->checkPortable->blockSignals(true);
118     ui->checkPortable->setChecked(state);
119     ui->checkPortable->blockSignals(false);
120     m_portable = state;
121 
122     delete m_config;
123     m_config = Q_NULLPTR;
124 
125     const QDir dir = appDir();
126 
127     // Get all new paths, in absolute
128     const QString newConfigPath = QFileInfo((state ? dir.path() : configPath) + SETTING_DEFAULT_CONFIG_FILE).absoluteFilePath();
129     const QString newConfigDirPath = QDir::cleanPath(QFileInfo(newConfigPath).absolutePath());
130     QString newDebugPath = newConfigDirPath + SETTING_DEFAULT_DEBUG_FILE;
131     QString newImagePath = newConfigDirPath + SETTING_DEFAULT_IMAGE_FILE;
132     QString newRomPath = m_pathRom; // No change here
133 
134     // Update the FS (still using absolute paths)
135     QFile(m_pathConfig).copy(newConfigPath);
136     QFile(m_pathImage).copy(newImagePath);
137 
138     // Remove old settings if previously portable
139     if (!state) {
140         QFile(m_pathConfig).remove();
141         QFile(m_pathImage).remove();
142     }
143 
144     // Update paths in memory (still using absolute paths)
145     m_pathConfig = newConfigPath;
146     m_pathImage = newImagePath;
147 
148     // Now changing the UI and qsettings content, with, if portable, paths relative to appDir()
149     if (state) {
150         newDebugPath = dir.relativeFilePath(newDebugPath);
151         newImagePath = dir.relativeFilePath(newImagePath);
152         newRomPath = dir.relativeFilePath(newRomPath);
153     }
154 
155     // Update new QSettings (memory + FS)
156     m_config = new QSettings(newConfigPath, QSettings::IniFormat); // Path is absolute
157     m_config->setValue(SETTING_DEBUGGER_IMAGE_PATH, newDebugPath);
158     m_config->setValue(SETTING_IMAGE_PATH, newImagePath);
159     m_config->setValue(SETTING_ROM_PATH, newRomPath);
160     m_config->sync();
161 
162     ui->pathDebug->setText(newDebugPath);
163     ui->pathImage->setText(newImagePath);
164     ui->pathRom->setText(newRomPath);
165     ui->pathConfig->setText(newConfigPath);
166 
167     ui->buttonChangeSavedDebugPath->setEnabled(!state);
168     ui->buttonChangeSavedImagePath->setEnabled(!state);
169 }
170 
setFrameskip(int value)171 void MainWindow::setFrameskip(int value) {
172     m_config->setValue(SETTING_CAPTURE_FRAMESKIP, value);
173     ui->apngSkip->setValue(value);
174     ui->apngSkipDisplay->setText(QString::number((ui->guiSkip->value() + 1) * (ui->apngSkip->value() + 1) - 1));
175 }
176 
setOptimizeRecord(bool state)177 void MainWindow::setOptimizeRecord(bool state) {
178     ui->checkOptimizeRecording->setChecked(state);
179     m_config->setValue(SETTING_CAPTURE_OPTIMIZE, state);
180     m_optimizeRecording = state;
181 }
182 
iconsLoad()183 void MainWindow::iconsLoad() {
184     QString iconPath = QStringLiteral(":/icons/resources/icons/");
185     m_iconStop.addPixmap(QPixmap(iconPath + QStringLiteral("stop.png")));
186     m_iconRun.addPixmap(QPixmap(iconPath + QStringLiteral("run.png")));
187     m_iconSave.addPixmap(QPixmap(iconPath + QStringLiteral("import.png")));
188     m_iconLoad.addPixmap(QPixmap(iconPath + QStringLiteral("export.png")));
189     m_iconEdit.addPixmap(QPixmap(iconPath + QStringLiteral("wizard.png")));
190     m_iconRemove.addPixmap(QPixmap(iconPath + QStringLiteral("exit.png")));
191     m_iconSearch.addPixmap(QPixmap(iconPath + QStringLiteral("search.png")));
192     m_iconGoto.addPixmap(QPixmap(iconPath + QStringLiteral("goto.png")));
193     m_iconSync.addPixmap(QPixmap(iconPath + QStringLiteral("refresh.png")));
194     m_iconAddMem.addPixmap(QPixmap(iconPath + QStringLiteral("add_mem.png")));
195     m_iconLcd.addPixmap(QPixmap(iconPath + QStringLiteral("lcd.png")));
196     m_iconUiEdit.addPixmap(QPixmap(iconPath + QStringLiteral("ui_edit.png")));
197     m_iconAscii.addPixmap(QPixmap(iconPath + QStringLiteral("characters.png")));
198     m_iconCheck.addPixmap(QPixmap(iconPath + QStringLiteral("check.png")));
199     m_iconCheckGray.addPixmap(QPixmap(iconPath + QStringLiteral("checkgray.png")));
200     m_actionAddMemory->setIcon(m_iconAddMem);
201     m_actionAddVisualizer->setIcon(m_iconLcd);
202     m_actionToggleUI->setIcon(m_iconUiEdit);
203 }
204 
bootImageCheck()205 bool MainWindow::bootImageCheck() {
206     QDir dir = appDir();
207     QDirIterator dirIt(dir, QDirIterator::NoIteratorFlags);
208     while (dirIt.hasNext()) {
209         dirIt.next();
210         QString dirItFile = dirIt.filePath();
211         if (QFileInfo(dirItFile).isFile()) {
212             if (QFileInfo(dirItFile).suffix() == QStringLiteral("cemu")) {
213                 if (!m_loadedBootImage) {
214                     m_loadedBootImage = bootImageImport(dirItFile);
215                 }
216                 QFile(dirItFile).remove();
217             }
218         }
219     }
220     return m_loadedBootImage;
221 }
222 
bootImageImport(const QString & bootImagePath)223 bool MainWindow::bootImageImport(const QString &bootImagePath) {
224     QString newConfigPath = configPath + SETTING_DEFAULT_CONFIG_FILE;
225     QString romPath = configPath + SETTING_DEFAULT_ROM_FILE;
226     QFile bootFile(bootImagePath);
227     QFile romFile(romPath);
228     romFile.remove();
229     if (!romFile.open(QIODevice::WriteOnly)) { return false; }
230     QFile settingsFile(newConfigPath);
231     settingsFile.remove();
232     if (!settingsFile.open(QIODevice::WriteOnly)) { return false; }
233     bootFile.open(QIODevice::ReadOnly);
234     QByteArray romData = bootFile.read(0x400000);
235     QByteArray settingsData = bootFile.readAll();
236     romFile.write(romData);
237     settingsFile.write(settingsData);
238     romFile.close();
239     settingsFile.close();
240     bootFile.close();
241     m_pathConfig = newConfigPath;
242     return true;
243 }
244 
bootImageExport()245 void MainWindow::bootImageExport() {
246     QMessageBox::information(this, MSG_INFORMATION, tr("A bootable image can be used to start CEmu with predefined configurations, without the need for any extra setup."
247                                                        "\n\nThe bootable image should be placed in the same directory as the CEmu executable. When CEmu is then started, "
248                                                        "the boot image will be loaded automatically and then removed for convience."));
249 
250     QString path = QFileDialog::getSaveFileName(this, tr("Save bootable CEmu image"),
251                                                 m_dir.absolutePath(),
252                                                 tr("Bootable CEmu images (*.cemu);"));
253 
254     if (!path.isEmpty()) {
255         m_dir = QFileInfo(path).absoluteDir();
256         QFile romFile(m_pathRom);
257         if (!romFile.open(QIODevice::ReadOnly)) {
258             return;
259         }
260         QByteArray romData = romFile.readAll();
261 
262         QFile settingsFile(m_pathConfig);
263         if (!settingsFile.open(QIODevice::ReadOnly)) {
264             return;
265         }
266         QByteArray settingsData = settingsFile.readAll();
267 
268         QFile writter(path);
269         writter.open(QIODevice::WriteOnly);
270         writter.write(romData);
271         writter.write(settingsData);
272         romFile.close();
273         settingsFile.close();
274         writter.close();
275     }
276 }
277 
setDebugSoftCommands(bool state)278 void MainWindow::setDebugSoftCommands(bool state) {
279     ui->checkDisableSoftCommands->blockSignals(true);
280     ui->checkDisableSoftCommands->setChecked(state);
281     ui->checkDisableSoftCommands->blockSignals(false);
282     m_config->setValue(SETTING_DEBUGGER_ENABLE_SOFT, state);
283     debug_flag(DBG_SOFT_COMMANDS, state);
284 }
285 
setDebugDisasmBoldSymbols(bool state)286 void MainWindow::setDebugDisasmBoldSymbols(bool state) {
287     ui->checkDisasmBoldSymbols->setChecked(state);
288     m_config->setValue(SETTING_DEBUGGER_BOLD_SYMBOLS, state);
289     disasm.bold_sym = state;
290     if (guiDebug) {
291         disasmUpdate();
292     }
293 }
294 
setDockGroupDrag(bool state)295 void MainWindow::setDockGroupDrag(bool state) {
296     ui->checkAllowGroupDrag->setChecked(state);
297     m_config->setValue(SETTING_WINDOW_GROUP_DRAG, state);
298     setUIDockEditMode(m_uiEditMode);
299 }
300 
setDebugDisasmDataCol(bool state)301 void MainWindow::setDebugDisasmDataCol(bool state) {
302     ui->checkDisasmDataCol->setChecked(state);
303     m_config->setValue(SETTING_DEBUGGER_DATA_COL, state);
304     disasm.bytes = state;
305     if (guiDebug) {
306         disasmUpdate();
307     }
308 }
309 
setDebugDisasmAddrCol(bool state)310 void MainWindow::setDebugDisasmAddrCol(bool state) {
311     ui->checkDisasmAddr->setChecked(state);
312     m_config->setValue(SETTING_DEBUGGER_ADDR_COL, state);
313     disasm.addr = state;
314     if (guiDebug) {
315         if (!disasm.addr) {
316             disasmUpdate();
317         } else {
318             disasmUpdateAddr(cpu.registers.PC, true);
319         }
320     }
321 }
322 
setDebugDisasmImplict(bool state)323 void MainWindow::setDebugDisasmImplict(bool state) {
324     ui->checkDisasmImplict->setChecked(state);
325     m_config->setValue(SETTING_DEBUGGER_IMPLICT, state);
326     disasm.implicit = state;
327     if (guiDebug) {
328         disasmUpdate();
329     }
330 }
331 
setDebugDisasmUppercase(bool state)332 void MainWindow::setDebugDisasmUppercase(bool state) {
333     ui->checkDisasmUppercase->setChecked(state);
334     m_config->setValue(SETTING_DEBUGGER_UPPERCASE, state);
335     disasm.uppercase = state;
336     if (guiDebug) {
337         disasmUpdate();
338     }
339 }
340 
setDebugDisasmSpace(bool state)341 void MainWindow::setDebugDisasmSpace(bool state) {
342     ui->checkAddSpace->setChecked(state);
343     m_config->setValue(SETTING_DEBUGGER_DISASM_SPACE, state);
344     disasm.comma = state ? ", " : ",";
345     if (guiDebug) {
346         disasmUpdate();
347     }
348 }
349 
setLcdSpi(bool state)350 void MainWindow::setLcdSpi(bool state) {
351     ui->checkSpi->setChecked(state);
352     m_config->setValue(SETTING_SCREEN_SPI, state);
353     emu_set_lcd_spi(state == false ? 0 : 1);
354     emit setLcdMode(state);
355 }
356 
setLcdDma(bool state)357 void MainWindow::setLcdDma(bool state) {
358     ui->checkDma->setChecked(state);
359     m_config->setValue(SETTING_DEBUGGER_IGNORE_DMA, !state);
360     bool ok;
361     int64_t cycleCounter = ui->cycleView->text().toLongLong(&ok);
362     if (!ok)
363         cycleCounter = debug.totalCycles - (!state ? debug.dmaCycles : 0);
364     else if (!state)
365         cycleCounter -= debug.dmaCycles;
366     else
367         cycleCounter += debug.dmaCycles;
368     ui->cycleView->setText(QString::number(cycleCounter));
369     m_ignoreDmaCycles = !state;
370 }
371 
setFocusSetting(bool state)372 void MainWindow::setFocusSetting(bool state) {
373     ui->checkFocus->setChecked(state);
374     m_config->setValue(SETTING_PAUSE_FOCUS, state);
375     m_pauseOnFocus = state;
376 }
377 
memLoadState()378 void MainWindow::memLoadState() {
379     ui->ramBytes->setValue(m_config->value(SETTING_DEBUGGER_RAM_BYTES, 8).toInt());
380     ui->flashBytes->setValue(m_config->value(SETTING_DEBUGGER_FLASH_BYTES, 8).toInt());
381     ui->ramAscii->setChecked(m_config->value(SETTING_DEBUGGER_RAM_ASCII, true).toBool());
382     ui->flashAscii->setChecked(m_config->value(SETTING_DEBUGGER_FLASH_ASCII, true).toBool());
383     ui->ramEdit->setAsciiArea(ui->ramAscii->isChecked());
384     ui->flashEdit->setAsciiArea(ui->flashAscii->isChecked());
385 }
386 
setMenuBarState(bool state)387 void MainWindow::setMenuBarState(bool state) {
388     ui->menubar->setHidden(state);
389     ui->actionHideMenuBar->setChecked(state);
390     m_config->setValue(SETTING_WINDOW_MENUBAR, state);
391 }
392 
setStatusBarState(bool state)393 void MainWindow::setStatusBarState(bool state) {
394     ui->statusBar->setHidden(state);
395     ui->actionHideStatusBar->setChecked(state);
396     m_config->setValue(SETTING_WINDOW_STATUSBAR, state);
397 }
398 
setDebugIgnoreBreakpoints(bool state)399 void MainWindow::setDebugIgnoreBreakpoints(bool state) {
400     ui->buttonToggleBreakpoints->setChecked(state);
401     m_config->setValue(SETTING_DEBUGGER_BREAK_IGNORE, state);
402     debug_flag(DBG_IGNORE, state);
403 }
404 
setDebugResetTrigger(bool state)405 void MainWindow::setDebugResetTrigger(bool state) {
406     ui->checkDebugResetTrigger->setChecked(state);
407     m_config->setValue(SETTING_DEBUGGER_RESET_OPENS, state);
408     debug_flag(DBG_OPEN_ON_RESET, state);
409 }
410 
setAutoSave(bool state)411 void MainWindow::setAutoSave(bool state) {
412     ui->checkSaveRestore->setChecked(state);
413     m_config->setValue(SETTING_RESTORE_ON_OPEN, state);
414     m_config->setValue(SETTING_SAVE_ON_CLOSE, state);
415 }
416 
setDebugAutoSave(bool state)417 void MainWindow::setDebugAutoSave(bool state) {
418     ui->checkSaveLoadDebug->setChecked(state);
419     m_config->setValue(SETTING_DEBUGGER_SAVE_ON_CLOSE, state);
420     m_config->setValue(SETTING_DEBUGGER_RESTORE_ON_OPEN, state);
421 }
422 
setFont(int fontSize)423 void MainWindow::setFont(int fontSize) {
424     ui->textSize->setValue(fontSize);
425     m_config->setValue(SETTING_DEBUGGER_TEXT_SIZE, ui->textSize->value());
426 
427     QFont monospace = QFontDatabase::systemFont(QFontDatabase::FixedFont);
428 
429     monospace.setPointSize(fontSize);
430     ui->console->setFont(monospace);
431     m_disasm->setFont(monospace);
432 
433     ui->stackView->setFont(monospace);
434 
435     ui->afregView->setFont(monospace);
436     ui->hlregView->setFont(monospace);
437     ui->deregView->setFont(monospace);
438     ui->bcregView->setFont(monospace);
439     ui->ixregView->setFont(monospace);
440     ui->iyregView->setFont(monospace);
441     ui->af_regView->setFont(monospace);
442     ui->hl_regView->setFont(monospace);
443     ui->de_regView->setFont(monospace);
444     ui->bc_regView->setFont(monospace);
445     ui->splregView->setFont(monospace);
446     ui->spsregView->setFont(monospace);
447     ui->mbregView->setFont(monospace);
448     ui->iregView->setFont(monospace);
449     ui->rregView->setFont(monospace);
450     ui->imregView->setFont(monospace);
451     ui->freqView->setFont(monospace);
452     ui->pcregView->setFont(monospace);
453     ui->lcdbaseView->setFont(monospace);
454     ui->lcdcurrView->setFont(monospace);
455     ui->cycleView->setFont(monospace);
456 }
457 
setKeypadColor(unsigned int color)458 void MainWindow::setKeypadColor(unsigned int color) {
459     ui->keypadWidget->setType(get_device_type(), color);
460     m_config->setValue(SETTING_KEYPAD_COLOR, color);
461 }
462 
setCalcSkinTopFromType()463 void MainWindow::setCalcSkinTopFromType() {
464     bool is83 = get_device_type() == TI83PCE;
465     ui->calcSkinTop->setStyleSheet(is83 ? QStringLiteral(".QFrame { border-image: url(:/skin/resources/skin/ti83pce.png) 0 0 0 0 stretch stretch; }")
466                                         : QStringLiteral(".QFrame { border-image: url(:/skin/resources/skin/ti84pce.png) 0 0 0 0 stretch stretch; }"));
467 }
468 
setImagePath()469 void MainWindow::setImagePath() {
470     QString saveImagePath = QFileDialog::getSaveFileName(this, tr("Set saved image to restore from"),
471                                                            m_dir.absolutePath(),
472                                                            tr("CEmu images (*.ce);;All files (*.*)"));
473     if (!saveImagePath.isEmpty()) {
474         m_dir = QFileInfo(saveImagePath).absoluteDir();
475         m_config->setValue(SETTING_IMAGE_PATH, saveImagePath);
476         ui->pathImage->setText(saveImagePath);
477     }
478 }
479 
setDebugPath()480 void MainWindow::setDebugPath() {
481     QString savePath = QFileDialog::getSaveFileName(this, tr("Set debugging information path"),
482                                                            m_dir.absolutePath(),
483                                                            tr("Debugging information (*.ini);;All files (*.*)"));
484     if (!savePath.isEmpty()) {
485         m_dir = QFileInfo(savePath).absoluteDir();
486         m_config->setValue(SETTING_DEBUGGER_IMAGE_PATH, savePath);
487         ui->pathDebug->setText(savePath);
488     }
489 }
490 
setUIDocks()491 void MainWindow::setUIDocks() {
492 
493     // Create "Docks" menu to make closing and opening docks more intuitive
494     m_menuDocks = new QMenu(TITLE_DOCKS, this);
495     ui->menubar->insertMenu(ui->menuExtras->menuAction(), m_menuDocks);
496 
497     // Convert any current docks
498     for (const auto &dock : findChildren<DockWidget*>()) {
499         dock->setState(m_uiEditMode);
500     }
501 
502     // Add the screen action
503     QAction *action = ui->screenDock->toggleViewAction();
504     action->setIcon(m_iconLcd);
505 
506     // Convert the tabs into DockWidgets
507     while (ui->tabWidget->count()) {
508         DockWidget *dw = new DockWidget(ui->tabWidget, this);
509 
510         // Fill "Docks" menu
511         QAction *action = dw->toggleViewAction();
512         action->setIcon(dw->windowIcon());
513         m_menuDocks->addAction(action);
514 
515         dw->setState(m_uiEditMode);
516         addDockWidget(Qt::RightDockWidgetArea, dw);
517         if (!m_dockPtrs.isEmpty()) {
518             tabifyDockWidget(m_dockPtrs.back(), dw);
519         }
520 
521         m_dockPtrs.append(dw);
522     }
523 
524     m_menuDocks->addAction(action);
525     m_menuDocks->addSeparator();
526 
527     m_menuDebug = new QMenu(TITLE_DEBUG, this);
528     ui->menubar->insertMenu(ui->menuExtras->menuAction(), m_menuDebug);
529 
530     // Convert the tabs into QDockWidgets
531     while (ui->tabDebug->count()) {
532         DockWidget *dw = new DockWidget(ui->tabDebug, this);
533 
534         // Fill "Docks" menu
535         QAction *action = dw->toggleViewAction();
536         action->setIcon(dw->windowIcon());
537         m_menuDebug->addAction(action);
538 
539         dw->setState(m_uiEditMode);
540         addDockWidget(Qt::RightDockWidgetArea, dw);
541         if (isFirstRun() || !opts.useSettings) {
542             dw->hide();
543             dw->close();
544         }
545     }
546 
547     // Load removable docks
548     setMemDocks();
549     setVisualizerDocks();
550     setKeyHistoryDocks();
551 
552     m_menuDebug->addSeparator();
553     m_menuDebug->addAction(m_actionAddMemory);
554     m_menuDebug->addAction(m_actionAddVisualizer);
555 
556     m_menuDocks->addSeparator();
557     m_menuDocks->addAction(m_actionToggleUI);
558 
559     ui->tabWidget->close();
560     ui->tabDebug->close();
561 
562     m_dockPtrs.append(ui->screenDock);
563 
564     // hide all the docks
565     for (const auto &dock : findChildren<DockWidget*>()) {
566         dock->setVisible(false);
567     }
568 }
569 
setUIDockEditMode(bool mode)570 void MainWindow::setUIDockEditMode(bool mode) {
571     if (mode) {
572         if (m_config->value(SETTING_WINDOW_GROUP_DRAG).toBool()) {
573             setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks
574 #if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
575                 | QMainWindow::GroupedDragging
576 #endif
577             );
578         } else {
579             setDockOptions(QMainWindow::AnimatedDocks | QMainWindow::AllowNestedDocks | QMainWindow::AllowTabbedDocks);
580         }
581     } else {
582         setDockOptions(0);
583     }
584     for (const auto &dock : findChildren<DockWidget*>()) {
585         dock->setState(mode);
586     }
587 }
588 
setUIEditMode(bool mode)589 void MainWindow::setUIEditMode(bool mode) {
590     m_uiEditMode = mode;
591     m_config->setValue(SETTING_UI_EDIT_MODE, mode);
592     m_actionToggleUI->setChecked(mode);
593     m_actionAddMemory->setEnabled(mode);
594     m_actionAddVisualizer->setEnabled(mode);
595     setUIBoundaries(mode);
596     setUIDockEditMode(mode);
597 }
598 
setThrottle(int mode)599 void MainWindow::setThrottle(int mode) {
600     ui->checkThrottle->setChecked(mode == Qt::Checked);
601     emu.setThrottle(mode == Qt::Checked);
602 }
603 
setAutoUpdates(int state)604 void MainWindow::setAutoUpdates(int state) {
605     m_config->setValue(SETTING_AUTOUPDATE, state);
606     ui->checkUpdates->setChecked(state);
607 
608     if (state == Qt::Checked) {
609         checkUpdate(false);
610     }
611 }
612 
checkUpdate(bool forceInfoBox)613 void MainWindow::checkUpdate(bool forceInfoBox) {
614     if (!CEMU_RELEASE) {
615         if (forceInfoBox) {
616             QMessageBox::warning(this, MSG_WARNING, tr("Checking updates is disabled for development builds"));
617         }
618         return;
619     }
620 
621     static const QString currentVersionReleaseURL = QStringLiteral("https://github.com/CE-Programming/CEmu/releases/tag/" CEMU_VERSION);
622     QNetworkAccessManager *manager = new QNetworkAccessManager(this);
623     connect(manager, &QNetworkAccessManager::finished, this, [=](QNetworkReply* reply) {
624         QString newVersionURL = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
625         if (!newVersionURL.isEmpty()) {
626             if (newVersionURL.compare(currentVersionReleaseURL) == 0) {
627                 if (forceInfoBox) {
628                     QMessageBox::information(this, tr("No update available"), tr("You already have the latest CEmu version") + QStringLiteral(" (" CEMU_VERSION ")"));
629                 }
630             } else {
631                 QMessageBox updateInfoBox(this);
632                 updateInfoBox.addButton(QMessageBox::Ok);
633                 updateInfoBox.setIconPixmap(QPixmap(QStringLiteral(":/icons/resources/icons/icon.png")));
634                 updateInfoBox.setWindowTitle(tr("CEmu update"));
635                 updateInfoBox.setText(tr("<b>A new version of CEmu is available!</b>"
636                                          "<br/>"
637                                          "You can <a href='%1'>download it here</a>.")
638                                      .arg(newVersionURL));
639                 updateInfoBox.setTextFormat(Qt::RichText);
640                 updateInfoBox.show();
641                 updateInfoBox.exec();
642             }
643         } else { // No internet connection? GitHub doesn't provide this redirection anymore?
644             if (forceInfoBox) {
645                 QMessageBox updateInfoBox(this);
646                 updateInfoBox.addButton(QMessageBox::Ok);
647                 updateInfoBox.setIcon(QMessageBox::Warning);
648                 updateInfoBox.setWindowTitle(tr("Update check failed"));
649                 updateInfoBox.setText(tr("<b>An error occurred while checking for CEmu updates.</b>"
650                                          "<br/>"
651                                          "You can however <a href='https://github.com/CE-Programming/CEmu/releases/latest'>go here</a> to check yourself."));
652                 updateInfoBox.setTextFormat(Qt::RichText);
653                 updateInfoBox.show();
654                 updateInfoBox.exec();
655             }
656         }
657     });
658 
659     manager->get(QNetworkRequest(QUrl(QStringLiteral("https://github.com/CE-Programming/CEmu/releases/latest"))));
660 }
661 
lcdAdjust()662 void MainWindow::lcdAdjust() {
663     float scale = ui->scaleLCD->value() / 100.0;
664     bool skin = ui->checkSkin->isChecked();
665     ui->calcSkinTop->setVisible(skin);
666     float w, h;
667     w = LCD_WIDTH * scale;
668     h = LCD_HEIGHT * scale;
669     ui->lcd->setFixedSize(w, h);
670     ui->lcd->move(skin ? 60 * scale : 0, skin ? 78 * scale : 0);
671     if (skin) {
672         w = 440 * scale;
673         h = 351 * scale;
674     }
675     ui->calcSkinTop->resize(w, h);
676     ui->screenWidget->setFixedSize(w, h);
677 }
678 
setLcdScale(int scale)679 void MainWindow::setLcdScale(int scale) {
680     int roundedScale = round(scale / 10.0) * 10;
681     m_config->setValue(SETTING_SCREEN_SCALE, roundedScale);
682     ui->scaleLCD->setValue(roundedScale);
683     lcdAdjust();
684 }
685 
setSkinToggle(bool enable)686 void MainWindow::setSkinToggle(bool enable) {
687     m_config->setValue(SETTING_SCREEN_SKIN, enable);
688     ui->checkSkin->setChecked(enable);
689     lcdAdjust();
690 }
691 
setDebugAutoEquates(bool enable)692 void MainWindow::setDebugAutoEquates(bool enable) {
693     m_config->setValue(SETTING_DEBUGGER_AUTO_EQUATES, enable);
694     ui->checkAutoEquates->setChecked(enable);
695     sendingHandler->setLoadEquates(enable);
696 }
697 
setGuiSkip(int value)698 void MainWindow::setGuiSkip(int value) {
699     m_config->setValue(SETTING_SCREEN_FRAMESKIP, value);
700     ui->guiSkip->blockSignals(true);
701     ui->guiSkip->setValue(value);
702     ui->guiSkip->blockSignals(false);
703     ui->apngSkipDisplay->setText(QString::number((ui->guiSkip->value() + 1) * (ui->apngSkip->value() + 1) - 1));
704     emit setLcdFrameskip(value);
705 }
706 
setStatusInterval(int value)707 void MainWindow::setStatusInterval(int value) {
708     m_config->setValue(SETTING_STATUS_INTERVAL, value);
709     ui->statusInterval->setValue(value);
710     m_timerFps.stop();
711     m_timerEmu.stop();
712     m_timerFpsTriggered = true;
713     m_timerEmuTriggered = true;
714     m_timerEmuTriggerable = true;
715     m_timerFpsTriggerable = true;
716     if (!value) {
717         m_timerEmuTriggerable = false;
718         m_timerFpsTriggerable = false;
719     } else {
720         m_timerFps.start(value * 1000);
721         m_timerEmu.start(value * 1000);
722     }
723 }
724 
setEmuSpeed(int value)725 void MainWindow::setEmuSpeed(int value) {
726     m_config->setValue(SETTING_EMUSPEED, value);
727     ui->emulationSpeedSpin->blockSignals(true);
728     ui->emulationSpeedSpin->setValue(value);
729     ui->emulationSpeedSpin->blockSignals(false);
730     ui->emulationSpeed->setValue(value);
731     emu.setSpeed(value);
732     if (value == 0) {
733       m_timerEmuTriggered = true;
734       showEmuSpeed(0);
735     }
736 }
737 
keypadChanged()738 void MainWindow::keypadChanged() {
739     QString name = sender()->objectName();
740     unsigned int color = KEYPAD_BLACK;
741 
742     if      (name == QStringLiteral("buttonWhite"))        color = KEYPAD_WHITE;
743     else if (name == QStringLiteral("buttonBlack"))        color = KEYPAD_BLACK;
744     else if (name == QStringLiteral("buttonGolden"))       color = KEYPAD_GOLDEN;
745     else if (name == QStringLiteral("buttonPlum"))         color = KEYPAD_PLUM;
746     else if (name == QStringLiteral("buttonPink"))         color = KEYPAD_PINK;
747     else if (name == QStringLiteral("buttonRed"))          color = KEYPAD_RED;
748     else if (name == QStringLiteral("buttonLightning"))    color = KEYPAD_LIGHTNING;
749     else if (name == QStringLiteral("buttonTrueBlue"))     color = KEYPAD_TRUE_BLUE;
750     else if (name == QStringLiteral("buttonDenim"))        color = KEYPAD_DENIM;
751     else if (name == QStringLiteral("buttonSilver"))       color = KEYPAD_SILVER;
752     else if (name == QStringLiteral("buttonSpaceGrey"))    color = KEYPAD_SPACEGREY;
753     else if (name == QStringLiteral("buttonCoral"))        color = KEYPAD_CORAL;
754     else if (name == QStringLiteral("buttonMint"))         color = KEYPAD_MINT;
755     else if (name == QStringLiteral("buttonRoseGold"))     color = KEYPAD_ROSEGOLD;
756     else if (name == QStringLiteral("buttonCrystalClear")) color = KEYPAD_CRYSTALCLEAR;
757 
758     setKeypadColor(color);
759 }
760 
keymapCustomSelected()761 void MainWindow::keymapCustomSelected() {
762     QFileDialog dialog(this);
763 
764     m_config->remove(SETTING_KEYPAD_CUSTOM_PATH);
765 
766     dialog.setDirectory(m_dir);
767     dialog.setFileMode(QFileDialog::ExistingFile);
768     dialog.setNameFilter(tr("Keymap Config (*.ini);;All files (*.*)"));
769 
770     if (!dialog.exec()) {
771         ui->radioCEmuKeys->setChecked(true);
772         return;
773     }
774 
775     m_dir = dialog.directory().absolutePath();
776     const auto& selectedFiles = dialog.selectedFiles();
777     if (selectedFiles.empty()) {
778         ui->radioCEmuKeys->setChecked(true);
779         return;
780     }
781 
782     m_config->setValue(SETTING_KEYPAD_CUSTOM_PATH, selectedFiles.first());
783     setKeymap(SETTING_KEYPAD_CUSTOM);
784 }
785 
keymapChanged()786 void MainWindow::keymapChanged() {
787     if (ui->radioNaturalKeys->isChecked()) {
788         setKeymap(SETTING_KEYPAD_NATURAL);
789     } else if (ui->radioCEmuKeys->isChecked()) {
790         setKeymap(SETTING_KEYPAD_CEMU);
791     } else if (ui->radioTilEmKeys->isChecked()) {
792         setKeymap(SETTING_KEYPAD_TILEM);
793     } else if (ui->radioWabbitemuKeys->isChecked()) {
794         setKeymap(SETTING_KEYPAD_WABBITEMU);
795     } else if (ui->radiojsTIfiedKeys->isChecked()) {
796         setKeymap(SETTING_KEYPAD_JSTIFIED);
797     } else if (ui->radioCustomKeys->isChecked()) {
798         setKeymap(SETTING_KEYPAD_CUSTOM);
799     }
800 }
801 
setKeymap(const QString & value)802 void MainWindow::setKeymap(const QString &value) {
803     QtKeypadBridge::KeymapMode mode;
804     QString map = value;
805     QString customPath = m_config->value(SETTING_KEYPAD_CUSTOM_PATH, QString()).toString();
806     if (!SETTING_KEYPAD_CUSTOM.compare(map, Qt::CaseInsensitive)) {
807         if (customPath.isEmpty() || !fileExists(customPath)) {
808             map = SETTING_KEYPAD_CEMU;
809         } else if (!keypadBridge->keymapImport(customPath)) {
810             QMessageBox::warning(this, MSG_WARNING, tr("Unable to set custom keymap."));
811             map = SETTING_KEYPAD_CEMU;
812         }
813     }
814     if (!SETTING_KEYPAD_NATURAL.compare(map, Qt::CaseInsensitive)) {
815         mode = QtKeypadBridge::KEYMAP_NATURAL;
816     } else if (!SETTING_KEYPAD_CEMU.compare(map, Qt::CaseInsensitive)) {
817         mode = QtKeypadBridge::KEYMAP_CEMU;
818     } else if (!SETTING_KEYPAD_TILEM.compare(map, Qt::CaseInsensitive)) {
819         mode = QtKeypadBridge::KEYMAP_TILEM;
820     } else if (!SETTING_KEYPAD_WABBITEMU.compare(map, Qt::CaseInsensitive)) {
821         mode = QtKeypadBridge::KEYMAP_WABBITEMU;
822     } else if (!SETTING_KEYPAD_JSTIFIED.compare(map, Qt::CaseInsensitive)) {
823         mode = QtKeypadBridge::KEYMAP_JSTIFIED;
824     } else {
825         mode = QtKeypadBridge::KEYMAP_CUSTOM;
826     }
827     m_config->setValue(SETTING_KEYPAD_KEYMAP, map);
828     keypadBridge->setKeymap(mode);
829     if (map == SETTING_KEYPAD_CEMU) {
830         ui->radioCEmuKeys->setChecked(true);
831     }
832 }
833 
keymapLoad()834 void MainWindow::keymapLoad() {
835     QString currKeyMap = m_config->value(SETTING_KEYPAD_KEYMAP, SETTING_KEYPAD_CEMU).toString();
836     if (!SETTING_KEYPAD_NATURAL.compare(currKeyMap, Qt::CaseInsensitive)) {
837         ui->radioNaturalKeys->setChecked(true);
838     } else if (!SETTING_KEYPAD_CEMU.compare(currKeyMap, Qt::CaseInsensitive)) {
839         ui->radioCEmuKeys->setChecked(true);
840     } else if (!SETTING_KEYPAD_TILEM.compare(currKeyMap, Qt::CaseInsensitive)) {
841         ui->radioTilEmKeys->setChecked(true);
842     } else if (!SETTING_KEYPAD_WABBITEMU.compare(currKeyMap, Qt::CaseInsensitive)) {
843         ui->radioWabbitemuKeys->setChecked(true);
844     } else if (!SETTING_KEYPAD_JSTIFIED.compare(currKeyMap, Qt::CaseInsensitive)) {
845         ui->radiojsTIfiedKeys->setChecked(true);
846     } else if (!SETTING_KEYPAD_CUSTOM.compare(currKeyMap, Qt::CaseInsensitive)) {
847         ui->radioCustomKeys->setChecked(true);
848     }
849     setKeymap(currKeyMap);
850 }
851 
setFullscreen(int value)852 void MainWindow::setFullscreen(int value) {
853     static QWidget *parentPtr = Q_NULLPTR;
854     if (parentPtr == Q_NULLPTR) {
855         parentPtr = ui->lcd->parentWidget();
856     }
857     switch (value) {
858         default:
859         case FULLSCREEN_NONE:
860             showNormal();
861             ui->lcd->setParent(parentPtr);
862             ui->lcd->showNormal();
863             lcdAdjust();
864             m_fullscreen = FULLSCREEN_NONE;
865             break;
866         case FULLSCREEN_ALL:
867             showFullScreen();
868             m_fullscreen = FULLSCREEN_ALL;
869             break;
870         case FULLSCREEN_LCD:
871             ui->lcd->setParent(this, Qt::Tool | Qt::FramelessWindowHint | Qt::CustomizeWindowHint);
872             ui->lcd->setFixedSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
873             ui->lcd->showFullScreen();
874             ui->lcd->installEventFilter(keypadBridge);
875             ui->lcd->setFocus();
876             m_fullscreen = FULLSCREEN_LCD;
877             break;
878     }
879 }
880 
setTop(bool state)881 void MainWindow::setTop(bool state) {
882     if (state) {
883         setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
884     } else {
885         setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
886     }
887     if (m_setup) {
888         show();
889     }
890     m_config->setValue(SETTING_ALWAYS_ON_TOP, state);
891     ui->checkAlwaysOnTop->setCheckState(state ? Qt::Checked : Qt::Unchecked);
892 }
893 
stateSaveInfo()894 void MainWindow::stateSaveInfo() {
895     QStringList slotNames;
896     QStringList slotPaths;
897 
898     for (int i = 0; i < ui->slotView->rowCount(); i++) {
899         slotNames.append(ui->slotView->item(i, SLOT_NAME_COL)->text());
900         slotPaths.append(ui->slotView->item(i, SLOT_EDIT_COL)->data(Qt::UserRole).toString());
901     }
902 
903     m_config->setValue(SETTING_SLOT_NAMES, slotNames);
904     m_config->setValue(SETTING_SLOT_PATHS, slotPaths);
905 }
906 
stateLoadInfo()907 void MainWindow::stateLoadInfo() {
908     QStringList slotNames = m_config->value(SETTING_SLOT_NAMES).toStringList();
909     QStringList slotPaths = m_config->value(SETTING_SLOT_PATHS).toStringList();
910 
911     for (int i = 0; i < slotNames.size(); i++) {
912         QString name = slotNames.at(i);
913         QString path = slotPaths.at(i);
914         stateAdd(name, path);
915     }
916 }
917 
setRecentSave(bool state)918 void MainWindow::setRecentSave(bool state) {
919     ui->checkSaveRecent->setChecked(state);
920     m_config->setValue(SETTING_RECENT_SAVE, state);
921 }
922 
setPreRevisionI(bool state)923 void MainWindow::setPreRevisionI(bool state) {
924     ui->checkPreI->setChecked(state);
925     m_config->setValue(SETTING_DEBUGGER_PRE_I, state);
926     cpu.preI = state;
927 }
928 
setNormalOs(bool state)929 void MainWindow::setNormalOs(bool state) {
930     ui->checkNormOs->setChecked(state);
931     m_config->setValue(SETTING_DEBUGGER_NORM_OS, state);
932     m_normalOs = state;
933     if (guiDebug) {
934         ui->opView->setEnabled(state);
935         ui->vatView->setEnabled(state);
936         ui->opStack->setEnabled(state);
937         ui->fpStack->setEnabled(state);
938         if (state) {
939             osUpdate();
940         }
941     }
942 }
943 
setUIBoundaries(bool state)944 void MainWindow::setUIBoundaries(bool state) {
945     m_config->setValue(SETTING_WINDOW_SEPARATOR, state);
946     if (state) {
947         setStyleSheet(QStringLiteral("QMainWindow::separator{ width: 4px; height: 4px; }"));
948     } else {
949         setStyleSheet(QStringLiteral("QMainWindow::separator{ width: 0px; height: 0px; }"));
950     }
951 }
952 
recentSaveInfo()953 void MainWindow::recentSaveInfo() {
954     QStringList paths;
955     QStringList selects;
956 
957     if (m_config->value(SETTING_RECENT_SAVE).toBool()) {
958         for (int i = 0; i < ui->varLoadedView->rowCount(); i++) {
959             paths.append(ui->varLoadedView->item(i, RECENT_PATH_COL)->text());
960             selects.append(static_cast<QAbstractButton *>(ui->varLoadedView->cellWidget(i, RECENT_SELECT_COL))->isChecked() ? TXT_YES : TXT_NO);
961         }
962     }
963 
964     m_config->setValue(SETTING_RECENT_PATHS, paths);
965     m_config->setValue(SETTING_RECENT_SELECT, selects);
966 }
967 
recentLoadInfo()968 void MainWindow::recentLoadInfo() {
969     QStringList paths = m_config->value(SETTING_RECENT_PATHS).toStringList();
970     QStringList selects = m_config->value(SETTING_RECENT_SELECT).toStringList();
971 
972     if (m_config->value(SETTING_RECENT_SAVE).toBool()) {
973         for (int i = 0; i < paths.size(); i++) {
974             const QString path = paths.at(i);
975             bool select = selects.at(i) == TXT_YES;
976             sendingHandler->addFile(path, select);
977         }
978     }
979 }
980 
setMemDocks()981 void MainWindow::setMemDocks() {
982     QStringList names = m_config->value(SETTING_WINDOW_MEMORY_DOCKS).toStringList();
983     QList<int> bytes = m_config->value(SETTING_WINDOW_MEMORY_DOCK_BYTES).value<QList<int>>();
984     QList<bool> ascii = m_config->value(SETTING_WINDOW_MEMORY_DOCK_ASCII).value<QList<bool>>();
985 
986     if (names.length() != bytes.length()) {
987         return;
988     }
989 
990     for (int i = 0; i < names.length(); i++) {
991         addMemDock(names.at(i), bytes.at(i), ascii.at(i));
992     }
993 }
994 
setVisualizerDocks()995 void MainWindow::setVisualizerDocks() {
996     QStringList names = m_config->value(SETTING_WINDOW_VISUALIZER_DOCKS).toStringList();
997     QStringList configs = m_config->value(SETTING_WINDOW_VISUALIZER_CONFIG).toStringList();
998 
999     if (names.length() != configs.length()) {
1000         return;
1001     }
1002 
1003     for (int i = 0; i < names.length(); i++) {
1004         addVisualizerDock(names.at(i), configs.at(i));
1005     }
1006 }
1007 
setKeyHistoryDocks()1008 void MainWindow::setKeyHistoryDocks() {
1009     QStringList names = m_config->value(SETTING_WINDOW_KEYHISTORY_DOCKS).toStringList();
1010     QList<int> sizes = m_config->value(SETTING_WINDOW_KEYHISTORY_CONFIG).value<QList<int>>();
1011 
1012     if (names.length() != sizes.length()) {
1013         return;
1014     }
1015 
1016     for (int i = 0; i < names.length(); i++) {
1017         addKeyHistoryDock(names.at(i), sizes.at(i));
1018     }
1019 }
1020 
1021 
addKeyHistoryDock(const QString & magic,int size)1022 void MainWindow::addKeyHistoryDock(const QString &magic, int size) {
1023     if (m_docksKeyHistory.contains(magic)) {
1024         return;
1025     }
1026 
1027     m_docksKeyHistory.append(magic);
1028     m_docksKeyHistorySize.append(size);
1029 
1030     DockWidget *dw = new DockWidget(TXT_KEYHISTORY_DOCK, this);
1031     KeyHistoryWidget *widget = new KeyHistoryWidget(this, size);
1032 
1033     dw->makeCloseableFloat(true);
1034 
1035     if (m_setup) {
1036         dw->setFloating(true);
1037         dw->setGeometry(QStyle::alignedRect(Qt::LeftToRight, Qt::AlignCenter, dw->minimumSize(), qApp->desktop()->availableGeometry()));
1038     }
1039 
1040     connect(ui->keypadWidget, &KeypadWidget::keyPressed, widget, &KeyHistoryWidget::add);
1041     connect(widget, &KeyHistoryWidget::fontSizeChanged, [this, widget, magic]{
1042         int index;
1043         if ((index = m_docksKeyHistory.indexOf(magic)) != -1) {
1044             m_docksKeyHistorySize[index] = widget->getFontSize();
1045         }
1046     });
1047     connect(dw, &DockWidget::closed, [this, magic]{
1048         int index;
1049         if ((index = m_docksKeyHistory.indexOf(magic)) != -1) {
1050             m_docksKeyHistory.removeAt(index);
1051             m_docksKeyHistorySize.removeAt(index);
1052         }
1053     });
1054 
1055     dw->setState(m_uiEditMode);
1056     addDockWidget(Qt::RightDockWidgetArea, dw);
1057     dw->setObjectName(magic);
1058     dw->setWidget(widget);
1059 
1060     if (m_setup) {
1061         dw->show();
1062         dw->activateWindow();
1063         dw->raise();
1064     }
1065 }
1066 
setVersion()1067 void MainWindow::setVersion() {
1068     m_config->setValue(SETTING_VERSION, QStringLiteral(CEMU_VERSION));
1069 }
1070 
checkVersion()1071 void MainWindow::checkVersion() {
1072     bool ask = false;
1073 
1074     if (isFirstRun()) {
1075         setVersion();
1076         return;
1077     }
1078 
1079     if (m_config->contains(SETTING_VERSION)) {
1080         if (m_config->value(SETTING_VERSION, QStringLiteral(CEMU_VERSION)).toString().compare(QStringLiteral(CEMU_VERSION))) {
1081             ask = true;
1082         }
1083     } else {
1084         ask = true;
1085     }
1086 
1087     if (ask) {
1088         if (CEMU_RELEASE) {
1089             setAutoUpdates(true);
1090         }
1091 
1092         m_keepSetup = true;
1093         QCheckBox *cb = new QCheckBox(tr("Keep migratable settings"));
1094         cb->setChecked(true);
1095         QMessageBox msgbox;
1096         msgbox.setText(tr("This version of CEmu is not compatible with your settings, probably made by an older version. "
1097                           "Would you like to erase them to prevent any unexpected behavior?"));
1098         msgbox.setIcon(QMessageBox::Icon::Question);
1099         msgbox.addButton(QMessageBox::Yes);
1100         msgbox.addButton(QMessageBox::No);
1101         msgbox.setDefaultButton(QMessageBox::Yes);
1102         msgbox.setCheckBox(cb);
1103 
1104         connect(cb, &QCheckBox::stateChanged, [this](int state) {
1105             m_keepSetup = static_cast<Qt::CheckState>(state) == Qt::CheckState::Checked;
1106         });
1107 
1108         if (msgbox.exec() == QMessageBox::Yes) {
1109             resetCEmu();
1110         }
1111         setVersion();
1112     }
1113 }
1114 
saveDebug()1115 void MainWindow::saveDebug() {
1116     if (m_config->value(SETTING_DEBUGGER_SAVE_ON_CLOSE, false).toBool()) {
1117         debugExportFile(m_config->value(SETTING_DEBUGGER_IMAGE_PATH).toString());
1118     }
1119 }
1120 
saveSettings()1121 void MainWindow::saveSettings() {
1122     if (opts.useSettings) {
1123         m_config->setValue(SETTING_WINDOW_POSITION, pos());
1124         m_config->setValue(SETTING_WINDOW_GEOMETRY, saveGeometry());
1125         m_config->setValue(SETTING_WINDOW_STATE, saveState());
1126         m_config->setValue(SETTING_CURRENT_DIR, m_dir.absolutePath());
1127         m_config->setValue(SETTING_DEBUGGER_FLASH_BYTES, ui->flashBytes->value());
1128         m_config->setValue(SETTING_DEBUGGER_RAM_BYTES, ui->ramBytes->value());
1129         m_config->setValue(SETTING_DEBUGGER_FLASH_ASCII, ui->flashAscii->isChecked());
1130         m_config->setValue(SETTING_DEBUGGER_RAM_ASCII, ui->ramAscii->isChecked());
1131         m_config->setValue(SETTING_WINDOW_FULLSCREEN, m_fullscreen);
1132         m_config->setValue(SETTING_WINDOW_MEMORY_DOCKS, m_docksMemory);
1133         m_config->setValue(SETTING_WINDOW_MEMORY_DOCK_BYTES, QVariant::fromValue(m_docksMemoryBytes));
1134         m_config->setValue(SETTING_WINDOW_MEMORY_DOCK_ASCII, QVariant::fromValue(m_docksMemoryAscii));
1135         m_config->setValue(SETTING_WINDOW_VISUALIZER_DOCKS, m_docksVisualizer);
1136         m_config->setValue(SETTING_WINDOW_VISUALIZER_CONFIG, m_docksVisualizerConfig);
1137         m_config->setValue(SETTING_WINDOW_KEYHISTORY_DOCKS, m_docksKeyHistory);
1138         m_config->setValue(SETTING_WINDOW_KEYHISTORY_CONFIG, QVariant::fromValue(m_docksKeyHistorySize));
1139 
1140         saveDebug();
1141         stateSaveInfo();
1142         recentSaveInfo();
1143 
1144         m_config->sync();
1145     }
1146 }
1147 
guiExport()1148 void MainWindow::guiExport() {
1149     QString filter = tr("Window Config (*.ini)");
1150     QString path = QFileDialog::getSaveFileName(this, tr("Save window configuration"),
1151                                                 m_dir.absolutePath(), filter, &filter);
1152     if (path.isEmpty()) {
1153         return;
1154     }
1155 
1156     QSettings window(path, QSettings::IniFormat);
1157     window.setValue(SETTING_SCREEN_SKIN, m_config->value(SETTING_SCREEN_SKIN));
1158     window.setValue(SETTING_WINDOW_FULLSCREEN, m_fullscreen);
1159     window.setValue(SETTING_WINDOW_MENUBAR, m_config->value(SETTING_WINDOW_MENUBAR));
1160     window.setValue(SETTING_WINDOW_STATUSBAR, m_config->value(SETTING_WINDOW_STATUSBAR));
1161     window.setValue(SETTING_WINDOW_SEPARATOR, m_config->value(SETTING_WINDOW_SEPARATOR));
1162     window.setValue(SETTING_WINDOW_STATE, saveState());
1163     window.setValue(SETTING_WINDOW_GEOMETRY, saveGeometry());
1164     window.setValue(SETTING_WINDOW_POSITION, pos());
1165     window.setValue(SETTING_WINDOW_MEMORY_DOCKS, m_docksMemory);
1166     window.setValue(SETTING_WINDOW_MEMORY_DOCK_BYTES, QVariant::fromValue(m_docksMemoryBytes));
1167     window.setValue(SETTING_WINDOW_MEMORY_DOCK_ASCII, QVariant::fromValue(m_docksMemoryAscii));
1168     window.setValue(SETTING_WINDOW_VISUALIZER_DOCKS, m_docksVisualizer);
1169     window.setValue(SETTING_WINDOW_VISUALIZER_CONFIG, m_docksVisualizerConfig);
1170     window.setValue(SETTING_WINDOW_KEYHISTORY_DOCKS, m_docksKeyHistory);
1171     window.setValue(SETTING_WINDOW_KEYHISTORY_CONFIG, QVariant::fromValue(m_docksKeyHistorySize));
1172     window.setValue(SETTING_UI_EDIT_MODE, m_uiEditMode);
1173     window.setValue(SETTING_STATUS_INTERVAL, ui->statusInterval->value());
1174     window.sync();
1175 }
1176 
guiImport()1177 void MainWindow::guiImport() {
1178     QString path = QFileDialog::getOpenFileName(this, tr("Select saved image to restore from"),
1179                                                 m_dir.absolutePath(),
1180                                                 tr("Window Config (*.ini);;All files (*.*)"));
1181     if (path.isEmpty()) {
1182         return;
1183     }
1184 
1185     QSettings window(path, QSettings::IniFormat);
1186     ipcCloseConnected();
1187     m_config->setValue(SETTING_SCREEN_SKIN, window.value(SETTING_SCREEN_SKIN));
1188     m_config->setValue(SETTING_WINDOW_FULLSCREEN, window.value(SETTING_WINDOW_FULLSCREEN));
1189     m_config->setValue(SETTING_WINDOW_GEOMETRY, window.value(SETTING_WINDOW_GEOMETRY));
1190     m_config->setValue(SETTING_WINDOW_STATE, window.value(SETTING_WINDOW_STATE));
1191     m_config->setValue(SETTING_WINDOW_POSITION, window.value(SETTING_WINDOW_POSITION));
1192     m_config->setValue(SETTING_WINDOW_MEMORY_DOCKS, window.value(SETTING_WINDOW_MEMORY_DOCKS));
1193     m_config->setValue(SETTING_WINDOW_MEMORY_DOCK_BYTES, window.value(SETTING_WINDOW_MEMORY_DOCK_BYTES));
1194     m_config->setValue(SETTING_WINDOW_MEMORY_DOCK_ASCII, window.value(SETTING_WINDOW_MEMORY_DOCK_ASCII));
1195     m_config->setValue(SETTING_WINDOW_VISUALIZER_DOCKS, window.value(SETTING_WINDOW_VISUALIZER_DOCKS));
1196     m_config->setValue(SETTING_WINDOW_VISUALIZER_CONFIG, window.value(SETTING_WINDOW_VISUALIZER_CONFIG));
1197     m_config->setValue(SETTING_WINDOW_KEYHISTORY_DOCKS, window.value(SETTING_WINDOW_KEYHISTORY_DOCKS));
1198     m_config->setValue(SETTING_WINDOW_KEYHISTORY_CONFIG, window.value(SETTING_WINDOW_KEYHISTORY_CONFIG));
1199     m_config->setValue(SETTING_UI_EDIT_MODE, window.value(SETTING_UI_EDIT_MODE));
1200     m_config->setValue(SETTING_WINDOW_MENUBAR, window.value(SETTING_WINDOW_MENUBAR));
1201     m_config->setValue(SETTING_WINDOW_STATUSBAR, window.value(SETTING_WINDOW_STATUSBAR));
1202     m_config->setValue(SETTING_WINDOW_SEPARATOR, window.value(SETTING_WINDOW_SEPARATOR));
1203     m_config->setValue(SETTING_STATUS_INTERVAL, window.value(SETTING_STATUS_INTERVAL));
1204     m_needReload = true;
1205     close();
1206 }
1207 
keymapExport()1208 void MainWindow::keymapExport() {
1209     QString filter = tr("Keymap Config (*.ini)");
1210     QString path = QFileDialog::getSaveFileName(this, tr("Save keymap configuration"),
1211                                                 m_dir.absolutePath(), filter, &filter);
1212     if (path.isEmpty()) {
1213         return;
1214     }
1215 
1216 
1217     keypadBridge->keymapExport(path);
1218 }
1219 
isFirstRun()1220 bool MainWindow::isFirstRun() {
1221     return !m_config->value(SETTING_FIRST_RUN, false).toBool();
1222 }
1223 
1224