mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-24 14:49:53 -06:00
434 lines
12 KiB
C++
434 lines
12 KiB
C++
/*
|
|
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 <stdlib.h>
|
|
#include <time.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <optional>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <algorithm>
|
|
|
|
#include <QProcess>
|
|
#include <QApplication>
|
|
#include <QMessageBox>
|
|
#include <QMenuBar>
|
|
#include <QMimeDatabase>
|
|
#include <QFileDialog>
|
|
#include <QInputDialog>
|
|
#include <QPaintEvent>
|
|
#include <QPainter>
|
|
#include <QKeyEvent>
|
|
#include <QMimeData>
|
|
#include <QVector>
|
|
#include <QCommandLineParser>
|
|
#include <QStandardPaths>
|
|
#ifndef _WIN32
|
|
#include <QGuiApplication>
|
|
#include <QSocketNotifier>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include <signal.h>
|
|
#ifndef APPLE
|
|
#include <qpa/qplatformnativeinterface.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
#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<QFileOpenEvent*>(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<QString>& romPath, const std::optional<QString>& 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 <windows.h>
|
|
|
|
int CALLBACK WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR cmdline, int cmdshow)
|
|
{
|
|
int ret = main(__argc, __argv);
|
|
|
|
printf("\n\n>");
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif
|