From d6efb03248e3c7751b0c7e5d3b6398ea671325b0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 20:59:11 +0200 Subject: HARK HARK HARK --- src/frontend/FrontendUtil.h | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/frontend/FrontendUtil.h (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h new file mode 100644 index 0000000..6a199b7 --- /dev/null +++ b/src/frontend/FrontendUtil.h @@ -0,0 +1,31 @@ +/* + Copyright 2016-2020 Arisotura + + 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 FRONTENDUTIL_H +#define FRONTENDUTIL_H + +#include "types.h" + +namespace FrontendUtil +{ + +// + +} + +#endif // FRONTENDUTIL_H -- cgit v1.2.3 From 931da1c66fb50138109f8bf4cb157db8f1e79c64 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 22:02:45 +0200 Subject: add a bunch of code --- src/CMakeLists.txt | 2 + src/Config.h | 2 + src/frontend/FrontendUtil.h | 38 ++++++- src/frontend/Util_ROM.cpp | 236 +++++++++++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/main.cpp | 19 ++-- 5 files changed, 284 insertions(+), 13 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 64d922c..dca0ca9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -26,6 +26,8 @@ add_library(core STATIC Savestate.cpp SPI.cpp SPU.cpp + types.h + version.h Wifi.cpp WifiAP.cpp ) diff --git a/src/Config.h b/src/Config.h index 84fd57b..0aeab85 100644 --- a/src/Config.h +++ b/src/Config.h @@ -19,6 +19,8 @@ #ifndef CONFIG_H #define CONFIG_H +#include + #include "types.h" namespace Config diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 6a199b7..7e171ce 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -21,10 +21,44 @@ #include "types.h" -namespace FrontendUtil +namespace Frontend { -// +enum +{ + ROMSlot_NDS = 0, + ROMSlot_GBA, + + ROMSlot_MAX +}; + +extern char ROMPath [ROMSlot_MAX][1024]; +extern char SRAMPath[ROMSlot_MAX][1024]; +extern bool SavestateLoaded; + + +// initialize the ROM handling utility +void Init_ROM(); + +// load a ROM file to the specified cart slot +// note: loading a ROM to the NDS slot resets emulation +bool LoadROM(char* file, int slot); + +// get the filename associated with the given savestate slot +void GetSavestateName(int slot, char* filename, int len); + +// determine whether the given savestate slot does contain a savestate +bool SavestateExists(int slot); + +// load the given savestate file +// if successful, emulation will continue from the savestate's point +bool LoadState(const char* filename); + +// save the current emulator state to the given file +bool SaveState(const char* filename); + +// undo the latest savestate load +void UndoStateLoad(); } diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index b3077f7..574148b 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -15,3 +15,239 @@ 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 "FrontendUtil.h" +#include "Config.h" +#include "qt_sdl/PlatformConfig.h" // FIXME!!! +#include "Platform.h" + +#include "NDS.h" +#include "GBACart.h" + + +namespace Frontend +{ + +char ROMPath [ROMSlot_MAX][1024]; +char SRAMPath [ROMSlot_MAX][1024]; +char PrevSRAMPath[ROMSlot_MAX][1024]; // for savestate 'undo load' + +bool SavestateLoaded; + + +void Init_ROM() +{ + SavestateLoaded = false; + + memset(ROMPath[ROMSlot_NDS], 0, 1024); + memset(ROMPath[ROMSlot_GBA], 0, 1024); + memset(SRAMPath[ROMSlot_NDS], 0, 1024); + memset(SRAMPath[ROMSlot_GBA], 0, 1024); + memset(PrevSRAMPath[ROMSlot_NDS], 0, 1024); + memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024); +} + +void SetupSRAMPath(int slot) +{ + strncpy(SRAMPath[slot], ROMPath[slot], 1023); + SRAMPath[slot][1023] = '\0'; + strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); +} + +bool LoadROM(char* file, int slot) +{ + char oldpath[1024]; + char oldsram[1024]; + strncpy(oldpath, ROMPath[slot], 1024); + strncpy(oldsram, SRAMPath[slot], 1024); + + strncpy(ROMPath[slot], file, 1023); + ROMPath[slot][1023] = '\0'; + + SetupSRAMPath(0); + SetupSRAMPath(1); + + if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], Config::DirectBoot)) + { + SavestateLoaded = false; + + // Reload the inserted GBA cartridge (if any) + if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]); + + strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety + return true; + } + else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot])) + { + SavestateLoaded = false; + + strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety + return true; + } + else + { + strncpy(ROMPath[slot], oldpath, 1024); + strncpy(SRAMPath[slot], oldsram, 1024); + return false; + } +} + + +// SAVESTATE TODO +// * configurable paths. not everyone wants their ROM directory to be polluted, I guess. + +void GetSavestateName(int slot, char* filename, int len) +{ + int pos; + + if (ROMPath[ROMSlot_NDS][0] == '\0') // running firmware, no ROM + { + strcpy(filename, "firmware"); + pos = 8; + } + else + { + int l = strlen(ROMPath[ROMSlot_NDS]); + pos = l; + while (ROMPath[ROMSlot_NDS][pos] != '.' && pos > 0) pos--; + if (pos == 0) pos = l; + + // avoid buffer overflow. shoddy + if (pos > len-5) pos = len-5; + + strncpy(&filename[0], ROMPath[ROMSlot_NDS], pos); + } + strcpy(&filename[pos], ".ml"); + filename[pos+3] = '0'+slot; + filename[pos+4] = '\0'; +} + +bool SavestateExists(int slot) +{ + char ssfile[1024]; + GetSavestateName(slot, ssfile, 1024); + return Platform::FileExists(ssfile); +} + +bool LoadState(const char* filename) +{ + u32 oldGBACartCRC = GBACart::CartCRC; + + // backup + Savestate* backup = new Savestate("timewarp.mln", true); + NDS::DoSavestate(backup); + delete backup; + + bool failed = false; + + Savestate* state = new Savestate(filename, false); + if (state->Error) + { + delete state; + + //uiMsgBoxError(MainWindow, "Error", "Could not load savestate file."); + + // current state might be crapoed, so restore from sane backup + state = new Savestate("timewarp.mln", false); + failed = true; + } + + NDS::DoSavestate(state); + delete state; + + if (!failed) + { + if (Config::SavestateRelocSRAM && ROMPath[ROMSlot_NDS][0]!='\0') + { + strncpy(PrevSRAMPath[ROMSlot_NDS], SRAMPath[0], 1024); + + strncpy(SRAMPath[ROMSlot_NDS], filename, 1019); + int len = strlen(SRAMPath[ROMSlot_NDS]); + strcpy(&SRAMPath[ROMSlot_NDS][len], ".sav"); + SRAMPath[ROMSlot_NDS][len+4] = '\0'; + + NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false); + } + + bool loadedPartialGBAROM = false; + + // in case we have a GBA cart inserted, and the GBA ROM changes + // due to having loaded a save state, we do not want to reload + // the previous cartridge on reset, or commit writes to any + // loaded save file. therefore, their paths are "nulled". + if (GBACart::CartInserted && GBACart::CartCRC != oldGBACartCRC) + { + ROMPath[ROMSlot_GBA][0] = '\0'; + SRAMPath[ROMSlot_GBA][0] = '\0'; + loadedPartialGBAROM = true; + } + + // TODO forward this to user in a meaningful way!! + /*char msg[64]; + if (slot > 0) sprintf(msg, "State loaded from slot %d%s", + slot, loadedPartialGBAROM ? " (GBA ROM header only)" : ""); + else sprintf(msg, "State loaded from file%s", + loadedPartialGBAROM ? " (GBA ROM header only)" : ""); + OSD::AddMessage(0, msg);*/ + + SavestateLoaded = true; + } + + return !failed; +} + +bool SaveState(const char* filename) +{ + Savestate* state = new Savestate(filename, true); + if (state->Error) + { + delete state; + return false; + } + else + { + NDS::DoSavestate(state); + delete state; + + if (Config::SavestateRelocSRAM && ROMPath[ROMSlot_NDS][0]!='\0') + { + strncpy(SRAMPath[ROMSlot_NDS], filename, 1019); + int len = strlen(SRAMPath[ROMSlot_NDS]); + strcpy(&SRAMPath[ROMSlot_NDS][len], ".sav"); + SRAMPath[ROMSlot_NDS][len+4] = '\0'; + + NDS::RelocateSave(SRAMPath[ROMSlot_NDS], true); + } + } + + /*char msg[64]; + if (slot > 0) sprintf(msg, "State saved to slot %d", slot); + else sprintf(msg, "State saved to file"); + OSD::AddMessage(0, msg);*/ + return true; +} + +void UndoStateLoad() +{ + if (!SavestateLoaded) return; + + // pray that this works + // what do we do if it doesn't??? + // but it should work. + Savestate* backup = new Savestate("timewarp.mln", false); + NDS::DoSavestate(backup); + delete backup; + + if (ROMPath[ROMSlot_NDS][0]!='\0') + { + strncpy(SRAMPath[ROMSlot_NDS], PrevSRAMPath[ROMSlot_NDS], 1024); + NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false); + } + + //OSD::AddMessage(0, "State load undone"); +} + +} diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 4999d94..648cd99 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -49,11 +49,6 @@ char* EmuDirectory; bool RunningSomething; -char ROMPath[2][1024]; -char SRAMPath[2][1024]; -char PrevSRAMPath[2][1024]; // for savestate 'undo load' - -bool SavestateLoaded; MainWindow* mainWindow; EmuThread* emuThread; @@ -334,8 +329,8 @@ void EmuThread::emuPause(bool refresh) { int status = refresh ? 2:3; PrevEmuStatus = EmuRunning; - EmuRunning = status;printf("emuPause %d -> %d %d\n", PrevEmuStatus, EmuRunning, EmuStatus); - while (EmuStatus != status);printf("wait done\n"); + EmuRunning = status; + while (EmuStatus != status); } void EmuThread::emuUnpause() @@ -370,7 +365,7 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { - setWindowTitle("melonDS - assfucking Qt version"); + setWindowTitle("melonDS " MELONDS_VERSION); QMenuBar* menubar = new QMenuBar(); { @@ -569,6 +564,7 @@ int main(int argc, char** argv) emuThread->start(); emuThread->emuPause(true); + #if 0 if (argc > 1) { char* file = argv[1]; @@ -601,11 +597,12 @@ int main(int argc, char** argv) } } } + #endif int ret = melon.exec(); -printf("melon over\n"); - emuThread->emuStop();printf("STOP\n"); - emuThread->wait();printf("farked\n"); + + emuThread->emuStop(); + emuThread->wait(); Config::Save(); -- cgit v1.2.3 From 3c883a2152c699b432c951b3498a1adc3d4c21b1 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 27 Apr 2020 22:32:33 +0200 Subject: hey look, it runs shit now! --- src/frontend/FrontendUtil.h | 2 +- src/frontend/Util_ROM.cpp | 2 +- src/frontend/qt_sdl/main.cpp | 60 +++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 58 insertions(+), 6 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 7e171ce..91f3cdd 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -42,7 +42,7 @@ void Init_ROM(); // load a ROM file to the specified cart slot // note: loading a ROM to the NDS slot resets emulation -bool LoadROM(char* file, int slot); +bool LoadROM(const char* file, int slot); // get the filename associated with the given savestate slot void GetSavestateName(int slot, char* filename, int len); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index 574148b..ffee69a 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -57,7 +57,7 @@ void SetupSRAMPath(int slot) strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); } -bool LoadROM(char* file, int slot) +bool LoadROM(const char* file, int slot) { char oldpath[1024]; char oldsram[1024]; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 648cd99..6b4c873 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -35,13 +35,15 @@ #include "types.h" #include "version.h" +#include "FrontendUtil.h" + #include "NDS.h" -#include "GBACart.h" #include "GPU.h" #include "SPU.h" #include "Wifi.h" #include "Platform.h" #include "Config.h" +#include "PlatformConfig.h" #include "Savestate.h" @@ -102,7 +104,7 @@ void EmuThread::run() char melontitle[100]; SDL_mutex* titlemutex = SDL_CreateMutex(); void* titledata[2] = {melontitle, titlemutex}; -printf("emu thread start: %d\n", EmuRunning); + while (EmuRunning != 0) { /*ProcessInput(); @@ -389,8 +391,56 @@ MainWindow::~MainWindow() void MainWindow::onOpenFile() { - QString filename = QFileDialog::getOpenFileName(this, "Open ROM", "", "DS ROMs (*.nds *.srl);;Any file (*.*)"); - printf("fark: %p %d %s\n", filename, filename.isEmpty(), filename.toStdString().c_str()); + emuThread->emuPause(true); + + QString filename = QFileDialog::getOpenFileName(this, + "Open ROM", + Config::LastROMFolder, + "DS ROMs (*.nds *.srl);;GBA ROMs (*.gba);;Any file (*.*)"); + if (filename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + // this shit is stupid + char file[1024]; + strncpy(file, filename.toStdString().c_str(), 1023); file[1023] = '\0'; + + 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]; + + int slot; bool res; + if (!strcasecmp(ext, "gba")) + { + slot = 1; + res = Frontend::LoadROM(file, Frontend::ROMSlot_GBA); + } + else + { + slot = 0; + res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); + } + + if (!res) + { + QMessageBox::critical(this, + "melonDS", + "Failed to load the ROM.\n\nMake sure the file is accessible and isn't used by another application."); + emuThread->emuUnpause(); + } + else if (slot == 1) + { + // checkme + emuThread->emuUnpause(); + } + else + { + emuThread->emuRun(); + } } @@ -557,6 +607,8 @@ int main(int argc, char** argv) } #endif + RunningSomething = false; + mainWindow = new MainWindow(); mainWindow->show(); -- cgit v1.2.3 From 63efc2e02aa51c44c9cb5abc621624aa1d7bf1e5 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 28 Apr 2020 22:45:11 +0200 Subject: add menu items for running the firmware and for quitting. --- src/frontend/FrontendUtil.h | 3 +++ src/frontend/Util_ROM.cpp | 16 ++++++++++++++++ src/frontend/qt_sdl/main.cpp | 44 +++++++++++++++++++++++++++++++++++++++++--- src/frontend/qt_sdl/main.h | 9 +++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 91f3cdd..eb4cb38 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -40,6 +40,9 @@ extern bool SavestateLoaded; // initialize the ROM handling utility void Init_ROM(); +// load the BIOS/firmware and boot from it +bool LoadBIOS(); + // load a ROM file to the specified cart slot // note: loading a ROM to the NDS slot resets emulation bool LoadROM(const char* file, int slot); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index ffee69a..cf40a78 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -57,6 +57,22 @@ void SetupSRAMPath(int slot) strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); } +bool LoadBIOS() +{ + // TODO: + // original code in the libui frontend called NDS::LoadGBAROM() if needed + // should this be carried over here? + // is that behavior consistent with that of LoadROM() below? + + ROMPath[ROMSlot_NDS][0] = '\0'; + SRAMPath[ROMSlot_NDS][0] = '\0'; + + NDS::LoadBIOS(); + + // TODO: error reporting? + return true; +} + bool LoadROM(const char* file, int slot) { char oldpath[1024]; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index e9b9122..cadcec9 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -394,10 +394,17 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) QMenuBar* menubar = new QMenuBar(); { QMenu* menu = menubar->addMenu("File"); - QAction* act; - act = menu->addAction("Open file..."); - connect(act, &QAction::triggered, this, &MainWindow::onOpenFile); + actOpenROM = menu->addAction("Open file..."); + connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); + + actBootFirmware = menu->addAction("Launch DS menu"); + connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); + + menu->addSeparator(); + + actQuit = menu->addAction("Quit"); + connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); } setMenuBar(menubar); @@ -411,6 +418,12 @@ MainWindow::~MainWindow() } +void MainWindow::keyPressEvent(QKeyEvent* event) +{ + printf("key press. %d %d %08X %08X\n", event->key(), event->nativeScanCode(), event->modifiers(), event->nativeModifiers()); +} + + void MainWindow::onOpenFile() { emuThread->emuPause(true); @@ -465,6 +478,31 @@ void MainWindow::onOpenFile() } } +void MainWindow::onBootFirmware() +{ + // TODO: ensure the firmware is actually bootable!! + // TODO: check the whole GBA cart shito + + emuThread->emuPause(true); + + bool res = Frontend::LoadBIOS(); + if (!res) + { + // TODO! + + emuThread->emuUnpause(); + } + else + { + emuThread->emuRun(); + } +} + +void MainWindow::onQuit() +{ + QApplication::quit(); +} + void MainWindow::onTitleUpdate(QString title) { diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index a1093fe..9ccbddd 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -75,13 +75,22 @@ public: explicit MainWindow(QWidget* parent = nullptr); ~MainWindow(); +protected: + void keyPressEvent(QKeyEvent* event) override; + private slots: void onOpenFile(); + void onBootFirmware(); + void onQuit(); void onTitleUpdate(QString title); private: MainWindowPanel* panel; + + QAction* actOpenROM; + QAction* actBootFirmware; + QAction* actQuit; }; #endif // MAIN_H -- cgit v1.2.3 From 5fbad464c27fd5290c837d78a72378215ee8e77f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 29 Apr 2020 00:50:23 +0200 Subject: hook up savestate shito --- src/frontend/FrontendUtil.h | 2 +- src/frontend/Util_ROM.cpp | 2 + src/frontend/qt_sdl/main.cpp | 141 ++++++++++++++++++++++++++++++++++++++++++- src/frontend/qt_sdl/main.h | 6 ++ 4 files changed, 149 insertions(+), 2 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index eb4cb38..d9f5c58 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -47,7 +47,7 @@ bool LoadBIOS(); // note: loading a ROM to the NDS slot resets emulation bool LoadROM(const char* file, int slot); -// get the filename associated with the given savestate slot +// get the filename associated with the given savestate slot (1-8) void GetSavestateName(int slot, char* filename, int len); // determine whether the given savestate slot does contain a savestate diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index cf40a78..19c8eb9 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -69,6 +69,8 @@ bool LoadBIOS() NDS::LoadBIOS(); + SavestateLoaded = false; + // TODO: error reporting? return true; } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index af4fd4b..a6a1423 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -395,7 +395,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { QMenu* menu = menubar->addMenu("File"); - actOpenROM = menu->addAction("Open file..."); + actOpenROM = menu->addAction("Open ROM..."); connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); //actBootFirmware = menu->addAction("Launch DS menu"); @@ -404,6 +404,49 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) menu->addSeparator(); + { + QMenu* submenu = menu->addMenu("Save state"); + + for (int i = 1; i < 9; i++) + { + char title[16]; + sprintf(title, "%d", i); + actSaveState[i] = submenu->addAction(title); + actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1+i-1))); + actSaveState[i]->setData(QVariant(i)); + connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); + } + + actSaveState[0] = submenu->addAction("File..."); + actSaveState[0]->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_F9)); + actSaveState[0]->setData(QVariant(0)); + connect(actSaveState[0], &QAction::triggered, this, &MainWindow::onSaveState); + } + { + QMenu* submenu = menu->addMenu("Load state"); + + for (int i = 1; i < 9; i++) + { + char title[16]; + sprintf(title, "%d", i); + actLoadState[i] = submenu->addAction(title); + actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1+i-1)); + actLoadState[i]->setData(QVariant(i)); + connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actLoadState[0] = submenu->addAction("File..."); + actLoadState[0]->setShortcut(QKeySequence(Qt::Key_F9)); + actLoadState[0]->setData(QVariant(0)); + connect(actLoadState[0], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actUndoStateLoad = menu->addAction("Undo state load"); + actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); + connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); + + menu->addSeparator(); + actQuit = menu->addAction("Quit"); connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); } @@ -499,6 +542,102 @@ void MainWindow::onBootFirmware() } } +void MainWindow::onSaveState() +{ + int slot = ((QAction*)sender())->data().toInt(); + + emuThread->emuPause(true); + + char filename[1024]; + if (slot > 0) + { + Frontend::GetSavestateName(slot, filename, 1024); + } + else + { + // TODO: specific 'last directory' for savestate files? + QString qfilename = QFileDialog::getSaveFileName(this, + "Save state", + Config::LastROMFolder, + "melonDS savestates (*.mln);;Any file (*.*)"); + if (qfilename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0'; + } + + if (Frontend::SaveState(filename)) + { + // TODO: OSD message + } + else + { + // TODO: OSD error message? + } + + emuThread->emuUnpause(); +} + +void MainWindow::onLoadState() +{ + int slot = ((QAction*)sender())->data().toInt(); + + emuThread->emuPause(true); + + char filename[1024]; + if (slot > 0) + { + Frontend::GetSavestateName(slot, filename, 1024); + } + else + { + // TODO: specific 'last directory' for savestate files? + QString qfilename = QFileDialog::getOpenFileName(this, + "Load state", + Config::LastROMFolder, + "melonDS savestates (*.ml*);;Any file (*.*)"); + if (qfilename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0'; + } + + if (!Platform::FileExists(filename)) + { + /*char msg[64]; + if (slot > 0) sprintf(msg, "State slot %d is empty", slot); + else sprintf(msg, "State file does not exist"); + OSD::AddMessage(0xFFA0A0, msg);*/ + + emuThread->emuUnpause(); + return; + } + + if (Frontend::LoadState(filename)) + { + // TODO: OSD message + } + else + { + // TODO: OSD error message? + } + + emuThread->emuUnpause(); +} + +void MainWindow::onUndoStateLoad() +{ + emuThread->emuPause(true); + Frontend::UndoStateLoad(); + emuThread->emuUnpause(); +} + void MainWindow::onQuit() { QApplication::quit(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 9ccbddd..8694838 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -81,6 +81,9 @@ protected: private slots: void onOpenFile(); void onBootFirmware(); + void onSaveState(); + void onLoadState(); + void onUndoStateLoad(); void onQuit(); void onTitleUpdate(QString title); @@ -90,6 +93,9 @@ private: QAction* actOpenROM; QAction* actBootFirmware; + QAction* actSaveState[9]; + QAction* actLoadState[9]; + QAction* actUndoStateLoad; QAction* actQuit; }; -- cgit v1.2.3 From aa4344e249d855bfc2ddd52af61d3213d9e99db3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 2 May 2020 20:25:39 +0200 Subject: add audio output. HARK HARK HARK --- src/CMakeLists.txt | 3 ++ src/frontend/FrontendUtil.h | 13 ++++++ src/frontend/Util_Audio.cpp | 77 ++++++++++++++++++++++++++++++ src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/main.cpp | 96 +++++++++++++++++++++++++++++++++++--- 5 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 src/frontend/Util_Audio.cpp (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dca0ca9..245e6a2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ add_library(core STATIC ARCodeList.cpp AREngine.cpp ARM.cpp + ARM_InstrTable.h ARMInterpreter.cpp ARMInterpreter_ALU.cpp ARMInterpreter_Branch.cpp @@ -12,11 +13,13 @@ add_library(core STATIC CP15.cpp CRC32.cpp DMA.cpp + FIFO.h GBACart.cpp GPU.cpp GPU2D.cpp GPU3D.cpp GPU3D_OpenGL.cpp + GPU3D_OpenGL_shaders.h GPU3D_Soft.cpp NDS.cpp NDSCart.cpp diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index d9f5c58..32f28d1 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -63,6 +63,19 @@ bool SaveState(const char* filename); // undo the latest savestate load void UndoStateLoad(); + +// initialize the audio utility +void Init_Audio(int outputfreq); + +// get how many samples to read from the core audio output +// based on how many are needed by the frontend (outlen in samples) +int AudioOut_GetNumSamples(int outlen); + +// resample audio from the core audio output to match the frontend's +// output frequency, and apply user-specified volume +// note: this assumes the output buffer is interleaved stereo +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen); + } #endif // FRONTENDUTIL_H diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp new file mode 100644 index 0000000..fe0ecab --- /dev/null +++ b/src/frontend/Util_Audio.cpp @@ -0,0 +1,77 @@ +/* + Copyright 2016-2020 Arisotura + + 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 "FrontendUtil.h" +#include "Config.h" +#include "qt_sdl/PlatformConfig.h" // FIXME!!! +#include "Platform.h" + +#include "NDS.h" +#include "GBACart.h" + + +namespace Frontend +{ + +int AudioOut_Freq; +float AudioOut_SampleFrac; + + +void Init_Audio(int outputfreq) +{ + AudioOut_Freq = outputfreq; + AudioOut_SampleFrac = 0; +} + +int AudioOut_GetNumSamples(int outlen) +{ + float f_len_in = (outlen * 32823.6328125) / (float)AudioOut_Freq; + f_len_in += AudioOut_SampleFrac; + int len_in = (int)floor(f_len_in); + AudioOut_SampleFrac = f_len_in - len_in; + + return len_in; +} + +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen) +{ + float res_incr = inlen / (float)outlen; + float res_timer = 0; + int res_pos = 0; + + int volume = Config::AudioVolume; + + for (int i = 0; i < outlen; i++) + { + outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8; + outbuf[i*2+1] = (inbuf[res_pos*2+1] * volume) >> 8; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos++; + } + } +} + +} diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index fb9f547..05a4029 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -6,6 +6,7 @@ SET(SOURCES_QT_SDL PlatformConfig.cpp ../Util_ROM.cpp + ../Util_Audio.cpp ../FrontendUtil.h ) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index bdf68bd..80c25b5 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -48,6 +48,8 @@ #include "Savestate.h" +// TODO: uniform variable spelling + char* EmuDirectory; bool RunningSomething; @@ -55,6 +57,48 @@ bool RunningSomething; MainWindow* mainWindow; EmuThread* emuThread; +SDL_AudioDeviceID audioDevice; +int audioFreq; +SDL_cond* audioSync; +SDL_mutex* audioSyncLock; + + +void audioCallback(void* data, Uint8* stream, int len) +{ + len /= (sizeof(s16) * 2); + + // resample incoming audio to match the output sample rate + + int len_in = Frontend::AudioOut_GetNumSamples(len); + s16 buf_in[1024*2]; + int num_in; + + SDL_LockMutex(audioSyncLock); + num_in = SPU::ReadOutput(buf_in, len_in); + SDL_CondSignal(audioSync); + SDL_UnlockMutex(audioSyncLock); + + if (num_in < 1) + { + memset(stream, 0, len*sizeof(s16)*2); + return; + } + + int margin = 6; + if (num_in < len_in-margin) + { + int last = num_in-1; + if (last < 0) last = 0; + + for (int i = num_in; i < len_in-margin; i++) + ((u32*)buf_in)[i] = ((u32*)buf_in)[last]; + + num_in = len_in-margin; + } + + Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len); +} + EmuThread::EmuThread(QObject* parent) : QThread(parent) { @@ -213,18 +257,18 @@ void EmuThread::run() mainWindow->update(); bool fastforward = false; - /*bool fastforward = HotkeyDown(HK_FastForward); + //bool fastforward = HotkeyDown(HK_FastForward); - if (Config::AudioSync && !fastforward) + if (Config::AudioSync && (!fastforward) && audioDevice) { - SDL_LockMutex(AudioSyncLock); + SDL_LockMutex(audioSyncLock); while (SPU::GetOutputSize() > 1024) { - int ret = SDL_CondWaitTimeout(AudioSync, AudioSyncLock, 500); + int ret = SDL_CondWaitTimeout(audioSync, audioSyncLock, 500); if (ret == SDL_MUTEX_TIMEDOUT) break; } - SDL_UnlockMutex(AudioSyncLock); - }*/ + SDL_UnlockMutex(audioSyncLock); + } float framerate = (1000.0f * nlines) / (60.0f * 263.0f); @@ -338,6 +382,7 @@ void EmuThread::emuRun() // checkme emit windowEmuStart(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } void EmuThread::emuPause(bool refresh) @@ -348,6 +393,7 @@ void EmuThread::emuPause(bool refresh) while (EmuStatus != status); //emit windowEmuPause(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); } void EmuThread::emuUnpause() @@ -355,11 +401,14 @@ void EmuThread::emuUnpause() EmuRunning = PrevEmuStatus; //emit windowEmuUnpause(); + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } void EmuThread::emuStop() { EmuRunning = 0; + + if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); } bool EmuThread::emuIsRunning() @@ -908,6 +957,32 @@ int main(int argc, char** argv) } #endif + audioSync = SDL_CreateCond(); + audioSyncLock = SDL_CreateMutex(); + + audioFreq = 48000; // TODO: make configurable? + SDL_AudioSpec whatIwant, whatIget; + memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); + whatIwant.freq = audioFreq; + whatIwant.format = AUDIO_S16LSB; + whatIwant.channels = 2; + whatIwant.samples = 1024; + whatIwant.callback = audioCallback; + audioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); + if (!audioDevice) + { + printf("Audio init failed: %s\n", SDL_GetError()); + } + else + { + audioFreq = whatIget.freq; + printf("Audio output frequency: %d Hz\n", audioFreq); + SDL_PauseAudioDevice(audioDevice, 1); + } + + Frontend::Init_ROM(); + Frontend::Init_Audio(audioFreq); + mainWindow = new MainWindow(); mainWindow->show(); @@ -955,6 +1030,15 @@ int main(int argc, char** argv) emuThread->emuStop(); emuThread->wait(); + //if (Joystick) SDL_JoystickClose(Joystick); + if (audioDevice) SDL_CloseAudioDevice(audioDevice); + //if (MicDevice) SDL_CloseAudioDevice(MicDevice); + + SDL_DestroyCond(audioSync); + SDL_DestroyMutex(audioSyncLock); + + //if (MicWavBuffer) delete[] MicWavBuffer; + Config::Save(); SDL_Quit(); -- cgit v1.2.3 From c5c9434ac9abaa0ae9c3125ee6e8bc4653846ebd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 17 May 2020 05:42:09 +0200 Subject: verify BIOS and firmware before booting games/firmware --- src/NDS.cpp | 2 +- src/frontend/FrontendUtil.h | 22 +++++++++- src/frontend/Util_ROM.cpp | 100 +++++++++++++++++++++++++++++++++++++++---- src/frontend/qt_sdl/main.cpp | 41 +++++++++++++++--- src/frontend/qt_sdl/main.h | 2 + 5 files changed, 149 insertions(+), 18 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/NDS.cpp b/src/NDS.cpp index 1d425a8..745ed28 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -587,7 +587,7 @@ bool DoSavestate_Scheduler(Savestate* file) } if (funcid == -1) { - printf("savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK STAPLEBUTTER.\n", i); + printf("savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK ARISOTURA.\n", i); return false; } } diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 32f28d1..f4f6850 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -32,6 +32,24 @@ enum ROMSlot_MAX }; +enum +{ + Load_OK = 0, + + Load_BIOS9Missing, + Load_BIOS9Bad, + + Load_BIOS7Missing, + Load_BIOS7Bad, + + Load_FirmwareMissing, + Load_FirmwareBad, + Load_FirmwareNotBootable, + + // TODO: more precise errors for ROM loading + Load_ROMLoadError, +}; + extern char ROMPath [ROMSlot_MAX][1024]; extern char SRAMPath[ROMSlot_MAX][1024]; extern bool SavestateLoaded; @@ -41,11 +59,11 @@ extern bool SavestateLoaded; void Init_ROM(); // load the BIOS/firmware and boot from it -bool LoadBIOS(); +int LoadBIOS(); // load a ROM file to the specified cart slot // note: loading a ROM to the NDS slot resets emulation -bool LoadROM(const char* file, int slot); +int LoadROM(const char* file, int slot); // get the filename associated with the given savestate slot (1-8) void GetSavestateName(int slot, char* filename, int len); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index 19c8eb9..3200de4 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -57,8 +57,77 @@ void SetupSRAMPath(int slot) strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); } -bool LoadBIOS() +int VerifyDSBIOS() { + FILE* f; + long len; + + f = Platform::OpenLocalFile(Config::BIOS9Path, "rb"); + if (!f) return Load_BIOS9Missing; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x1000) + { + fclose(f); + return Load_BIOS9Bad; + } + + fclose(f); + + f = Platform::OpenLocalFile(Config::BIOS7Path, "rb"); + if (!f) return Load_BIOS7Missing; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x4000) + { + fclose(f); + return Load_BIOS7Bad; + } + + fclose(f); + + return Load_OK; +} + +int VerifyDSFirmware() +{ + FILE* f; + long len; + + f = Platform::OpenLocalFile(Config::FirmwarePath, "rb"); + if (!f) return Load_FirmwareMissing; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len == 0x20000) + { + // 128KB firmware, not bootable + fclose(f); + return Load_FirmwareNotBootable; + } + else if (len != 0x40000 && len != 0x80000) + { + fclose(f); + return Load_FirmwareBad; + } + + fclose(f); + + return Load_OK; +} + +int LoadBIOS() +{ + int res; + + res = VerifyDSBIOS(); + if (res != Load_OK) return res; + + res = VerifyDSFirmware(); + if (res != Load_OK) return res; + // TODO: // original code in the libui frontend called NDS::LoadGBAROM() if needed // should this be carried over here? @@ -71,12 +140,26 @@ bool LoadBIOS() SavestateLoaded = false; - // TODO: error reporting? - return true; + return Load_OK; } -bool LoadROM(const char* file, int slot) +int LoadROM(const char* file, int slot) { + int res; + bool directboot = Config::DirectBoot != 0; + + res = VerifyDSBIOS(); + if (res != Load_OK) return res; + + res = VerifyDSFirmware(); + if (res != Load_OK) + { + if (res == Load_FirmwareNotBootable) + directboot = true; + else + return res; + } + char oldpath[1024]; char oldsram[1024]; strncpy(oldpath, ROMPath[slot], 1024); @@ -88,28 +171,29 @@ bool LoadROM(const char* file, int slot) SetupSRAMPath(0); SetupSRAMPath(1); - if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], Config::DirectBoot)) + if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], directboot)) { SavestateLoaded = false; // Reload the inserted GBA cartridge (if any) + // TODO: report failure there?? if (ROMPath[ROMSlot_GBA][0] != '\0') NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]); strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety - return true; + return Load_OK; } else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot])) { SavestateLoaded = false; strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety - return true; + return Load_OK; } else { strncpy(ROMPath[slot], oldpath, 1024); strncpy(SRAMPath[slot], oldsram, 1024); - return false; + return Load_ROMLoadError; } } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 8b97320..ec4b04b 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -570,6 +570,27 @@ void MainWindow::keyPressEvent(QKeyEvent* event) } +QString MainWindow::loadErrorStr(int error) +{ + switch (error) + { + case Frontend::Load_BIOS9Missing: return "DS ARM9 BIOS was not found or could not be accessed."; + case Frontend::Load_BIOS9Bad: return "DS ARM9 BIOS is not a valid BIOS dump."; + + case Frontend::Load_BIOS7Missing: return "DS ARM7 BIOS was not found or could not be accessed."; + case Frontend::Load_BIOS7Bad: return "DS ARM7 BIOS is not a valid BIOS dump."; + + case Frontend::Load_FirmwareMissing: return "DS firmware was not found or could not be accessed."; + case Frontend::Load_FirmwareBad: return "DS firmware is not a valid firmware dump."; + case Frontend::Load_FirmwareNotBootable: return "DS firmware is not bootable."; + + case Frontend::Load_ROMLoadError: return "Failed to load the ROM. Make sure the file is accessible and isn't used by another application."; + + default: return "Unknown error during launch; smack Arisotura."; + } +} + + void MainWindow::onOpenFile() { emuThread->emuPause(true); @@ -584,6 +605,11 @@ void MainWindow::onOpenFile() return; } + // TODO: validate the input file!! + // * check that it is a proper ROM + // * ensure the binary offsets are sane + // * etc + // this shit is stupid char file[1024]; strncpy(file, filename.toStdString().c_str(), 1023); file[1023] = '\0'; @@ -594,7 +620,7 @@ void MainWindow::onOpenFile() Config::LastROMFolder[pos] = '\0'; char* ext = &file[strlen(file)-3]; - int slot; bool res; + int slot; int res; if (!strcasecmp(ext, "gba")) { slot = 1; @@ -606,11 +632,11 @@ void MainWindow::onOpenFile() res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); } - if (!res) + if (res != Frontend::Load_OK) { QMessageBox::critical(this, "melonDS", - "Failed to load the ROM.\n\nMake sure the file is accessible and isn't used by another application."); + loadErrorStr(res)); emuThread->emuUnpause(); } else if (slot == 1) @@ -631,11 +657,12 @@ void MainWindow::onBootFirmware() emuThread->emuPause(true); - bool res = Frontend::LoadBIOS(); - if (!res) + int res = Frontend::LoadBIOS(); + if (res != Frontend::Load_OK) { - // TODO! - + QMessageBox::critical(this, + "melonDS", + loadErrorStr(res)); emuThread->emuUnpause(); } else diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index bb5e903..ee0094f 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -110,6 +110,8 @@ private slots: void onOpenEmuSettings(); private: + QString loadErrorStr(int error); + MainWindowPanel* panel; QAction* actOpenROM; -- cgit v1.2.3 From 2ebb21ce3bcf1a445dce7b13f64e44d6098a3acd Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 22:22:22 +0200 Subject: hook up pause and reset, w/ relevant hotkeys --- src/frontend/FrontendUtil.h | 3 ++ src/frontend/Util_ROM.cpp | 47 +++++++++++++++++++++- src/frontend/qt_sdl/main.cpp | 93 +++++++++++++++++++++++--------------------- src/frontend/qt_sdl/main.h | 10 +++-- 4 files changed, 104 insertions(+), 49 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index f4f6850..d838b63 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -65,6 +65,9 @@ int LoadBIOS(); // note: loading a ROM to the NDS slot resets emulation int LoadROM(const char* file, int slot); +// reset execution of the current ROM +int Reset(); + // get the filename associated with the given savestate slot (1-8) void GetSavestateName(int slot, char* filename, int len); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index 3200de4..e13eb2c 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -50,6 +50,11 @@ void Init_ROM() memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024); } +// TODO: currently, when failing to load a ROM for whatever reason, we attempt +// to revert to the previous state and resume execution; this may not be a very +// good thing, depending on what state the core was left in. +// should we do a better state revert (via the savestate system)? completely stop? + void SetupSRAMPath(int slot) { strncpy(SRAMPath[slot], ROMPath[slot], 1023); @@ -184,7 +189,7 @@ int LoadROM(const char* file, int slot) } else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot])) { - SavestateLoaded = false; + SavestateLoaded = false; // checkme?? strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety return Load_OK; @@ -197,6 +202,46 @@ int LoadROM(const char* file, int slot) } } +int Reset() +{ + int res; + bool directboot = Config::DirectBoot != 0; + + res = VerifyDSBIOS(); + if (res != Load_OK) return res; + + res = VerifyDSFirmware(); + if (res != Load_OK) + { + if (res == Load_FirmwareNotBootable) + directboot = true; + else + return res; + } + + SavestateLoaded = false; + + if (ROMPath[ROMSlot_NDS][0] == '\0') + { + NDS::LoadBIOS(); + } + else + { + SetupSRAMPath(0); + if (!NDS::LoadROM(ROMPath[ROMSlot_NDS], SRAMPath[ROMSlot_NDS], directboot)) + return Load_ROMLoadError; + } + + if (ROMPath[ROMSlot_GBA][0] != '\0') + { + SetupSRAMPath(1); + if (!NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA])) + return Load_ROMLoadError; + } + + return Load_OK; +} + // SAVESTATE TODO // * configurable paths. not everyone wants their ROM directory to be polluted, I guess. diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 35eac70..a73362d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -113,6 +113,8 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); + connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); + connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); emit windowEmuStop(); } @@ -152,14 +154,10 @@ void EmuThread::run() { Input::Process(); - /*if (Input::HotkeyPressed(HK_FastForwardToggle)) - { - Config::LimitFPS = !Config::LimitFPS; - // TODO: reflect in UI! - }*/ + if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); - //if (Input::HotkeyPressed(HK_Pause)) uiQueueMain(TogglePause, NULL); - //if (Input::HotkeyPressed(HK_Reset)) uiQueueMain(Reset, NULL); + if (Input::HotkeyPressed(HK_Pause)) emit windowEmuPause(); + if (Input::HotkeyPressed(HK_Reset)) emit windowEmuReset(); if (GBACart::CartInserted && GBACart::HasSolarSensor) { @@ -318,18 +316,15 @@ void EmuThread::run() lastmeasuretick = lasttick; fpslimitcount = 0; - if (EmuRunning == 2) + /*if (Screen_UseGL) { - /*if (Screen_UseGL) - { - uiGLBegin(GLContext); - uiGLMakeContextCurrent(GLContext); - GLScreen_DrawScreen(); - uiGLEnd(GLContext); - } - uiAreaQueueRedrawAll(MainDrawArea);*/ - mainWindow->update(); + uiGLBegin(GLContext); + uiGLMakeContextCurrent(GLContext); + GLScreen_DrawScreen(); + uiGLEnd(GLContext); } + uiAreaQueueRedrawAll(MainDrawArea);*/ + mainWindow->update(); //if (Screen_UseGL) uiGLMakeContextCurrent(NULL); @@ -338,7 +333,7 @@ void EmuThread::run() sprintf(melontitle, "melonDS " MELONDS_VERSION); changeWindowTitle(melontitle); - SDL_Delay(100); + SDL_Delay(75); } } @@ -375,12 +370,11 @@ void EmuThread::emuRun() if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } -void EmuThread::emuPause(bool refresh) +void EmuThread::emuPause() { - int status = refresh ? 2:3; PrevEmuStatus = EmuRunning; - EmuRunning = status; - while (EmuStatus != status); + EmuRunning = 2; + while (EmuStatus != 2); //emit windowEmuPause(); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); @@ -774,7 +768,7 @@ void MainWindow::dropEvent(QDropEvent* event) QList urls = event->mimeData()->urls(); if (urls.count() > 1) return; // not handling more than one file at once - emuThread->emuPause(true); + emuThread->emuPause(); QString filename = urls.at(0).toLocalFile(); QString ext = filename.right(3); @@ -836,7 +830,7 @@ QString MainWindow::loadErrorStr(int error) void MainWindow::onOpenFile() { - emuThread->emuPause(true); + emuThread->emuPause(); QString filename = QFileDialog::getOpenFileName(this, "Open ROM", @@ -897,7 +891,7 @@ void MainWindow::onBootFirmware() { // TODO: check the whole GBA cart shito - emuThread->emuPause(true); + emuThread->emuPause(); int res = Frontend::LoadBIOS(); if (res != Frontend::Load_OK) @@ -917,7 +911,7 @@ void MainWindow::onSaveState() { int slot = ((QAction*)sender())->data().toInt(); - emuThread->emuPause(true); + emuThread->emuPause(); char filename[1024]; if (slot > 0) @@ -958,7 +952,7 @@ void MainWindow::onLoadState() { int slot = ((QAction*)sender())->data().toInt(); - emuThread->emuPause(true); + emuThread->emuPause(); char filename[1024]; if (slot > 0) @@ -1006,7 +1000,7 @@ void MainWindow::onLoadState() void MainWindow::onUndoStateLoad() { - emuThread->emuPause(true); + emuThread->emuPause(); Frontend::UndoStateLoad(); emuThread->emuUnpause(); } @@ -1019,9 +1013,11 @@ void MainWindow::onQuit() void MainWindow::onPause(bool checked) { - if (emuThread->emuIsRunning()) + if (!RunningSomething) return; + + if (checked) { - emuThread->emuPause(true); + emuThread->emuPause(); } else { @@ -1031,7 +1027,26 @@ void MainWindow::onPause(bool checked) void MainWindow::onReset() { - // + if (!RunningSomething) return; + + emuThread->emuPause(); + + 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(); + } } void MainWindow::onStop() @@ -1047,7 +1062,7 @@ void MainWindow::onOpenEmuSettings() void MainWindow::onOpenInputConfig() { - emuThread->emuPause(true); + emuThread->emuPause(); InputConfigDialog* dlg = InputConfigDialog::openDlg(this); connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); @@ -1143,7 +1158,7 @@ void MainWindow::onEmuStart() } actSaveState[0]->setEnabled(true); actLoadState[0]->setEnabled(true); - actUndoStateLoad->setEnabled(true); + actUndoStateLoad->setEnabled(false); actPause->setEnabled(true); actPause->setChecked(false); @@ -1165,16 +1180,6 @@ void MainWindow::onEmuStop() actStop->setEnabled(false); } -void MainWindow::onEmuPause() -{ - // -} - -void MainWindow::onEmuUnpause() -{ - // -} - int main(int argc, char** argv) @@ -1281,7 +1286,7 @@ int main(int argc, char** argv) emuThread = new EmuThread(); emuThread->start(); - emuThread->emuPause(true); + emuThread->emuPause(); if (argc > 1) { diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index b478f4e..22f045f 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -38,7 +38,7 @@ public: // to be called from the UI thread void emuRun(); - void emuPause(bool refresh); + void emuPause(); void emuUnpause(); void emuStop(); @@ -49,7 +49,10 @@ signals: void windowEmuStart(); void windowEmuStop(); - void windowPauseToggle(); + void windowEmuPause(); + void windowEmuReset(); + + void windowLimitFPSChange(); private: volatile int EmuStatus; @@ -130,14 +133,13 @@ private slots: void onEmuStart(); void onEmuStop(); - void onEmuPause(); - void onEmuUnpause(); private: QString loadErrorStr(int error); MainWindowPanel* panel; +public: QAction* actOpenROM; QAction* actBootFirmware; QAction* actSaveState[9]; -- cgit v1.2.3 From a9b275bc253510d77b8b6fcd80e6d24b56bc8680 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 22:58:04 +0200 Subject: reimplement Stop --- src/SPU.cpp | 3 +++ src/frontend/FrontendUtil.h | 4 ++++ src/frontend/Util_ROM.cpp | 14 ++++++++++++++ src/frontend/qt_sdl/Platform.cpp | 4 ++-- src/frontend/qt_sdl/main.cpp | 37 +++++++++++++++++++++++++++++++------ 5 files changed, 54 insertions(+), 8 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/SPU.cpp b/src/SPU.cpp index 710533e..1ebfd4c 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -116,6 +116,9 @@ void Reset() void Stop() { memset(OutputBuffer, 0, 2*OutputBufferSize*2); + + OutputReadOffset = 0; + OutputWriteOffset = 0; } void DoSavestate(Savestate* file) diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index d838b63..faa29de 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -65,6 +65,10 @@ int LoadBIOS(); // note: loading a ROM to the NDS slot resets emulation int LoadROM(const char* file, int slot); +// unload the ROM loaded in the specified cart slot +// simulating ejection of the cartridge +void UnloadROM(int slot); + // reset execution of the current ROM int Reset(); diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index e13eb2c..ec3186a 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -202,6 +202,20 @@ int LoadROM(const char* file, int slot) } } +void UnloadROM(int slot) +{ + if (slot == ROMSlot_NDS) + { + // TODO! + } + else if (slot == ROMSlot_GBA) + { + GBACart::Eject(); + } + + ROMPath[slot][0] = '\0'; +} + int Reset() { int res; diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 6a5b466..dd838d7 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -55,7 +55,7 @@ char* EmuDirectory; -void Stop(bool internal); +void emuStop(); namespace Platform @@ -133,7 +133,7 @@ void DeInit() void StopEmu() { - //Stop(true); + emuStop(); } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a73362d..67272af 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -115,8 +115,6 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); - - emit windowEmuStop(); } void EmuThread::run() @@ -140,7 +138,6 @@ void EmuThread::run() } Input::Init(); - /*Touching = false;*/ u32 nframes = 0; u32 starttick = SDL_GetTicks(); @@ -376,7 +373,6 @@ void EmuThread::emuPause() EmuRunning = 2; while (EmuStatus != 2); - //emit windowEmuPause(); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); } @@ -384,7 +380,6 @@ void EmuThread::emuUnpause() { EmuRunning = PrevEmuStatus; - //emit windowEmuUnpause(); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); } @@ -703,6 +698,18 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) panel->setMinimumSize(256, 384); + for (int i = 0; i < 9; i++) + { + actSaveState[i]->setEnabled(false); + actLoadState[i]->setEnabled(false); + } + actUndoStateLoad->setEnabled(false); + + actPause->setEnabled(false); + actReset->setEnabled(false); + actStop->setEnabled(false); + + actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0); actScreenRotation[Config::ScreenRotation]->setChecked(true); @@ -1051,7 +1058,10 @@ void MainWindow::onReset() void MainWindow::onStop() { - // + if (!RunningSomething) return; + + emuThread->emuPause(); + NDS::Stop(); } @@ -1168,6 +1178,8 @@ void MainWindow::onEmuStart() void MainWindow::onEmuStop() { + emuThread->emuPause(); + for (int i = 0; i < 9; i++) { actSaveState[i]->setEnabled(false); @@ -1181,6 +1193,19 @@ void MainWindow::onEmuStop() } +void emuStop() +{ + RunningSomething = false; + + Frontend::UnloadROM(Frontend::ROMSlot_NDS); + Frontend::UnloadROM(Frontend::ROMSlot_GBA); + + emit emuThread->windowEmuStop(); + + //OSD::AddMessage(0xFFC040, "Shutdown"); +} + + int main(int argc, char** argv) { -- cgit v1.2.3 From 9e43c85b4d86cdb79242045c5fcf3929e3568322 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 20 May 2020 23:55:18 +0200 Subject: hook up microphone shit. I did my best. --- src/frontend/FrontendUtil.h | 11 +++ src/frontend/Util_Audio.cpp | 64 +++++++++++++++- src/frontend/qt_sdl/main.cpp | 177 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 247 insertions(+), 5 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index faa29de..6a6f8ea 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -101,6 +101,17 @@ int AudioOut_GetNumSamples(int outlen); // note: this assumes the output buffer is interleaved stereo void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen); +// feed silence to the microphone input +void Mic_FeedSilence(); + +// feed random noise to the microphone input +void Mic_FeedNoise(); + +// feed an external buffer to the microphone input +// buffer should be mono +void Mic_FeedExternalBuffer(); +void Mic_SetExternalBuffer(s16* buffer, u32 len); + } #endif // FRONTENDUTIL_H diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index fe0ecab..2088efe 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -26,7 +27,6 @@ #include "Platform.h" #include "NDS.h" -#include "GBACart.h" namespace Frontend @@ -35,13 +35,22 @@ namespace Frontend int AudioOut_Freq; float AudioOut_SampleFrac; +s16* MicBuffer; +u32 MicBufferLength; +u32 MicBufferReadPos; + void Init_Audio(int outputfreq) { AudioOut_Freq = outputfreq; AudioOut_SampleFrac = 0; + + MicBuffer = nullptr; + MicBufferLength = 0; + MicBufferReadPos = 0; } + int AudioOut_GetNumSamples(int outlen) { float f_len_in = (outlen * 32823.6328125) / (float)AudioOut_Freq; @@ -74,4 +83,57 @@ void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen) } } + +void Mic_FeedSilence() +{ + MicBufferReadPos = 0; + NDS::MicInputFrame(NULL, 0); +} + +void Mic_FeedNoise() +{ + // note: DS games seem to expect very saturated 'blowing into mic' noise + + s16 tmp[735]; + + for (int i = 0; i < 735; i++) + { + int val = rand() >> 8; + if (val < -0x8000) val = -0x8000; + else if (val > 0x7FFF) val = 0x7FFF; + + tmp[i] = val; + } + + NDS::MicInputFrame(tmp, 735); +} + +void Mic_FeedExternalBuffer() +{ + if (!MicBuffer) return Mic_FeedSilence(); + + if ((MicBufferReadPos + 735) > MicBufferLength) + { + s16 tmp[735]; + u32 len1 = MicBufferLength - MicBufferReadPos; + memcpy(&tmp[0], &MicBuffer[MicBufferReadPos], len1*sizeof(s16)); + memcpy(&tmp[len1], &MicBuffer[0], (735 - len1)*sizeof(s16)); + + NDS::MicInputFrame(tmp, 735); + MicBufferReadPos = 735 - len1; + } + else + { + NDS::MicInputFrame(&MicBuffer[MicBufferReadPos], 735); + MicBufferReadPos += 735; + } +} + +void Mic_SetExternalBuffer(s16* buffer, u32 len) +{ + MicBuffer = buffer; + MicBufferLength = len; + MicBufferReadPos = 0; +} + } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 67272af..1615fa3 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -66,6 +66,13 @@ int audioFreq; SDL_cond* audioSync; SDL_mutex* audioSyncLock; +SDL_AudioDeviceID micDevice; +s16 micExtBuffer[2048]; +u32 micExtBufferWritePos; + +u32 micWavLength; +s16* micWavBuffer; + void audioCallback(void* data, Uint8* stream, int len) { @@ -104,6 +111,133 @@ void audioCallback(void* data, Uint8* stream, int len) } +void micLoadWav(const char* name) +{ + SDL_AudioSpec format; + memset(&format, 0, sizeof(SDL_AudioSpec)); + + if (micWavBuffer) delete[] micWavBuffer; + micWavBuffer = nullptr; + micWavLength = 0; + + u8* buf; + u32 len; + if (!SDL_LoadWAV(name, &format, &buf, &len)) + return; + + const u64 dstfreq = 44100; + + if (format.format == AUDIO_S16 || format.format == AUDIO_U16) + { + int srcinc = format.channels; + len /= (2 * srcinc); + + micWavLength = (len * dstfreq) / format.freq; + if (micWavLength < 735) micWavLength = 735; + micWavBuffer = new s16[micWavLength]; + + float res_incr = len / (float)micWavLength; + float res_timer = 0; + int res_pos = 0; + + for (int i = 0; i < micWavLength; i++) + { + u16 val = ((u16*)buf)[res_pos]; + if (SDL_AUDIO_ISUNSIGNED(format.format)) val ^= 0x8000; + + micWavBuffer[i] = val; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos += srcinc; + } + } + } + else if (format.format == AUDIO_S8 || format.format == AUDIO_U8) + { + int srcinc = format.channels; + len /= srcinc; + + micWavLength = (len * dstfreq) / format.freq; + if (micWavLength < 735) micWavLength = 735; + micWavBuffer = new s16[micWavLength]; + + float res_incr = len / (float)micWavLength; + float res_timer = 0; + int res_pos = 0; + + for (int i = 0; i < micWavLength; i++) + { + u16 val = buf[res_pos] << 8; + if (SDL_AUDIO_ISUNSIGNED(format.format)) val ^= 0x8000; + + micWavBuffer[i] = val; + + res_timer += res_incr; + while (res_timer >= 1.0) + { + res_timer -= 1.0; + res_pos += srcinc; + } + } + } + else + printf("bad WAV format %08X\n", format.format); + + SDL_FreeWAV(buf); +} + +void micCallback(void* data, Uint8* stream, int len) +{ + s16* input = (s16*)stream; + len /= sizeof(s16); + + int maxlen = sizeof(micExtBuffer) / sizeof(s16); + + if ((micExtBufferWritePos + len) > maxlen) + { + u32 len1 = maxlen - micExtBufferWritePos; + memcpy(&micExtBuffer[micExtBufferWritePos], &input[0], len1*sizeof(s16)); + memcpy(&micExtBuffer[0], &input[len1], (len - len1)*sizeof(s16)); + micExtBufferWritePos = len - len1; + } + else + { + memcpy(&micExtBuffer[micExtBufferWritePos], input, len*sizeof(s16)); + micExtBufferWritePos += len; + } +} + +void micProcess() +{ + int type = Config::MicInputType; + bool cmd = Input::HotkeyDown(HK_Mic); + + if (type != 1 && !cmd) + { + type = 0; + } + + switch (type) + { + case 0: // no mic + Frontend::Mic_FeedSilence(); + break; + + case 1: // host mic + case 3: // WAV + Frontend::Mic_FeedExternalBuffer(); + break; + + case 2: // white noise + Frontend::Mic_FeedNoise(); + break; + } +} + + EmuThread::EmuThread(QObject* parent) : QThread(parent) { EmuStatus = 0; @@ -189,9 +323,9 @@ void EmuThread::run() } // microphone input - /*FeedMicInput(); + micProcess(); - if (Screen_UseGL) + /*if (Screen_UseGL) { uiGLBegin(GLContext); uiGLMakeContextCurrent(GLContext); @@ -365,6 +499,7 @@ void EmuThread::emuRun() // checkme emit windowEmuStart(); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); + if (micDevice) SDL_PauseAudioDevice(micDevice, 0); } void EmuThread::emuPause() @@ -374,6 +509,7 @@ void EmuThread::emuPause() while (EmuStatus != 2); if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); + if (micDevice) SDL_PauseAudioDevice(micDevice, 1); } void EmuThread::emuUnpause() @@ -381,6 +517,7 @@ void EmuThread::emuUnpause() EmuRunning = PrevEmuStatus; if (audioDevice) SDL_PauseAudioDevice(audioDevice, 0); + if (micDevice) SDL_PauseAudioDevice(micDevice, 0); } void EmuThread::emuStop() @@ -388,6 +525,7 @@ void EmuThread::emuStop() EmuRunning = 0; if (audioDevice) SDL_PauseAudioDevice(audioDevice, 1); + if (micDevice) SDL_PauseAudioDevice(micDevice, 1); } bool EmuThread::emuIsRunning() @@ -1300,9 +1438,40 @@ int main(int argc, char** argv) SDL_PauseAudioDevice(audioDevice, 1); } + memset(&whatIwant, 0, sizeof(SDL_AudioSpec)); + whatIwant.freq = 44100; + whatIwant.format = AUDIO_S16LSB; + whatIwant.channels = 1; + whatIwant.samples = 1024; + whatIwant.callback = micCallback; + micDevice = SDL_OpenAudioDevice(NULL, 1, &whatIwant, &whatIget, 0); + if (!micDevice) + { + printf("Mic init failed: %s\n", SDL_GetError()); + } + else + { + SDL_PauseAudioDevice(micDevice, 1); + } + + + memset(micExtBuffer, 0, sizeof(micExtBuffer)); + micExtBufferWritePos = 0; + micWavBuffer = nullptr; + Frontend::Init_ROM(); Frontend::Init_Audio(audioFreq); + if (Config::MicInputType == 1) + { + Frontend::Mic_SetExternalBuffer(micExtBuffer, sizeof(micExtBuffer)/sizeof(s16)); + } + else if (Config::MicInputType == 3) + { + micLoadWav(Config::MicWavPath); + Frontend::Mic_SetExternalBuffer(micWavBuffer, micWavLength); + } + Input::JoystickID = Config::JoystickID; Input::OpenJoystick(); @@ -1349,12 +1518,12 @@ int main(int argc, char** argv) Input::CloseJoystick(); if (audioDevice) SDL_CloseAudioDevice(audioDevice); - //if (MicDevice) SDL_CloseAudioDevice(MicDevice); + if (micDevice) SDL_CloseAudioDevice(micDevice); SDL_DestroyCond(audioSync); SDL_DestroyMutex(audioSyncLock); - //if (MicWavBuffer) delete[] MicWavBuffer; + if (micWavBuffer) delete[] micWavBuffer; Config::Save(); -- cgit v1.2.3 From 8f9369beebf60f3b10d75f50507b194f2ccdaceb Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 21 May 2020 18:43:07 +0200 Subject: add screen layout system --- src/frontend/FrontendUtil.h | 26 +++ src/frontend/Util_Video.cpp | 334 +++++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/main.cpp | 213 ++++++++++++++++++----- src/frontend/qt_sdl/main.h | 17 +- 5 files changed, 545 insertions(+), 46 deletions(-) create mode 100644 src/frontend/Util_Video.cpp (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 6a6f8ea..1fd7329 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -89,6 +89,32 @@ bool SaveState(const char* filename); void UndoStateLoad(); +// setup the display layout based on the provided display size and parameters +// * screenWidth/screenHeight: size of the host display +// * screenLayout: how the DS screens are laid out +// 0 = natural (top screen above bottom screen always) +// 1 = vertical +// 2 = horizontal +// * rotation: angle at which the DS screens are presented: 0/1/2/3 = 0/90/180/270 +// * sizing: how the display size is shared between the two screens +// 0 = even (both screens get same size) +// 1 = emphasize top screen (make top screen as big as possible, fit bottom screen in remaining space) +// 2 = emphasize bottom screen +// * screenGap: size of the gap between the two screens +// * integerScale: force screens to be scaled up at integer scaling factors +void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale); + +// get a 2x3 transform matrix for the given screen (0=top, 1=bottom) +// note: the transform assumes an origin point at the top left of the display, +// X going left and Y going down +// for each screen the source coordinates should be (0,0) and (256,192) +float* GetScreenTransform(int screen); + +// de-transform the provided host display coordinates to get coordinates +// on the bottom screen +void GetTouchCoords(int& x, int& y); + + // initialize the audio utility void Init_Audio(int outputfreq); diff --git a/src/frontend/Util_Video.cpp b/src/frontend/Util_Video.cpp new file mode 100644 index 0000000..cd4e21c --- /dev/null +++ b/src/frontend/Util_Video.cpp @@ -0,0 +1,334 @@ +/* + Copyright 2016-2020 Arisotura + + 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 "FrontendUtil.h" + + +namespace Frontend +{ + +float TopScreenMtx[6]; +float BotScreenMtx[6]; +float TouchMtx[6]; + + +void M23_Identity(float* m) +{ + m[0] = 1; m[1] = 0; + m[2] = 0; m[3] = 1; + m[4] = 0; m[5] = 0; +} + +void M23_Scale(float* m, float s) +{ + m[0] *= s; m[1] *= s; + m[2] *= s; m[3] *= s; + m[4] *= s; m[5] *= s; +} + +void M23_RotateFast(float* m, int angle) +{ + if (angle == 0) return; + + float temp[4]; memcpy(temp, m, sizeof(float)*4); + + switch (angle) + { + case 1: // 90 + m[0] = temp[2]; + m[1] = temp[3]; + m[2] = -temp[0]; + m[3] = -temp[1]; + break; + + case 2: // 180 + m[0] = -temp[0]; + m[1] = -temp[1]; + m[2] = -temp[2]; + m[3] = -temp[3]; + break; + + case 3: // 270 + m[0] = -temp[2]; + m[1] = -temp[3]; + m[2] = temp[0]; + m[3] = temp[1]; + break; + } +} + +void M23_Translate(float* m, float tx, float ty) +{ + m[4] += tx; + m[5] += ty; +} + +void M23_Multiply(float* m, float* _a, float* _b) +{ + float a[6]; memcpy(a, _a, 6*sizeof(float)); + float b[6]; memcpy(b, _b, 6*sizeof(float)); + + m[0] = (a[0] * b[0]) + (a[2] * b[1]); + m[1] = (a[1] * b[0]) + (a[3] * b[1]); + + m[2] = (a[0] * b[2]) + (a[2] * b[3]); + m[3] = (a[1] * b[2]) + (a[3] * b[3]); + + m[4] = (a[0] * b[4]) + (a[2] * b[5]) + a[4]; + m[5] = (a[1] * b[4]) + (a[3] * b[5]) + a[5]; +} + +void M23_Transform(float* m, float& x, float& y) +{ + float vx = x; + float vy = y; + + x = (vx * m[0]) + (vy * m[2]) + m[4]; + y = (vx * m[1]) + (vy * m[3]) + m[5]; +} + + +void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale) +{ + float refpoints[4][2] = + { + {0, 0}, {256, 192}, + {0, 0}, {256, 192} + }; + + int layout = screenLayout == 0 + ? ((rotation % 2 == 0) ? 0 : 1) + : screenLayout - 1; + + float botScale = 1; + float botTrans[4] = {0}; + + M23_Identity(TopScreenMtx); + M23_Identity(BotScreenMtx); + + M23_Translate(TopScreenMtx, -256/2, -192/2); + M23_Translate(BotScreenMtx, -256/2, -192/2); + + // rotation + { + float rotmtx[6]; + M23_Identity(rotmtx); + + M23_RotateFast(rotmtx, rotation); + M23_Multiply(TopScreenMtx, rotmtx, TopScreenMtx); + M23_Multiply(BotScreenMtx, rotmtx, BotScreenMtx); + + M23_Transform(TopScreenMtx, refpoints[0][0], refpoints[0][1]); + M23_Transform(TopScreenMtx, refpoints[1][0], refpoints[1][1]); + M23_Transform(BotScreenMtx, refpoints[2][0], refpoints[2][1]); + M23_Transform(BotScreenMtx, refpoints[3][0], refpoints[3][1]); + } + + // move screens apart + { + int idx = layout == 0 ? 1 : 0; + float offset = + (((layout == 0 && (rotation % 2 == 0)) || (layout == 1 && (rotation % 2 == 1)) + ? 192.f : 256.f) + + screenGap) / 2.f; + if (rotation == 1 || rotation == 2) + offset *= -1.f; + + M23_Translate(TopScreenMtx, (idx==0)?-offset:0, (idx==1)?-offset:0); + M23_Translate(BotScreenMtx, (idx==0)?offset:0, (idx==1)?offset:0); + + refpoints[0][idx] -= offset; + refpoints[1][idx] -= offset; + refpoints[2][idx] += offset; + refpoints[3][idx] += offset; + + botTrans[idx] = offset; + } + + // scale + { + if (sizing == 0) + { + float minX = refpoints[0][0], maxX = minX; + float minY = refpoints[0][1], maxY = minY; + + for (int i = 1; i < 4; i++) + { + minX = std::min(minX, refpoints[i][0]); + minY = std::min(minY, refpoints[i][1]); + maxX = std::max(maxX, refpoints[i][0]); + maxY = std::max(maxY, refpoints[i][1]); + } + + float hSize = maxX - minX; + float vSize = maxY - minY; + + // scale evenly + float scale = std::min(screenWidth / hSize, screenHeight / vSize); + + if (integerScale) + scale = floor(scale); + + M23_Scale(TopScreenMtx, scale); + M23_Scale(BotScreenMtx, scale); + + for (int i = 0; i < 4; i++) + { + refpoints[i][0] *= scale; + refpoints[i][1] *= scale; + } + + botScale = scale; + } + else + { + int primOffset = (sizing == 1) ? 0 : 2; + int secOffset = (sizing == 1) ? 2 : 0; + float* primMtx = (sizing == 1) ? TopScreenMtx : BotScreenMtx; + float* secMtx = (sizing == 1) ? BotScreenMtx : TopScreenMtx; + + float primMinX = refpoints[primOffset][0], primMaxX = primMinX; + float primMinY = refpoints[primOffset][1], primMaxY = primMinY; + float secMinX = refpoints[secOffset][0], secMaxX = secMinX; + float secMinY = refpoints[secOffset][1], secMaxY = secMinY; + + primMinX = std::min(primMinX, refpoints[primOffset+1][0]); + primMinY = std::min(primMinY, refpoints[primOffset+1][1]); + primMaxX = std::max(primMaxX, refpoints[primOffset+1][0]); + primMaxY = std::max(primMaxY, refpoints[primOffset+1][1]); + + secMinX = std::min(secMinX, refpoints[secOffset+1][0]); + secMinY = std::min(secMinY, refpoints[secOffset+1][1]); + secMaxX = std::max(secMaxX, refpoints[secOffset+1][0]); + secMaxY = std::max(secMaxY, refpoints[secOffset+1][1]); + + float primHSize = layout == 1 ? std::max(primMaxX, -primMinX) : primMaxX - primMinX; + float primVSize = layout == 0 ? std::max(primMaxY, -primMinY) : primMaxY - primMinY; + + float secHSize = layout == 1 ? std::max(secMaxX, -secMinX) : secMaxX - secMinX; + float secVSize = layout == 0 ? std::max(secMaxY, -secMinY) : secMaxY - secMinY; + + float primScale = std::min(screenWidth / primHSize, screenHeight / primVSize); + float secScale = 1.f; + + if (layout == 0) + { + if (screenHeight - primVSize * primScale < secVSize) + primScale = std::min((screenWidth - secHSize) / primHSize, (screenHeight - secVSize) / primVSize); + else + secScale = std::min((screenHeight - primVSize * primScale) / secVSize, screenWidth / secHSize); + } + else + { + if (screenWidth - primHSize * primScale < secHSize) + primScale = std::min((screenWidth - secHSize) / primHSize, (screenHeight - secVSize) / primVSize); + else + secScale = std::min((screenWidth - primHSize * primScale) / secHSize, screenHeight / secVSize); + } + + if (integerScale) + { + primScale = floor(primScale); + secScale = floor(secScale); + } + + M23_Scale(primMtx, primScale); + M23_Scale(secMtx, secScale); + + refpoints[primOffset+0][0] *= primScale; + refpoints[primOffset+0][1] *= primScale; + refpoints[primOffset+1][0] *= primScale; + refpoints[primOffset+1][1] *= primScale; + refpoints[secOffset+0][0] *= secScale; + refpoints[secOffset+0][1] *= secScale; + refpoints[secOffset+1][0] *= secScale; + refpoints[secOffset+1][1] *= secScale; + + botScale = (sizing == 1) ? secScale : primScale; + } + } + + // position + { + float minX = refpoints[0][0], maxX = minX; + float minY = refpoints[0][1], maxY = minY; + + for (int i = 1; i < 4; i++) + { + minX = std::min(minX, refpoints[i][0]); + minY = std::min(minY, refpoints[i][1]); + maxX = std::max(maxX, refpoints[i][0]); + maxY = std::max(maxY, refpoints[i][1]); + } + + float width = maxX - minX; + float height = maxY - minY; + + float tx = (screenWidth/2) - (width/2) - minX; + float ty = (screenHeight/2) - (height/2) - minY; + + M23_Translate(TopScreenMtx, tx, ty); + M23_Translate(BotScreenMtx, tx, ty); + + botTrans[2] = tx; botTrans[3] = ty; + } + + // prepare a 'reverse' matrix for the touchscreen + // this matrix undoes the transforms applied to the bottom screen + // and can be used to calculate touchscreen coords from host screen coords + { + M23_Identity(TouchMtx); + + M23_Translate(TouchMtx, -botTrans[2], -botTrans[3]); + M23_Scale(TouchMtx, 1.f / botScale); + M23_Translate(TouchMtx, -botTrans[0], -botTrans[1]); + + float rotmtx[6]; + M23_Identity(rotmtx); + M23_RotateFast(rotmtx, (4-rotation) & 3); + M23_Multiply(TouchMtx, rotmtx, TouchMtx); + + M23_Translate(TouchMtx, 256/2, 192/2); + } +} + +float* GetScreenTransform(int screen) +{ + if (screen == 0) return TopScreenMtx; + else return BotScreenMtx; +} + +void GetTouchCoords(int& x, int& y) +{ + float vx = x; + float vy = y; + + M23_Transform(TouchMtx, vx, vy); + + x = (int)vx; + y = (int)vy; +} + +} + diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index ffd7d17..7bc81f0 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -10,6 +10,7 @@ SET(SOURCES_QT_SDL PlatformConfig.cpp ../Util_ROM.cpp + ../Util_Video.cpp ../Util_Audio.cpp ../FrontendUtil.h ../mic_blow.h diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 245324f..0d10378 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -62,6 +62,8 @@ bool RunningSomething; MainWindow* mainWindow; EmuThread* emuThread; +int autoScreenSizing = 0; + SDL_AudioDeviceID audioDevice; int audioFreq; SDL_cond* audioSync; @@ -250,16 +252,19 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); + connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged())); } void EmuThread::run() { + u32 mainScreenPos[3]; + NDS::Init(); - /*MainScreenPos[0] = 0; - MainScreenPos[1] = 0; - MainScreenPos[2] = 0; - AutoScreenSizing = 0;*/ + mainScreenPos[0] = 0; + mainScreenPos[1] = 0; + mainScreenPos[2] = 0; + autoScreenSizing = 0; /*if (Screen_UseGL) { @@ -333,14 +338,14 @@ void EmuThread::run() }*/ // auto screen layout - /*{ - MainScreenPos[2] = MainScreenPos[1]; - MainScreenPos[1] = MainScreenPos[0]; - MainScreenPos[0] = NDS::PowerControl9 >> 15; + { + mainScreenPos[2] = mainScreenPos[1]; + mainScreenPos[1] = mainScreenPos[0]; + mainScreenPos[0] = NDS::PowerControl9 >> 15; int guess; - if (MainScreenPos[0] == MainScreenPos[2] && - MainScreenPos[0] != MainScreenPos[1]) + if (mainScreenPos[0] == mainScreenPos[2] && + mainScreenPos[0] != mainScreenPos[1]) { // constant flickering, likely displaying 3D on both screens // TODO: when both screens are used for 2D only...??? @@ -348,18 +353,18 @@ void EmuThread::run() } else { - if (MainScreenPos[0] == 1) + if (mainScreenPos[0] == 1) guess = 1; else guess = 2; } - if (guess != AutoScreenSizing) + if (guess != autoScreenSizing) { - AutoScreenSizing = guess; - SetupScreenRects(WindowWidth, WindowHeight); + autoScreenSizing = guess; + emit screenLayoutChange(); } - }*/ + } // emulate u32 nlines = NDS::RunFrame(); @@ -540,6 +545,9 @@ MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) screen[0] = new QImage(256, 192, QImage::Format_RGB32); screen[1] = new QImage(256, 192, QImage::Format_RGB32); + screenTrans[0].reset(); + screenTrans[1].reset(); + touching = false; } @@ -549,6 +557,64 @@ MainWindowPanel::~MainWindowPanel() delete screen[1]; } +void MainWindowPanel::ensureProperMinSize() +{ + bool isHori = (Config::ScreenRotation == 1 || Config::ScreenRotation == 3); + int gap = Config::ScreenGap; + + int w = 256; + int h = 192; + + if (Config::ScreenLayout == 0) // natural + { + if (isHori) + setMinimumSize(h+gap+h, w); + else + setMinimumSize(w, h+gap+h); + } + else if (Config::ScreenLayout == 1) // vertical + { + if (isHori) + setMinimumSize(h, w+gap+w); + else + setMinimumSize(w, h+gap+h); + } + else // horizontal + { + if (isHori) + setMinimumSize(h+gap+h, w); + else + setMinimumSize(w+gap+w, h); + } +} + +void MainWindowPanel::setupScreenLayout() +{ + int w = width(); + int h = height(); + float* mtx; + + int sizing = Config::ScreenSizing; + if (sizing == 3) sizing = autoScreenSizing; + + Frontend::SetupScreenLayout(w, h, + Config::ScreenLayout, + Config::ScreenRotation, + sizing, + Config::ScreenGap, + Config::IntegerScaling != 0); + + mtx = Frontend::GetScreenTransform(0); + screenTrans[0].setMatrix(mtx[0], mtx[1], 0.f, + mtx[2], mtx[3], 0.f, + mtx[4], mtx[5], 1.f); + + mtx = Frontend::GetScreenTransform(1); + screenTrans[1].setMatrix(mtx[0], mtx[1], 0.f, + mtx[2], mtx[3], 0.f, + mtx[4], mtx[5], 1.f); +} + void MainWindowPanel::paintEvent(QPaintEvent* event) { QPainter painter(this); @@ -562,27 +628,18 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) memcpy(screen[0]->scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); memcpy(screen[1]->scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); - QRect src = QRect(0, 0, 256, 192); + QRect screenrc = QRect(0, 0, 256, 192); - QRect dstTop = QRect(0, 0, 256, 192); // TODO - QRect dstBot = QRect(0, 192, 256, 192); // TODO + painter.setTransform(screenTrans[0]); + painter.drawImage(screenrc, *screen[0]); - painter.drawImage(dstTop, *screen[0]); - painter.drawImage(dstBot, *screen[1]); + painter.setTransform(screenTrans[1]); + painter.drawImage(screenrc, *screen[1]); } - -void MainWindowPanel::transformTSCoords(int& x, int& y) +void MainWindowPanel::resizeEvent(QResizeEvent* event) { - // TODO: actual screen de-transform taking screen layout/rotation/etc into account - - y -= 192; - - // clamp to screen range - if (x < 0) x = 0; - else if (x > 255) x = 255; - if (y < 0) y = 0; - else if (y > 191) y = 191; + setupScreenLayout(); } void MainWindowPanel::mousePressEvent(QMouseEvent* event) @@ -593,11 +650,11 @@ void MainWindowPanel::mousePressEvent(QMouseEvent* event) int x = event->pos().x(); int y = event->pos().y(); - if (x >= 0 && x < 256 && y >= 192 && y < 384) + Frontend::GetTouchCoords(x, y); + + if (x >= 0 && x < 256 && y >= 0 && y < 192) { touching = true; - - transformTSCoords(x, y); NDS::TouchScreen(x, y); } } @@ -623,10 +680,22 @@ void MainWindowPanel::mouseMoveEvent(QMouseEvent* event) int x = event->pos().x(); int y = event->pos().y(); - transformTSCoords(x, y); + Frontend::GetTouchCoords(x, y); + + // clamp to screen range + if (x < 0) x = 0; + else if (x > 255) x = 255; + if (y < 0) y = 0; + else if (y > 191) y = 191; + NDS::TouchScreen(x, y); } +void MainWindowPanel::onScreenLayoutChanged() +{ + setupScreenLayout(); +} + MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) { @@ -750,7 +819,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) int data = i*90; actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); actScreenRotation[i]->setActionGroup(grpScreenRotation); - actScreenRotation[i]->setData(QVariant(data)); + actScreenRotation[i]->setData(QVariant(i)); actScreenRotation[i]->setCheckable(true); } @@ -834,8 +903,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) panel = new MainWindowPanel(this); setCentralWidget(panel); - panel->setMinimumSize(256, 384); + panel->ensureProperMinSize(); + resize(Config::WindowWidth, Config::WindowHeight); for (int i = 0; i < 9; i++) { @@ -877,6 +947,16 @@ MainWindow::~MainWindow() { } +void MainWindow::resizeEvent(QResizeEvent* event) +{ + int w = event->size().width(); + int h = event->size().height(); + + Config::WindowWidth = w; + Config::WindowHeight = h; + + // TODO: detect when the window gets maximized! +} void MainWindow::keyPressEvent(QKeyEvent* event) { @@ -1264,32 +1344,79 @@ void MainWindow::onChangeSavestateSRAMReloc(bool checked) void MainWindow::onChangeScreenSize() { - // + int factor = ((QAction*)sender())->data().toInt(); + + bool isHori = (Config::ScreenRotation == 1 || Config::ScreenRotation == 3); + int gap = Config::ScreenGap; + + int w = 256*factor; + int h = 192*factor; + + QSize diff = size() - panel->size(); + + if (Config::ScreenLayout == 0) // natural + { + if (isHori) + resize(QSize(h+gap+h, w) + diff); + else + resize(QSize(w, h+gap+h) + diff); + } + else if (Config::ScreenLayout == 1) // vertical + { + if (isHori) + resize(QSize(h, w+gap+w) + diff); + else + resize(QSize(w, h+gap+h) + diff); + } + else // horizontal + { + if (isHori) + resize(QSize(h+gap+h, w) + diff); + else + resize(QSize(w+gap+w, h) + diff); + } } void MainWindow::onChangeScreenRotation(QAction* act) { - // + int rot = act->data().toInt(); + Config::ScreenRotation = rot; + + panel->ensureProperMinSize(); + panel->setupScreenLayout(); } void MainWindow::onChangeScreenGap(QAction* act) { - // + int gap = act->data().toInt(); + Config::ScreenGap = gap; + + panel->ensureProperMinSize(); + panel->setupScreenLayout(); } void MainWindow::onChangeScreenLayout(QAction* act) { - // + int layout = act->data().toInt(); + Config::ScreenLayout = layout; + + panel->ensureProperMinSize(); + panel->setupScreenLayout(); } void MainWindow::onChangeScreenSizing(QAction* act) { - // + int sizing = act->data().toInt(); + Config::ScreenSizing = sizing; + + panel->setupScreenLayout(); } void MainWindow::onChangeIntegerScaling(bool checked) { - // + Config::IntegerScaling = checked?1:0; + + panel->setupScreenLayout(); } void MainWindow::onChangeScreenFiltering(bool checked) diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 7051a08..2d14eea 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -54,6 +54,8 @@ signals: void windowLimitFPSChange(); + void screenLayoutChange(); + private: volatile int EmuStatus; int PrevEmuStatus; @@ -69,18 +71,25 @@ public: explicit MainWindowPanel(QWidget* parent); ~MainWindowPanel(); + void ensureProperMinSize(); + void setupScreenLayout(); + protected: void paintEvent(QPaintEvent* event) override; + void resizeEvent(QResizeEvent* event) override; + void mousePressEvent(QMouseEvent* event) override; void mouseReleaseEvent(QMouseEvent* event) override; void mouseMoveEvent(QMouseEvent* event) override; +private slots: + void onScreenLayoutChanged(); + private: QImage* screen[2]; + QTransform screenTrans[2]; bool touching; - - void transformTSCoords(int& x, int& y); }; @@ -93,6 +102,8 @@ public: ~MainWindow(); protected: + void resizeEvent(QResizeEvent* event) override; + void keyPressEvent(QKeyEvent* event) override; void keyReleaseEvent(QKeyEvent* event) override; @@ -138,9 +149,9 @@ private slots: private: QString loadErrorStr(int error); +public: MainWindowPanel* panel; -public: QAction* actOpenROM; QAction* actBootFirmware; QAction* actSaveState[9]; -- cgit v1.2.3 From 16252a85e70feaa1bd10312a357ac74bd677ade3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 24 May 2020 23:16:56 +0200 Subject: separate screen handling shit to a specialized class --- src/frontend/FrontendUtil.h | 5 +- src/frontend/Util_Video.cpp | 6 +- src/frontend/qt_sdl/main.cpp | 168 +++++++++++++++++++++++++------------------ src/frontend/qt_sdl/main.h | 22 +++++- 4 files changed, 122 insertions(+), 79 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 1fd7329..359018f 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -104,11 +104,12 @@ void UndoStateLoad(); // * integerScale: force screens to be scaled up at integer scaling factors void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int rotation, int sizing, int screenGap, bool integerScale); -// get a 2x3 transform matrix for the given screen (0=top, 1=bottom) +// get a 2x3 transform matrix for each screen // note: the transform assumes an origin point at the top left of the display, // X going left and Y going down // for each screen the source coordinates should be (0,0) and (256,192) -float* GetScreenTransform(int screen); +// 'top' and 'bot' should point each to an array of 6 floats +void GetScreenTransforms(float* top, float* bot); // de-transform the provided host display coordinates to get coordinates // on the bottom screen diff --git a/src/frontend/Util_Video.cpp b/src/frontend/Util_Video.cpp index 0f5ff66..87cb9b5 100644 --- a/src/frontend/Util_Video.cpp +++ b/src/frontend/Util_Video.cpp @@ -313,10 +313,10 @@ void SetupScreenLayout(int screenWidth, int screenHeight, int screenLayout, int } } -float* GetScreenTransform(int screen) +void GetScreenTransforms(float* top, float* bot) { - if (screen == 0) return TopScreenMtx; - else return BotScreenMtx; + memcpy(top, TopScreenMtx, 6*sizeof(float)); + memcpy(bot, BotScreenMtx, 6*sizeof(float)); } void GetTouchCoords(int& x, int& y) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 922206f..0e0a21b 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -543,24 +543,22 @@ bool EmuThread::emuIsRunning() } -MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) +void ScreenHandler::screenSetupLayout(int w, int h) { - screen[0] = new QImage(256, 192, QImage::Format_RGB32); - screen[1] = new QImage(256, 192, QImage::Format_RGB32); - - screenTrans[0].reset(); - screenTrans[1].reset(); + int sizing = Config::ScreenSizing; + if (sizing == 3) sizing = autoScreenSizing; - touching = false; -} + Frontend::SetupScreenLayout(w, h, + Config::ScreenLayout, + Config::ScreenRotation, + sizing, + Config::ScreenGap, + Config::IntegerScaling != 0); -MainWindowPanel::~MainWindowPanel() -{ - delete screen[0]; - delete screen[1]; + Frontend::GetScreenTransforms(screenMatrix[0], screenMatrix[1]); } -void MainWindowPanel::ensureProperMinSize() +QSize ScreenHandler::screenGetMinSize() { bool isHori = (Config::ScreenRotation == 1 || Config::ScreenRotation == 3); int gap = Config::ScreenGap; @@ -571,48 +569,110 @@ void MainWindowPanel::ensureProperMinSize() if (Config::ScreenLayout == 0) // natural { if (isHori) - setMinimumSize(h+gap+h, w); + return QSize(h+gap+h, w); else - setMinimumSize(w, h+gap+h); + return QSize(w, h+gap+h); } else if (Config::ScreenLayout == 1) // vertical { if (isHori) - setMinimumSize(h, w+gap+w); + return QSize(h, w+gap+w); else - setMinimumSize(w, h+gap+h); + return QSize(w, h+gap+h); } else // horizontal { if (isHori) - setMinimumSize(h+gap+h, w); + return QSize(h+gap+h, w); else - setMinimumSize(w+gap+w, h); + return QSize(w+gap+w, h); + } +} + +void ScreenHandler::screenOnMousePress(QMouseEvent* event) +{ + event->accept(); + if (event->button() != Qt::LeftButton) return; + + int x = event->pos().x(); + int y = event->pos().y(); + + Frontend::GetTouchCoords(x, y); + + if (x >= 0 && x < 256 && y >= 0 && y < 192) + { + touching = true; + NDS::TouchScreen(x, y); + } +} + +void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) +{ + event->accept(); + if (event->button() != Qt::LeftButton) return; + + if (touching) + { + touching = false; + NDS::ReleaseScreen(); } } +void ScreenHandler::screenOnMouseMove(QMouseEvent* event) +{ + event->accept(); + if (!(event->buttons() & Qt::LeftButton)) return; + if (!touching) return; + + int x = event->pos().x(); + int y = event->pos().y(); + + Frontend::GetTouchCoords(x, y); + + // clamp to screen range + if (x < 0) x = 0; + else if (x > 255) x = 255; + if (y < 0) y = 0; + else if (y > 191) y = 191; + + NDS::TouchScreen(x, y); +} + + +MainWindowPanel::MainWindowPanel(QWidget* parent) : QWidget(parent) +{ + screen[0] = QImage(256, 192, QImage::Format_RGB32); + screen[1] = QImage(256, 192, QImage::Format_RGB32); + + screenTrans[0].reset(); + screenTrans[1].reset(); + + touching = false; +} + +MainWindowPanel::~MainWindowPanel() +{ +} + +void MainWindowPanel::ensureProperMinSize() +{ + setMinimumSize(screenGetMinSize()); +} + void MainWindowPanel::setupScreenLayout() { int w = width(); int h = height(); float* mtx; - int sizing = Config::ScreenSizing; - if (sizing == 3) sizing = autoScreenSizing; - - Frontend::SetupScreenLayout(w, h, - Config::ScreenLayout, - Config::ScreenRotation, - sizing, - Config::ScreenGap, - Config::IntegerScaling != 0); + screenSetupLayout(w, h); - mtx = Frontend::GetScreenTransform(0); + mtx = screenMatrix[0]; screenTrans[0].setMatrix(mtx[0], mtx[1], 0.f, mtx[2], mtx[3], 0.f, mtx[4], mtx[5], 1.f); - mtx = Frontend::GetScreenTransform(1); + mtx = screenMatrix[1]; screenTrans[1].setMatrix(mtx[0], mtx[1], 0.f, mtx[2], mtx[3], 0.f, mtx[4], mtx[5], 1.f); @@ -628,18 +688,18 @@ void MainWindowPanel::paintEvent(QPaintEvent* event) int frontbuf = GPU::FrontBuffer; if (!GPU::Framebuffer[frontbuf][0] || !GPU::Framebuffer[frontbuf][1]) return; - memcpy(screen[0]->scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); - memcpy(screen[1]->scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); + memcpy(screen[0].scanLine(0), GPU::Framebuffer[frontbuf][0], 256*192*4); + memcpy(screen[1].scanLine(0), GPU::Framebuffer[frontbuf][1], 256*192*4); painter.setRenderHint(QPainter::SmoothPixmapTransform, Config::ScreenFilter!=0); QRect screenrc(0, 0, 256, 192); painter.setTransform(screenTrans[0]); - painter.drawImage(screenrc, *screen[0]); + painter.drawImage(screenrc, screen[0]); painter.setTransform(screenTrans[1]); - painter.drawImage(screenrc, *screen[1]); + painter.drawImage(screenrc, screen[1]); } void MainWindowPanel::resizeEvent(QResizeEvent* event) @@ -649,51 +709,17 @@ void MainWindowPanel::resizeEvent(QResizeEvent* event) void MainWindowPanel::mousePressEvent(QMouseEvent* event) { - event->accept(); - if (event->button() != Qt::LeftButton) return; - - int x = event->pos().x(); - int y = event->pos().y(); - - Frontend::GetTouchCoords(x, y); - - if (x >= 0 && x < 256 && y >= 0 && y < 192) - { - touching = true; - NDS::TouchScreen(x, y); - } + screenOnMousePress(event); } void MainWindowPanel::mouseReleaseEvent(QMouseEvent* event) { - event->accept(); - if (event->button() != Qt::LeftButton) return; - - if (touching) - { - touching = false; - NDS::ReleaseScreen(); - } + screenOnMouseRelease(event); } void MainWindowPanel::mouseMoveEvent(QMouseEvent* event) { - event->accept(); - if (!(event->buttons() & Qt::LeftButton)) return; - if (!touching) return; - - int x = event->pos().x(); - int y = event->pos().y(); - - Frontend::GetTouchCoords(x, y); - - // clamp to screen range - if (x < 0) x = 0; - else if (x > 255) x = 255; - if (y < 0) y = 0; - else if (y > 191) y = 191; - - NDS::TouchScreen(x, y); + screenOnMouseMove(event); } void MainWindowPanel::onScreenLayoutChanged() diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index c0dc465..1121da1 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -64,7 +64,24 @@ private: }; -class MainWindowPanel : public QWidget +class ScreenHandler +{ +protected: + void screenSetupLayout(int w, int h); + + QSize screenGetMinSize(); + + void screenOnMousePress(QMouseEvent* event); + void screenOnMouseRelease(QMouseEvent* event); + void screenOnMouseMove(QMouseEvent* event); + + float screenMatrix[2][6]; + + bool touching; +}; + + +class MainWindowPanel : public QWidget, public ScreenHandler { Q_OBJECT @@ -88,9 +105,8 @@ private slots: void onScreenLayoutChanged(); private: - QImage* screen[2]; + QImage screen[2]; QTransform screenTrans[2]; - bool touching; }; -- cgit v1.2.3 From bc4a83abca991effe082f76f81cd6b4eef6ef0ba Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 24 May 2020 23:47:11 +0200 Subject: make frontend-util audio module config-agnostic --- src/frontend/FrontendUtil.h | 4 ++-- src/frontend/Util_Audio.cpp | 7 +------ src/frontend/qt_sdl/main.cpp | 2 +- 3 files changed, 4 insertions(+), 9 deletions(-) (limited to 'src/frontend/FrontendUtil.h') diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 359018f..6b83cbc 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -124,9 +124,9 @@ void Init_Audio(int outputfreq); int AudioOut_GetNumSamples(int outlen); // resample audio from the core audio output to match the frontend's -// output frequency, and apply user-specified volume +// output frequency, and apply specified volume // note: this assumes the output buffer is interleaved stereo -void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen); +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume); // feed silence to the microphone input void Mic_FeedSilence(); diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index d4c3333..fc80c90 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -22,9 +22,6 @@ #include #include "FrontendUtil.h" -#include "Config.h" -#include "qt_sdl/PlatformConfig.h" // FIXME!!! -#include "Platform.h" #include "NDS.h" @@ -63,14 +60,12 @@ int AudioOut_GetNumSamples(int outlen) return len_in; } -void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen) +void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume) { float res_incr = inlen / (float)outlen; float res_timer = 0; int res_pos = 0; - int volume = Config::AudioVolume; - for (int i = 0; i < outlen; i++) { outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0e0a21b..fac4709 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -110,7 +110,7 @@ void audioCallback(void* data, Uint8* stream, int len) num_in = len_in-margin; } - Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len); + Frontend::AudioOut_Resample(buf_in, num_in, (s16*)stream, len, Config::AudioVolume); } -- cgit v1.2.3