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); |