From 561781b9542fb5667ed43a38ac28c6b90a318405 Mon Sep 17 00:00:00 2001
From: patata <patataofcourse@gmail.com>
Date: Fri, 28 Oct 2022 12:27:55 +0200
Subject: CLI 2.0: Electric Boogaloo (#1546)

* CLI: begin QT reimplementation

* Add first batch of parameters
* ROM loading (no archives yet)
* --fullscreen
* --boot (non-functional???)

* fix --boot

* archives!

* Add disclaimer that a.zip|b.nds is kind of fucky

* remove shit7 debugging

* Apply requested changes
---
 src/frontend/qt_sdl/CLI.cpp        | 135 +++++++++++++++++++++++++++++++++++++
 src/frontend/qt_sdl/CLI.h          |  41 +++++++++++
 src/frontend/qt_sdl/CMakeLists.txt |   3 +
 src/frontend/qt_sdl/ROMManager.cpp |  17 ++---
 src/frontend/qt_sdl/main.cpp       |  72 +++++++++++++-------
 src/frontend/qt_sdl/main.h         |   2 +-
 6 files changed, 235 insertions(+), 35 deletions(-)
 create mode 100644 src/frontend/qt_sdl/CLI.cpp
 create mode 100644 src/frontend/qt_sdl/CLI.h

(limited to 'src')

diff --git a/src/frontend/qt_sdl/CLI.cpp b/src/frontend/qt_sdl/CLI.cpp
new file mode 100644
index 0000000..5d8ebd1
--- /dev/null
+++ b/src/frontend/qt_sdl/CLI.cpp
@@ -0,0 +1,135 @@
+/*
+    Copyright 2021-2022 melonDS team
+
+    This file is part of melonDS.
+
+    melonDS is free software: you can redistribute it and/or modify it under
+    the terms of the GNU General Public License as published by the Free
+    Software Foundation, either version 3 of the License, or (at your option)
+    any later version.
+
+    melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
+    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with melonDS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <QApplication>
+#include <QCommandLineParser>
+#include <QStringList>
+
+#include "CLI.h"
+
+namespace CLI
+{
+
+CommandLineOptions* ManageArgs(QApplication& melon)
+{
+    QCommandLineParser parser;
+    parser.addHelpOption();
+
+    parser.addPositionalArgument("nds", "Nintendo DS ROM (or an archive file which contains it) to load into Slot-1");
+    parser.addPositionalArgument("gba", "GBA ROM (or an archive file which contains it) to load into Slot-2");
+
+    parser.addOption(QCommandLineOption({"b", "boot"}, "Whether to boot firmware on startup. Defaults to \"auto\" (boot if NDS rom given)", "auto/always/never", "auto"));
+    parser.addOption(QCommandLineOption({"f", "fullscreen"}, "Start melonDS in fullscreen mode"));
+    
+#ifdef ARCHIVE_SUPPORT_ENABLED
+    parser.addOption(QCommandLineOption({"a", "archive-file"}, "Specify file to load inside an archive given (NDS)", "rom"));
+    parser.addOption(QCommandLineOption({"A", "archive-file-gba"}, "Specify file to load inside an archive given (GBA)", "rom"));
+#endif
+
+    parser.process(melon);
+
+    CommandLineOptions* options = new CommandLineOptions;
+
+    options->fullscreen = parser.isSet("fullscreen");
+    
+    QStringList posargs = parser.positionalArguments();
+    switch (posargs.size())
+    {
+        default:
+            printf("Too many positional arguments; ignoring 3 onwards\n");
+        case 2:
+            options->gbaRomPath = QStringList(posargs[1]);
+        case 1:
+            options->dsRomPath = QStringList(posargs[0]);
+        case 0:
+            break;
+    }
+
+    QString bootMode = parser.value("boot");
+    if (bootMode == "auto")
+    {
+        options->boot = posargs.size() > 0;
+    } 
+    else if (bootMode == "always")
+    {
+        options->boot = true;
+    }
+    else if (bootMode == "never")
+    {
+        options->boot = false;
+    }
+    else
+    {
+        printf("ERROR: -b/--boot only accepts auto/always/never as arguments\n");
+        exit(1);
+    }
+
+#ifdef ARCHIVE_SUPPORT_ENABLED
+    if (parser.isSet("archive-file"))
+    {
+        if (options->dsRomPath.isEmpty())
+        {
+            options->errorsToDisplay += "Option -a/--archive-file given, but no archive specified!";
+        }
+        else
+        {
+            options->dsRomPath += parser.value("archive-file");
+        }
+    } 
+    else if (!options->dsRomPath.isEmpty())
+    {
+        //TODO-CLI: try to automatically find ROM
+        QStringList paths = options->dsRomPath[0].split("|");
+        if (paths.size() >= 2)
+        {
+            printf("Warning: use the a.zip|b.nds format at your own risk!\n");
+            options->dsRomPath = paths;
+        }
+    }
+
+    if (parser.isSet("archive-file-gba"))
+    {
+        if (options->gbaRomPath.isEmpty())
+        {
+            options->errorsToDisplay += "Option -A/--archive-file-gba given, but no archive specified!";
+        }
+        else
+        {
+            options->gbaRomPath += parser.value("archive-file-gba");
+        }
+    }
+    else if (!options->gbaRomPath.isEmpty())
+    {
+        //TODO-CLI: try to automatically find ROM
+        QStringList paths = options->gbaRomPath[0].split("|");
+        if (paths.size() >= 2)
+        {
+            printf("Warning: use the a.zip|b.gba format at your own risk!\n");
+            options->gbaRomPath = paths;
+        }
+    }
+#endif
+
+    return options;
+}
+
+}
\ No newline at end of file
diff --git a/src/frontend/qt_sdl/CLI.h b/src/frontend/qt_sdl/CLI.h
new file mode 100644
index 0000000..18520fa
--- /dev/null
+++ b/src/frontend/qt_sdl/CLI.h
@@ -0,0 +1,41 @@
+/*
+    Copyright 2021-2022 melonDS team
+
+    This file is part of melonDS.
+
+    melonDS is free software: you can redistribute it and/or modify it under
+    the terms of the GNU General Public License as published by the Free
+    Software Foundation, either version 3 of the License, or (at your option)
+    any later version.
+
+    melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
+    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+    FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+    
+    You should have received a copy of the GNU General Public License along
+    with melonDS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#ifndef CLI_H
+#define CLI_H
+
+#include <QApplication>
+#include <QStringList>
+
+namespace CLI {
+
+struct CommandLineOptions
+{
+    QStringList errorsToDisplay = {};
+
+    QStringList dsRomPath;
+    QStringList gbaRomPath;
+    bool fullscreen;
+    bool boot;
+};
+
+extern CommandLineOptions* ManageArgs(QApplication& melon);
+
+}
+
+#endif
diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt
index bfa58bf..6b964f3 100644
--- a/src/frontend/qt_sdl/CMakeLists.txt
+++ b/src/frontend/qt_sdl/CMakeLists.txt
@@ -49,6 +49,9 @@ set(SOURCES_QT_SDL
     ../duckstation/gl/context.cpp
 
     ${CMAKE_SOURCE_DIR}/res/melon.qrc
+
+    CLI.h
+    CLI.cpp
 )
 
 if (APPLE)
diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp
index 716a454..e5479b2 100644
--- a/src/frontend/qt_sdl/ROMManager.cpp
+++ b/src/frontend/qt_sdl/ROMManager.cpp
@@ -503,6 +503,7 @@ bool LoadROM(QStringList filepath, bool reset)
         if (len > 0x40000000)
         {
             fclose(f);
+            delete[] filedata;
             return false;
         }
 
@@ -528,14 +529,14 @@ bool LoadROM(QStringList filepath, bool reset)
     {
         // file inside archive
 
-        u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
-        if (lenread < 0) return false;
-        if (!filedata) return false;
-        if (lenread != filelen)
-        {
-            delete[] filedata;
-            return false;
-        }
+            s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
+            if (lenread < 0) return false;
+            if (!filedata) return false;
+            if (lenread != filelen)
+            {
+                delete[] filedata;
+                return false;
+            }
 
         std::string std_archivepath = filepath.at(0).toStdString();
         basepath = std_archivepath.substr(0, LastSep(std_archivepath));
diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp
index bea8c6d..e8e30a7 100644
--- a/src/frontend/qt_sdl/main.cpp
+++ b/src/frontend/qt_sdl/main.cpp
@@ -36,6 +36,7 @@
 #include <QKeyEvent>
 #include <QMimeData>
 #include <QVector>
+#include <QCommandLineParser>
 #ifndef _WIN32
 #include <QGuiApplication>
 #include <QSocketNotifier>
@@ -94,6 +95,8 @@
 #include "ArchiveUtil.h"
 #include "CameraManager.h"
 
+#include "CLI.h"
+
 // TODO: uniform variable spelling
 
 bool RunningSomething;
@@ -2126,7 +2129,7 @@ bool MainWindow::verifySetup()
     return true;
 }
 
-bool MainWindow::preloadROMs(QString filename, QString gbafilename)
+bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot)
 {
     if (!verifySetup())
     {
@@ -2134,9 +2137,8 @@ bool MainWindow::preloadROMs(QString filename, QString gbafilename)
     }
 
     bool gbaloaded = false;
-    if (!gbafilename.isEmpty())
+    if (!gbafile.isEmpty())
     {
-        QStringList gbafile = gbafilename.split('|');
         if (!ROMManager::LoadGBAROM(gbafile))
         {
             // TODO: better error reporting?
@@ -2147,20 +2149,33 @@ bool MainWindow::preloadROMs(QString filename, QString gbafilename)
         gbaloaded = true;
     }
 
-    QStringList file = filename.split('|');
-    if (!ROMManager::LoadROM(file, true))
+    bool ndsloaded = false;
+    if (!file.isEmpty())
     {
-        // TODO: better error reporting?
-        QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
-        return false;
+        if (!ROMManager::LoadROM(file, true))
+        {
+            // TODO: better error reporting?
+            QMessageBox::critical(this, "melonDS", "Failed to load the ROM.");
+            return false;
+        }
+        recentFileList.removeAll(file.join("|"));
+        recentFileList.prepend(file.join("|"));
+        updateRecentFilesMenu();
+        ndsloaded = true;
     }
 
-    recentFileList.removeAll(filename);
-    recentFileList.prepend(filename);
-    updateRecentFilesMenu();
-
-    NDS::Start();
-    emuThread->emuRun();
+    if (boot)
+    {
+        if (ndsloaded)
+        {
+            NDS::Start();
+            emuThread->emuRun();
+        }
+        else
+        {
+            onBootFirmware();
+        }
+    }
 
     updateCartInserted(false);
 
@@ -2428,7 +2443,7 @@ void MainWindow::onBootFirmware()
     }
 
     if (!ROMManager::LoadBIOS())
-    {
+{
         // TODO: better error reporting?
         QMessageBox::critical(this, "melonDS", "This firmware is not bootable.");
         emuThread->emuUnpause();
@@ -3115,8 +3130,7 @@ void MainWindow::onTitleUpdate(QString title)
     setWindowTitle(title);
 }
 
-void MainWindow::onFullscreenToggled()
-{
+void ToggleFullscreen(MainWindow* mainWindow) {
     if (!mainWindow->isFullScreen())
     {
         mainWindow->showFullScreen();
@@ -3130,6 +3144,11 @@ void MainWindow::onFullscreenToggled()
     }
 }
 
+void MainWindow::onFullscreenToggled()
+{
+    ToggleFullscreen(this);
+}
+
 void MainWindow::onEmuStart()
 {
     for (int i = 1; i < 9; i++)
@@ -3217,7 +3236,7 @@ bool MelonApplication::event(QEvent *event)
         QFileOpenEvent *openEvent = static_cast<QFileOpenEvent*>(event);
 
         emuThread->emuPause();
-        if (!mainWindow->preloadROMs(openEvent->file(), ""))
+        if (!mainWindow->preloadROMs(openEvent->file().split("|"), {}, true))
             emuThread->emuUnpause();
     }
 
@@ -3233,10 +3252,16 @@ int main(int argc, char** argv)
     printf("melonDS " MELONDS_VERSION "\n");
     printf(MELONDS_URL "\n");
 
+    // easter egg - not worth checking other cases for something so dumb
+    if (argc != 0 && (!strcasecmp(argv[0], "derpDS") || !strcasecmp(argv[0], "./derpDS")))
+        printf("did you just call me a derp???\n");
+    
     Platform::Init(argc, argv);
 
     MelonApplication melon(argc, argv);
 
+    CLI::CommandLineOptions* options = CLI::ManageArgs(melon);
+
     // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl
     SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1");
 
@@ -3339,6 +3364,8 @@ int main(int argc, char** argv)
     Input::OpenJoystick();
 
     mainWindow = new MainWindow();
+    if (options->fullscreen)
+        ToggleFullscreen(mainWindow);
 
     emuThread = new EmuThread();
     emuThread->start();
@@ -3348,14 +3375,7 @@ int main(int argc, char** argv)
 
     QObject::connect(&melon, &QApplication::applicationStateChanged, mainWindow, &MainWindow::onAppStateChanged);
 
-    if (argc > 1)
-    {
-        QString file = argv[1];
-        QString gbafile = "";
-        if (argc > 2) gbafile = argv[2];
-
-        mainWindow->preloadROMs(file, gbafile);
-    }
+    mainWindow->preloadROMs(options->dsRomPath, options->gbaRomPath, options->boot);
 
     int ret = melon.exec();
 
diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h
index 2e2a9ab..9f9fc7c 100644
--- a/src/frontend/qt_sdl/main.h
+++ b/src/frontend/qt_sdl/main.h
@@ -238,7 +238,7 @@ public:
     bool hasOGL;
     GL::Context* getOGLContext();
 
-    bool preloadROMs(QString filename, QString gbafilename);
+    bool preloadROMs(QStringList file, QStringList gbafile, bool boot);
 
     void onAppStateChanged(Qt::ApplicationState state);
 
-- 
cgit v1.2.3