diff options
Diffstat (limited to 'src/frontend/qt_sdl/main.cpp')
-rw-r--r-- | src/frontend/qt_sdl/main.cpp | 1311 |
1 files changed, 823 insertions, 488 deletions
diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 31bb3c0..88704b6 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2021 Arisotura + Copyright 2016-2022 melonDS team This file is part of melonDS. @@ -25,6 +25,7 @@ #include <string> #include <algorithm> +#include <QProcess> #include <QApplication> #include <QMessageBox> #include <QMenuBar> @@ -54,12 +55,17 @@ #include "EmuSettingsDialog.h" #include "InputConfig/InputConfigDialog.h" #include "VideoSettingsDialog.h" +#include "CameraSettingsDialog.h" #include "AudioSettingsDialog.h" #include "FirmwareSettingsDialog.h" +#include "PathSettingsDialog.h" +#include "MPSettingsDialog.h" #include "WifiSettingsDialog.h" #include "InterfaceSettingsDialog.h" #include "ROMInfoDialog.h" +#include "RAMInfoDialog.h" #include "TitleManagerDialog.h" +#include "PowerManagement/PowerManagementDialog.h" #include "types.h" #include "version.h" @@ -74,13 +80,16 @@ #include "SPU.h" #include "Wifi.h" #include "Platform.h" +#include "LocalMP.h" #include "Config.h" #include "Savestate.h" #include "main_shaders.h" +#include "ROMManager.h" #include "ArchiveUtil.h" +#include "CameraManager.h" // TODO: uniform variable spelling @@ -97,6 +106,7 @@ bool videoSettingsDirty; SDL_AudioDeviceID audioDevice; int audioFreq; +bool audioMuted; SDL_cond* audioSync; SDL_mutex* audioSyncLock; @@ -107,9 +117,22 @@ u32 micExtBufferWritePos; u32 micWavLength; s16* micWavBuffer; +CameraManager* camManager[2]; +bool camStarted[2]; + +const struct { int id; float ratio; const char* label; } aspectRatios[] = +{ + { 0, 1, "4:3 (native)" }, + { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"}, + { 1, (16.f / 9) / (4.f / 3), "16:9" }, + { 2, (21.f / 9) / (4.f / 3), "21:9" }, + { 3, 0, "window" } +}; + void micCallback(void* data, Uint8* stream, int len); + void audioCallback(void* data, Uint8* stream, int len) { len /= (sizeof(s16) * 2); @@ -125,7 +148,7 @@ void audioCallback(void* data, Uint8* stream, int len) SDL_CondSignal(audioSync); SDL_UnlockMutex(audioSyncLock); - if (num_in < 1) + if ((num_in < 1) || audioMuted) { memset(stream, 0, len*sizeof(s16)*2); return; @@ -145,6 +168,23 @@ void audioCallback(void* data, Uint8* stream, int len) Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len, Config::AudioVolume); } +void audioMute() +{ + int inst = Platform::InstanceID(); + audioMuted = false; + + switch (Config::MPAudioMode) + { + case 1: // only instance 1 + if (inst > 0) audioMuted = true; + break; + + case 2: // only currently focused instance + if (!mainWindow->isActiveWindow()) audioMuted = true; + break; + } +} + void micOpen() { @@ -180,7 +220,7 @@ void micClose() micDevice = 0; } -void micLoadWav(const char* name) +void micLoadWav(std::string name) { SDL_AudioSpec format; memset(&format, 0, sizeof(SDL_AudioSpec)); @@ -191,7 +231,7 @@ void micLoadWav(const char* name) u8* buf; u32 len; - if (!SDL_LoadWAV(name, &format, &buf, &len)) + if (!SDL_LoadWAV(name.c_str(), &format, &buf, &len)) return; const u64 dstfreq = 44100; @@ -317,7 +357,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) EmuPause = 0; RunningSomething = false; - connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(repaint())); + connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint())); connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); @@ -325,7 +365,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger())); connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger())); - connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged())); + connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged())); connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); @@ -413,6 +453,8 @@ void EmuThread::run() double frameLimitError = 0.0; double lastMeasureTime = lastTime; + u32 winUpdateCount = 0, winUpdateFreq = 1; + char melontitle[100]; while (EmuRunning != 0) @@ -499,7 +541,7 @@ void EmuThread::run() micProcess(); // auto screen layout - if (Config::ScreenSizing == 3) + if (Config::ScreenSizing == screenSizing_Auto) { mainScreenPos[2] = mainScreenPos[1]; mainScreenPos[1] = mainScreenPos[0]; @@ -511,14 +553,14 @@ void EmuThread::run() { // constant flickering, likely displaying 3D on both screens // TODO: when both screens are used for 2D only...??? - guess = 0; + guess = screenSizing_Even; } else { if (mainScreenPos[0] == 1) - guess = 1; + guess = screenSizing_EmphTop; else - guess = 2; + guess = screenSizing_EmphBot; } if (guess != autoScreenSizing) @@ -541,6 +583,12 @@ void EmuThread::run() // emulate u32 nlines = NDS::RunFrame(); + if (ROMManager::NDSSave) + ROMManager::NDSSave->CheckFlush(); + + if (ROMManager::GBASave) + ROMManager::GBASave->CheckFlush(); + FrontBufferLock.lock(); FrontBuffer = GPU::FrontBuffer; #ifdef OGLRENDERER_ENABLED @@ -563,11 +611,16 @@ void EmuThread::run() if (EmuRunning == 0) break; - emit windowUpdate(); + winUpdateCount++; + if (winUpdateCount >= winUpdateFreq) + { + emit windowUpdate(); + winUpdateCount = 0; + } bool fastforward = Input::HotkeyDown(HK_FastForward); - if (Config::AudioSync && (!fastforward) && audioDevice) + if (Config::AudioSync && !fastforward && audioDevice) { SDL_LockMutex(audioSyncLock); while (SPU::GetOutputSize() > 1024) @@ -616,7 +669,15 @@ void EmuThread::run() float fpstarget = 1.0/frametimeStep; - sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); + winUpdateFreq = fps / (u32)round(fpstarget); + if (winUpdateFreq < 1) + winUpdateFreq = 1; + + int inst = Platform::InstanceID(); + if (inst == 0) + sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); + else + sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); changeWindowTitle(melontitle); } } @@ -631,7 +692,11 @@ void EmuThread::run() EmuStatus = EmuRunning; - sprintf(melontitle, "melonDS " MELONDS_VERSION); + int inst = Platform::InstanceID(); + if (inst == 0) + sprintf(melontitle, "melonDS " MELONDS_VERSION); + else + sprintf(melontitle, "melonDS (%d)", inst+1); changeWindowTitle(melontitle); SDL_Delay(75); @@ -719,19 +784,39 @@ bool EmuThread::emuIsActive() return (RunningSomething == 1); } +ScreenHandler::ScreenHandler(QWidget* widget) +{ + widget->setMouseTracking(true); + widget->setAttribute(Qt::WA_AcceptTouchEvents); + QTimer* mouseTimer = setupMouseTimer(); + widget->connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) widget->setCursor(Qt::BlankCursor);}); +} + +ScreenHandler::~ScreenHandler() +{ + mouseTimer->stop(); +} void ScreenHandler::screenSetupLayout(int w, int h) { int sizing = Config::ScreenSizing; if (sizing == 3) sizing = autoScreenSizing; - float aspectRatios[] = + float aspectTop, aspectBot; + + for (auto ratio : aspectRatios) { - 1.f, - (16.f/9)/(4.f/3), - (21.f/9)/(4.f/3), - ((float)w/h)/(4.f/3) - }; + if (ratio.id == Config::ScreenAspectTop) + aspectTop = ratio.ratio; + if (ratio.id == Config::ScreenAspectBot) + aspectBot = ratio.ratio; + } + + if (aspectTop == 0) + aspectTop = (float) w / h; + + if (aspectBot == 0) + aspectBot = (float) w / h; Frontend::SetupScreenLayout(w, h, Config::ScreenLayout, @@ -740,8 +825,8 @@ void ScreenHandler::screenSetupLayout(int w, int h) Config::ScreenGap, Config::IntegerScaling != 0, Config::ScreenSwap != 0, - aspectRatios[Config::ScreenAspectTop], - aspectRatios[Config::ScreenAspectBot]); + aspectTop, + aspectBot); numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind); } @@ -754,6 +839,11 @@ QSize ScreenHandler::screenGetMinSize(int factor = 1) int w = 256 * factor; int h = 192 * factor; + if (Config::ScreenSizing == 4 || Config::ScreenSizing == 5) + { + return QSize(w, h); + } + if (Config::ScreenLayout == 0) // natural { if (isHori) @@ -889,7 +979,7 @@ void ScreenHandler::screenHandleTouch(QTouchEvent* event) void ScreenHandler::showCursor() { - mainWindow->panel->setCursor(Qt::ArrowCursor); + mainWindow->panelWidget->setCursor(Qt::ArrowCursor); mouseTimer->start(); } @@ -903,7 +993,7 @@ QTimer* ScreenHandler::setupMouseTimer() return mouseTimer; } -ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent) +ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenHandler(this) { screen[0] = QImage(256, 192, QImage::Format_RGB32); screen[1] = QImage(256, 192, QImage::Format_RGB32); @@ -911,17 +1001,12 @@ ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent) screenTrans[0].reset(); screenTrans[1].reset(); - touching = false; - - setAttribute(Qt::WA_AcceptTouchEvents); - OSD::Init(nullptr); } ScreenPanelNative::~ScreenPanelNative() { OSD::DeInit(nullptr); - mouseTimer->stop(); } void ScreenPanelNative::setupScreenLayout() @@ -1020,17 +1105,11 @@ void ScreenPanelNative::onScreenLayoutChanged() } -ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent) -{ - touching = false; - - setAttribute(Qt::WA_AcceptTouchEvents); -} +ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QOpenGLWidget(parent), ScreenHandler(this) +{} ScreenPanelGL::~ScreenPanelGL() { - mouseTimer->stop(); - makeCurrent(); OSD::DeInit(this); @@ -1281,11 +1360,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) oldW = Config::WindowWidth; oldH = Config::WindowHeight; - oldMax = Config::WindowMaximized!=0; + oldMax = Config::WindowMaximized; setWindowTitle("melonDS " MELONDS_VERSION); setAttribute(Qt::WA_DeleteOnClose); setAcceptDrops(true); + setFocusPolicy(Qt::ClickFocus); + + int inst = Platform::InstanceID(); QMenuBar* menubar = new QMenuBar(); { @@ -1295,16 +1377,16 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open)); - actOpenROMArchive = menu->addAction("Open ROM inside archive..."); + /*actOpenROMArchive = menu->addAction("Open ROM inside archive..."); connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive); - actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT)); + actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ recentMenu = menu->addMenu("Open recent"); for (int i = 0; i < 10; ++i) { - char* item = Config::RecentROMList[i]; - if (strlen(item) > 0) - recentFileList.push_back(item); + std::string item = Config::RecentROMList[i]; + if (!item.empty()) + recentFileList.push_back(QString::fromStdString(item)); } updateRecentFilesMenu(); @@ -1314,6 +1396,41 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) menu->addSeparator(); + actCurrentCart = menu->addAction("DS slot: " + ROMManager::CartLabel()); + actCurrentCart->setEnabled(false); + + actInsertCart = menu->addAction("Insert cart..."); + connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart); + + actEjectCart = menu->addAction("Eject cart"); + connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart); + + menu->addSeparator(); + + actCurrentGBACart = menu->addAction("GBA slot: " + ROMManager::GBACartLabel()); + actCurrentGBACart->setEnabled(false); + + actInsertGBACart = menu->addAction("Insert ROM cart..."); + connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart); + + { + QMenu* submenu = menu->addMenu("Insert add-on cart"); + + actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); + actInsertGBAAddon[0]->setData(QVariant(NDS::GBAAddon_RAMExpansion)); + connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + } + + actEjectGBACart = menu->addAction("Eject cart"); + connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart); + + menu->addSeparator(); + + actImportSavefile = menu->addAction("Import savefile"); + connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); + + menu->addSeparator(); + { QMenu* submenu = menu->addMenu("Save state"); @@ -1351,9 +1468,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); - actImportSavefile = menu->addAction("Import savefile"); - connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); - menu->addSeparator(); actQuit = menu->addAction("Quit"); @@ -1377,20 +1491,39 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) menu->addSeparator(); + actPowerManagement = menu->addAction("Power management"); + connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); + + menu->addSeparator(); + actEnableCheats = menu->addAction("Enable cheats"); actEnableCheats->setCheckable(true); connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats); - actSetupCheats = menu->addAction("Setup cheat codes"); - actSetupCheats->setMenuRole(QAction::NoRole); - connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats); + //if (inst == 0) + { + actSetupCheats = menu->addAction("Setup cheat codes"); + actSetupCheats->setMenuRole(QAction::NoRole); + connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats); + + menu->addSeparator(); + actROMInfo = menu->addAction("ROM info"); + connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); - menu->addSeparator(); - actROMInfo = menu->addAction("ROM info"); - connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); + actRAMInfo = menu->addAction("RAM search"); + connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo); + + actTitleManager = menu->addAction("Manage DSi titles"); + connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); + } - actTitleManager = menu->addAction("Manage DSi titles"); - connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); + { + menu->addSeparator(); + QMenu* submenu = menu->addMenu("Multiplayer"); + + actMPNewInstance = submenu->addAction("Launch new instance"); + connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance); + } } { QMenu* menu = menubar->addMenu("Config"); @@ -1399,7 +1532,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); #ifdef __APPLE__ - QAction* actPreferences = menu->addAction("Preferences..."); + actPreferences = menu->addAction("Preferences..."); connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); actPreferences->setMenuRole(QAction::PreferencesRole); #endif @@ -1410,17 +1543,26 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actVideoSettings = menu->addAction("Video settings"); connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); + actCameraSettings = menu->addAction("Camera settings"); + connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); + actAudioSettings = menu->addAction("Audio settings"); connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); + actMPSettings = menu->addAction("Multiplayer settings"); + connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); + actWifiSettings = menu->addAction("Wifi settings"); connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); + actFirmwareSettings = menu->addAction("Firmware settings"); + connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); + actInterfaceSettings = menu->addAction("Interface settings"); connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); - actFirmwareSettings = menu->addAction("Firmware settings"); - connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); + actPathSettings = menu->addAction("Path settings"); + connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); { QMenu* submenu = menu->addMenu("Savestate settings"); @@ -1503,7 +1645,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"}; - for (int i = 0; i < 6; i++) + for (int i = 0; i < screenSizing_MAX; i++) { actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); actScreenSizing[i]->setActionGroup(grpScreenSizing); @@ -1522,34 +1664,34 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { QMenu* submenu = menu->addMenu("Aspect ratio"); grpScreenAspectTop = new QActionGroup(submenu); + grpScreenAspectBot = new QActionGroup(submenu); + actScreenAspectTop = new QAction*[sizeof(aspectRatios) / sizeof(aspectRatios[0])]; + actScreenAspectBot = new QAction*[sizeof(aspectRatios) / sizeof(aspectRatios[0])]; - const char* aspectRatiosTop[] = {"Top 4:3 (native)", "Top 16:9", "Top 21:9", "Top window"}; - - for (int i = 0; i < 4; i++) + for (int i = 0; i < 2; i++) { - actScreenAspectTop[i] = submenu->addAction(QString(aspectRatiosTop[i])); - actScreenAspectTop[i]->setActionGroup(grpScreenAspectTop); - actScreenAspectTop[i]->setData(QVariant(i)); - actScreenAspectTop[i]->setCheckable(true); - } - - connect(grpScreenAspectTop, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectTop); + QActionGroup* group = grpScreenAspectTop; + QAction** actions = actScreenAspectTop; - submenu->addSeparator(); - - grpScreenAspectBot = new QActionGroup(submenu); + if (i == 1) + { + group = grpScreenAspectBot; + submenu->addSeparator(); + actions = actScreenAspectBot; + } - const char* aspectRatiosBot[] = {"Bottom 4:3 (native)", "Bottom 16:9", "Bottom 21:9", "Bottom window"}; + for (int j = 0; j < sizeof(aspectRatios) / sizeof(aspectRatios[0]); j++) + { + auto ratio = aspectRatios[j]; + QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label); + actions[j] = submenu->addAction(label); + actions[j]->setActionGroup(group); + actions[j]->setData(QVariant(ratio.id)); + actions[j]->setCheckable(true); + } - for (int i = 0; i < 4; i++) - { - actScreenAspectBot[i] = submenu->addAction(QString(aspectRatiosBot[i])); - actScreenAspectBot[i]->setActionGroup(grpScreenAspectBot); - actScreenAspectBot[i]->setData(QVariant(i)); - actScreenAspectBot[i]->setCheckable(true); + connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect); } - - connect(grpScreenAspectBot, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspectBot); } actScreenFiltering = menu->addAction("Screen filtering"); @@ -1574,6 +1716,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) resize(Config::WindowWidth, Config::WindowHeight); + if (Config::FirmwareUsername == "Arisotura") + actMPNewInstance->setText("Fart"); + #ifdef Q_OS_MAC QPoint screenCenter = screen()->availableGeometry().center(); QRect frameGeo = frameGeometry(); @@ -1588,6 +1733,16 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) createScreenPanel(); + actEjectCart->setEnabled(false); + actEjectGBACart->setEnabled(false); + + if (Config::ConsoleType == 1) + { + actInsertGBACart->setEnabled(false); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(false); + } + for (int i = 0; i < 9; i++) { actSaveState[i]->setEnabled(false); @@ -1601,14 +1756,17 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actStop->setEnabled(false); actFrameStep->setEnabled(false); + actPowerManagement->setEnabled(false); + actSetupCheats->setEnabled(false); - actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); - actEnableCheats->setChecked(Config::EnableCheats != 0); + actEnableCheats->setChecked(Config::EnableCheats); actROMInfo->setEnabled(false); + actRAMInfo->setEnabled(false); - actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0); + actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM); actScreenRotation[Config::ScreenRotation]->setChecked(true); @@ -1623,18 +1781,36 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actScreenLayout[Config::ScreenLayout]->setChecked(true); actScreenSizing[Config::ScreenSizing]->setChecked(true); - actIntegerScaling->setChecked(Config::IntegerScaling != 0); + actIntegerScaling->setChecked(Config::IntegerScaling); - actScreenSwap->setChecked(Config::ScreenSwap != 0); + actScreenSwap->setChecked(Config::ScreenSwap); - actScreenAspectTop[Config::ScreenAspectTop]->setChecked(true); - actScreenAspectBot[Config::ScreenAspectBot]->setChecked(true); + for (int i = 0; i < sizeof(aspectRatios) / sizeof(aspectRatios[0]); i++) + { + if (Config::ScreenAspectTop == aspectRatios[i].id) + actScreenAspectTop[i]->setChecked(true); + if (Config::ScreenAspectBot == aspectRatios[i].id) + actScreenAspectBot[i]->setChecked(true); + } - actScreenFiltering->setChecked(Config::ScreenFilter != 0); - actShowOSD->setChecked(Config::ShowOSD != 0); + actScreenFiltering->setChecked(Config::ScreenFilter); + actShowOSD->setChecked(Config::ShowOSD); - actLimitFramerate->setChecked(Config::LimitFPS != 0); - actAudioSync->setChecked(Config::AudioSync != 0); + actLimitFramerate->setChecked(Config::LimitFPS); + actAudioSync->setChecked(Config::AudioSync); + + if (inst > 0) + { + actEmuSettings->setEnabled(false); + actVideoSettings->setEnabled(false); + actMPSettings->setEnabled(false); + actWifiSettings->setEnabled(false); + actInterfaceSettings->setEnabled(false); + +#ifdef __APPLE__ + actPreferences->setEnabled(false); +#endif // __APPLE__ + } } MainWindow::~MainWindow() @@ -1645,17 +1821,13 @@ void MainWindow::createScreenPanel() { hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); - QTimer* mouseTimer; - if (hasOGL) { - panelGL = new ScreenPanelGL(this); + ScreenPanelGL* panelGL = new ScreenPanelGL(this); panelGL->show(); panel = panelGL; - panelGL->setMouseTracking(true); - mouseTimer = panelGL->setupMouseTimer(); - connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) panelGL->setCursor(Qt::BlankCursor);}); + panelWidget = panelGL; if (!panelGL->isValid()) hasOGL = false; @@ -1672,17 +1844,14 @@ void MainWindow::createScreenPanel() if (!hasOGL) { - panelNative = new ScreenPanelNative(this); + ScreenPanelNative* panelNative = new ScreenPanelNative(this); panel = panelNative; - panel->show(); - - panelNative->setMouseTracking(true); - mouseTimer = panelNative->setupMouseTimer(); - connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) panelNative->setCursor(Qt::BlankCursor);}); + panelWidget = panelNative; + panelWidget->show(); } - setCentralWidget(panel); + setCentralWidget(panelWidget); - connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); + connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged())); emit screenLayoutChange(); } @@ -1690,7 +1859,7 @@ QOpenGLContext* MainWindow::getOGLContext() { if (!hasOGL) return nullptr; - QOpenGLWidget* glpanel = (QOpenGLWidget*)panel; + QOpenGLWidget* glpanel = dynamic_cast<QOpenGLWidget*>(panel); return glpanel->context(); } @@ -1755,9 +1924,9 @@ void MainWindow::dragEnterEvent(QDragEnterEvent* event) QStringList acceptedExts{".nds", ".srl", ".dsi", ".gba", ".rar", ".zip", ".7z", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"}; - for(const QString &ext : acceptedExts) + for (const QString &ext : acceptedExts) { - if(filename.endsWith(ext, Qt::CaseInsensitive)) + if (filename.endsWith(ext, Qt::CaseInsensitive)) event->acceptProposedAction(); } } @@ -1769,69 +1938,79 @@ void MainWindow::dropEvent(QDropEvent* event) QList<QUrl> urls = event->mimeData()->urls(); if (urls.count() > 1) return; // not handling more than one file at once - emuThread->emuPause(); - QString filename = urls.at(0).toLocalFile(); - QString ext = filename.right(3).toLower(); + QStringList arcexts{".zip", ".7z", ".rar", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"}; - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); - - char _filename[1024]; - strncpy(_filename, filename.toStdString().c_str(), 1023); _filename[1023] = '\0'; + emuThread->emuPause(); - int slot; int res; - if (ext == "gba") - { - slot = 1; - res = Frontend::LoadROM(_filename, Frontend::ROMSlot_GBA); - } - else if(ext == "nds" || ext == "srl" || ext == "dsi") + if (!verifySetup()) { - slot = 0; - res = Frontend::LoadROM(_filename, Frontend::ROMSlot_NDS); + emuThread->emuUnpause(); + return; } - else + + for (const QString &ext : arcexts) { - QByteArray romBuffer; - QString romFileName = pickAndExtractFileFromArchive(_filename, &romBuffer); - if(romFileName.isEmpty()) - { - res = Frontend::Load_ROMLoadError; - } - else + if (filename.endsWith(ext, Qt::CaseInsensitive)) { - slot = (romFileName.endsWith(".gba", Qt::CaseInsensitive) ? 1 : 0); - QString sramFileName = QFileInfo(_filename).absolutePath() + QDir::separator() + QFileInfo(romFileName).completeBaseName() + ".sav"; - - if(slot == 0) - strncpy(Frontend::NDSROMExtension, QFileInfo(romFileName).suffix().toStdString().c_str(), 4); + QString arcfile = pickFileFromArchive(filename); + if (arcfile.isEmpty()) + { + emuThread->emuUnpause(); + return; + } - res = Frontend::LoadROM((const u8*)romBuffer.constData(), romBuffer.size(), - _filename, romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(), - slot); + filename += "|" + arcfile; } } - if (res != Frontend::Load_OK) - { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else if (slot == 1) + QStringList file = filename.split('|'); + + if (filename.endsWith(".gba", Qt::CaseInsensitive)) { - // checkme + if (!ROMManager::LoadGBAROM(file)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + emuThread->emuUnpause(); + + updateCartInserted(true); } else { + if (!ROMManager::LoadROM(file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + NDS::Start(); emuThread->emuRun(); + + updateCartInserted(false); } } +void MainWindow::focusInEvent(QFocusEvent* event) +{ + audioMute(); +} + +void MainWindow::focusOutEvent(QFocusEvent* event) +{ + audioMute(); +} + void MainWindow::onAppStateChanged(Qt::ApplicationState state) { if (state == Qt::ApplicationInactive) @@ -1846,145 +2025,179 @@ void MainWindow::onAppStateChanged(Qt::ApplicationState state) } } -QString MainWindow::loadErrorStr(int error) +bool MainWindow::verifySetup() { - switch (error) + QString res = ROMManager::VerifySetup(); + if (!res.isEmpty()) { - case Frontend::Load_BIOS9Missing: - return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_BIOS9Bad: - return "DS ARM9 BIOS is not a valid BIOS dump."; + QMessageBox::critical(this, "melonDS", res); + return false; + } - case Frontend::Load_BIOS7Missing: - return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_BIOS7Bad: - return "DS ARM7 BIOS is not a valid BIOS dump."; + return true; +} - case Frontend::Load_FirmwareMissing: - return "DS firmware was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_FirmwareBad: - return "DS firmware is not a valid firmware dump."; - case Frontend::Load_FirmwareNotBootable: - return "DS firmware is not bootable."; +bool MainWindow::preloadROMs(QString filename, QString gbafilename) +{ + if (!verifySetup()) + { + return false; + } - case Frontend::Load_DSiBIOS9Missing: - return "DSi ARM9 BIOS was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_DSiBIOS9Bad: - return "DSi ARM9 BIOS is not a valid BIOS dump."; + bool gbaloaded = false; + if (!gbafilename.isEmpty()) + { + QStringList gbafile = gbafilename.split('|'); + if (!ROMManager::LoadGBAROM(gbafile)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); + return false; + } - case Frontend::Load_DSiBIOS7Missing: - return "DSi ARM7 BIOS was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_DSiBIOS7Bad: - return "DSi ARM7 BIOS is not a valid BIOS dump."; + gbaloaded = true; + } - case Frontend::Load_DSiNANDMissing: - return "DSi NAND was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_DSiNANDBad: - return "DSi NAND is not a valid NAND dump."; + QStringList file = filename.split('|'); + if (!ROMManager::LoadROM(file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + return false; + } - case Frontend::Load_ROMLoadError: - return "Failed to load the ROM. Make sure the file is accessible and isn't used by another application."; + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + NDS::Start(); + emuThread->emuRun(); - default: return "Unknown error during launch; smack Arisotura."; + updateCartInserted(false); + + if (gbaloaded) + { + updateCartInserted(true); } + + return true; } -void MainWindow::loadROM(QByteArray *romData, QString archiveFileName, QString romFileName) +QString MainWindow::pickFileFromArchive(QString archiveFileName) { - recentFileList.removeAll(archiveFileName); - recentFileList.prepend(archiveFileName); - updateRecentFilesMenu(); + QVector<QString> archiveROMList = Archive::ListArchive(archiveFileName); - // Strip entire archive name and get folder path - strncpy(Config::LastROMFolder, QFileInfo(archiveFileName).absolutePath().toStdString().c_str(), 1024); + QString romFileName = ""; // file name inside archive - QString sramFileName = QFileInfo(archiveFileName).absolutePath() + QDir::separator() + QFileInfo(romFileName).completeBaseName() + ".sav"; + if (archiveROMList.size() > 2) + { + archiveROMList.removeFirst(); - int slot; int res; - if (romFileName.endsWith("gba")) + bool ok; + QString toLoad = QInputDialog::getItem(this, "melonDS", + "This archive contains multiple files. Select which ROM you want to load.", archiveROMList.toList(), 0, false, &ok); + if (!ok) // User clicked on cancel + return QString(); + + romFileName = toLoad; + } + else if (archiveROMList.size() == 2) { - slot = 1; - res = Frontend::LoadROM((const u8*)romData->constData(), romData->size(), - archiveFileName.toStdString().c_str(), - romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(), - Frontend::ROMSlot_GBA); + romFileName = archiveROMList.at(1); } - else + else if ((archiveROMList.size() == 1) && (archiveROMList[0] == QString("OK"))) { - strncpy(Frontend::NDSROMExtension, QFileInfo(romFileName).suffix().toStdString().c_str(), 4); - slot = 0; - res = Frontend::LoadROM((const u8*)romData->constData(), romData->size(), - archiveFileName.toStdString().c_str(), - romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(), - Frontend::ROMSlot_NDS); + QMessageBox::warning(this, "melonDS", "This archive is empty."); } - - if (res != Frontend::Load_OK) + else { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); + QMessageBox::critical(this, "melonDS", "This archive could not be read. It may be corrupt or you don't have the permissions."); } - else if (slot == 1) + + return romFileName; +} + +QStringList MainWindow::pickROM(bool gba) +{ + QString console; + QStringList romexts; + QStringList arcexts{"*.zip", "*.7z", "*.rar", "*.tar", "*.tar.gz", "*.tar.xz", "*.tar.bz2"}; + QStringList ret; + + if (gba) { - // checkme - emuThread->emuUnpause(); + console = "GBA"; + romexts.append("*.gba"); } else { - emuThread->emuRun(); + console = "DS"; + romexts.append({"*.nds", "*.dsi", "*.ids", "*.srl"}); } -} -void MainWindow::loadROM(QString filename) -{ - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); + QString filter = romexts.join(' ') + " " + arcexts.join(' '); + filter = console + " ROMs (" + filter + ");;Any file (*.*)"; - // TODO: validate the input file!! - // * check that it is a proper ROM - // * ensure the binary offsets are sane - // * etc + QString filename = QFileDialog::getOpenFileName(this, + "Open "+console+" ROM", + QString::fromStdString(Config::LastROMFolder), + filter); + if (filename.isEmpty()) + return ret; - // this shit is stupid - char file[1024]; - strncpy(file, filename.toStdString().c_str(), 1023); file[1023] = '\0'; + int pos = filename.length() - 1; + while (filename[pos] != '/' && filename[pos] != '\\' && pos > 0) pos--; + QString path_dir = filename.left(pos); + QString path_file = filename.mid(pos+1); - int pos = strlen(file)-1; - while (file[pos] != '/' && file[pos] != '\\' && pos > 0) pos--; - strncpy(Config::LastROMFolder, file, pos); - Config::LastROMFolder[pos] = '\0'; - char* ext = &file[strlen(file)-3]; + Config::LastROMFolder = path_dir.toStdString(); - int slot; int res; - if (!strcasecmp(ext, "gba")) + bool isarc = false; + for (const auto& ext : arcexts) { - slot = 1; - res = Frontend::LoadROM(file, Frontend::ROMSlot_GBA); + int l = ext.length() - 1; + if (path_file.right(l).toLower() == ext.right(l)) + { + isarc = true; + break; + } } - else + + if (isarc) { - slot = 0; - res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); - } + path_file = pickFileFromArchive(filename); + if (path_file.isEmpty()) + return ret; - if (res != Frontend::Load_OK) + ret.append(filename); + ret.append(path_file); + } + else { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); + ret.append(filename); } - else if (slot == 1) + + return ret; +} + +void MainWindow::updateCartInserted(bool gba) +{ + bool inserted; + if (gba) { - // checkme - emuThread->emuUnpause(); + inserted = ROMManager::GBACartInserted() && (Config::ConsoleType == 0); + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + actEjectGBACart->setEnabled(inserted); } else { - emuThread->emuRun(); + inserted = ROMManager::CartInserted(); + actCurrentCart->setText("DS slot: " + ROMManager::CartLabel()); + actEjectCart->setEnabled(inserted); + actImportSavefile->setEnabled(inserted); + actSetupCheats->setEnabled(inserted); + actROMInfo->setEnabled(inserted); + actRAMInfo->setEnabled(inserted); } } @@ -1992,99 +2205,43 @@ void MainWindow::onOpenFile() { emuThread->emuPause(); - QString filename = QFileDialog::getOpenFileName(this, - "Open ROM", - Config::LastROMFolder, - "DS ROMs (*.nds *.dsi *.srl);;GBA ROMs (*.gba *.zip);;Any file (*.*)"); - if (filename.isEmpty()) + if (!verifySetup()) { emuThread->emuUnpause(); return; } - loadROM(filename); -} - -void MainWindow::onOpenFileArchive() -{ - emuThread->emuPause(); - - QString archiveFileName = QFileDialog::getOpenFileName(this, - "Open ROM Archive", - Config::LastROMFolder, - "Archived ROMs (*.zip *.7z *.rar *.tar *.tar.gz *.tar.xz *.tar.bz2);;Any file (*.*)"); - if (archiveFileName.isEmpty()) + QStringList file = pickROM(false); + if (file.isEmpty()) { emuThread->emuUnpause(); return; } - QByteArray romBuffer; - QString romFileName = pickAndExtractFileFromArchive(archiveFileName, &romBuffer); - if(!romFileName.isEmpty()) + if (!ROMManager::LoadROM(file, true)) { - loadROM(&romBuffer, archiveFileName, romFileName); + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; } -} -QString MainWindow::pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer) -{ - printf("Finding list of ROMs...\n"); - QVector<QString> archiveROMList = Archive::ListArchive(archiveFileName.toUtf8().constData()); - - - QString romFileName; // file name inside archive - - if (archiveROMList.size() > 2) - { - archiveROMList.removeFirst(); - - bool ok; - QString toLoad = QInputDialog::getItem(this, "melonDS", - "The archive was found to have multiple files. Select which ROM you want to load.", archiveROMList.toList(), 0, false, &ok); - if(!ok) // User clicked on cancel - return QString(); + QString filename = file.join('|'); + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); - printf("Extracting '%s'\n", toLoad.toUtf8().constData()); - QVector<QString> extractResult = Archive::ExtractFileFromArchive(archiveFileName.toUtf8().constData(), toLoad.toUtf8().constData(), romBuffer); - if (extractResult[0] != QString("Err")) - { - romFileName = extractResult[0]; - } - else - { - QMessageBox::critical(this, "melonDS", QString("There was an error while trying to extract the ROM from the archive: ") + extractResult[1]); - } - } - else if (archiveROMList.size() == 2) - { - printf("Extracting the only ROM in archive\n"); - QVector<QString> extractResult = Archive::ExtractFileFromArchive(archiveFileName.toUtf8().constData(), archiveROMList.at(1).toUtf8().constData(), romBuffer); - if (extractResult[0] != QString("Err")) - { - romFileName = extractResult[0]; - } - else - { - QMessageBox::critical(this, "melonDS", QString("There was an error while trying to extract the ROM from the archive: ") + extractResult[1]); - } - } - else if ((archiveROMList.size() == 1) && (archiveROMList[0] == QString("OK"))) - { - QMessageBox::warning(this, "melonDS", "The archive is intact, but there are no files inside."); - } - else - { - QMessageBox::critical(this, "melonDS", "The archive could not be read. It may be corrupt or you don't have the permissions."); - } + NDS::Start(); + emuThread->emuRun(); - return romFileName; + updateCartInserted(false); } void MainWindow::onClearRecentFiles() { recentFileList.clear(); - memset(Config::RecentROMList, 0, 10 * 1024); + for (int i = 0; i < 10; i++) + Config::RecentROMList[i] = ""; updateRecentFilesMenu(); } @@ -2092,8 +2249,10 @@ void MainWindow::updateRecentFilesMenu() { recentMenu->clear(); - for(int i = 0; i < recentFileList.size(); ++i) + for (int i = 0; i < recentFileList.size(); ++i) { + if (i >= 10) break; + QString item_full = recentFileList.at(i); QString item_display = item_full; int itemlen = item_full.length(); @@ -2120,16 +2279,18 @@ void MainWindow::updateRecentFilesMenu() actRecentFile_i->setData(item_full); connect(actRecentFile_i, &QAction::triggered, this, &MainWindow::onClickRecentFile); - if(i < 10) - strncpy(Config::RecentROMList[i], recentFileList.at(i).toStdString().c_str(), 1024); + Config::RecentROMList[i] = recentFileList.at(i).toStdString(); } + while (recentFileList.size() > 10) + recentFileList.removeLast(); + recentMenu->addSeparator(); QAction *actClearRecentList = recentMenu->addAction("Clear"); connect(actClearRecentList, &QAction::triggered, this, &MainWindow::onClearRecentFiles); - if(recentFileList.empty()) + if (recentFileList.empty()) actClearRecentList->setEnabled(false); Config::Save(); @@ -2138,48 +2299,139 @@ void MainWindow::updateRecentFilesMenu() void MainWindow::onClickRecentFile() { QAction *act = (QAction *)sender(); - QString fileName = act->data().toString(); + QString filename = act->data().toString(); + QStringList file = filename.split('|'); + + emuThread->emuPause(); - if (fileName.endsWith(".gba", Qt::CaseInsensitive) || - fileName.endsWith(".nds", Qt::CaseInsensitive) || - fileName.endsWith(".srl", Qt::CaseInsensitive) || - fileName.endsWith(".dsi", Qt::CaseInsensitive)) + if (!verifySetup()) { - emuThread->emuPause(); - loadROM(fileName); + emuThread->emuUnpause(); + return; } - else + + if (!ROMManager::LoadROM(file, true)) { - // Archives - QString archiveFileName = fileName; - QByteArray romBuffer; - QString romFileName = MainWindow::pickAndExtractFileFromArchive(archiveFileName, &romBuffer); - if(!romFileName.isEmpty()) - { - emuThread->emuPause(); - loadROM(&romBuffer, archiveFileName, romFileName); - } + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; } + + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + NDS::Start(); + emuThread->emuRun(); + + updateCartInserted(false); } void MainWindow::onBootFirmware() { - // TODO: check the whole GBA cart shito + emuThread->emuPause(); + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadBIOS()) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); + emuThread->emuUnpause(); + return; + } + + NDS::Start(); + emuThread->emuRun(); +} + +void MainWindow::onInsertCart() +{ emuThread->emuPause(); - int res = Frontend::LoadBIOS(); - if (res != Frontend::Load_OK) + QStringList file = pickROM(false); + if (file.isEmpty()) { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); emuThread->emuUnpause(); + return; } - else + + if (!ROMManager::LoadROM(file, false)) { - emuThread->emuRun(); + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; } + + emuThread->emuUnpause(); + + updateCartInserted(false); +} + +void MainWindow::onEjectCart() +{ + emuThread->emuPause(); + + ROMManager::EjectCart(); + + emuThread->emuUnpause(); + + updateCartInserted(false); +} + +void MainWindow::onInsertGBACart() +{ + emuThread->emuPause(); + + QStringList file = pickROM(true); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadGBAROM(file)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onInsertGBAAddon() +{ + QAction* act = (QAction*)sender(); + int type = act->data().toInt(); + + emuThread->emuPause(); + + ROMManager::LoadGBAAddon(type); + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onEjectGBACart() +{ + emuThread->emuPause(); + + ROMManager::EjectGBACart(); + + emuThread->emuUnpause(); + + updateCartInserted(true); } void MainWindow::onSaveState() @@ -2188,17 +2440,17 @@ void MainWindow::onSaveState() emuThread->emuPause(); - char filename[1024]; + std::string filename; if (slot > 0) { - Frontend::GetSavestateName(slot, filename, 1024); + filename = ROMManager::GetSavestateName(slot); } else { // TODO: specific 'last directory' for savestate files? QString qfilename = QFileDialog::getSaveFileName(this, "Save state", - Config::LastROMFolder, + QString::fromStdString(Config::LastROMFolder), "melonDS savestates (*.mln);;Any file (*.*)"); if (qfilename.isEmpty()) { @@ -2206,10 +2458,10 @@ void MainWindow::onSaveState() return; } - strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0'; + filename = qfilename.toStdString(); } - if (Frontend::SaveState(filename)) + if (ROMManager::SaveState(filename)) { char msg[64]; if (slot > 0) sprintf(msg, "State saved to slot %d", slot); @@ -2232,17 +2484,17 @@ void MainWindow::onLoadState() emuThread->emuPause(); - char filename[1024]; + std::string filename; if (slot > 0) { - Frontend::GetSavestateName(slot, filename, 1024); + filename = ROMManager::GetSavestateName(slot); } else { // TODO: specific 'last directory' for savestate files? QString qfilename = QFileDialog::getOpenFileName(this, "Load state", - Config::LastROMFolder, + QString::fromStdString(Config::LastROMFolder), "melonDS savestates (*.ml*);;Any file (*.*)"); if (qfilename.isEmpty()) { @@ -2250,7 +2502,7 @@ void MainWindow::onLoadState() return; } - strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0'; + filename = qfilename.toStdString(); } if (!Platform::FileExists(filename)) @@ -2264,7 +2516,7 @@ void MainWindow::onLoadState() return; } - if (Frontend::LoadState(filename)) + if (ROMManager::LoadState(filename)) { char msg[64]; if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); @@ -2284,7 +2536,7 @@ void MainWindow::onLoadState() void MainWindow::onUndoStateLoad() { emuThread->emuPause(); - Frontend::UndoStateLoad(); + ROMManager::UndoStateLoad(); emuThread->emuUnpause(); OSD::AddMessage(0, "State load undone"); @@ -2292,36 +2544,52 @@ void MainWindow::onUndoStateLoad() void MainWindow::onImportSavefile() { - if (!RunningSomething) return; - emuThread->emuPause(); QString path = QFileDialog::getOpenFileName(this, "Select savefile", - Config::LastROMFolder, + QString::fromStdString(Config::LastROMFolder), "Savefiles (*.sav *.bin *.dsv);;Any file (*.*)"); - if (!path.isEmpty()) + if (path.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + FILE* f = Platform::OpenFile(path.toStdString(), "rb", true); + if (!f) + { + QMessageBox::critical(this, "melonDS", "Could not open the given savefile."); + emuThread->emuUnpause(); + return; + } + + if (RunningSomething) { if (QMessageBox::warning(this, - "Emulation will be reset and data overwritten", + "melonDS", "The emulation will be reset and the current savefile overwritten.", - QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok) + QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) { - int res = Frontend::Reset(); - if (res != Frontend::Load_OK) - { - QMessageBox::critical(this, "melonDS", "Reset failed\n" + loadErrorStr(res)); - } - else - { - int diff = Frontend::ImportSRAM(path.toStdString().c_str()); - if (diff > 0) - OSD::AddMessage(0, "Trimmed savefile"); - else if (diff < 0) - OSD::AddMessage(0, "Savefile shorter than SRAM"); - } + emuThread->emuUnpause(); + return; } + + ROMManager::Reset(); } + + u32 len; + fseek(f, 0, SEEK_END); + len = (u32)ftell(f); + + u8* data = new u8[len]; + fseek(f, 0, SEEK_SET); + fread(data, len, 1, f); + + NDS::LoadSave(data, len); + delete[] data; + + fclose(f); emuThread->emuUnpause(); } @@ -2360,19 +2628,10 @@ void MainWindow::onReset() actUndoStateLoad->setEnabled(false); - int res = Frontend::Reset(); - if (res != Frontend::Load_OK) - { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else - { - OSD::AddMessage(0, "Reset"); - emuThread->emuRun(); - } + ROMManager::Reset(); + + OSD::AddMessage(0, "Reset"); + emuThread->emuRun(); } void MainWindow::onStop() @@ -2393,7 +2652,7 @@ void MainWindow::onFrameStep() void MainWindow::onEnableCheats(bool checked) { Config::EnableCheats = checked?1:0; - Frontend::EnableCheats(Config::EnableCheats != 0); + ROMManager::EnableCheats(Config::EnableCheats != 0); } void MainWindow::onSetupCheats() @@ -2414,11 +2673,33 @@ void MainWindow::onROMInfo() ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this); } +void MainWindow::onRAMInfo() +{ + RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this); +} + void MainWindow::onOpenTitleManager() { TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this); } +void MainWindow::onMPNewInstance() +{ + //QProcess::startDetached(QApplication::applicationFilePath()); + QProcess newinst; + newinst.setProgram(QApplication::applicationFilePath()); + newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1)); + +#ifdef __WIN32__ + newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args) + { + args->flags |= CREATE_NEW_CONSOLE; + }); +#endif + + newinst.startDetached(); +} + void MainWindow::onOpenEmuSettings() { emuThread->emuPause(); @@ -2431,11 +2712,33 @@ void MainWindow::onEmuSettingsDialogFinished(int res) { emuThread->emuUnpause(); + if (Config::ConsoleType == 1) + { + actInsertGBACart->setEnabled(false); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(false); + actEjectGBACart->setEnabled(false); + } + else + { + actInsertGBACart->setEnabled(true); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(true); + actEjectGBACart->setEnabled(ROMManager::GBACartInserted()); + } + if (EmuSettingsDialog::needsReset) onReset(); + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + if (!RunningSomething) - actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); +} + +void MainWindow::onOpenPowerManagement() +{ + PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this); } void MainWindow::onOpenInputConfig() @@ -2457,6 +2760,27 @@ void MainWindow::onOpenVideoSettings() connect(dlg, &VideoSettingsDialog::updateVideoSettings, this, &MainWindow::onUpdateVideoSettings); } +void MainWindow::onOpenCameraSettings() +{ + emuThread->emuPause(); + + camStarted[0] = camManager[0]->isStarted(); + camStarted[1] = camManager[1]->isStarted(); + if (camStarted[0]) camManager[0]->stop(); + if (camStarted[1]) camManager[1]->stop(); + + CameraSettingsDialog* dlg = CameraSettingsDialog::openDlg(this); + connect(dlg, &CameraSettingsDialog::finished, this, &MainWindow::onCameraSettingsFinished); +} + +void MainWindow::onCameraSettingsFinished(int res) +{ + if (camStarted[0]) camManager[0]->start(); + if (camStarted[1]) camManager[1]->start(); + + emuThread->emuUnpause(); +} + void MainWindow::onOpenAudioSettings() { AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this); @@ -2480,6 +2804,22 @@ void MainWindow::onFirmwareSettingsFinished(int res) emuThread->emuUnpause(); } +void MainWindow::onOpenPathSettings() +{ + emuThread->emuPause(); + + PathSettingsDialog* dlg = PathSettingsDialog::openDlg(this); + connect(dlg, &PathSettingsDialog::finished, this, &MainWindow::onPathSettingsFinished); +} + +void MainWindow::onPathSettingsFinished(int res) +{ + if (PathSettingsDialog::needsReset) + onReset(); + + emuThread->emuUnpause(); +} + void MainWindow::onUpdateAudioSettings() { SPU::SetInterpolation(Config::AudioInterp); @@ -2515,6 +2855,22 @@ void MainWindow::onAudioSettingsFinished(int res) micOpen(); } +void MainWindow::onOpenMPSettings() +{ + emuThread->emuPause(); + + MPSettingsDialog* dlg = MPSettingsDialog::openDlg(this); + connect(dlg, &MPSettingsDialog::finished, this, &MainWindow::onMPSettingsFinished); +} + +void MainWindow::onMPSettingsFinished(int res) +{ + audioMute(); + LocalMP::SetRecvTimeout(Config::MPRecvTimeout); + + emuThread->emuUnpause(); +} + void MainWindow::onOpenWifiSettings() { emuThread->emuPause(); @@ -2525,12 +2881,6 @@ void MainWindow::onOpenWifiSettings() void MainWindow::onWifiSettingsFinished(int res) { - if (Wifi::MPInited) - { - Platform::MP_DeInit(); - Platform::MP_Init(); - } - Platform::LAN_DeInit(); Platform::LAN_Init(); @@ -2550,10 +2900,7 @@ void MainWindow::onOpenInterfaceSettings() void MainWindow::onUpdateMouseTimer() { - if (hasOGL) - panelGL->mouseTimer->setInterval(Config::MouseHideSeconds*1000); - else - panelNative->mouseTimer->setInterval(Config::MouseHideSeconds*1000); + panel->mouseTimer->setInterval(Config::MouseHideSeconds*1000); } void MainWindow::onInterfaceSettingsFinished(int res) @@ -2569,8 +2916,8 @@ void MainWindow::onChangeSavestateSRAMReloc(bool checked) void MainWindow::onChangeScreenSize() { int factor = ((QAction*)sender())->data().toInt(); - QSize diff = size() - panel->size(); - resize(dynamic_cast<ScreenHandler*>(panel)->screenGetMinSize(factor) + diff); + QSize diff = size() - panelWidget->size(); + resize(panel->screenGetMinSize(factor) + diff); } void MainWindow::onChangeScreenRotation(QAction* act) @@ -2601,6 +2948,22 @@ void MainWindow::onChangeScreenSwap(bool checked) { Config::ScreenSwap = checked?1:0; + // Swap between top and bottom screen when displaying one screen. + if (Config::ScreenSizing == screenSizing_TopOnly) + { + // Bottom Screen. + Config::ScreenSizing = screenSizing_BotOnly; + actScreenSizing[screenSizing_TopOnly]->setChecked(false); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + } + else if (Config::ScreenSizing == screenSizing_BotOnly) + { + // Top Screen. + Config::ScreenSizing = screenSizing_TopOnly; + actScreenSizing[screenSizing_BotOnly]->setChecked(false); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + } + emit screenLayoutChange(); } @@ -2612,18 +2975,19 @@ void MainWindow::onChangeScreenSizing(QAction* act) emit screenLayoutChange(); } -void MainWindow::onChangeScreenAspectTop(QAction* act) +void MainWindow::onChangeScreenAspect(QAction* act) { int aspect = act->data().toInt(); - Config::ScreenAspectTop = aspect; - - emit screenLayoutChange(); -} + QActionGroup* group = act->actionGroup(); -void MainWindow::onChangeScreenAspectBot(QAction* act) -{ - int aspect = act->data().toInt(); - Config::ScreenAspectBot = aspect; + if (group == grpScreenAspectTop) + { + Config::ScreenAspectTop = aspect; + } + else + { + Config::ScreenAspectBot = aspect; + } emit screenLayoutChange(); } @@ -2678,39 +3042,24 @@ void MainWindow::onFullscreenToggled() void MainWindow::onEmuStart() { - // TODO: make savestates work in DSi mode!! - if (Config::ConsoleType == 1) + for (int i = 1; i < 9; i++) { - for (int i = 0; i < 9; i++) - { - actSaveState[i]->setEnabled(false); - actLoadState[i]->setEnabled(false); - } - actUndoStateLoad->setEnabled(false); - } - else - { - for (int i = 1; i < 9; i++) - { - actSaveState[i]->setEnabled(true); - actLoadState[i]->setEnabled(Frontend::SavestateExists(i)); - } - actSaveState[0]->setEnabled(true); - actLoadState[0]->setEnabled(true); - actUndoStateLoad->setEnabled(false); + actSaveState[i]->setEnabled(true); + actLoadState[i]->setEnabled(ROMManager::SavestateExists(i)); } + actSaveState[0]->setEnabled(true); + actLoadState[0]->setEnabled(true); + actUndoStateLoad->setEnabled(false); actPause->setEnabled(true); actPause->setChecked(false); actReset->setEnabled(true); actStop->setEnabled(true); actFrameStep->setEnabled(true); - actImportSavefile->setEnabled(true); - actSetupCheats->setEnabled(true); - actTitleManager->setEnabled(false); + actPowerManagement->setEnabled(true); - actROMInfo->setEnabled(true); + actTitleManager->setEnabled(false); } void MainWindow::onEmuStop() @@ -2723,17 +3072,15 @@ void MainWindow::onEmuStop() actLoadState[i]->setEnabled(false); } actUndoStateLoad->setEnabled(false); - actImportSavefile->setEnabled(false); actPause->setEnabled(false); actReset->setEnabled(false); actStop->setEnabled(false); actFrameStep->setEnabled(false); - actSetupCheats->setEnabled(false); - actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0); + actPowerManagement->setEnabled(false); - actROMInfo->setEnabled(false); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); } void MainWindow::onUpdateVideoSettings(bool glchange) @@ -2743,16 +3090,10 @@ void MainWindow::onUpdateVideoSettings(bool glchange) emuThread->emuPause(); if (hasOGL) - { emuThread->deinitOpenGL(); - delete panelGL; - } - else - { - delete panelNative; - } + delete panel; createScreenPanel(); - connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint())); + connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint())); if (hasOGL) emuThread->initOpenGL(); } @@ -2767,9 +3108,6 @@ void emuStop() { RunningSomething = false; - Frontend::UnloadROM(Frontend::ROMSlot_NDS); - Frontend::UnloadROM(Frontend::ROMSlot_GBA); - emit emuThread->windowEmuStop(); OSD::AddMessage(0xFFC040, "Shutdown"); @@ -2788,7 +3126,8 @@ bool MelonApplication::event(QEvent *event) QFileOpenEvent *openEvent = static_cast<QFileOpenEvent*>(event); emuThread->emuPause(); - mainWindow->loadROM(openEvent->file()); + if (!mainWindow->preloadROMs(openEvent->file(), "")) + emuThread->emuUnpause(); } return QApplication::event(event); @@ -2796,7 +3135,7 @@ bool MelonApplication::event(QEvent *event) int main(int argc, char** argv) { - srand(time(NULL)); + srand(time(nullptr)); printf("melonDS " MELONDS_VERSION "\n"); printf(MELONDS_URL "\n"); @@ -2816,9 +3155,13 @@ int main(int argc, char** argv) { printf("SDL couldn't init joystick\n"); } - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0) + if (SDL_Init(SDL_INIT_AUDIO) < 0) { - QMessageBox::critical(NULL, "melonDS", "SDL shat itself :("); + const char* err = SDL_GetError(); + QString errorStr = "Failed to initialize SDL. This could indicate an issue with your audio driver.\n\nThe error was: "; + errorStr += err; + + QMessageBox::critical(NULL, "melonDS", errorStr); return 1; } @@ -2843,7 +3186,7 @@ int main(int argc, char** argv) SANITIZE(Config::ScreenRotation, 0, 3); SANITIZE(Config::ScreenGap, 0, 500); SANITIZE(Config::ScreenLayout, 0, 3); - SANITIZE(Config::ScreenSizing, 0, 5); + SANITIZE(Config::ScreenSizing, 0, (int)screenSizing_MAX); SANITIZE(Config::ScreenAspectTop, 0, 4); SANITIZE(Config::ScreenAspectBot, 0, 4); #undef SANITIZE @@ -2856,6 +3199,7 @@ int main(int argc, char** argv) format.setSwapInterval(0); QSurfaceFormat::setDefaultFormat(format); + audioMuted = false; audioSync = SDL_CreateCond(); audioSyncLock = SDL_CreateMutex(); @@ -2881,13 +3225,18 @@ int main(int argc, char** argv) micDevice = 0; - memset(micExtBuffer, 0, sizeof(micExtBuffer)); micExtBufferWritePos = 0; micWavBuffer = nullptr; - Frontend::Init_ROM(); - Frontend::EnableCheats(Config::EnableCheats != 0); + camStarted[0] = false; + camStarted[1] = false; + camManager[0] = new CameraManager(0, 640, 480, true); + camManager[1] = new CameraManager(1, 640, 480, true); + camManager[0]->setXFlip(Config::Camera[0].XFlip); + camManager[1]->setXFlip(Config::Camera[1].XFlip); + + ROMManager::EnableCheats(Config::EnableCheats != 0); Frontend::Init_Audio(audioFreq); @@ -2910,33 +3259,17 @@ int main(int argc, char** argv) emuThread->start(); emuThread->emuPause(); + audioMute(); + QObject::connect(&melon, &QApplication::applicationStateChanged, mainWindow, &MainWindow::onAppStateChanged); if (argc > 1) { - char* file = argv[1]; - char* ext = &file[strlen(file)-3]; - - if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl") || !strcasecmp(ext, "dsi")) - { - int res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); + QString file = argv[1]; + QString gbafile = ""; + if (argc > 2) gbafile = argv[2]; - if (res == Frontend::Load_OK) - { - if (argc > 2) - { - file = argv[2]; - ext = &file[strlen(file)-3]; - - if (!strcasecmp(ext, "gba")) - { - Frontend::LoadROM(file, Frontend::ROMSlot_GBA); - } - } - - emuThread->emuRun(); - } - } + mainWindow->preloadROMs(file, gbafile); } int ret = melon.exec(); @@ -2947,8 +3280,6 @@ int main(int argc, char** argv) Input::CloseJoystick(); - Frontend::DeInit_ROM(); - if (audioDevice) SDL_CloseAudioDevice(audioDevice); micClose(); @@ -2957,6 +3288,9 @@ int main(int argc, char** argv) if (micWavBuffer) delete[] micWavBuffer; + delete camManager[0]; + delete camManager[1]; + Config::Save(); SDL_Quit(); @@ -2972,7 +3306,7 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho { int argc = 0; wchar_t** argv_w = CommandLineToArgvW(GetCommandLineW(), &argc); - char* nullarg = ""; + char nullarg[] = {'\0'}; char** argv = new char*[argc]; for (int i = 0; i < argc; i++) @@ -2987,7 +3321,8 @@ int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdsho if (argv_w) LocalFree(argv_w); - /*if (AttachConsole(ATTACH_PARENT_PROCESS)) + //if (AttachConsole(ATTACH_PARENT_PROCESS)) + /*if (AllocConsole()) { freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); |