/* Copyright 2016-2023 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #include #ifndef APPLE #include #endif #endif #include #include "OpenGLSupport.h" #include "duckstation/gl/context.h" #include "main.h" #include "CheatsDialog.h" #include "DateTimeDialog.h" #include "EmuSettingsDialog.h" #include "InputConfig/InputConfigDialog.h" #include "VideoSettingsDialog.h" #include "ROMInfoDialog.h" #include "RAMInfoDialog.h" #include "PowerManagement/PowerManagementDialog.h" #include "version.h" #include "Config.h" #include "DSi.h" #include "EmuInstance.h" #include "ArchiveUtil.h" #include "CameraManager.h" #include "CLI.h" // TODO: uniform variable spelling using namespace melonDS; QString NdsRomMimeType = "application/x-nintendo-ds-rom"; QStringList NdsRomExtensions { ".nds", ".srl", ".dsi", ".ids" }; QString GbaRomMimeType = "application/x-gba-rom"; QStringList GbaRomExtensions { ".gba", ".agb" }; QString* systemThemeName; // This list of supported archive formats is based on libarchive(3) version 3.6.2 (2022-12-09). QStringList ArchiveMimeTypes { #ifdef ARCHIVE_SUPPORT_ENABLED "application/zip", "application/x-7z-compressed", "application/vnd.rar", // *.rar "application/x-tar", "application/x-compressed-tar", // *.tar.gz "application/x-xz-compressed-tar", "application/x-bzip-compressed-tar", "application/x-lz4-compressed-tar", "application/x-zstd-compressed-tar", "application/x-tarz", // *.tar.Z "application/x-lzip-compressed-tar", "application/x-lzma-compressed-tar", "application/x-lrzip-compressed-tar", "application/x-tzo", // *.tar.lzo #endif }; QStringList ArchiveExtensions { #ifdef ARCHIVE_SUPPORT_ENABLED ".zip", ".7z", ".rar", ".tar", ".tar.gz", ".tgz", ".tar.xz", ".txz", ".tar.bz2", ".tbz2", ".tar.lz4", ".tlz4", ".tar.zst", ".tzst", ".tar.Z", ".taz", ".tar.lz", ".tar.lzma", ".tlz", ".tar.lrz", ".tlrz", ".tar.lzo", ".tzo" #endif }; QString emuDirectory; bool RunningSomething; //MainWindow* mainWindow; //EmuThread* emuThread; EmuInstance* testinst; CameraManager* camManager[2]; bool camStarted[2]; static bool FileExtensionInList(const QString& filename, const QStringList& extensions, Qt::CaseSensitivity cs = Qt::CaseInsensitive) { return std::any_of(extensions.cbegin(), extensions.cend(), [&](const auto& ext) { return filename.endsWith(ext, cs); }); } static bool MimeTypeInList(const QMimeType& mimetype, const QStringList& superTypeNames) { return std::any_of(superTypeNames.cbegin(), superTypeNames.cend(), [&](const auto& superTypeName) { return mimetype.inherits(superTypeName); }); } static bool NdsRomByExtension(const QString& filename) { return FileExtensionInList(filename, NdsRomExtensions); } static bool GbaRomByExtension(const QString& filename) { return FileExtensionInList(filename, GbaRomExtensions); } static bool SupportedArchiveByExtension(const QString& filename) { return FileExtensionInList(filename, ArchiveExtensions); } static bool NdsRomByMimetype(const QMimeType& mimetype) { return mimetype.inherits(NdsRomMimeType); } static bool GbaRomByMimetype(const QMimeType& mimetype) { return mimetype.inherits(GbaRomMimeType); } static bool SupportedArchiveByMimetype(const QMimeType& mimetype) { return MimeTypeInList(mimetype, ArchiveMimeTypes); } static bool ZstdNdsRomByExtension(const QString& filename) { return filename.endsWith(".zst", Qt::CaseInsensitive) && NdsRomByExtension(filename.left(filename.size() - 4)); } static bool ZstdGbaRomByExtension(const QString& filename) { return filename.endsWith(".zst", Qt::CaseInsensitive) && GbaRomByExtension(filename.left(filename.size() - 4)); } static bool FileIsSupportedFiletype(const QString& filename, bool insideArchive = false) { if (ZstdNdsRomByExtension(filename) || ZstdGbaRomByExtension(filename)) return true; if (NdsRomByExtension(filename) || GbaRomByExtension(filename) || SupportedArchiveByExtension(filename)) return true; const auto matchmode = insideArchive ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, matchmode); return NdsRomByMimetype(mimetype) || GbaRomByMimetype(mimetype) || SupportedArchiveByMimetype(mimetype); } void pathInit() { // First, check for the portable directory next to the executable. QString appdirpath = QCoreApplication::applicationDirPath(); QString portablepath = appdirpath + QDir::separator() + "portable"; #if defined(__APPLE__) // On Apple platforms we may need to navigate outside an app bundle. // The executable directory would be "melonDS.app/Contents/MacOS", so we need to go a total of three steps up. QDir bundledir(appdirpath); if (bundledir.cd("..") && bundledir.cd("..") && bundledir.dirName().endsWith(".app") && bundledir.cd("..")) { portablepath = bundledir.absolutePath() + QDir::separator() + "portable"; } #endif QDir portabledir(portablepath); if (portabledir.exists()) { emuDirectory = portabledir.absolutePath(); } else { // If no overrides are specified, use the default path. #if defined(__WIN32__) && defined(WIN32_PORTABLE) emuDirectory = appdirpath; #else QString confdir; QDir config(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)); config.mkdir("melonDS"); confdir = config.absolutePath() + QDir::separator() + "melonDS"; emuDirectory = confdir; #endif } } void emuStop() { RunningSomething = false; //emit emuThread->windowEmuStop(); } MelonApplication::MelonApplication(int& argc, char** argv) : QApplication(argc, argv) { #if !defined(Q_OS_APPLE) setWindowIcon(QIcon(":/melon-icon")); #if defined(Q_OS_UNIX) setDesktopFileName(QString("net.kuribo64.melonDS")); #endif #endif } bool MelonApplication::event(QEvent *event) { if (event->type() == QEvent::FileOpen) { QFileOpenEvent *openEvent = static_cast(event); /*emuThread->emuPause(); const QStringList file = mainWindow->splitArchivePath(openEvent->file(), true); if (!mainWindow->preloadROMs(file, {}, true)) emuThread->emuUnpause();*/ } return QApplication::event(event); } int main(int argc, char** argv) { srand(time(nullptr)); qputenv("QT_SCALE_FACTOR", "1"); #if QT_VERSION_MAJOR == 6 && defined(__WIN32__) // Allow using the system dark theme palette on Windows qputenv("QT_QPA_PLATFORM", "windows:darkmode=2"); #endif 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"); MelonApplication melon(argc, argv); pathInit(); 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"); if (SDL_Init(SDL_INIT_HAPTIC) < 0) { printf("SDL couldn't init rumble\n"); } if (SDL_Init(SDL_INIT_JOYSTICK) < 0) { printf("SDL couldn't init joystick\n"); } if (SDL_Init(SDL_INIT_AUDIO) < 0) { 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(nullptr, "melonDS", errorStr); return 1; } SDL_JoystickEventState(SDL_ENABLE); SDL_InitSubSystem(SDL_INIT_VIDEO); SDL_EnableScreenSaver(); SDL_DisableScreenSaver(); if (!Config::Load()) QMessageBox::critical(nullptr, "melonDS", "Unable to write to config.\nPlease check the write permissions of the folder you placed melonDS in."); camStarted[0] = false; camStarted[1] = false; camManager[0] = new CameraManager(0, 640, 480, true); camManager[1] = new CameraManager(1, 640, 480, true); systemThemeName = new QString(QApplication::style()->objectName()); { Config::Table cfg = Config::GetGlobalTable(); QString uitheme = cfg.GetQString("UITheme"); if (!uitheme.isEmpty()) { QApplication::setStyle(uitheme); } } /* mainWindow = new MainWindow(); if (options->fullscreen) ToggleFullscreen(mainWindow); MainWindow* poop = new MainWindow(mainWindow); emuThread = new EmuThread(); emuThread->attachWindow(mainWindow); emuThread->attachWindow(poop); emuThread->start(); emuThread->emuPause();*/ testinst = new EmuInstance(0); /*AudioInOut::Init(emuThread); ROMManager::EnableCheats(*emuThread->NDS, Config::EnableCheats != 0); AudioInOut::AudioMute(mainWindow); QObject::connect(&melon, &QApplication::applicationStateChanged, mainWindow, &MainWindow::onAppStateChanged); bool memberSyntaxUsed = false; const auto prepareRomPath = [&](const std::optional& romPath, const std::optional& romArchivePath) -> QStringList { if (!romPath.has_value()) return {}; if (romArchivePath.has_value()) return { *romPath, *romArchivePath }; const QStringList path = mainWindow->splitArchivePath(*romPath, true); if (path.size() > 1) memberSyntaxUsed = true; return path; }; const QStringList dsfile = prepareRomPath(options->dsRomPath, options->dsRomArchivePath); const QStringList gbafile = prepareRomPath(options->gbaRomPath, options->gbaRomArchivePath); if (memberSyntaxUsed) printf("Warning: use the a.zip|b.nds format at your own risk!\n"); mainWindow->preloadROMs(dsfile, gbafile, options->boot);*/ int ret = melon.exec(); delete options; /*emuThread->emuStop(); emuThread->wait(); delete emuThread;*/ delete testinst; //AudioInOut::DeInit(); delete camManager[0]; delete camManager[1]; Config::Save(); SDL_Quit(); return ret; } #ifdef __WIN32__ #include int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdshow) { int ret = main(__argc, __argv); printf("\n\n>"); return ret; } #endif