diff options
author | Arisotura <thetotalworm@gmail.com> | 2022-01-07 14:00:43 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-01-07 14:00:43 +0100 |
commit | e665e25bd3ea4b9af932e182b1c93e77b762ccb0 (patch) | |
tree | 06b5ceb336e42868806113cbf1f45add2b56a3e8 /src/frontend | |
parent | c4cd9da674bcfb8dca89086ce3ffa2d3b03438eb (diff) |
Custom path support (#1333)
also including:
* getting rid of shitty strings
* all new, cleaner ROM handling code
* base for DSi savestates
* GBA slot addons (for now, memory cart)
Diffstat (limited to 'src/frontend')
28 files changed, 2510 insertions, 1753 deletions
diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index f52dced..a0085a8 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -21,100 +21,12 @@ #include "types.h" +#include <string> #include <vector> namespace Frontend { -enum -{ - ROMSlot_NDS = 0, - ROMSlot_GBA, - - ROMSlot_MAX -}; - -enum -{ - Load_OK = 0, - - Load_BIOS9Missing, - Load_BIOS9Bad, - - Load_BIOS7Missing, - Load_BIOS7Bad, - - Load_FirmwareMissing, - Load_FirmwareBad, - Load_FirmwareNotBootable, - - Load_DSiBIOS9Missing, - Load_DSiBIOS9Bad, - - Load_DSiBIOS7Missing, - Load_DSiBIOS7Bad, - - Load_DSiNANDMissing, - Load_DSiNANDBad, - - // TODO: more precise errors for ROM loading - Load_ROMLoadError, -}; - -extern char ROMPath [ROMSlot_MAX][1024]; -extern char SRAMPath[ROMSlot_MAX][1024]; -extern bool SavestateLoaded; - -// Stores type of nds rom i.e. nds/srl/dsi. Should be updated everytime an NDS rom is loaded from an archive -extern char NDSROMExtension[4]; - -// initialize the ROM handling utility -void Init_ROM(); - -// deinitialize the ROM handling utility -void DeInit_ROM(); - -// load the BIOS/firmware and boot from it -int LoadBIOS(); - -// load a ROM file to the specified cart slot -// note: loading a ROM to the NDS slot resets emulation -int LoadROM(const char* file, int slot); -int LoadROM(const u8 *romdata, u32 romlength, const char *archivefilename, const char *romfilename, const char *sramfilename, int slot); - -// unload the ROM loaded in the specified cart slot -// simulating ejection of the cartridge -void UnloadROM(int slot); - -void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef); -void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef); - -// 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); - -// 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(); - -// imports savedata from an external file. Returns the difference between the filesize and the SRAM size -int ImportSRAM(const char* filename); - -// enable or disable cheats -void EnableCheats(bool enable); - - // 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 diff --git a/src/frontend/SharedConfig.h b/src/frontend/SharedConfig.h deleted file mode 100644 index 3598e74..0000000 --- a/src/frontend/SharedConfig.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - Copyright 2016-2021 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 SHAREDCONFIG_H -#define SHAREDCONFIG_H - -namespace Config -{ - -extern int ConsoleType; -extern int DirectBoot; -extern int SavestateRelocSRAM; - -extern int ExternalBIOSEnable; - -extern char BIOS9Path[1024]; -extern char BIOS7Path[1024]; -extern char FirmwarePath[1024]; - -extern char DSiBIOS9Path[1024]; -extern char DSiBIOS7Path[1024]; -extern char DSiFirmwarePath[1024]; -extern char DSiNANDPath[1024]; - -} - -#endif diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp deleted file mode 100644 index 4d23c70..0000000 --- a/src/frontend/Util_ROM.cpp +++ /dev/null @@ -1,845 +0,0 @@ -/* - Copyright 2016-2021 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 <stdio.h> -#include <string.h> - -#include <utility> - -#ifdef ARCHIVE_SUPPORT_ENABLED -#include "ArchiveUtil.h" -#endif -#include "FrontendUtil.h" -#include "SharedConfig.h" -#include "Platform.h" - -#include "NDS.h" -#include "DSi.h" -#include "GBACart.h" - -#include "AREngine.h" - - -namespace Frontend -{ - -char ROMPath [ROMSlot_MAX][1024]; -char SRAMPath [ROMSlot_MAX][1024]; -char PrevSRAMPath[ROMSlot_MAX][1024]; // for savestate 'undo load' - -char NDSROMExtension[4]; - -bool SavestateLoaded; - -ARCodeFile* CheatFile; -bool CheatsOn; - - -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); - - CheatFile = nullptr; - CheatsOn = false; -} - -void DeInit_ROM() -{ - if (CheatFile) - { - delete CheatFile; - CheatFile = nullptr; - } -} - -// 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); - SRAMPath[slot][1023] = '\0'; - strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3); -} - -int VerifyDSBIOS() -{ - FILE* f; - long len; - - if (!Config::ExternalBIOSEnable) return Load_OK; - - 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 VerifyDSiBIOS() -{ - FILE* f; - long len; - - // TODO: check the first 32 bytes - - f = Platform::OpenLocalFile(Config::DSiBIOS9Path, "rb"); - if (!f) return Load_DSiBIOS9Missing; - - fseek(f, 0, SEEK_END); - len = ftell(f); - if (len != 0x10000) - { - fclose(f); - return Load_DSiBIOS9Bad; - } - - fclose(f); - - f = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb"); - if (!f) return Load_DSiBIOS7Missing; - - fseek(f, 0, SEEK_END); - len = ftell(f); - if (len != 0x10000) - { - fclose(f); - return Load_DSiBIOS7Bad; - } - - fclose(f); - - return Load_OK; -} - -int VerifyDSFirmware() -{ - FILE* f; - long len; - - if (!Config::ExternalBIOSEnable) return Load_FirmwareNotBootable; - - f = Platform::OpenLocalFile(Config::FirmwarePath, "rb"); - if (!f) return Load_FirmwareNotBootable; - - 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 VerifyDSiFirmware() -{ - FILE* f; - long len; - - f = Platform::OpenLocalFile(Config::DSiFirmwarePath, "rb"); - if (!f) return Load_FirmwareMissing; - - fseek(f, 0, SEEK_END); - len = ftell(f); - if (len != 0x20000) - { - // not 128KB - // TODO: check whether those work - fclose(f); - return Load_FirmwareBad; - } - - fclose(f); - - return Load_OK; -} - -int SetupDSiNAND() -{ - FILE* f; - long len; - - f = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b"); - if (!f) return Load_DSiNANDMissing; - - // TODO: some basic checks - // check that it has the nocash footer, and all - - DSi::SDMMCFile = f; - - return Load_OK; -} - -void LoadCheats() -{ - if (CheatFile) - { - delete CheatFile; - CheatFile = nullptr; - } - - char filename[1024]; - if (ROMPath[ROMSlot_NDS][0] != '\0') - { - strncpy(filename, ROMPath[ROMSlot_NDS], 1023); - filename[1023] = '\0'; - strncpy(filename + strlen(ROMPath[ROMSlot_NDS]) - 3, "mch", 3); - } - else - { - strncpy(filename, "firmware.mch", 1023); - } - - // TODO: check for error (malformed cheat file, ...) - CheatFile = new ARCodeFile(filename); - - AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); -} - -int LoadBIOS() -{ - DSi::CloseDSiNAND(); - - int res; - - res = VerifyDSBIOS(); - if (res != Load_OK) return res; - - if (Config::ConsoleType == 1) - { - res = VerifyDSiBIOS(); - if (res != Load_OK) return res; - - res = VerifyDSiFirmware(); - if (res != Load_OK) return res; - - res = SetupDSiNAND(); - if (res != Load_OK) return res; - } - else - { - 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? - // is that behavior consistent with that of LoadROM() below? - - ROMPath[ROMSlot_NDS][0] = '\0'; - SRAMPath[ROMSlot_NDS][0] = '\0'; - - NDS::SetConsoleType(Config::ConsoleType); - NDS::LoadBIOS(); - - SavestateLoaded = false; - - LoadCheats(); - - return Load_OK; -} - -int LoadROM(const u8 *romdata, u32 romlength, const char *archivefilename, const char *romfilename, const char *sramfilename, int slot) -{ - int res; - bool directboot = Config::DirectBoot != 0; - - if (Config::ConsoleType == 1 && slot == 1) - { - // cannot load a GBA ROM into a DSi - return Load_ROMLoadError; - } - - res = VerifyDSBIOS(); - if (res != Load_OK) return res; - - if (Config::ConsoleType == 1) - { - res = VerifyDSiBIOS(); - if (res != Load_OK) return res; - - res = VerifyDSiFirmware(); - if (res != Load_OK) return res; - - res = SetupDSiNAND(); - if (res != Load_OK) return res; - - GBACart::Eject(); - ROMPath[ROMSlot_GBA][0] = '\0'; - } - else - { - 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); - strncpy(oldsram, SRAMPath[slot], 1024); - - strncpy(SRAMPath[slot], sramfilename, 1024); - strncpy(ROMPath[slot], archivefilename, 1024); - - NDS::SetConsoleType(Config::ConsoleType); - - if (slot == ROMSlot_NDS && NDS::LoadROM(romdata, romlength, SRAMPath[slot], directboot)) - { - SavestateLoaded = false; - - LoadCheats(); - - // 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 Load_OK; - } - else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(romdata, romlength, romfilename, SRAMPath[slot])) - { - SavestateLoaded = false; // checkme?? - - strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety - return Load_OK; - } - else - { - strncpy(ROMPath[slot], oldpath, 1024); - strncpy(SRAMPath[slot], oldsram, 1024); - return Load_ROMLoadError; - } -} - -int LoadROM(const char* file, int slot) -{ - DSi::CloseDSiNAND(); - - int res; - bool directboot = Config::DirectBoot != 0; - - if (Config::ConsoleType == 1 && slot == 1) - { - // cannot load a GBA ROM into a DSi - return Load_ROMLoadError; - } - - res = VerifyDSBIOS(); - if (res != Load_OK) return res; - - if (Config::ConsoleType == 1) - { - res = VerifyDSiBIOS(); - if (res != Load_OK) return res; - - res = VerifyDSiFirmware(); - if (res != Load_OK) return res; - - res = SetupDSiNAND(); - if (res != Load_OK) return res; - - GBACart::Eject(); - ROMPath[ROMSlot_GBA][0] = '\0'; - } - else - { - 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); - strncpy(oldsram, SRAMPath[slot], 1024); - - strncpy(ROMPath[slot], file, 1023); - ROMPath[slot][1023] = '\0'; - - SetupSRAMPath(0); - SetupSRAMPath(1); - - NDS::SetConsoleType(Config::ConsoleType); - - if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], directboot)) - { - SavestateLoaded = false; - - LoadCheats(); - - // 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 Load_OK; - } - else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot], SRAMPath[slot])) - { - SavestateLoaded = false; // checkme?? - - strncpy(PrevSRAMPath[slot], SRAMPath[slot], 1024); // safety - return Load_OK; - } - else - { - strncpy(ROMPath[slot], oldpath, 1024); - strncpy(SRAMPath[slot], oldsram, 1024); - return Load_ROMLoadError; - } -} - -void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef) -{ - int index = 0; - for (int i = 0; i < 4; i++) - { - for (int j = 0; j < 4; j++) - { - for (int k = 0; k < 8; k++) - { - for (int l = 0; l < 8; l++) - { - u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F; - u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31; - u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31; - u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31; - u8 a = pal_index ? 255: 0; - u32* row = &iconRef[256 * i + 32 * k + 8 * j]; - row[l] = (a << 24) | (r << 16) | (g << 8) | b; - index++; - } - } - } - } -} - -#define SEQ_FLIPV(i) ((i & 0b1000000000000000) >> 15) -#define SEQ_FLIPH(i) ((i & 0b0100000000000000) >> 14) -#define SEQ_PAL(i) ((i & 0b0011100000000000) >> 11) -#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8) -#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0) - -void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef) -{ - for (int i = 0; i < 64; i++) - { - if (!sequence[i]) - break; - u32* frame = &animatedTexRef[32 * 32 * i]; - ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame); - - if (SEQ_FLIPH(sequence[i])) - { - for (int x = 0; x < 32; x++) - { - for (int y = 0; y < 32/2; y++) - { - std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]); - } - } - } - if (SEQ_FLIPV(sequence[i])) - { - for (int x = 0; x < 32/2; x++) - { - for (int y = 0; y < 32; y++) - { - std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]); - } - } - } - - for (int j = 0; j < SEQ_DUR(sequence[i]); j++) - animatedSequenceRef.push_back(i); - } -} - -void UnloadROM(int slot) -{ - if (slot == ROMSlot_NDS) - { - // TODO! - } - else if (slot == ROMSlot_GBA) - { - GBACart::Eject(); - } - - ROMPath[slot][0] = '\0'; - - DSi::CloseDSiNAND(); -} - -int Reset() -{ - DSi::CloseDSiNAND(); - - int res; - bool directboot = Config::DirectBoot != 0; - - res = VerifyDSBIOS(); - if (res != Load_OK) return res; - - if (Config::ConsoleType == 1) - { - res = VerifyDSiBIOS(); - if (res != Load_OK) return res; - - res = VerifyDSiFirmware(); - if (res != Load_OK) return res; - - res = SetupDSiNAND(); - if (res != Load_OK) return res; - - GBACart::Eject(); - ROMPath[ROMSlot_GBA][0] = '\0'; - } - else - { - res = VerifyDSFirmware(); - if (res != Load_OK) - { - if (res == Load_FirmwareNotBootable) - directboot = true; - else - return res; - } - } - - SavestateLoaded = false; - - NDS::SetConsoleType(Config::ConsoleType); - - if (ROMPath[ROMSlot_NDS][0] == '\0') - { - NDS::LoadBIOS(); - } - else - { - char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_NDS]); - strncpy(ext, ROMPath[ROMSlot_NDS] + _len - 4, 4); - - if(!strncasecmp(ext, ".nds", 4) || !strncasecmp(ext, ".srl", 4) || !strncasecmp(ext, ".dsi", 4)) - { - SetupSRAMPath(0); - if (!NDS::LoadROM(ROMPath[ROMSlot_NDS], SRAMPath[ROMSlot_NDS], directboot)) - return Load_ROMLoadError; - } -#ifdef ARCHIVE_SUPPORT_ENABLED - else - { - u8 *romdata = nullptr; u32 romlen; - char romfilename[1024] = {0}, sramfilename[1024]; - strncpy(sramfilename, SRAMPath[ROMSlot_NDS], 1024); // Use existing SRAMPath - - int pos = strlen(sramfilename) - 1; - while(pos > 0 && sramfilename[pos] != '/' && sramfilename[pos] != '\\') - --pos; - - strncpy(romfilename, &sramfilename[pos + 1], 1024); - strncpy(&romfilename[strlen(romfilename) - 3], NDSROMExtension, 3); // extension could be nds, srl or dsi - printf("RESET loading from archive : %s\n", romfilename); - romlen = Archive::ExtractFileFromArchive(ROMPath[ROMSlot_NDS], romfilename, &romdata); - if(!romdata) - return Load_ROMLoadError; - - bool ok = NDS::LoadROM(romdata, romlen, sramfilename, directboot); - delete romdata; - if(!ok) - return Load_ROMLoadError; - } -#endif - } - - if (ROMPath[ROMSlot_GBA][0] != '\0') - { - char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_GBA]); - strncpy(ext, ROMPath[ROMSlot_GBA] + _len - 4, 4); - - if(!strncasecmp(ext, ".gba", 4)) - { - SetupSRAMPath(1); - if (!NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA])) - return Load_ROMLoadError; - } -#ifdef ARCHIVE_SUPPORT_ENABLED - else - { - u8 *romdata = nullptr; u32 romlen; - char romfilename[1024] = {0}, sramfilename[1024]; - strncpy(sramfilename, SRAMPath[ROMSlot_GBA], 1024); // Use existing SRAMPath - - int pos = strlen(sramfilename) - 1; - while(pos > 0 && sramfilename[pos] != '/' && sramfilename[pos] != '\\') - --pos; - - strncpy(romfilename, &sramfilename[pos + 1], 1024); - strncpy(&romfilename[strlen(romfilename) - 3], "gba", 3); - printf("RESET loading from archive : %s\n", romfilename); - romlen = Archive::ExtractFileFromArchive(ROMPath[ROMSlot_GBA], romfilename, &romdata); - if(!romdata) - return Load_ROMLoadError; - - bool ok = NDS::LoadGBAROM(romdata, romlen, romfilename, SRAMPath[ROMSlot_GBA]); - delete romdata; - if(!ok) - return Load_ROMLoadError; - } -#endif - } - - LoadCheats(); - - return Load_OK; -} - - -// 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 - { - char *rompath; - char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_NDS]); - strncpy(ext, ROMPath[ROMSlot_NDS] + _len - 4, 4); - - if(!strncasecmp(ext, ".nds", 4) || !strncasecmp(ext, ".srl", 4) || !strncasecmp(ext, ".dsi", 4)) - rompath = ROMPath[ROMSlot_NDS]; - else - rompath = SRAMPath[ROMSlot_NDS]; // If archive, construct ssname from sram file - - int l = strlen(rompath); - pos = l; - while (rompath[pos] != '.' && pos > 0) pos--; - if (pos == 0) pos = l; - - // avoid buffer overflow. shoddy - if (pos > len-5) pos = len-5; - - strncpy(&filename[0], rompath, 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); - } - } - - 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); - } -} - -int ImportSRAM(const char* filename) -{ - FILE* file = fopen(filename, "rb"); - fseek(file, 0, SEEK_END); - u32 size = ftell(file); - u8* importData = new u8[size]; - rewind(file); - fread(importData, size, 1, file); - fclose(file); - - int diff = NDS::ImportSRAM(importData, size); - delete[] importData; - return diff; -} - -void EnableCheats(bool enable) -{ - CheatsOn = enable; - if (CheatFile) - AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); -} - -} diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index 6919d48..af8e4a9 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -17,43 +17,66 @@ */ #include "ArchiveUtil.h" +#include "Platform.h" namespace Archive { -QVector<QString> ListArchive(const char* path) +#ifdef __WIN32__ +#define melon_archive_open(a, f, b) archive_read_open_filename_w(a, (const wchar_t*)f.utf16(), b) +#else +#define melon_archive_open(a, f, b) archive_read_open_filename(a, f.toUtf8().constData(), b) +#endif // __WIN32__ + +bool compareCI(const QString& s1, const QString& s2) +{ + return s1.toLower() < s2.toLower(); +} + +QVector<QString> ListArchive(QString path) { struct archive *a; struct archive_entry *entry; int r; - QVector<QString> fileList = {"OK"}; - + QVector<QString> fileList; + a = archive_read_new(); + archive_read_support_filter_all(a); archive_read_support_format_all(a); - r = archive_read_open_filename(a, path, 10240); + + //r = archive_read_open_filename(a, path, 10240); + r = melon_archive_open(a, path, 10240); if (r != ARCHIVE_OK) { return QVector<QString> {"Err"}; } - - while (archive_read_next_header(a, &entry) == ARCHIVE_OK) + + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { - fileList.push_back(archive_entry_pathname(entry)); - archive_read_data_skip(a); + if (archive_entry_filetype(entry) != AE_IFREG) + continue; + + fileList.push_back(archive_entry_pathname_utf8(entry)); + archive_read_data_skip(a); } + archive_read_close(a); - archive_read_free(a); + archive_read_free(a); + if (r != ARCHIVE_OK) { return QVector<QString> {"Err"}; } - + + std::stable_sort(fileList.begin(), fileList.end(), compareCI); + fileList.prepend("OK"); + return fileList; } -QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer) +QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer) { struct archive *a = archive_read_new(); struct archive_entry *entry; @@ -61,8 +84,9 @@ QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile archive_read_support_format_all(a); archive_read_support_filter_all(a); - - r = archive_read_open_filename(a, path, 10240); + + //r = archive_read_open_filename(a, path, 10240); + r = melon_archive_open(a, path, 10240); if (r != ARCHIVE_OK) { return QVector<QString> {"Err"}; @@ -70,7 +94,7 @@ QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { - if (strcmp(wantedFile, archive_entry_pathname(entry)) == 0) + if (strcmp(wantedFile.toUtf8().constData(), archive_entry_pathname_utf8(entry)) == 0) { break; } @@ -92,7 +116,45 @@ QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile } -u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata) +u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize) +{ + struct archive *a = archive_read_new(); + struct archive_entry *entry; + int r; + + if (!filedata) return -1; + + archive_read_support_format_all(a); + archive_read_support_filter_all(a); + + //r = archive_read_open_filename(a, path, 10240); + r = melon_archive_open(a, path, 10240); + if (r != ARCHIVE_OK) + { + return -1; + } + + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) + { + if (strcmp(wantedFile.toUtf8().constData(), archive_entry_pathname_utf8(entry)) == 0) + { + break; + } + } + + size_t bytesToRead = archive_entry_size(entry); + if (filesize) *filesize = bytesToRead; + *filedata = new u8[bytesToRead]; + ssize_t bytesRead = archive_read_data(a, *filedata, bytesToRead); + + archive_read_close(a); + archive_read_free(a); + + return (u32)bytesRead; + +} + +/*u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata) { QByteArray romBuffer; QVector<QString> extractResult = ExtractFileFromArchive(path, wantedFile, &romBuffer); @@ -107,6 +169,6 @@ u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdat memcpy(*romdata, romBuffer.data(), len); return len; -} +}*/ } diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index a8a4a14..ce5e192 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -34,10 +34,11 @@ namespace Archive { - -QVector<QString> ListArchive(const char* path); -QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer); -u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata); + +QVector<QString> ListArchive(QString path); +u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize); +//QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer); +//u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata); } diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index d4ce678..dff3055 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -62,7 +62,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent) : QDialog(parent), ui( connect(grpMicMode, SIGNAL(buttonClicked(int)), this, SLOT(onChangeMicMode(int))); grpMicMode->button(Config::MicInputType)->setChecked(true); - ui->txtMicWavPath->setText(Config::MicWavPath); + ui->txtMicWavPath->setText(QString::fromStdString(Config::MicWavPath)); bool iswav = (Config::MicInputType == 3); ui->txtMicWavPath->setEnabled(iswav); @@ -77,7 +77,7 @@ AudioSettingsDialog::~AudioSettingsDialog() void AudioSettingsDialog::on_AudioSettingsDialog_accepted() { Config::MicInputType = grpMicMode->checkedId(); - strncpy(Config::MicWavPath, ui->txtMicWavPath->text().toStdString().c_str(), 1023); Config::MicWavPath[1023] = '\0'; + Config::MicWavPath = ui->txtMicWavPath->text().toStdString(); Config::Save(); closeDlg(); diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index ad38e37..b1dfd45 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -12,6 +12,7 @@ SET(SOURCES_QT_SDL VideoSettingsDialog.cpp AudioSettingsDialog.cpp FirmwareSettingsDialog.cpp + PathSettingsDialog.cpp WifiSettingsDialog.cpp InterfaceSettingsDialog.cpp ROMInfoDialog.cpp @@ -24,16 +25,16 @@ SET(SOURCES_QT_SDL font.h Platform.cpp QPathInput.h + ROMManager.cpp + SaveManager.cpp ArchiveUtil.h ArchiveUtil.cpp - ../Util_ROM.cpp ../Util_Video.cpp ../Util_Audio.cpp ../FrontendUtil.h ../mic_blow.h - ../SharedConfig.h ${CMAKE_SOURCE_DIR}/res/melon.qrc ) diff --git a/src/frontend/qt_sdl/CheatsDialog.cpp b/src/frontend/qt_sdl/CheatsDialog.cpp index afa0805..28211ad 100644 --- a/src/frontend/qt_sdl/CheatsDialog.cpp +++ b/src/frontend/qt_sdl/CheatsDialog.cpp @@ -24,6 +24,7 @@ #include "types.h" #include "Platform.h" #include "Config.h" +#include "ROMManager.h" #include "CheatsDialog.h" #include "ui_CheatsDialog.h" @@ -33,15 +34,13 @@ CheatsDialog* CheatsDialog::currentDlg = nullptr; extern std::string EmuDirectory; -namespace Frontend { extern ARCodeFile* CheatFile; } - CheatsDialog::CheatsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::CheatsDialog) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - codeFile = Frontend::CheatFile; + codeFile = ROMManager::GetCheatFile(); QStandardItemModel* model = new QStandardItemModel(); ui->tvCodeList->setModel(model); @@ -55,7 +54,7 @@ CheatsDialog::CheatsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Cheats { ARCodeCat& cat = *i; - QStandardItem* catitem = new QStandardItem(cat.Name); + QStandardItem* catitem = new QStandardItem(QString::fromStdString(cat.Name)); catitem->setEditable(true); catitem->setData(QVariant::fromValue(i)); root->appendRow(catitem); @@ -64,7 +63,7 @@ CheatsDialog::CheatsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::Cheats { ARCode& code = *j; - QStandardItem* codeitem = new QStandardItem(code.Name); + QStandardItem* codeitem = new QStandardItem(QString::fromStdString(code.Name)); codeitem->setEditable(true); codeitem->setCheckable(true); codeitem->setCheckState(code.Enabled ? Qt::Checked : Qt::Unchecked); @@ -113,13 +112,12 @@ void CheatsDialog::on_btnNewCat_clicked() ARCodeCat cat; cat.Codes.clear(); - memset(cat.Name, 0, 128); - strncpy(cat.Name, "(new category)", 127); + cat.Name = "(new category)"; codeFile->Categories.push_back(cat); ARCodeCatList::iterator id = codeFile->Categories.end(); id--; - QStandardItem* catitem = new QStandardItem(cat.Name); + QStandardItem* catitem = new QStandardItem(QString::fromStdString(cat.Name)); catitem->setEditable(true); catitem->setData(QVariant::fromValue(id)); root->appendRow(catitem); @@ -160,8 +158,7 @@ void CheatsDialog::on_btnNewARCode_clicked() ARCodeCat& cat = *it_cat; ARCode code; - memset(code.Name, 0, 128); - strncpy(code.Name, "(new AR code)", 127); + code.Name = "(new AR code)"; code.Enabled = true; code.CodeLen = 0; memset(code.Code, 0, sizeof(code.Code)); @@ -169,7 +166,7 @@ void CheatsDialog::on_btnNewARCode_clicked() cat.Codes.push_back(code); ARCodeList::iterator id = cat.Codes.end(); id--; - QStandardItem* codeitem = new QStandardItem(code.Name); + QStandardItem* codeitem = new QStandardItem(QString::fromStdString(code.Name)); codeitem->setEditable(true); codeitem->setCheckable(true); codeitem->setCheckState(code.Enabled ? Qt::Checked : Qt::Unchecked); @@ -275,13 +272,12 @@ void CheatsDialog::onCheatEntryModified(QStandardItem* item) if (item->text().isEmpty()) { - QString oldname = QString(cat.Name); + QString oldname = QString::fromStdString(cat.Name); item->setText(oldname.isEmpty() ? "(blank category name?)" : oldname); } else { - strncpy(cat.Name, item->text().toStdString().c_str(), 127); - cat.Name[127] = '\0'; + cat.Name = item->text().toStdString(); } } else if (data.canConvert<ARCodeList::iterator>()) @@ -290,13 +286,12 @@ void CheatsDialog::onCheatEntryModified(QStandardItem* item) if (item->text().isEmpty()) { - QString oldname = QString(code.Name); + QString oldname = QString::fromStdString(code.Name); item->setText(oldname.isEmpty() ? "(blank code name?)" : oldname); } else { - strncpy(code.Name, item->text().toStdString().c_str(), 127); - code.Name[127] = '\0'; + code.Name = item->text().toStdString(); } code.Enabled = (item->checkState() == Qt::Checked); diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 30babaf..cf2e0d6 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -36,268 +36,276 @@ int JoystickID; int WindowWidth; int WindowHeight; -int WindowMaximized; +bool WindowMaximized; int ScreenRotation; int ScreenGap; int ScreenLayout; -int ScreenSwap; +bool ScreenSwap; int ScreenSizing; -int IntegerScaling; +bool IntegerScaling; int ScreenAspectTop; int ScreenAspectBot; -int ScreenFilter; +bool ScreenFilter; -int ScreenUseGL; -int ScreenVSync; +bool ScreenUseGL; +bool ScreenVSync; int ScreenVSyncInterval; int _3DRenderer; -int Threaded3D; +bool Threaded3D; int GL_ScaleFactor; -int GL_BetterPolygons; +bool GL_BetterPolygons; -int LimitFPS; -int AudioSync; -int ShowOSD; +bool LimitFPS; +bool AudioSync; +bool ShowOSD; int ConsoleType; -int DirectBoot; +bool DirectBoot; #ifdef JIT_ENABLED -int JIT_Enable = false; +bool JIT_Enable = false; int JIT_MaxBlockSize = 32; -int JIT_BranchOptimisations = true; -int JIT_LiteralOptimisations = true; -int JIT_FastMemory = true; +bool JIT_BranchOptimisations = true; +bool JIT_LiteralOptimisations = true; +bool JIT_FastMemory = true; #endif -int ExternalBIOSEnable; +bool ExternalBIOSEnable; -char BIOS9Path[1024]; -char BIOS7Path[1024]; -char FirmwarePath[1024]; +std::string BIOS9Path; +std::string BIOS7Path; +std::string FirmwarePath; -char DSiBIOS9Path[1024]; -char DSiBIOS7Path[1024]; -char DSiFirmwarePath[1024]; -char DSiNANDPath[1024]; +std::string DSiBIOS9Path; +std::string DSiBIOS7Path; +std::string DSiFirmwarePath; +std::string DSiNANDPath; -int DLDIEnable; -char DLDISDPath[1024]; +bool DLDIEnable; +std::string DLDISDPath; int DLDISize; -int DLDIReadOnly; -int DLDIFolderSync; -char DLDIFolderPath[1024]; +bool DLDIReadOnly; +bool DLDIFolderSync; +std::string DLDIFolderPath; -int DSiSDEnable; -char DSiSDPath[1024]; +bool DSiSDEnable; +std::string DSiSDPath; int DSiSDSize; -int DSiSDReadOnly; -int DSiSDFolderSync; -char DSiSDFolderPath[1024]; +bool DSiSDReadOnly; +bool DSiSDFolderSync; +std::string DSiSDFolderPath; -int FirmwareOverrideSettings; -char FirmwareUsername[64]; +bool FirmwareOverrideSettings; +std::string FirmwareUsername; int FirmwareLanguage; int FirmwareBirthdayMonth; int FirmwareBirthdayDay; int FirmwareFavouriteColour; -char FirmwareMessage[1024]; -char FirmwareMAC[18]; -int RandomizeMAC; +std::string FirmwareMessage; +std::string FirmwareMAC; +bool RandomizeMAC; -int SocketBindAnyAddr; -char LANDevice[128]; -int DirectLAN; +bool SocketBindAnyAddr; +std::string LANDevice; +bool DirectLAN; -int SavestateRelocSRAM; +bool SavestateRelocSRAM; int AudioInterp; int AudioBitrate; int AudioVolume; int MicInputType; -char MicWavPath[1024]; +std::string MicWavPath; -char LastROMFolder[1024]; +std::string LastROMFolder; -char RecentROMList[10][1024]; +std::string RecentROMList[10]; -int EnableCheats; +std::string SaveFilePath; +std::string SavestatePath; +std::string CheatFilePath; -int MouseHide; +bool EnableCheats; + +bool MouseHide; int MouseHideSeconds; -int PauseLostFocus; +bool PauseLostFocus; const char* kConfigFile = "melonDS.ini"; ConfigEntry ConfigFile[] = { - {"Key_A", 0, &KeyMapping[0], -1, NULL, 0}, - {"Key_B", 0, &KeyMapping[1], -1, NULL, 0}, - {"Key_Select", 0, &KeyMapping[2], -1, NULL, 0}, - {"Key_Start", 0, &KeyMapping[3], -1, NULL, 0}, - {"Key_Right", 0, &KeyMapping[4], -1, NULL, 0}, - {"Key_Left", 0, &KeyMapping[5], -1, NULL, 0}, - {"Key_Up", 0, &KeyMapping[6], -1, NULL, 0}, - {"Key_Down", 0, &KeyMapping[7], -1, NULL, 0}, - {"Key_R", 0, &KeyMapping[8], -1, NULL, 0}, - {"Key_L", 0, &KeyMapping[9], -1, NULL, 0}, - {"Key_X", 0, &KeyMapping[10], -1, NULL, 0}, - {"Key_Y", 0, &KeyMapping[11], -1, NULL, 0}, - - {"Joy_A", 0, &JoyMapping[0], -1, NULL, 0}, - {"Joy_B", 0, &JoyMapping[1], -1, NULL, 0}, - {"Joy_Select", 0, &JoyMapping[2], -1, NULL, 0}, - {"Joy_Start", 0, &JoyMapping[3], -1, NULL, 0}, - {"Joy_Right", 0, &JoyMapping[4], -1, NULL, 0}, - {"Joy_Left", 0, &JoyMapping[5], -1, NULL, 0}, - {"Joy_Up", 0, &JoyMapping[6], -1, NULL, 0}, - {"Joy_Down", 0, &JoyMapping[7], -1, NULL, 0}, - {"Joy_R", 0, &JoyMapping[8], -1, NULL, 0}, - {"Joy_L", 0, &JoyMapping[9], -1, NULL, 0}, - {"Joy_X", 0, &JoyMapping[10], -1, NULL, 0}, - {"Joy_Y", 0, &JoyMapping[11], -1, NULL, 0}, - - {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1, NULL, 0}, - {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1, NULL, 0}, - {"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1, NULL, 0}, - {"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1, NULL, 0}, - {"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1, NULL, 0}, - {"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1, NULL, 0}, - {"HKKey_FullscreenToggle", 0, &HKKeyMapping[HK_FullscreenToggle], -1, NULL, 0}, - {"HKKey_SwapScreens", 0, &HKKeyMapping[HK_SwapScreens], -1, NULL, 0}, - {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1, NULL, 0}, - {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1, NULL, 0}, - {"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1, NULL, 0}, - - {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1, NULL, 0}, - {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1, NULL, 0}, - {"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1, NULL, 0}, - {"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1, NULL, 0}, - {"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1, NULL, 0}, - {"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1, NULL, 0}, - {"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1, NULL, 0}, - {"HKJoy_SwapScreens", 0, &HKJoyMapping[HK_SwapScreens], -1, NULL, 0}, - {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1, NULL, 0}, - {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1, NULL, 0}, - {"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1, NULL, 0}, - - {"JoystickID", 0, &JoystickID, 0, NULL, 0}, - - {"WindowWidth", 0, &WindowWidth, 256, NULL, 0}, - {"WindowHeight", 0, &WindowHeight, 384, NULL, 0}, - {"WindowMax", 0, &WindowMaximized, 0, NULL, 0}, - - {"ScreenRotation", 0, &ScreenRotation, 0, NULL, 0}, - {"ScreenGap", 0, &ScreenGap, 0, NULL, 0}, - {"ScreenLayout", 0, &ScreenLayout, 0, NULL, 0}, - {"ScreenSwap", 0, &ScreenSwap, 0, NULL, 0}, - {"ScreenSizing", 0, &ScreenSizing, 0, NULL, 0}, - {"IntegerScaling", 0, &IntegerScaling, 0, NULL, 0}, - {"ScreenAspectTop",0, &ScreenAspectTop,0, NULL, 0}, - {"ScreenAspectBot",0, &ScreenAspectBot,0, NULL, 0}, - {"ScreenFilter", 0, &ScreenFilter, 1, NULL, 0}, - - {"ScreenUseGL", 0, &ScreenUseGL, 0, NULL, 0}, - {"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0}, - {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, NULL, 0}, - - {"3DRenderer", 0, &_3DRenderer, 0, NULL, 0}, - {"Threaded3D", 0, &Threaded3D, 1, NULL, 0}, - - {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, NULL, 0}, - {"GL_BetterPolygons", 0, &GL_BetterPolygons, 0, NULL, 0}, - - {"LimitFPS", 0, &LimitFPS, 1, NULL, 0}, - {"AudioSync", 0, &AudioSync, 0, NULL, 0}, - {"ShowOSD", 0, &ShowOSD, 1, NULL, 0}, - - {"ConsoleType", 0, &ConsoleType, 0, NULL, 0}, - {"DirectBoot", 0, &DirectBoot, 1, NULL, 0}, + {"Key_A", 0, &KeyMapping[0], -1}, + {"Key_B", 0, &KeyMapping[1], -1}, + {"Key_Select", 0, &KeyMapping[2], -1}, + {"Key_Start", 0, &KeyMapping[3], -1}, + {"Key_Right", 0, &KeyMapping[4], -1}, + {"Key_Left", 0, &KeyMapping[5], -1}, + {"Key_Up", 0, &KeyMapping[6], -1}, + {"Key_Down", 0, &KeyMapping[7], -1}, + {"Key_R", 0, &KeyMapping[8], -1}, + {"Key_L", 0, &KeyMapping[9], -1}, + {"Key_X", 0, &KeyMapping[10], -1}, + {"Key_Y", 0, &KeyMapping[11], -1}, + + {"Joy_A", 0, &JoyMapping[0], -1}, + {"Joy_B", 0, &JoyMapping[1], -1}, + {"Joy_Select", 0, &JoyMapping[2], -1}, + {"Joy_Start", 0, &JoyMapping[3], -1}, + {"Joy_Right", 0, &JoyMapping[4], -1}, + {"Joy_Left", 0, &JoyMapping[5], -1}, + {"Joy_Up", 0, &JoyMapping[6], -1}, + {"Joy_Down", 0, &JoyMapping[7], -1}, + {"Joy_R", 0, &JoyMapping[8], -1}, + {"Joy_L", 0, &JoyMapping[9], -1}, + {"Joy_X", 0, &JoyMapping[10], -1}, + {"Joy_Y", 0, &JoyMapping[11], -1}, + + {"HKKey_Lid", 0, &HKKeyMapping[HK_Lid], -1}, + {"HKKey_Mic", 0, &HKKeyMapping[HK_Mic], -1}, + {"HKKey_Pause", 0, &HKKeyMapping[HK_Pause], -1}, + {"HKKey_Reset", 0, &HKKeyMapping[HK_Reset], -1}, + {"HKKey_FastForward", 0, &HKKeyMapping[HK_FastForward], -1}, + {"HKKey_FastForwardToggle", 0, &HKKeyMapping[HK_FastForwardToggle], -1}, + {"HKKey_FullscreenToggle", 0, &HKKeyMapping[HK_FullscreenToggle], -1}, + {"HKKey_SwapScreens", 0, &HKKeyMapping[HK_SwapScreens], -1}, + {"HKKey_SolarSensorDecrease", 0, &HKKeyMapping[HK_SolarSensorDecrease], -1}, + {"HKKey_SolarSensorIncrease", 0, &HKKeyMapping[HK_SolarSensorIncrease], -1}, + {"HKKey_FrameStep", 0, &HKKeyMapping[HK_FrameStep], -1}, + + {"HKJoy_Lid", 0, &HKJoyMapping[HK_Lid], -1}, + {"HKJoy_Mic", 0, &HKJoyMapping[HK_Mic], -1}, + {"HKJoy_Pause", 0, &HKJoyMapping[HK_Pause], -1}, + {"HKJoy_Reset", 0, &HKJoyMapping[HK_Reset], -1}, + {"HKJoy_FastForward", 0, &HKJoyMapping[HK_FastForward], -1}, + {"HKJoy_FastForwardToggle", 0, &HKJoyMapping[HK_FastForwardToggle], -1}, + {"HKJoy_FullscreenToggle", 0, &HKJoyMapping[HK_FullscreenToggle], -1}, + {"HKJoy_SwapScreens", 0, &HKJoyMapping[HK_SwapScreens], -1}, + {"HKJoy_SolarSensorDecrease", 0, &HKJoyMapping[HK_SolarSensorDecrease], -1}, + {"HKJoy_SolarSensorIncrease", 0, &HKJoyMapping[HK_SolarSensorIncrease], -1}, + {"HKJoy_FrameStep", 0, &HKJoyMapping[HK_FrameStep], -1}, + + {"JoystickID", 0, &JoystickID, 0}, + + {"WindowWidth", 0, &WindowWidth, 256}, + {"WindowHeight", 0, &WindowHeight, 384}, + {"WindowMax", 1, &WindowMaximized, false}, + + {"ScreenRotation", 0, &ScreenRotation, 0}, + {"ScreenGap", 0, &ScreenGap, 0}, + {"ScreenLayout", 0, &ScreenLayout, 0}, + {"ScreenSwap", 1, &ScreenSwap, false}, + {"ScreenSizing", 0, &ScreenSizing, 0}, + {"IntegerScaling", 1, &IntegerScaling, false}, + {"ScreenAspectTop",0, &ScreenAspectTop,0}, + {"ScreenAspectBot",0, &ScreenAspectBot,0}, + {"ScreenFilter", 1, &ScreenFilter, true}, + + {"ScreenUseGL", 1, &ScreenUseGL, false}, + {"ScreenVSync", 1, &ScreenVSync, false}, + {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1}, + + {"3DRenderer", 0, &_3DRenderer, 0}, + {"Threaded3D", 1, &Threaded3D, true}, + + {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1}, + {"GL_BetterPolygons", 1, &GL_BetterPolygons, false}, + + {"LimitFPS", 1, &LimitFPS, true}, + {"AudioSync", 1, &AudioSync, false}, + {"ShowOSD", 1, &ShowOSD, true}, + + {"ConsoleType", 0, &ConsoleType, 0}, + {"DirectBoot", 1, &DirectBoot, true}, #ifdef JIT_ENABLED - {"JIT_Enable", 0, &JIT_Enable, 0, NULL, 0}, - {"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32, NULL, 0}, - {"JIT_BranchOptimisations", 0, &JIT_BranchOptimisations, 1, NULL, 0}, - {"JIT_LiteralOptimisations", 0, &JIT_LiteralOptimisations, 1, NULL, 0}, + {"JIT_Enable", 1, &JIT_Enable, false}, + {"JIT_MaxBlockSize", 0, &JIT_MaxBlockSize, 32}, + {"JIT_BranchOptimisations", 1, &JIT_BranchOptimisations, true}, + {"JIT_LiteralOptimisations", 1, &JIT_LiteralOptimisations, true}, #ifdef __APPLE__ - {"JIT_FastMemory", 0, &JIT_FastMemory, 0, NULL, 0}, + {"JIT_FastMemory", 1, &JIT_FastMemory, false}, #else - {"JIT_FastMemory", 0, &JIT_FastMemory, 1, NULL, 0}, + {"JIT_FastMemory", 1, &JIT_FastMemory, true}, #endif #endif - {"ExternalBIOSEnable", 0, &ExternalBIOSEnable, 0, NULL, 0}, - - {"BIOS9Path", 1, BIOS9Path, 0, "", 1023}, - {"BIOS7Path", 1, BIOS7Path, 0, "", 1023}, - {"FirmwarePath", 1, FirmwarePath, 0, "", 1023}, - - {"DSiBIOS9Path", 1, DSiBIOS9Path, 0, "", 1023}, - {"DSiBIOS7Path", 1, DSiBIOS7Path, 0, "", 1023}, - {"DSiFirmwarePath", 1, DSiFirmwarePath, 0, "", 1023}, - {"DSiNANDPath", 1, DSiNANDPath, 0, "", 1023}, - - {"DLDIEnable", 0, &DLDIEnable, 0, NULL, 0}, - {"DLDISDPath", 1, DLDISDPath, 0, "dldi.bin", 1023}, - {"DLDISize", 0, &DLDISize, 0, NULL, 0}, - {"DLDIReadOnly", 0, &DLDIReadOnly, 0, NULL, 0}, - {"DLDIFolderSync", 0, &DLDIFolderSync, 0, NULL, 0}, - {"DLDIFolderPath", 1, DLDIFolderPath, 0, "", 1023}, - - {"DSiSDEnable", 0, &DSiSDEnable, 0, NULL, 0}, - {"DSiSDPath", 1, DSiSDPath, 0, "dsisd.bin", 1023}, - {"DSiSDSize", 0, &DSiSDSize, 0, NULL, 0}, - {"DSiSDReadOnly", 0, &DSiSDReadOnly, 0, NULL, 0}, - {"DSiSDFolderSync", 0, &DSiSDFolderSync, 0, NULL, 0}, - {"DSiSDFolderPath", 1, DSiSDFolderPath, 0, "", 1023}, - - {"FirmwareOverrideSettings", 0, &FirmwareOverrideSettings, false, NULL, 0}, - {"FirmwareUsername", 1, FirmwareUsername, 0, "melonDS", 63}, - {"FirmwareLanguage", 0, &FirmwareLanguage, 1, NULL, 0}, - {"FirmwareBirthdayMonth", 0, &FirmwareBirthdayMonth, 0, NULL, 0}, - {"FirmwareBirthdayDay", 0, &FirmwareBirthdayDay, 0, NULL, 0}, - {"FirmwareFavouriteColour", 0, &FirmwareFavouriteColour, 0, NULL, 0}, - {"FirmwareMessage", 1, FirmwareMessage, 0, "", 1023}, - {"FirmwareMAC", 1, FirmwareMAC, 0, "", 17}, - {"RandomizeMAC", 0, &RandomizeMAC, 0, NULL, 0}, - - {"SockBindAnyAddr", 0, &SocketBindAnyAddr, 0, NULL, 0}, - {"LANDevice", 1, LANDevice, 0, "", 127}, - {"DirectLAN", 0, &DirectLAN, 0, NULL, 0}, - - {"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 0, NULL, 0}, - - {"AudioInterp", 0, &AudioInterp, 0, NULL, 0}, - {"AudioBitrate", 0, &AudioBitrate, 0, NULL, 0}, - {"AudioVolume", 0, &AudioVolume, 256, NULL, 0}, - {"MicInputType", 0, &MicInputType, 1, NULL, 0}, - {"MicWavPath", 1, MicWavPath, 0, "", 1023}, - - {"LastROMFolder", 1, LastROMFolder, 0, "", 1023}, - - {"RecentROM_0", 1, RecentROMList[0], 0, "", 1023}, - {"RecentROM_1", 1, RecentROMList[1], 0, "", 1023}, - {"RecentROM_2", 1, RecentROMList[2], 0, "", 1023}, - {"RecentROM_3", 1, RecentROMList[3], 0, "", 1023}, - {"RecentROM_4", 1, RecentROMList[4], 0, "", 1023}, - {"RecentROM_5", 1, RecentROMList[5], 0, "", 1023}, - {"RecentROM_6", 1, RecentROMList[6], 0, "", 1023}, - {"RecentROM_7", 1, RecentROMList[7], 0, "", 1023}, - {"RecentROM_8", 1, RecentROMList[8], 0, "", 1023}, - {"RecentROM_9", 1, RecentROMList[9], 0, "", 1023}, - - {"EnableCheats", 0, &EnableCheats, 0, NULL, 0}, - - {"MouseHide", 0, &MouseHide, 0, NULL, 0}, - {"MouseHideSeconds", 0, &MouseHideSeconds, 5, NULL, 0}, - {"PauseLostFocus", 0, &PauseLostFocus, 0, NULL, 0}, - - {"", -1, NULL, 0, NULL, 0} + {"ExternalBIOSEnable", 1, &ExternalBIOSEnable, false}, + + {"BIOS9Path", 2, &BIOS9Path, ""}, + {"BIOS7Path", 2, &BIOS7Path, ""}, + {"FirmwarePath", 2, &FirmwarePath, ""}, + + {"DSiBIOS9Path", 2, &DSiBIOS9Path, ""}, + {"DSiBIOS7Path", 2, &DSiBIOS7Path, ""}, + {"DSiFirmwarePath", 2, &DSiFirmwarePath, ""}, + {"DSiNANDPath", 2, &DSiNANDPath, ""}, + + {"DLDIEnable", 1, &DLDIEnable, false}, + {"DLDISDPath", 2, &DLDISDPath, "dldi.bin"}, + {"DLDISize", 0, &DLDISize, 0}, + {"DLDIReadOnly", 1, &DLDIReadOnly, false}, + {"DLDIFolderSync", 1, &DLDIFolderSync, false}, + {"DLDIFolderPath", 2, &DLDIFolderPath, ""}, + + {"DSiSDEnable", 1, &DSiSDEnable, false}, + {"DSiSDPath", 2, &DSiSDPath, "dsisd.bin"}, + {"DSiSDSize", 0, &DSiSDSize, 0}, + {"DSiSDReadOnly", 1, &DSiSDReadOnly, false}, + {"DSiSDFolderSync", 1, &DSiSDFolderSync, false}, + {"DSiSDFolderPath", 2, &DSiSDFolderPath, ""}, + + {"FirmwareOverrideSettings", 1, &FirmwareOverrideSettings, false}, + {"FirmwareUsername", 2, &FirmwareUsername, "melonDS"}, + {"FirmwareLanguage", 0, &FirmwareLanguage, 1}, + {"FirmwareBirthdayMonth", 0, &FirmwareBirthdayMonth, 1}, + {"FirmwareBirthdayDay", 0, &FirmwareBirthdayDay, 1}, + {"FirmwareFavouriteColour", 0, &FirmwareFavouriteColour, 0}, + {"FirmwareMessage", 2, &FirmwareMessage, ""}, + {"FirmwareMAC", 2, &FirmwareMAC, ""}, + {"RandomizeMAC", 1, &RandomizeMAC, false}, + + {"SockBindAnyAddr", 1, &SocketBindAnyAddr, false}, + {"LANDevice", 2, &LANDevice, ""}, + {"DirectLAN", 1, &DirectLAN, false}, + + {"SavStaRelocSRAM", 1, &SavestateRelocSRAM, false}, + + {"AudioInterp", 0, &AudioInterp, 0}, + {"AudioBitrate", 0, &AudioBitrate, 0}, + {"AudioVolume", 0, &AudioVolume, 256}, + {"MicInputType", 0, &MicInputType, 1}, + {"MicWavPath", 2, &MicWavPath, ""}, + + {"LastROMFolder", 2, &LastROMFolder, ""}, + + {"RecentROM_0", 2, &RecentROMList[0], ""}, + {"RecentROM_1", 2, &RecentROMList[1], ""}, + {"RecentROM_2", 2, &RecentROMList[2], ""}, + {"RecentROM_3", 2, &RecentROMList[3], ""}, + {"RecentROM_4", 2, &RecentROMList[4], ""}, + {"RecentROM_5", 2, &RecentROMList[5], ""}, + {"RecentROM_6", 2, &RecentROMList[6], ""}, + {"RecentROM_7", 2, &RecentROMList[7], ""}, + {"RecentROM_8", 2, &RecentROMList[8], ""}, + {"RecentROM_9", 2, &RecentROMList[9], ""}, + + {"SaveFilePath", 2, &SaveFilePath, ""}, + {"SavestatePath", 2, &SavestatePath, ""}, + {"CheatFilePath", 2, &CheatFilePath, ""}, + + {"EnableCheats", 1, &EnableCheats, false}, + + {"MouseHide", 1, &MouseHide, false}, + {"MouseHideSeconds", 0, &MouseHideSeconds, 5}, + {"PauseLostFocus", 1, &PauseLostFocus, false}, + + {"", -1, nullptr, 0} }; @@ -308,12 +316,11 @@ void Load() { if (!entry->Value) break; - if (entry->Type == 0) - *(int*)entry->Value = entry->DefaultInt; - else + switch (entry->Type) { - strncpy((char*)entry->Value, entry->DefaultStr, entry->StrLength); - ((char*)entry->Value)[entry->StrLength] = '\0'; + case 0: *(int*)entry->Value = std::get<int>(entry->Default); break; + case 1: *(bool*)entry->Value = std::get<bool>(entry->Default); break; + case 2: *(std::string*)entry->Value = std::get<std::string>(entry->Default); break; } entry++; @@ -341,10 +348,12 @@ void Load() if (!strncmp(entry->Name, entryname, 32)) { - if (entry->Type == 0) - *(int*)entry->Value = strtol(entryval, NULL, 10); - else - strncpy((char*)entry->Value, entryval, entry->StrLength); + switch (entry->Type) + { + case 0: *(int*)entry->Value = strtol(entryval, NULL, 10); break; + case 1: *(bool*)entry->Value = strtol(entryval, NULL, 10) ? true:false; break; + case 2: *(std::string*)entry->Value = entryval; break; + } break; } @@ -366,10 +375,12 @@ void Save() { if (!entry->Value) break; - if (entry->Type == 0) - fprintf(f, "%s=%d\r\n", entry->Name, *(int*)entry->Value); - else - fprintf(f, "%s=%s\r\n", entry->Name, (char*)entry->Value); + switch (entry->Type) + { + case 0: fprintf(f, "%s=%d\r\n", entry->Name, *(int*)entry->Value); break; + case 1: fprintf(f, "%s=%d\r\n", entry->Name, *(bool*)entry->Value ? 1:0); break; + case 2: fprintf(f, "%s=%s\r\n", entry->Name, (*(std::string*)entry->Value).c_str()); break; + } entry++; } diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index ad9b4c6..902ec6d 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -19,6 +19,9 @@ #ifndef PLATFORMCONFIG_H #define PLATFORMCONFIG_H +#include <variant> +#include <string> + enum { HK_Lid = 0, @@ -41,11 +44,9 @@ namespace Config struct ConfigEntry { char Name[32]; - int Type; - void* Value; - int DefaultInt; - const char* DefaultStr; - int StrLength; // should be set to actual array length minus one + int Type; // 0=int 1=bool 2=string + void* Value; // pointer to the value variable + std::variant<int, bool, std::string> Default; }; @@ -59,99 +60,103 @@ extern int JoystickID; extern int WindowWidth; extern int WindowHeight; -extern int WindowMaximized; +extern bool WindowMaximized; extern int ScreenRotation; extern int ScreenGap; extern int ScreenLayout; -extern int ScreenSwap; +extern bool ScreenSwap; extern int ScreenSizing; extern int ScreenAspectTop; extern int ScreenAspectBot; -extern int IntegerScaling; -extern int ScreenFilter; +extern bool IntegerScaling; +extern bool ScreenFilter; -extern int ScreenUseGL; -extern int ScreenVSync; +extern bool ScreenUseGL; +extern bool ScreenVSync; extern int ScreenVSyncInterval; extern int _3DRenderer; -extern int Threaded3D; +extern bool Threaded3D; extern int GL_ScaleFactor; -extern int GL_BetterPolygons; +extern bool GL_BetterPolygons; -extern int LimitFPS; -extern int AudioSync; -extern int ShowOSD; +extern bool LimitFPS; +extern bool AudioSync; +extern bool ShowOSD; extern int ConsoleType; -extern int DirectBoot; +extern bool DirectBoot; #ifdef JIT_ENABLED -extern int JIT_Enable; +extern bool JIT_Enable; extern int JIT_MaxBlockSize; -extern int JIT_BranchOptimisations; -extern int JIT_LiteralOptimisations; -extern int JIT_FastMemory; +extern bool JIT_BranchOptimisations; +extern bool JIT_LiteralOptimisations; +extern bool JIT_FastMemory; #endif -extern int ExternalBIOSEnable; +extern bool ExternalBIOSEnable; -extern char BIOS9Path[1024]; -extern char BIOS7Path[1024]; -extern char FirmwarePath[1024]; +extern std::string BIOS9Path; +extern std::string BIOS7Path; +extern std::string FirmwarePath; -extern char DSiBIOS9Path[1024]; -extern char DSiBIOS7Path[1024]; -extern char DSiFirmwarePath[1024]; -extern char DSiNANDPath[1024]; +extern std::string DSiBIOS9Path; +extern std::string DSiBIOS7Path; +extern std::string DSiFirmwarePath; +extern std::string DSiNANDPath; -extern int DLDIEnable; -extern char DLDISDPath[1024]; +extern bool DLDIEnable; +extern std::string DLDISDPath; extern int DLDISize; -extern int DLDIReadOnly; -extern int DLDIFolderSync; -extern char DLDIFolderPath[1024]; +extern bool DLDIReadOnly; +extern bool DLDIFolderSync; +extern std::string DLDIFolderPath; -extern int DSiSDEnable; -extern char DSiSDPath[1024]; +extern bool DSiSDEnable; +extern std::string DSiSDPath; extern int DSiSDSize; -extern int DSiSDReadOnly; -extern int DSiSDFolderSync; -extern char DSiSDFolderPath[1024]; +extern bool DSiSDReadOnly; +extern bool DSiSDFolderSync; +extern std::string DSiSDFolderPath; -extern int FirmwareOverrideSettings; -extern char FirmwareUsername[64]; +extern bool FirmwareOverrideSettings; +extern std::string FirmwareUsername; extern int FirmwareLanguage; extern int FirmwareBirthdayMonth; extern int FirmwareBirthdayDay; extern int FirmwareFavouriteColour; -extern char FirmwareMessage[1024]; -extern char FirmwareMAC[18]; -extern int RandomizeMAC; +extern std::string FirmwareMessage; +extern std::string FirmwareMAC; +extern bool RandomizeMAC; -extern int SocketBindAnyAddr; -extern char LANDevice[128]; -extern int DirectLAN; +extern bool SocketBindAnyAddr; +extern std::string LANDevice; +extern bool DirectLAN; -extern int SavestateRelocSRAM; +extern bool SavestateRelocSRAM; extern int AudioInterp; extern int AudioBitrate; extern int AudioVolume; extern int MicInputType; -extern char MicWavPath[1024]; +extern std::string MicWavPath; + +extern std::string LastROMFolder; -extern char LastROMFolder[1024]; +extern std::string RecentROMList[10]; -extern char RecentROMList[10][1024]; +extern std::string SaveFilePath; +extern std::string SavestatePath; +extern std::string CheatFilePath; -extern int EnableCheats; +extern bool EnableCheats; -extern int MouseHide; +extern bool MouseHide; extern int MouseHideSeconds; -extern int PauseLostFocus; +extern bool PauseLostFocus; void Load(); diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index fd2ca85..7511881 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -42,27 +42,27 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - ui->chkExternalBIOS->setChecked(Config::ExternalBIOSEnable != 0); - ui->txtBIOS9Path->setText(Config::BIOS9Path); - ui->txtBIOS7Path->setText(Config::BIOS7Path); - ui->txtFirmwarePath->setText(Config::FirmwarePath); + ui->chkExternalBIOS->setChecked(Config::ExternalBIOSEnable); + ui->txtBIOS9Path->setText(QString::fromStdString(Config::BIOS9Path)); + ui->txtBIOS7Path->setText(QString::fromStdString(Config::BIOS7Path)); + ui->txtFirmwarePath->setText(QString::fromStdString(Config::FirmwarePath)); - ui->txtDSiBIOS9Path->setText(Config::DSiBIOS9Path); - ui->txtDSiBIOS7Path->setText(Config::DSiBIOS7Path); - ui->txtDSiFirmwarePath->setText(Config::DSiFirmwarePath); - ui->txtDSiNANDPath->setText(Config::DSiNANDPath); + ui->txtDSiBIOS9Path->setText(QString::fromStdString(Config::DSiBIOS9Path)); + ui->txtDSiBIOS7Path->setText(QString::fromStdString(Config::DSiBIOS7Path)); + ui->txtDSiFirmwarePath->setText(QString::fromStdString(Config::DSiFirmwarePath)); + ui->txtDSiNANDPath->setText(QString::fromStdString(Config::DSiNANDPath)); ui->cbxConsoleType->addItem("DS"); ui->cbxConsoleType->addItem("DSi (experimental)"); ui->cbxConsoleType->setCurrentIndex(Config::ConsoleType); - ui->chkDirectBoot->setChecked(Config::DirectBoot != 0); + ui->chkDirectBoot->setChecked(Config::DirectBoot); #ifdef JIT_ENABLED - ui->chkEnableJIT->setChecked(Config::JIT_Enable != 0); - ui->chkJITBranchOptimisations->setChecked(Config::JIT_BranchOptimisations != 0); - ui->chkJITLiteralOptimisations->setChecked(Config::JIT_LiteralOptimisations != 0); - ui->chkJITFastMemory->setChecked(Config::JIT_FastMemory != 0); + ui->chkEnableJIT->setChecked(Config::JIT_Enable); + ui->chkJITBranchOptimisations->setChecked(Config::JIT_BranchOptimisations); + ui->chkJITLiteralOptimisations->setChecked(Config::JIT_LiteralOptimisations); + ui->chkJITFastMemory->setChecked(Config::JIT_FastMemory); #ifdef __APPLE__ ui->chkJITFastMemory->setDisabled(true); #endif @@ -101,20 +101,20 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new ui->cbxDSiSDSize->addItem(sizelbl); } - ui->cbDLDIEnable->setChecked(Config::DLDIEnable != 0); - ui->txtDLDISDPath->setText(Config::DLDISDPath); + ui->cbDLDIEnable->setChecked(Config::DLDIEnable); + ui->txtDLDISDPath->setText(QString::fromStdString(Config::DLDISDPath)); ui->cbxDLDISize->setCurrentIndex(Config::DLDISize); - ui->cbDLDIReadOnly->setChecked(Config::DLDIReadOnly != 0); - ui->cbDLDIFolder->setChecked(Config::DLDIFolderSync != 0); - ui->txtDLDIFolder->setText(Config::DLDIFolderPath); + ui->cbDLDIReadOnly->setChecked(Config::DLDIReadOnly); + ui->cbDLDIFolder->setChecked(Config::DLDIFolderSync); + ui->txtDLDIFolder->setText(QString::fromStdString(Config::DLDIFolderPath)); on_cbDLDIEnable_toggled(); - ui->cbDSiSDEnable->setChecked(Config::DSiSDEnable != 0); - ui->txtDSiSDPath->setText(Config::DSiSDPath); + ui->cbDSiSDEnable->setChecked(Config::DSiSDEnable); + ui->txtDSiSDPath->setText(QString::fromStdString(Config::DSiSDPath)); ui->cbxDSiSDSize->setCurrentIndex(Config::DSiSDSize); - ui->cbDSiSDReadOnly->setChecked(Config::DSiSDReadOnly != 0); - ui->cbDSiSDFolder->setChecked(Config::DSiSDFolderSync != 0); - ui->txtDSiSDFolder->setText(Config::DSiSDFolderPath); + ui->cbDSiSDReadOnly->setChecked(Config::DSiSDReadOnly); + ui->cbDSiSDFolder->setChecked(Config::DSiSDFolderSync); + ui->txtDSiSDFolder->setText(QString::fromStdString(Config::DSiSDFolderPath)); on_cbDSiSDEnable_toggled(); } @@ -140,8 +140,7 @@ void EmuSettingsDialog::verifyFirmware() // looked at has 0x180 bytes from the header repeated at 0x3FC80, but // bytes 0x0C-0x14 are different. - char filename[1024]; - strncpy(filename, ui->txtFirmwarePath->text().toStdString().c_str(), 1023); filename[1023] = '\0'; + std::string filename = ui->txtFirmwarePath->text().toStdString(); FILE* f = Platform::OpenLocalFile(filename, "rb"); if (!f) return; u8 chk1[0x180], chk2[0x180]; @@ -175,24 +174,24 @@ void EmuSettingsDialog::done(int r) verifyFirmware(); int consoleType = ui->cbxConsoleType->currentIndex(); - int directBoot = ui->chkDirectBoot->isChecked() ? 1:0; + bool directBoot = ui->chkDirectBoot->isChecked(); - int jitEnable = ui->chkEnableJIT->isChecked() ? 1:0; + bool jitEnable = ui->chkEnableJIT->isChecked(); int jitMaxBlockSize = ui->spnJITMaximumBlockSize->value(); - int jitBranchOptimisations = ui->chkJITBranchOptimisations->isChecked() ? 1:0; - int jitLiteralOptimisations = ui->chkJITLiteralOptimisations->isChecked() ? 1:0; - int jitFastMemory = ui->chkJITFastMemory->isChecked() ? 1:0; + bool jitBranchOptimisations = ui->chkJITBranchOptimisations->isChecked(); + bool jitLiteralOptimisations = ui->chkJITLiteralOptimisations->isChecked(); + bool jitFastMemory = ui->chkJITFastMemory->isChecked(); - int externalBiosEnable = ui->chkExternalBIOS->isChecked() ? 1:0; + bool externalBiosEnable = ui->chkExternalBIOS->isChecked(); std::string bios9Path = ui->txtBIOS9Path->text().toStdString(); std::string bios7Path = ui->txtBIOS7Path->text().toStdString(); std::string firmwarePath = ui->txtFirmwarePath->text().toStdString(); - int dldiEnable = ui->cbDLDIEnable->isChecked() ? 1:0; + bool dldiEnable = ui->cbDLDIEnable->isChecked(); std::string dldiSDPath = ui->txtDLDISDPath->text().toStdString(); int dldiSize = ui->cbxDLDISize->currentIndex(); - int dldiReadOnly = ui->cbDLDIReadOnly->isChecked() ? 1:0; - int dldiFolderSync = ui->cbDLDIFolder->isChecked() ? 1:0; + bool dldiReadOnly = ui->cbDLDIReadOnly->isChecked(); + bool dldiFolderSync = ui->cbDLDIFolder->isChecked(); std::string dldiFolderPath = ui->txtDLDIFolder->text().toStdString(); std::string dsiBios9Path = ui->txtDSiBIOS9Path->text().toStdString(); @@ -200,11 +199,11 @@ void EmuSettingsDialog::done(int r) std::string dsiFirmwarePath = ui->txtDSiFirmwarePath->text().toStdString(); std::string dsiNANDPath = ui->txtDSiNANDPath->text().toStdString(); - int dsiSDEnable = ui->cbDSiSDEnable->isChecked() ? 1:0; + bool dsiSDEnable = ui->cbDSiSDEnable->isChecked(); std::string dsiSDPath = ui->txtDSiSDPath->text().toStdString(); int dsiSDSize = ui->cbxDSiSDSize->currentIndex(); - int dsiSDReadOnly = ui->cbDSiSDReadOnly->isChecked() ? 1:0; - int dsiSDFolderSync = ui->cbDSiSDFolder->isChecked() ? 1:0; + bool dsiSDReadOnly = ui->cbDSiSDReadOnly->isChecked(); + bool dsiSDFolderSync = ui->cbDSiSDFolder->isChecked(); std::string dsiSDFolderPath = ui->txtDSiSDFolder->text().toStdString(); if (consoleType != Config::ConsoleType @@ -217,25 +216,25 @@ void EmuSettingsDialog::done(int r) || jitFastMemory != Config::JIT_FastMemory #endif || externalBiosEnable != Config::ExternalBIOSEnable - || strcmp(Config::BIOS9Path, bios9Path.c_str()) != 0 - || strcmp(Config::BIOS7Path, bios7Path.c_str()) != 0 - || strcmp(Config::FirmwarePath, firmwarePath.c_str()) != 0 + || bios9Path != Config::BIOS9Path + || bios7Path != Config::BIOS7Path + || firmwarePath != Config::FirmwarePath || dldiEnable != Config::DLDIEnable - || strcmp(Config::DLDISDPath, dldiSDPath.c_str()) != 0 + || dldiSDPath != Config::DLDISDPath || dldiSize != Config::DLDISize || dldiReadOnly != Config::DLDIReadOnly || dldiFolderSync != Config::DLDIFolderSync - || strcmp(Config::DLDIFolderPath, dldiFolderPath.c_str()) != 0 - || strcmp(Config::DSiBIOS9Path, dsiBios9Path.c_str()) != 0 - || strcmp(Config::DSiBIOS7Path, dsiBios7Path.c_str()) != 0 - || strcmp(Config::DSiFirmwarePath, dsiFirmwarePath.c_str()) != 0 - || strcmp(Config::DSiNANDPath, dsiNANDPath.c_str()) != 0 + || dldiFolderPath != Config::DLDIFolderPath + || dsiBios9Path != Config::DSiBIOS9Path + || dsiBios7Path != Config::DSiBIOS7Path + || dsiFirmwarePath != Config::DSiFirmwarePath + || dsiNANDPath != Config::DSiNANDPath || dsiSDEnable != Config::DSiSDEnable - || strcmp(Config::DSiSDPath, dsiSDPath.c_str()) != 0 + || dsiSDPath != Config::DSiSDPath || dsiSDSize != Config::DSiSDSize || dsiSDReadOnly != Config::DSiSDReadOnly || dsiSDFolderSync != Config::DSiSDFolderSync - || strcmp(Config::DSiSDFolderPath, dsiSDFolderPath.c_str()) != 0) + || dsiSDFolderPath != Config::DSiSDFolderPath) { if (RunningSomething && QMessageBox::warning(this, "Reset necessary to apply changes", @@ -244,28 +243,28 @@ void EmuSettingsDialog::done(int r) return; Config::ExternalBIOSEnable = externalBiosEnable; - strncpy(Config::BIOS9Path, bios9Path.c_str(), 1023); Config::BIOS9Path[1023] = '\0'; - strncpy(Config::BIOS7Path, bios7Path.c_str(), 1023); Config::BIOS7Path[1023] = '\0'; - strncpy(Config::FirmwarePath, firmwarePath.c_str(), 1023); Config::FirmwarePath[1023] = '\0'; + Config::BIOS9Path = bios9Path; + Config::BIOS7Path = bios7Path; + Config::FirmwarePath = firmwarePath; Config::DLDIEnable = dldiEnable; - strncpy(Config::DLDISDPath, dldiSDPath.c_str(), 1023); Config::DLDISDPath[1023] = '\0'; + Config::DLDISDPath = dldiSDPath; Config::DLDISize = dldiSize; Config::DLDIReadOnly = dldiReadOnly; Config::DLDIFolderSync = dldiFolderSync; - strncpy(Config::DLDIFolderPath, dldiFolderPath.c_str(), 1023); Config::DLDIFolderPath[1023] = '\0'; + Config::DLDIFolderPath = dldiFolderPath; - strncpy(Config::DSiBIOS9Path, dsiBios9Path.c_str(), 1023); Config::DSiBIOS9Path[1023] = '\0'; - strncpy(Config::DSiBIOS7Path, dsiBios7Path.c_str(), 1023); Config::DSiBIOS7Path[1023] = '\0'; - strncpy(Config::DSiFirmwarePath, dsiFirmwarePath.c_str(), 1023); Config::DSiFirmwarePath[1023] = '\0'; - strncpy(Config::DSiNANDPath, dsiNANDPath.c_str(), 1023); Config::DSiNANDPath[1023] = '\0'; + Config::DSiBIOS9Path = dsiBios9Path; + Config::DSiBIOS7Path = dsiBios7Path; + Config::DSiFirmwarePath = dsiFirmwarePath; + Config::DSiNANDPath = dsiNANDPath; Config::DSiSDEnable = dsiSDEnable; - strncpy(Config::DSiSDPath, dsiSDPath.c_str(), 1023); Config::DSiSDPath[1023] = '\0'; + Config::DSiSDPath = dsiSDPath; Config::DSiSDSize = dsiSDSize; Config::DSiSDReadOnly = dsiSDReadOnly; Config::DSiSDFolderSync = dsiSDFolderSync; - strncpy(Config::DSiSDFolderPath, dsiSDFolderPath.c_str(), 1023); Config::DSiSDFolderPath[1023] = '\0'; + Config::DSiSDFolderPath = dsiSDFolderPath; #ifdef JIT_ENABLED Config::JIT_Enable = jitEnable; diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp index 0b2cad6..976934f 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp @@ -35,7 +35,7 @@ FirmwareSettingsDialog::FirmwareSettingsDialog(QWidget* parent) : QDialog(parent ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - ui->usernameEdit->setText(Config::FirmwareUsername); + ui->usernameEdit->setText(QString::fromStdString(Config::FirmwareUsername)); ui->languageBox->addItems(languages); ui->languageBox->setCurrentIndex(Config::FirmwareLanguage); @@ -59,12 +59,12 @@ FirmwareSettingsDialog::FirmwareSettingsDialog(QWidget* parent) : QDialog(parent } ui->colorsEdit->setCurrentIndex(Config::FirmwareFavouriteColour); - ui->messageEdit->setText(Config::FirmwareMessage); + ui->messageEdit->setText(QString::fromStdString(Config::FirmwareMessage)); ui->overrideFirmwareBox->setChecked(Config::FirmwareOverrideSettings); - ui->txtMAC->setText(Config::FirmwareMAC); - ui->cbRandomizeMAC->setChecked(Config::RandomizeMAC != 0); + ui->txtMAC->setText(QString::fromStdString(Config::FirmwareMAC)); + ui->cbRandomizeMAC->setChecked(Config::RandomizeMAC); on_cbRandomizeMAC_toggled(); } @@ -123,7 +123,7 @@ void FirmwareSettingsDialog::done(int r) return; } - int newOverride = ui->overrideFirmwareBox->isChecked(); + bool newOverride = ui->overrideFirmwareBox->isChecked(); std::string newName = ui->usernameEdit->text().toStdString(); int newLanguage = ui->languageBox->currentIndex(); @@ -133,16 +133,16 @@ void FirmwareSettingsDialog::done(int r) std::string newMessage = ui->messageEdit->text().toStdString(); std::string newMAC = ui->txtMAC->text().toStdString(); - int newRandomizeMAC = ui->cbRandomizeMAC->isChecked() ? 1:0; + bool newRandomizeMAC = ui->cbRandomizeMAC->isChecked(); if ( newOverride != Config::FirmwareOverrideSettings - || strcmp(newName.c_str(), Config::FirmwareUsername) != 0 + || newName != Config::FirmwareUsername || newLanguage != Config::FirmwareLanguage || newFavColor != Config::FirmwareFavouriteColour || newBirthdayDay != Config::FirmwareBirthdayDay || newBirthdayMonth != Config::FirmwareBirthdayMonth - || strcmp(newMessage.c_str(), Config::FirmwareMessage) != 0 - || strcmp(newMAC.c_str(), Config::FirmwareMAC) != 0 + || newMessage != Config::FirmwareMessage + || newMAC != Config::FirmwareMAC || newRandomizeMAC != Config::RandomizeMAC) { if (RunningSomething @@ -153,14 +153,14 @@ void FirmwareSettingsDialog::done(int r) Config::FirmwareOverrideSettings = newOverride; - strncpy(Config::FirmwareUsername, newName.c_str(), 63); Config::FirmwareUsername[63] = '\0'; + Config::FirmwareUsername = newName; Config::FirmwareLanguage = newLanguage; Config::FirmwareFavouriteColour = newFavColor; Config::FirmwareBirthdayDay = newBirthdayDay; Config::FirmwareBirthdayMonth = newBirthdayMonth; - strncpy(Config::FirmwareMessage, newMessage.c_str(), 1023); Config::FirmwareMessage[1023] = '\0'; + Config::FirmwareMessage = newMessage; - strncpy(Config::FirmwareMAC, newMAC.c_str(), 17); Config::FirmwareMAC[17] = '\0'; + Config::FirmwareMAC = newMAC; Config::RandomizeMAC = newRandomizeMAC; Config::Save(); diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.h b/src/frontend/qt_sdl/FirmwareSettingsDialog.h index 1ae409f..7ed8b0b 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.h +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.h @@ -109,7 +109,7 @@ public: } currentDlg = new FirmwareSettingsDialog(parent); - currentDlg->show(); + currentDlg->open(); return currentDlg; } static void closeDlg() diff --git a/src/frontend/qt_sdl/LAN_PCap.cpp b/src/frontend/qt_sdl/LAN_PCap.cpp index ed3eee9..dcc2310 100644 --- a/src/frontend/qt_sdl/LAN_PCap.cpp +++ b/src/frontend/qt_sdl/LAN_PCap.cpp @@ -318,7 +318,7 @@ bool Init(bool open_adapter) PCapAdapterData = &Adapters[0]; for (int i = 0; i < NumAdapters; i++) { - if (!strncmp(Adapters[i].DeviceName, Config::LANDevice, 128)) + if (!strncmp(Adapters[i].DeviceName, Config::LANDevice.c_str(), 128)) PCapAdapterData = &Adapters[i]; } diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp new file mode 100644 index 0000000..5a2fa1a --- /dev/null +++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp @@ -0,0 +1,119 @@ +/* + Copyright 2016-2021 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 <stdio.h> +#include <QFileDialog> +#include <QMessageBox> + +#include "types.h" +#include "Config.h" + +#include "PathSettingsDialog.h" +#include "ui_PathSettingsDialog.h" + + +PathSettingsDialog* PathSettingsDialog::currentDlg = nullptr; + +extern std::string EmuDirectory; +extern bool RunningSomething; + +bool PathSettingsDialog::needsReset = false; + + +PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PathSettingsDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + ui->txtSaveFilePath->setText(QString::fromStdString(Config::SaveFilePath)); + ui->txtSavestatePath->setText(QString::fromStdString(Config::SavestatePath)); + ui->txtCheatFilePath->setText(QString::fromStdString(Config::CheatFilePath)); +} + +PathSettingsDialog::~PathSettingsDialog() +{ + delete ui; +} + +void PathSettingsDialog::done(int r) +{ + needsReset = false; + + if (r == QDialog::Accepted) + { + std::string saveFilePath = ui->txtSaveFilePath->text().toStdString(); + std::string savestatePath = ui->txtSavestatePath->text().toStdString(); + std::string cheatFilePath = ui->txtCheatFilePath->text().toStdString(); + + if ( saveFilePath != Config::SaveFilePath + || savestatePath != Config::SavestatePath + || cheatFilePath != Config::CheatFilePath) + { + if (RunningSomething + && QMessageBox::warning(this, "Reset necessary to apply changes", + "The emulation will be reset for the changes to take place.", + QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) + return; + + Config::SaveFilePath = saveFilePath; + Config::SavestatePath = savestatePath; + Config::CheatFilePath = cheatFilePath; + + Config::Save(); + + needsReset = true; + } + } + + QDialog::done(r); + + closeDlg(); +} + +void PathSettingsDialog::on_btnSaveFileBrowse_clicked() +{ + QString dir = QFileDialog::getExistingDirectory(this, + "Select save files path...", + QString::fromStdString(EmuDirectory)); + + if (dir.isEmpty()) return; + + ui->txtSaveFilePath->setText(dir); +} + +void PathSettingsDialog::on_btnSavestateBrowse_clicked() +{ + QString dir = QFileDialog::getExistingDirectory(this, + "Select savestates path...", + QString::fromStdString(EmuDirectory)); + + if (dir.isEmpty()) return; + + ui->txtSavestatePath->setText(dir); +} + +void PathSettingsDialog::on_btnCheatFileBrowse_clicked() +{ + QString dir = QFileDialog::getExistingDirectory(this, + "Select cheat files path...", + QString::fromStdString(EmuDirectory)); + + if (dir.isEmpty()) return; + + ui->txtCheatFilePath->setText(dir); +} diff --git a/src/frontend/qt_sdl/PathSettingsDialog.h b/src/frontend/qt_sdl/PathSettingsDialog.h new file mode 100644 index 0000000..6a0fea2 --- /dev/null +++ b/src/frontend/qt_sdl/PathSettingsDialog.h @@ -0,0 +1,67 @@ + +/* + Copyright 2016-2021 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 PATHSETTINGSDIALOG_H +#define PATHSETTINGSDIALOG_H + +#include <QDialog> + +namespace Ui { class PathSettingsDialog; } +class PathSettingsDialog; + +class PathSettingsDialog : public QDialog +{ + Q_OBJECT + +public: + explicit PathSettingsDialog(QWidget* parent); + ~PathSettingsDialog(); + + static PathSettingsDialog* currentDlg; + static PathSettingsDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new PathSettingsDialog(parent); + currentDlg->open(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + + static bool needsReset; + +private slots: + void done(int r); + + void on_btnSaveFileBrowse_clicked(); + void on_btnSavestateBrowse_clicked(); + void on_btnCheatFileBrowse_clicked(); + +private: + Ui::PathSettingsDialog* ui; +}; + +#endif // PATHSETTINGSDIALOG_H diff --git a/src/frontend/qt_sdl/PathSettingsDialog.ui b/src/frontend/qt_sdl/PathSettingsDialog.ui new file mode 100644 index 0000000..95f5acc --- /dev/null +++ b/src/frontend/qt_sdl/PathSettingsDialog.ui @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>PathSettingsDialog</class> + <widget class="QDialog" name="PathSettingsDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>439</width> + <height>166</height> + </rect> + </property> + <property name="windowTitle"> + <string>Path settings - melonDS</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="1"> + <widget class="QLineEdit" name="txtSaveFilePath"> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="2"> + <widget class="QPushButton" name="btnCheatFileBrowse"> + <property name="text"> + <string>Browse...</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Savestates path:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLineEdit" name="txtCheatFilePath"> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="3" column="0" colspan="3"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Leave a path blank to use the current ROM's path.</string> + </property> + </widget> + </item> + <item row="5" column="0" colspan="3"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Cheat files path:</string> + </property> + </widget> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="btnSaveFileBrowse"> + <property name="text"> + <string>Browse...</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="btnSavestateBrowse"> + <property name="text"> + <string>Browse...</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="txtSavestatePath"> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Save files path:</string> + </property> + </widget> + </item> + <item row="4" column="0" colspan="3"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string/> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>txtSaveFilePath</tabstop> + <tabstop>btnSaveFileBrowse</tabstop> + <tabstop>txtSavestatePath</tabstop> + <tabstop>btnSavestateBrowse</tabstop> + <tabstop>txtCheatFilePath</tabstop> + <tabstop>btnCheatFileBrowse</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>PathSettingsDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>PathSettingsDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 812c953..0197264 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -52,6 +52,7 @@ #include "Platform.h" #include "Config.h" +#include "ROMManager.h" #include "LAN_Socket.h" #include "LAN_PCap.h" #include <string> @@ -207,7 +208,7 @@ bool GetConfigArray(ConfigEntry entry, void* data) { case Firm_MAC: { - char* mac_in = Config::FirmwareMAC; + std::string& mac_in = Config::FirmwareMAC; u8* mac_out = (u8*)data; int o = 0; @@ -372,6 +373,19 @@ bool Mutex_TryLock(Mutex* mutex) } +void WriteNDSSave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen) +{ + if (ROMManager::NDSSave) + ROMManager::NDSSave->RequestFlush(savedata, savelen, writeoffset, writelen); +} + +void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen) +{ + if (ROMManager::GBASave) + ROMManager::GBASave->RequestFlush(savedata, savelen, writeoffset, writelen); +} + + bool MP_Init() { int opt_true = 1; diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp index 9166efe..5fbca0f 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.cpp +++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp @@ -45,14 +45,14 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI u32 iconData[32 * 32];
- Frontend::ROMIcon(NDSCart::Banner.Icon, NDSCart::Banner.Palette, iconData);
+ ROMManager::ROMIcon(NDSCart::Banner.Icon, NDSCart::Banner.Palette, iconData);
iconImage = QImage(reinterpret_cast<unsigned char*>(iconData), 32, 32, QImage::Format_ARGB32).copy();
ui->iconImage->setPixmap(QPixmap::fromImage(iconImage));
if (NDSCart::Banner.Version == 0x103)
{
u32 animatedIconData[32 * 32 * 64] = {0};
- Frontend::AnimatedROMIcon(NDSCart::Banner.DSiIcon, NDSCart::Banner.DSiPalette, NDSCart::Banner.DSiSequence, animatedIconData, animatedSequence);
+ ROMManager::AnimatedROMIcon(NDSCart::Banner.DSiIcon, NDSCart::Banner.DSiPalette, NDSCart::Banner.DSiSequence, animatedIconData, animatedSequence);
for (int i = 0; i < 64; i++)
{
@@ -130,7 +130,7 @@ void ROMInfoDialog::on_saveIconButton_clicked() {
QString filename = QFileDialog::getSaveFileName(this,
"Save Icon",
- Config::LastROMFolder,
+ QString::fromStdString(Config::LastROMFolder),
"PNG Images (*.png)");
if (filename.isEmpty())
return;
diff --git a/src/frontend/qt_sdl/ROMInfoDialog.h b/src/frontend/qt_sdl/ROMInfoDialog.h index 5193554..8fdab74 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.h +++ b/src/frontend/qt_sdl/ROMInfoDialog.h @@ -25,7 +25,7 @@ #include <QImage>
#include "types.h"
-#include "FrontendUtil.h"
+#include "ROMManager.h"
namespace Ui { class ROMInfoDialog; }
class ROMInfoDialog;
@@ -58,7 +58,7 @@ public: private slots:
void done(int r);
-
+
void on_saveIconButton_clicked();
void iconSetFrame(int frame);
diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp new file mode 100644 index 0000000..2b9bbd3 --- /dev/null +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -0,0 +1,843 @@ +/* + Copyright 2016-2021 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 <stdio.h> +#include <string.h> + +#include <string> +#include <utility> + +#ifdef ARCHIVE_SUPPORT_ENABLED +#include "ArchiveUtil.h" +#endif +#include "ROMManager.h" +#include "Config.h" +#include "Platform.h" + +#include "NDS.h" +#include "DSi.h" + + +namespace ROMManager +{ + +int CartType = -1; +std::string BaseROMDir = ""; +std::string BaseROMName = ""; +std::string BaseAssetName = ""; + +int GBACartType = -1; +std::string BaseGBAROMDir = ""; +std::string BaseGBAROMName = ""; +std::string BaseGBAAssetName = ""; + +SaveManager* NDSSave = nullptr; +SaveManager* GBASave = nullptr; + +bool SavestateLoaded = false; +std::string PreviousSaveFile = ""; + +ARCodeFile* CheatFile = nullptr; +bool CheatsOn = false; + + +int LastSep(std::string path) +{ + int i = path.length() - 1; + while (i >= 0) + { + if (path[i] == '/' || path[i] == '\\') + return i; + + i--; + } + + return -1; +} + +std::string GetAssetPath(bool gba, std::string configpath, std::string ext, std::string file="") +{ + if (configpath.empty()) + configpath = gba ? BaseGBAROMDir : BaseROMDir; + + if (file.empty()) + { + file = gba ? BaseGBAAssetName : BaseAssetName; + if (file.empty()) + file = "firmware"; + } + + for (;;) + { + int i = configpath.length() - 1; + if (configpath[i] == '/' || configpath[i] == '\\') + configpath = configpath.substr(0, i); + else + break; + } + + if (!configpath.empty()) + configpath += "/"; + + return configpath + file + ext; +} + + +QString VerifyDSBIOS() +{ + FILE* f; + long len; + + f = Platform::OpenLocalFile(Config::BIOS9Path, "rb"); + if (!f) return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings."; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x1000) + { + fclose(f); + return "DS ARM9 BIOS is not a valid BIOS dump."; + } + + fclose(f); + + f = Platform::OpenLocalFile(Config::BIOS7Path, "rb"); + if (!f) return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings."; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x4000) + { + fclose(f); + return "DS ARM7 BIOS is not a valid BIOS dump."; + } + + fclose(f); + + return ""; +} + +QString VerifyDSiBIOS() +{ + FILE* f; + long len; + + // TODO: check the first 32 bytes + + f = Platform::OpenLocalFile(Config::DSiBIOS9Path, "rb"); + if (!f) return "DSi ARM9 BIOS was not found or could not be accessed. Check your emu settings."; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x10000) + { + fclose(f); + return "DSi ARM9 BIOS is not a valid BIOS dump."; + } + + fclose(f); + + f = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb"); + if (!f) return "DSi ARM7 BIOS was not found or could not be accessed. Check your emu settings."; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x10000) + { + fclose(f); + return "DSi ARM7 BIOS is not a valid BIOS dump."; + } + + fclose(f); + + return ""; +} + +QString VerifyDSFirmware() +{ + FILE* f; + long len; + + f = Platform::OpenLocalFile(Config::FirmwarePath, "rb"); + if (!f) return "DS firmware was not found or could not be accessed. Check your emu settings."; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len == 0x20000) + { + // 128KB firmware, not bootable + fclose(f); + // TODO report it somehow? detect in core? + return ""; + } + else if (len != 0x40000 && len != 0x80000) + { + fclose(f); + return "DS firmware is not a valid firmware dump."; + } + + fclose(f); + + return ""; +} + +QString VerifyDSiFirmware() +{ + FILE* f; + long len; + + f = Platform::OpenLocalFile(Config::DSiFirmwarePath, "rb"); + if (!f) return "DSi firmware was not found or could not be accessed. Check your emu settings."; + + fseek(f, 0, SEEK_END); + len = ftell(f); + if (len != 0x20000) + { + // not 128KB + // TODO: check whether those work + fclose(f); + return "DSi firmware is not a valid firmware dump."; + } + + fclose(f); + + return ""; +} + +QString VerifyDSiNAND() +{ + FILE* f; + long len; + + f = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b"); + if (!f) return "DSi NAND was not found or could not be accessed. Check your emu settings."; + + // TODO: some basic checks + // check that it has the nocash footer, and all + + fclose(f); + + return ""; +} + +QString VerifySetup() +{ + QString res; + + if (Config::ExternalBIOSEnable) + { + res = VerifyDSBIOS(); + if (!res.isEmpty()) return res; + } + + if (Config::ConsoleType == 1) + { + res = VerifyDSiBIOS(); + if (!res.isEmpty()) return res; + + if (Config::ExternalBIOSEnable) + { + res = VerifyDSiFirmware(); + if (!res.isEmpty()) return res; + } + + res = VerifyDSiNAND(); + if (!res.isEmpty()) return res; + } + else + { + if (Config::ExternalBIOSEnable) + { + res = VerifyDSFirmware(); + if (!res.isEmpty()) return res; + } + } + + return ""; +} + + +std::string GetSavestateName(int slot) +{ + std::string ext = ".ml"; + ext += (char)('0'+slot); + return GetAssetPath(false, Config::SavestatePath, ext); +} + +bool SavestateExists(int slot) +{ + std::string ssfile = GetSavestateName(slot); + return Platform::FileExists(ssfile); +} + +bool LoadState(std::string filename) +{ + // 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; + + // current state might be crapoed, so restore from sane backup + state = new Savestate("timewarp.mln", false); + failed = true; + } + + bool res = NDS::DoSavestate(state); + delete state; + + if (!res) + { + failed = true; + state = new Savestate("timewarp.mln", false); + NDS::DoSavestate(state); + delete state; + } + + if (failed) return false; + + if (Config::SavestateRelocSRAM && NDSSave) + { + PreviousSaveFile = NDSSave->GetPath(); + + std::string savefile = filename.substr(LastSep(filename)+1); + savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile); + NDSSave->SetPath(savefile, true); + } + + SavestateLoaded = true; + + return true; +} + +bool SaveState(std::string filename) +{ + Savestate* state = new Savestate(filename, true); + if (state->Error) + { + delete state; + return false; + } + + NDS::DoSavestate(state); + delete state; + + if (Config::SavestateRelocSRAM && NDSSave) + { + std::string savefile = filename.substr(LastSep(filename)+1); + savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile); + NDSSave->SetPath(savefile, false); + } + + 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 (NDSSave && (!PreviousSaveFile.empty())) + { + NDSSave->SetPath(PreviousSaveFile, true); + } +} + + +void UnloadCheats() +{ + if (CheatFile) + { + delete CheatFile; + CheatFile = nullptr; + } +} + +void LoadCheats() +{ + UnloadCheats(); + + std::string filename = GetAssetPath(false, Config::CheatFilePath, ".mch"); + + // TODO: check for error (malformed cheat file, ...) + CheatFile = new ARCodeFile(filename); + + AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); +} + +void EnableCheats(bool enable) +{ + CheatsOn = enable; + if (CheatFile) + AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); +} + +ARCodeFile* GetCheatFile() +{ + return CheatFile; +} + + +void Reset() +{ + NDS::SetConsoleType(Config::ConsoleType); + if (Config::ConsoleType == 1) EjectGBACart(); + NDS::Reset(); + + if ((CartType != -1) && NDSSave) + { + std::string oldsave = NDSSave->GetPath(); + std::string newsave = GetAssetPath(false, Config::SaveFilePath, ".sav"); + if (oldsave != newsave) + NDSSave->SetPath(newsave, false); + } + + if ((GBACartType != -1) && GBASave) + { + std::string oldsave = GBASave->GetPath(); + std::string newsave = GetAssetPath(true, Config::SaveFilePath, ".sav"); + if (oldsave != newsave) + GBASave->SetPath(newsave, false); + } + + if (!BaseROMName.empty()) + { + if (Config::DirectBoot || NDS::NeedsDirectBoot()) + { + NDS::SetupDirectBoot(BaseROMName); + } + } +} + + +bool LoadBIOS() +{ + NDS::SetConsoleType(Config::ConsoleType); + + if (NDS::NeedsDirectBoot()) + return false; + + /*if (NDSSave) delete NDSSave; + NDSSave = nullptr; + + CartType = -1; + BaseROMDir = ""; + BaseROMName = ""; + BaseAssetName = "";*/ + + NDS::Reset(); + return true; +} + + +bool LoadROM(QStringList filepath, bool reset) +{ + if (filepath.empty()) return false; + + u8* filedata; + u32 filelen; + + std::string basepath; + std::string romname; + + int num = filepath.count(); + if (num == 1) + { + // regular file + + std::string filename = filepath.at(0).toStdString(); + FILE* f = Platform::OpenFile(filename, "rb", true); + if (!f) return false; + + fseek(f, 0, SEEK_END); + long len = ftell(f); + if (len > 0x40000000) + { + fclose(f); + return false; + } + + fseek(f, 0, SEEK_SET); + filedata = new u8[len]; + size_t nread = fread(filedata, (size_t)len, 1, f); + if (nread != 1) + { + fclose(f); + delete[] filedata; + return false; + } + + fclose(f); + filelen = (u32)len; + + int pos = LastSep(filename); + basepath = filename.substr(0, pos); + romname = filename.substr(pos+1); + } +#ifdef ARCHIVE_SUPPORT_ENABLED + else if (num == 2) + { + // file inside archive + + u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen); + if (lenread < 0) return false; + if (!filedata) return false; + if (lenread != filelen) + { + delete[] filedata; + return false; + } + + std::string std_archivepath = filepath.at(0).toStdString(); + basepath = std_archivepath.substr(0, LastSep(std_archivepath)); + + std::string std_romname = filepath.at(1).toStdString(); + romname = std_romname.substr(LastSep(std_romname)+1); + } +#endif + else + return false; + + if (NDSSave) delete NDSSave; + NDSSave = nullptr; + + BaseROMDir = basepath; + BaseROMName = romname; + BaseAssetName = romname.substr(0, romname.rfind('.')); + + if (reset) + { + NDS::SetConsoleType(Config::ConsoleType); + NDS::Reset(); + } + + u32 savelen = 0; + u8* savedata = nullptr; + + std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav"); + FILE* sav = Platform::OpenFile(savname, "rb", true); + if (sav) + { + fseek(sav, 0, SEEK_END); + savelen = (u32)ftell(sav); + + fseek(sav, 0, SEEK_SET); + savedata = new u8[savelen]; + fread(savedata, savelen, 1, sav); + fclose(sav); + } + + bool res = NDS::LoadCart(filedata, filelen, savedata, savelen); + if (res && reset) + { + if (Config::DirectBoot || NDS::NeedsDirectBoot()) + { + NDS::SetupDirectBoot(romname); + } + } + + if (res) + { + CartType = 0; + NDSSave = new SaveManager(savname); + + LoadCheats(); + } + + if (savedata) delete[] savedata; + delete[] filedata; + return res; +} + +void EjectCart() +{ + if (NDSSave) delete NDSSave; + NDSSave = nullptr; + + UnloadCheats(); + + NDS::EjectCart(); + + CartType = -1; + BaseROMDir = ""; + BaseROMName = ""; + BaseAssetName = ""; +} + +bool CartInserted() +{ + return CartType != -1; +} + +QString CartLabel() +{ + if (CartType == -1) + return "(none)"; + + QString ret = QString::fromStdString(BaseROMName); + + int maxlen = 32; + if (ret.length() > maxlen) + ret = ret.left(maxlen-6) + "..." + ret.right(3); + + return ret; +} + + +bool LoadGBAROM(QStringList filepath) +{ + if (Config::ConsoleType == 1) return false; + if (filepath.empty()) return false; + + u8* filedata; + u32 filelen; + + std::string basepath; + std::string romname; + + int num = filepath.count(); + if (num == 1) + { + // regular file + + std::string filename = filepath.at(0).toStdString(); + FILE* f = Platform::OpenFile(filename, "rb", true); + if (!f) return false; + + fseek(f, 0, SEEK_END); + long len = ftell(f); + if (len > 0x40000000) + { + fclose(f); + return false; + } + + fseek(f, 0, SEEK_SET); + filedata = new u8[len]; + size_t nread = fread(filedata, (size_t)len, 1, f); + if (nread != 1) + { + fclose(f); + delete[] filedata; + return false; + } + + fclose(f); + filelen = (u32)len; + + int pos = LastSep(filename); + basepath = filename.substr(0, pos); + romname = filename.substr(pos+1); + } +#ifdef ARCHIVE_SUPPORT_ENABLED + else if (num == 2) + { + // file inside archive + + u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen); + if (lenread < 0) return false; + if (!filedata) return false; + if (lenread != filelen) + { + delete[] filedata; + return false; + } + + std::string std_archivepath = filepath.at(0).toStdString(); + basepath = std_archivepath.substr(0, LastSep(std_archivepath)); + + std::string std_romname = filepath.at(1).toStdString(); + romname = std_romname.substr(LastSep(std_romname)+1); + } +#endif + else + return false; + + if (GBASave) delete GBASave; + GBASave = nullptr; + + BaseGBAROMDir = basepath; + BaseGBAROMName = romname; + BaseGBAAssetName = romname.substr(0, romname.rfind('.')); + + u32 savelen = 0; + u8* savedata = nullptr; + + std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav"); + FILE* sav = Platform::OpenFile(savname, "rb", true); + if (sav) + { + fseek(sav, 0, SEEK_END); + savelen = (u32)ftell(sav); + + fseek(sav, 0, SEEK_SET); + savedata = new u8[savelen]; + fread(savedata, savelen, 1, sav); + fclose(sav); + } + + bool res = NDS::LoadGBACart(filedata, filelen, savedata, savelen); + + if (res) + { + GBACartType = 0; + GBASave = new SaveManager(savname); + } + + if (savedata) delete[] savedata; + delete[] filedata; + return res; +} + +void LoadGBAAddon(int type) +{ + if (Config::ConsoleType == 1) return; + + if (GBASave) delete GBASave; + GBASave = nullptr; + + NDS::LoadGBAAddon(type); + + GBACartType = type; + BaseGBAROMDir = ""; + BaseGBAROMName = ""; + BaseGBAAssetName = ""; +} + +void EjectGBACart() +{ + if (GBASave) delete GBASave; + GBASave = nullptr; + + NDS::EjectGBACart(); + + GBACartType = -1; + BaseGBAROMDir = ""; + BaseGBAROMName = ""; + BaseGBAAssetName = ""; +} + +bool GBACartInserted() +{ + return GBACartType != -1; +} + +QString GBACartLabel() +{ + if (Config::ConsoleType == 1) return "none (DSi)"; + + switch (GBACartType) + { + case 0: + { + QString ret = QString::fromStdString(BaseGBAROMName); + + int maxlen = 32; + if (ret.length() > maxlen) + ret = ret.left(maxlen-6) + "..." + ret.right(3); + + return ret; + } + + case NDS::GBAAddon_RAMExpansion: + return "Memory expansion"; + } + + return "(none)"; +} + + +void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef) +{ + int index = 0; + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + for (int k = 0; k < 8; k++) + { + for (int l = 0; l < 8; l++) + { + u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F; + u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31; + u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31; + u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31; + u8 a = pal_index ? 255: 0; + u32* row = &iconRef[256 * i + 32 * k + 8 * j]; + row[l] = (a << 24) | (r << 16) | (g << 8) | b; + index++; + } + } + } + } +} + +#define SEQ_FLIPV(i) ((i & 0b1000000000000000) >> 15) +#define SEQ_FLIPH(i) ((i & 0b0100000000000000) >> 14) +#define SEQ_PAL(i) ((i & 0b0011100000000000) >> 11) +#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8) +#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0) + +void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef) +{ + for (int i = 0; i < 64; i++) + { + if (!sequence[i]) + break; + u32* frame = &animatedTexRef[32 * 32 * i]; + ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame); + + if (SEQ_FLIPH(sequence[i])) + { + for (int x = 0; x < 32; x++) + { + for (int y = 0; y < 32/2; y++) + { + std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]); + } + } + } + if (SEQ_FLIPV(sequence[i])) + { + for (int x = 0; x < 32/2; x++) + { + for (int y = 0; y < 32; y++) + { + std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]); + } + } + } + + for (int j = 0; j < SEQ_DUR(sequence[i]); j++) + animatedSequenceRef.push_back(i); + } +} + +} diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h new file mode 100644 index 0000000..8c97965 --- /dev/null +++ b/src/frontend/qt_sdl/ROMManager.h @@ -0,0 +1,66 @@ +/* + Copyright 2016-2021 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 ROMMANAGER_H +#define ROMMANAGER_H + +#include "types.h" +#include "SaveManager.h" +#include "AREngine.h" + +#include <string> +#include <vector> + +namespace ROMManager +{ + +extern SaveManager* NDSSave; +extern SaveManager* GBASave; + +QString VerifySetup(); +void Reset(); +bool LoadBIOS(); + +bool LoadROM(QStringList filepath, bool reset); +void EjectCart(); +bool CartInserted(); +QString CartLabel(); + +bool LoadGBAROM(QStringList filepath); +void LoadGBAAddon(int type); +void EjectGBACart(); +bool GBACartInserted(); +QString GBACartLabel(); + +std::string GetSavestateName(int slot); +bool SavestateExists(int slot); +bool LoadState(std::string filename); +bool SaveState(std::string filename); +void UndoStateLoad(); + +void EnableCheats(bool enable); +ARCodeFile* GetCheatFile(); + +void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef); +void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], + u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], + std::vector<int> &animatedSequenceRef); + +} + +#endif // ROMMANAGER_H diff --git a/src/frontend/qt_sdl/SaveManager.cpp b/src/frontend/qt_sdl/SaveManager.cpp new file mode 100644 index 0000000..20ac543 --- /dev/null +++ b/src/frontend/qt_sdl/SaveManager.cpp @@ -0,0 +1,194 @@ +/* + Copyright 2016-2021 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 <stdio.h> +#include <string.h> + +#include "SaveManager.h" +#include "Platform.h" + + +SaveManager::SaveManager(std::string path) : QThread() +{ + SecondaryBuffer = nullptr; + SecondaryBufferLength = 0; + SecondaryBufferLock = new QMutex(); + + Running = false; + + Path = path; + + Buffer = nullptr; + Length = 0; + FlushRequested = false; + + FlushVersion = 0; + PreviousFlushVersion = 0; + TimeAtLastFlushRequest = 0; + + if (!path.empty()) + { + Running = true; + start(); + } +} + +SaveManager::~SaveManager() +{ + if (Running) + { + Running = false; + wait(); + FlushSecondaryBuffer(); + } + + if (SecondaryBuffer) delete[] SecondaryBuffer; + + delete SecondaryBufferLock; + + if (Buffer) delete[] Buffer; +} + +std::string SaveManager::GetPath() +{ + return Path; +} + +void SaveManager::SetPath(std::string path, bool reload) +{ + Path = path; + + if (reload) + { + FILE* f = Platform::OpenFile(Path, "rb", true); + if (f) + { + fread(Buffer, 1, Length, f); + fclose(f); + } + } + else + FlushRequested = true; +} + +void SaveManager::RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen) +{ + if (Length != savelen) + { + if (Buffer) delete[] Buffer; + + Length = savelen; + Buffer = new u8[Length]; + + memcpy(Buffer, savedata, Length); + } + else + { + if ((writeoffset+writelen) > savelen) + { + u32 len = savelen - writeoffset; + memcpy(&Buffer[writeoffset], &savedata[writeoffset], len); + len = writelen - len; + if (len > savelen) len = savelen; + memcpy(&Buffer[0], &savedata[0], len); + } + else + { + memcpy(&Buffer[writeoffset], &savedata[writeoffset], writelen); + } + } + + FlushRequested = true; +} + +void SaveManager::CheckFlush() +{ + if (!FlushRequested) return; + + SecondaryBufferLock->lock(); + + printf("SaveManager: Flush requested\n"); + + if (SecondaryBufferLength != Length) + { + if (SecondaryBuffer) delete[] SecondaryBuffer; + + SecondaryBufferLength = Length; + SecondaryBuffer = new u8[SecondaryBufferLength]; + } + + memcpy(SecondaryBuffer, Buffer, Length); + + FlushRequested = false; + FlushVersion++; + TimeAtLastFlushRequest = time(nullptr); + + SecondaryBufferLock->unlock(); +} + +void SaveManager::run() +{ + for (;;) + { + QThread::msleep(100); + + if (!Running) return; + + // We debounce for two seconds after last flush request to ensure that writing has finished. + if (TimeAtLastFlushRequest == 0 || difftime(time(nullptr), TimeAtLastFlushRequest) < 2) + { + continue; + } + + FlushSecondaryBuffer(); + } +} + +void SaveManager::FlushSecondaryBuffer(u8* dst, u32 dstLength) +{ + if (!SecondaryBuffer) return; + + // When flushing to a file, there's no point in re-writing the exact same data. + if (!dst && !NeedsFlush()) return; + // When flushing to memory, we don't know if dst already has any data so we only check that we CAN flush. + if (dst && dstLength < SecondaryBufferLength) return; + + SecondaryBufferLock->lock(); + if (dst) + { + memcpy(dst, SecondaryBuffer, SecondaryBufferLength); + } + else + { + FILE* f = Platform::OpenFile(Path, "wb"); + if (f) + { + printf("SaveManager: Written\n"); + fwrite(SecondaryBuffer, SecondaryBufferLength, 1, f); + fclose(f); + } + } + PreviousFlushVersion = FlushVersion; + TimeAtLastFlushRequest = 0; + SecondaryBufferLock->unlock(); +} + +bool SaveManager::NeedsFlush() +{ + return FlushVersion != PreviousFlushVersion; +} diff --git a/src/frontend/qt_sdl/SaveManager.h b/src/frontend/qt_sdl/SaveManager.h new file mode 100644 index 0000000..9ccb0e3 --- /dev/null +++ b/src/frontend/qt_sdl/SaveManager.h @@ -0,0 +1,70 @@ +/* + Copyright 2016-2021 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 SAVEMANAGER_H +#define SAVEMANAGER_H + +#include <string> +#include <unistd.h> +#include <time.h> +#include <atomic> +#include <QThread> +#include <QMutex> + +#include "types.h" + +class SaveManager : public QThread +{ + Q_OBJECT + void run() override; + +public: + SaveManager(std::string path); + ~SaveManager(); + + std::string GetPath(); + void SetPath(std::string path, bool reload); + + void RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen); + void CheckFlush(); + + bool NeedsFlush(); + void FlushSecondaryBuffer(u8* dst = nullptr, u32 dstLength = 0); + +private: + std::string Path; + + std::atomic_bool Running; + + u8* Buffer; + u32 Length; + bool FlushRequested; + + QMutex* SecondaryBufferLock; + u8* SecondaryBuffer; + u32 SecondaryBufferLength; + + time_t TimeAtLastFlushRequest; + + // We keep versions in case the user closes the application before + // a flush cycle is finished. + u32 PreviousFlushVersion; + u32 FlushVersion; +}; + +#endif // SAVEMANAGER_H diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index 0a5e65d..5af3636 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -23,7 +23,7 @@ #include "types.h" #include "Platform.h" #include "Config.h" -#include "FrontendUtil.h" +#include "ROMManager.h" #include "DSi_NAND.h" #include "TitleManagerDialog.h" @@ -111,7 +111,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) DSi_NAND::GetTitleInfo(category, titleid, version, &header, &banner); u32 icondata[32*32]; - Frontend::ROMIcon(banner.Icon, banner.Palette, icondata); + ROMManager::ROMIcon(banner.Icon, banner.Palette, icondata); QImage iconimg((const uchar*)icondata, 32, 32, QImage::Format_ARGB32); QIcon icon(QPixmap::fromImage(iconimg.copy())); diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp index d438179..e584b6a 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -55,7 +55,7 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne ui->rbDirectMode->setText("Direct mode (requires " PCAP_NAME " and ethernet connection)"); - ui->cbBindAnyAddr->setChecked(Config::SocketBindAnyAddr != 0); + ui->cbBindAnyAddr->setChecked(Config::SocketBindAnyAddr); int sel = 0; for (int i = 0; i < LAN_PCap::NumAdapters; i++) @@ -64,13 +64,14 @@ WifiSettingsDialog::WifiSettingsDialog(QWidget* parent) : QDialog(parent), ui(ne ui->cbxDirectAdapter->addItem(QString(adapter->FriendlyName)); - if (!strncmp(adapter->DeviceName, Config::LANDevice, 128)) + if (!strncmp(adapter->DeviceName, Config::LANDevice.c_str(), 128)) sel = i; } ui->cbxDirectAdapter->setCurrentIndex(sel); - ui->rbDirectMode->setChecked(Config::DirectLAN != 0); - ui->rbIndirectMode->setChecked(Config::DirectLAN == 0); + // errrr??? + ui->rbDirectMode->setChecked(Config::DirectLAN); + ui->rbIndirectMode->setChecked(!Config::DirectLAN); if (!haspcap) ui->rbDirectMode->setEnabled(false); updateAdapterControls(); @@ -87,19 +88,18 @@ void WifiSettingsDialog::done(int r) if (r == QDialog::Accepted) { - Config::SocketBindAnyAddr = ui->cbBindAnyAddr->isChecked() ? 1:0; - Config::DirectLAN = ui->rbDirectMode->isChecked() ? 1:0; + Config::SocketBindAnyAddr = ui->cbBindAnyAddr->isChecked(); + Config::DirectLAN = ui->rbDirectMode->isChecked(); int sel = ui->cbxDirectAdapter->currentIndex(); if (sel < 0 || sel >= LAN_PCap::NumAdapters) sel = 0; if (LAN_PCap::NumAdapters < 1) { - Config::LANDevice[0] = '\0'; + Config::LANDevice = ""; } else { - strncpy(Config::LANDevice, LAN_PCap::Adapters[sel].DeviceName, 127); - Config::LANDevice[127] = '\0'; + Config::LANDevice = LAN_PCap::Adapters[sel].DeviceName; } Config::Save(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index bf7c261..5711a05 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -56,6 +56,7 @@ #include "VideoSettingsDialog.h" #include "AudioSettingsDialog.h" #include "FirmwareSettingsDialog.h" +#include "PathSettingsDialog.h" #include "WifiSettingsDialog.h" #include "InterfaceSettingsDialog.h" #include "ROMInfoDialog.h" @@ -80,6 +81,7 @@ #include "main_shaders.h" +#include "ROMManager.h" #include "ArchiveUtil.h" // TODO: uniform variable spelling @@ -180,7 +182,7 @@ void micClose() micDevice = 0; } -void micLoadWav(const char* name) +void micLoadWav(std::string name) { SDL_AudioSpec format; memset(&format, 0, sizeof(SDL_AudioSpec)); @@ -191,7 +193,7 @@ void micLoadWav(const char* name) u8* buf; u32 len; - if (!SDL_LoadWAV(name, &format, &buf, &len)) + if (!SDL_LoadWAV(name.c_str(), &format, &buf, &len)) return; const u64 dstfreq = 44100; @@ -541,6 +543,12 @@ void EmuThread::run() // emulate u32 nlines = NDS::RunFrame(); + if (ROMManager::NDSSave) + ROMManager::NDSSave->CheckFlush(); + + if (ROMManager::GBASave) + ROMManager::GBASave->CheckFlush(); + FrontBufferLock.lock(); FrontBuffer = GPU::FrontBuffer; #ifdef OGLRENDERER_ENABLED @@ -1281,7 +1289,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) oldW = Config::WindowWidth; oldH = Config::WindowHeight; - oldMax = Config::WindowMaximized!=0; + oldMax = Config::WindowMaximized; setWindowTitle("melonDS " MELONDS_VERSION); setAttribute(Qt::WA_DeleteOnClose); @@ -1295,16 +1303,16 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open)); - actOpenROMArchive = menu->addAction("Open ROM inside archive..."); + /*actOpenROMArchive = menu->addAction("Open ROM inside archive..."); connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive); - actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT)); + actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ recentMenu = menu->addMenu("Open recent"); for (int i = 0; i < 10; ++i) { - char* item = Config::RecentROMList[i]; - if (strlen(item) > 0) - recentFileList.push_back(item); + std::string item = Config::RecentROMList[i]; + if (!item.empty()) + recentFileList.push_back(QString::fromStdString(item)); } updateRecentFilesMenu(); @@ -1314,6 +1322,41 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) menu->addSeparator(); + actCurrentCart = menu->addAction("DS slot: " + ROMManager::CartLabel()); + actCurrentCart->setEnabled(false); + + actInsertCart = menu->addAction("Insert cart..."); + connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart); + + actEjectCart = menu->addAction("Eject cart"); + connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart); + + menu->addSeparator(); + + actCurrentGBACart = menu->addAction("GBA slot: " + ROMManager::GBACartLabel()); + actCurrentGBACart->setEnabled(false); + + actInsertGBACart = menu->addAction("Insert ROM cart..."); + connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart); + + { + QMenu* submenu = menu->addMenu("Insert add-on cart"); + + actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); + actInsertGBAAddon[0]->setData(QVariant(NDS::GBAAddon_RAMExpansion)); + connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + } + + actEjectGBACart = menu->addAction("Eject cart"); + connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart); + + menu->addSeparator(); + + actImportSavefile = menu->addAction("Import savefile"); + connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); + + menu->addSeparator(); + { QMenu* submenu = menu->addMenu("Save state"); @@ -1351,9 +1394,6 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); - actImportSavefile = menu->addAction("Import savefile"); - connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); - menu->addSeparator(); actQuit = menu->addAction("Quit"); @@ -1422,6 +1462,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actFirmwareSettings = menu->addAction("Firmware settings"); connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); + actPathSettings = menu->addAction("Path settings"); + connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); + { QMenu* submenu = menu->addMenu("Savestate settings"); @@ -1588,6 +1631,16 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) createScreenPanel(); + actEjectCart->setEnabled(false); + actEjectGBACart->setEnabled(false); + + if (Config::ConsoleType == 1) + { + actInsertGBACart->setEnabled(false); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(false); + } + for (int i = 0; i < 9; i++) { actSaveState[i]->setEnabled(false); @@ -1602,13 +1655,13 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actFrameStep->setEnabled(false); actSetupCheats->setEnabled(false); - actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); - actEnableCheats->setChecked(Config::EnableCheats != 0); + actEnableCheats->setChecked(Config::EnableCheats); actROMInfo->setEnabled(false); - actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM != 0); + actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM); actScreenRotation[Config::ScreenRotation]->setChecked(true); @@ -1623,18 +1676,18 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actScreenLayout[Config::ScreenLayout]->setChecked(true); actScreenSizing[Config::ScreenSizing]->setChecked(true); - actIntegerScaling->setChecked(Config::IntegerScaling != 0); + actIntegerScaling->setChecked(Config::IntegerScaling); - actScreenSwap->setChecked(Config::ScreenSwap != 0); + actScreenSwap->setChecked(Config::ScreenSwap); actScreenAspectTop[Config::ScreenAspectTop]->setChecked(true); actScreenAspectBot[Config::ScreenAspectBot]->setChecked(true); - actScreenFiltering->setChecked(Config::ScreenFilter != 0); - actShowOSD->setChecked(Config::ShowOSD != 0); + actScreenFiltering->setChecked(Config::ScreenFilter); + actShowOSD->setChecked(Config::ShowOSD); - actLimitFramerate->setChecked(Config::LimitFPS != 0); - actAudioSync->setChecked(Config::AudioSync != 0); + actLimitFramerate->setChecked(Config::LimitFPS); + actAudioSync->setChecked(Config::AudioSync); } MainWindow::~MainWindow() @@ -1755,9 +1808,9 @@ void MainWindow::dragEnterEvent(QDragEnterEvent* event) QStringList acceptedExts{".nds", ".srl", ".dsi", ".gba", ".rar", ".zip", ".7z", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"}; - for(const QString &ext : acceptedExts) + for (const QString &ext : acceptedExts) { - if(filename.endsWith(ext, Qt::CaseInsensitive)) + if (filename.endsWith(ext, Qt::CaseInsensitive)) event->acceptProposedAction(); } } @@ -1769,66 +1822,66 @@ void MainWindow::dropEvent(QDropEvent* event) QList<QUrl> urls = event->mimeData()->urls(); if (urls.count() > 1) return; // not handling more than one file at once - emuThread->emuPause(); - QString filename = urls.at(0).toLocalFile(); - QString ext = filename.right(3).toLower(); + QStringList arcexts{".zip", ".7z", ".rar", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"}; - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); - - char _filename[1024]; - strncpy(_filename, filename.toStdString().c_str(), 1023); _filename[1023] = '\0'; + emuThread->emuPause(); - int slot; int res; - if (ext == "gba") - { - slot = 1; - res = Frontend::LoadROM(_filename, Frontend::ROMSlot_GBA); - } - else if(ext == "nds" || ext == "srl" || ext == "dsi") + if (!verifySetup()) { - slot = 0; - res = Frontend::LoadROM(_filename, Frontend::ROMSlot_NDS); + emuThread->emuUnpause(); + return; } - else + + for (const QString &ext : arcexts) { - QByteArray romBuffer; - QString romFileName = pickAndExtractFileFromArchive(_filename, &romBuffer); - if(romFileName.isEmpty()) + if (filename.endsWith(ext, Qt::CaseInsensitive)) { - res = Frontend::Load_ROMLoadError; - } - else - { - slot = (romFileName.endsWith(".gba", Qt::CaseInsensitive) ? 1 : 0); - QString sramFileName = QFileInfo(_filename).absolutePath() + QDir::separator() + QFileInfo(romFileName).completeBaseName() + ".sav"; - - if(slot == 0) - strncpy(Frontend::NDSROMExtension, QFileInfo(romFileName).suffix().toStdString().c_str(), 4); + QString arcfile = pickFileFromArchive(filename); + if (arcfile.isEmpty()) + { + emuThread->emuUnpause(); + return; + } - res = Frontend::LoadROM((const u8*)romBuffer.constData(), romBuffer.size(), - _filename, romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(), - slot); + filename += "|" + arcfile; } } - if (res != Frontend::Load_OK) - { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else if (slot == 1) + QStringList file = filename.split('|'); + + if (filename.endsWith(".gba", Qt::CaseInsensitive)) { - // checkme + if (!ROMManager::LoadGBAROM(file)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + emuThread->emuUnpause(); + + updateCartInserted(true); } else { + if (!ROMManager::LoadROM(file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + NDS::Start(); emuThread->emuRun(); + + updateCartInserted(false); } } @@ -1846,145 +1899,178 @@ void MainWindow::onAppStateChanged(Qt::ApplicationState state) } } -QString MainWindow::loadErrorStr(int error) +bool MainWindow::verifySetup() { - switch (error) + QString res = ROMManager::VerifySetup(); + if (!res.isEmpty()) { - case Frontend::Load_BIOS9Missing: - return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_BIOS9Bad: - return "DS ARM9 BIOS is not a valid BIOS dump."; + QMessageBox::critical(this, "melonDS", res); + return false; + } - case Frontend::Load_BIOS7Missing: - return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_BIOS7Bad: - return "DS ARM7 BIOS is not a valid BIOS dump."; + return true; +} - case Frontend::Load_FirmwareMissing: - return "DS firmware was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_FirmwareBad: - return "DS firmware is not a valid firmware dump."; - case Frontend::Load_FirmwareNotBootable: - return "DS firmware is not bootable."; +bool MainWindow::preloadROMs(QString filename, QString gbafilename) +{ + if (!verifySetup()) + { + return false; + } - case Frontend::Load_DSiBIOS9Missing: - return "DSi ARM9 BIOS was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_DSiBIOS9Bad: - return "DSi ARM9 BIOS is not a valid BIOS dump."; + bool gbaloaded = false; + if (!gbafilename.isEmpty()) + { + QStringList gbafile = gbafilename.split('|'); + if (!ROMManager::LoadGBAROM(gbafile)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); + return false; + } - case Frontend::Load_DSiBIOS7Missing: - return "DSi ARM7 BIOS was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_DSiBIOS7Bad: - return "DSi ARM7 BIOS is not a valid BIOS dump."; + gbaloaded = true; + } - case Frontend::Load_DSiNANDMissing: - return "DSi NAND was not found or could not be accessed. Check your emu settings."; - case Frontend::Load_DSiNANDBad: - return "DSi NAND is not a valid NAND dump."; + QStringList file = filename.split('|'); + if (!ROMManager::LoadROM(file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + return false; + } + + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + NDS::Start(); + emuThread->emuRun(); - case Frontend::Load_ROMLoadError: - return "Failed to load the ROM. Make sure the file is accessible and isn't used by another application."; + updateCartInserted(false); - default: return "Unknown error during launch; smack Arisotura."; + if (gbaloaded) + { + updateCartInserted(true); } + + return true; } -void MainWindow::loadROM(QByteArray *romData, QString archiveFileName, QString romFileName) +QString MainWindow::pickFileFromArchive(QString archiveFileName) { - recentFileList.removeAll(archiveFileName); - recentFileList.prepend(archiveFileName); - updateRecentFilesMenu(); + QVector<QString> archiveROMList = Archive::ListArchive(archiveFileName); - // Strip entire archive name and get folder path - strncpy(Config::LastROMFolder, QFileInfo(archiveFileName).absolutePath().toStdString().c_str(), 1024); + QString romFileName = ""; // file name inside archive - QString sramFileName = QFileInfo(archiveFileName).absolutePath() + QDir::separator() + QFileInfo(romFileName).completeBaseName() + ".sav"; + if (archiveROMList.size() > 2) + { + archiveROMList.removeFirst(); - int slot; int res; - if (romFileName.endsWith("gba")) + bool ok; + QString toLoad = QInputDialog::getItem(this, "melonDS", + "This archive contains multiple files. Select which ROM you want to load.", archiveROMList.toList(), 0, false, &ok); + if (!ok) // User clicked on cancel + return QString(); + + romFileName = toLoad; + } + else if (archiveROMList.size() == 2) { - slot = 1; - res = Frontend::LoadROM((const u8*)romData->constData(), romData->size(), - archiveFileName.toStdString().c_str(), - romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(), - Frontend::ROMSlot_GBA); + romFileName = archiveROMList.at(1); } - else + else if ((archiveROMList.size() == 1) && (archiveROMList[0] == QString("OK"))) { - strncpy(Frontend::NDSROMExtension, QFileInfo(romFileName).suffix().toStdString().c_str(), 4); - slot = 0; - res = Frontend::LoadROM((const u8*)romData->constData(), romData->size(), - archiveFileName.toStdString().c_str(), - romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(), - Frontend::ROMSlot_NDS); + QMessageBox::warning(this, "melonDS", "This archive is empty."); } - - if (res != Frontend::Load_OK) + else { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); + QMessageBox::critical(this, "melonDS", "This archive could not be read. It may be corrupt or you don't have the permissions."); } - else if (slot == 1) + + return romFileName; +} + +QStringList MainWindow::pickROM(bool gba) +{ + QString console; + QStringList romexts; + QStringList arcexts{"*.zip", "*.7z", "*.rar", "*.tar", "*.tar.gz", "*.tar.xz", "*.tar.bz2"}; + QStringList ret; + + if (gba) { - // checkme - emuThread->emuUnpause(); + console = "GBA"; + romexts.append("*.gba"); } else { - emuThread->emuRun(); + console = "DS"; + romexts.append({"*.nds", "*.dsi", "*.ids", "*.srl"}); } -} -void MainWindow::loadROM(QString filename) -{ - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); + QString filter = romexts.join(' ') + " " + arcexts.join(' '); + filter = console + " ROMs (" + filter + ");;Any file (*.*)"; - // TODO: validate the input file!! - // * check that it is a proper ROM - // * ensure the binary offsets are sane - // * etc + QString filename = QFileDialog::getOpenFileName(this, + "Open "+console+" ROM", + QString::fromStdString(Config::LastROMFolder), + filter); + if (filename.isEmpty()) + return ret; - // this shit is stupid - char file[1024]; - strncpy(file, filename.toStdString().c_str(), 1023); file[1023] = '\0'; + int pos = filename.length() - 1; + while (filename[pos] != '/' && filename[pos] != '\\' && pos > 0) pos--; + QString path_dir = filename.left(pos); + QString path_file = filename.mid(pos+1); - int pos = strlen(file)-1; - while (file[pos] != '/' && file[pos] != '\\' && pos > 0) pos--; - strncpy(Config::LastROMFolder, file, pos); - Config::LastROMFolder[pos] = '\0'; - char* ext = &file[strlen(file)-3]; + Config::LastROMFolder = path_dir.toStdString(); - int slot; int res; - if (!strcasecmp(ext, "gba")) + bool isarc = false; + for (const auto& ext : arcexts) { - slot = 1; - res = Frontend::LoadROM(file, Frontend::ROMSlot_GBA); + int l = ext.length() - 1; + if (path_file.right(l).toLower() == ext.right(l)) + { + isarc = true; + break; + } + } + + if (isarc) + { + path_file = pickFileFromArchive(filename); + if (path_file.isEmpty()) + return ret; + + ret.append(filename); + ret.append(path_file); } else { - slot = 0; - res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); + ret.append(filename); } - if (res != Frontend::Load_OK) + return ret; +} + +void MainWindow::updateCartInserted(bool gba) +{ + bool inserted; + if (gba) { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else if (slot == 1) - { - // checkme - emuThread->emuUnpause(); + inserted = ROMManager::GBACartInserted() && (Config::ConsoleType == 0); + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + actEjectGBACart->setEnabled(inserted); } else { - emuThread->emuRun(); + inserted = ROMManager::CartInserted(); + actCurrentCart->setText("DS slot: " + ROMManager::CartLabel()); + actEjectCart->setEnabled(inserted); + actImportSavefile->setEnabled(inserted); + actSetupCheats->setEnabled(inserted); + actROMInfo->setEnabled(inserted); } } @@ -1992,99 +2078,43 @@ void MainWindow::onOpenFile() { emuThread->emuPause(); - QString filename = QFileDialog::getOpenFileName(this, - "Open ROM", - Config::LastROMFolder, - "DS ROMs (*.nds *.dsi *.srl);;GBA ROMs (*.gba *.zip);;Any file (*.*)"); - if (filename.isEmpty()) + if (!verifySetup()) { emuThread->emuUnpause(); return; } - loadROM(filename); -} - -void MainWindow::onOpenFileArchive() -{ - emuThread->emuPause(); - - QString archiveFileName = QFileDialog::getOpenFileName(this, - "Open ROM Archive", - Config::LastROMFolder, - "Archived ROMs (*.zip *.7z *.rar *.tar *.tar.gz *.tar.xz *.tar.bz2);;Any file (*.*)"); - if (archiveFileName.isEmpty()) + QStringList file = pickROM(false); + if (file.isEmpty()) { emuThread->emuUnpause(); return; } - QByteArray romBuffer; - QString romFileName = pickAndExtractFileFromArchive(archiveFileName, &romBuffer); - if(!romFileName.isEmpty()) + if (!ROMManager::LoadROM(file, true)) { - loadROM(&romBuffer, archiveFileName, romFileName); + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; } -} - -QString MainWindow::pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer) -{ - printf("Finding list of ROMs...\n"); - QVector<QString> archiveROMList = Archive::ListArchive(archiveFileName.toUtf8().constData()); - - - QString romFileName; // file name inside archive - if (archiveROMList.size() > 2) - { - archiveROMList.removeFirst(); + QString filename = file.join('|'); + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); - bool ok; - QString toLoad = QInputDialog::getItem(this, "melonDS", - "The archive was found to have multiple files. Select which ROM you want to load.", archiveROMList.toList(), 0, false, &ok); - if(!ok) // User clicked on cancel - return QString(); + NDS::Start(); + emuThread->emuRun(); - printf("Extracting '%s'\n", toLoad.toUtf8().constData()); - QVector<QString> extractResult = Archive::ExtractFileFromArchive(archiveFileName.toUtf8().constData(), toLoad.toUtf8().constData(), romBuffer); - if (extractResult[0] != QString("Err")) - { - romFileName = extractResult[0]; - } - else - { - QMessageBox::critical(this, "melonDS", QString("There was an error while trying to extract the ROM from the archive: ") + extractResult[1]); - } - } - else if (archiveROMList.size() == 2) - { - printf("Extracting the only ROM in archive\n"); - QVector<QString> extractResult = Archive::ExtractFileFromArchive(archiveFileName.toUtf8().constData(), archiveROMList.at(1).toUtf8().constData(), romBuffer); - if (extractResult[0] != QString("Err")) - { - romFileName = extractResult[0]; - } - else - { - QMessageBox::critical(this, "melonDS", QString("There was an error while trying to extract the ROM from the archive: ") + extractResult[1]); - } - } - else if ((archiveROMList.size() == 1) && (archiveROMList[0] == QString("OK"))) - { - QMessageBox::warning(this, "melonDS", "The archive is intact, but there are no files inside."); - } - else - { - QMessageBox::critical(this, "melonDS", "The archive could not be read. It may be corrupt or you don't have the permissions."); - } - - return romFileName; + updateCartInserted(false); } void MainWindow::onClearRecentFiles() { recentFileList.clear(); - memset(Config::RecentROMList, 0, 10 * 1024); + for (int i = 0; i < 10; i++) + Config::RecentROMList[i] = ""; updateRecentFilesMenu(); } @@ -2092,8 +2122,10 @@ void MainWindow::updateRecentFilesMenu() { recentMenu->clear(); - for(int i = 0; i < recentFileList.size(); ++i) + for (int i = 0; i < recentFileList.size(); ++i) { + if (i >= 10) break; + QString item_full = recentFileList.at(i); QString item_display = item_full; int itemlen = item_full.length(); @@ -2120,16 +2152,18 @@ void MainWindow::updateRecentFilesMenu() actRecentFile_i->setData(item_full); connect(actRecentFile_i, &QAction::triggered, this, &MainWindow::onClickRecentFile); - if(i < 10) - strncpy(Config::RecentROMList[i], recentFileList.at(i).toStdString().c_str(), 1024); + Config::RecentROMList[i] = recentFileList.at(i).toStdString(); } + while (recentFileList.size() > 10) + recentFileList.removeLast(); + recentMenu->addSeparator(); QAction *actClearRecentList = recentMenu->addAction("Clear"); connect(actClearRecentList, &QAction::triggered, this, &MainWindow::onClearRecentFiles); - if(recentFileList.empty()) + if (recentFileList.empty()) actClearRecentList->setEnabled(false); Config::Save(); @@ -2138,48 +2172,139 @@ void MainWindow::updateRecentFilesMenu() void MainWindow::onClickRecentFile() { QAction *act = (QAction *)sender(); - QString fileName = act->data().toString(); + QString filename = act->data().toString(); + QStringList file = filename.split('|'); + + emuThread->emuPause(); - if (fileName.endsWith(".gba", Qt::CaseInsensitive) || - fileName.endsWith(".nds", Qt::CaseInsensitive) || - fileName.endsWith(".srl", Qt::CaseInsensitive) || - fileName.endsWith(".dsi", Qt::CaseInsensitive)) + if (!verifySetup()) { - emuThread->emuPause(); - loadROM(fileName); + emuThread->emuUnpause(); + return; } - else + + if (!ROMManager::LoadROM(file, true)) { - // Archives - QString archiveFileName = fileName; - QByteArray romBuffer; - QString romFileName = MainWindow::pickAndExtractFileFromArchive(archiveFileName, &romBuffer); - if(!romFileName.isEmpty()) - { - emuThread->emuPause(); - loadROM(&romBuffer, archiveFileName, romFileName); - } + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; } + + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + NDS::Start(); + emuThread->emuRun(); + + updateCartInserted(false); } void MainWindow::onBootFirmware() { - // TODO: check the whole GBA cart shito + emuThread->emuPause(); + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadBIOS()) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); + emuThread->emuUnpause(); + return; + } + + NDS::Start(); + emuThread->emuRun(); +} + +void MainWindow::onInsertCart() +{ emuThread->emuPause(); - int res = Frontend::LoadBIOS(); - if (res != Frontend::Load_OK) + QStringList file = pickROM(false); + if (file.isEmpty()) { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); emuThread->emuUnpause(); + return; } - else + + if (!ROMManager::LoadROM(file, false)) { - emuThread->emuRun(); + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + emuThread->emuUnpause(); + + updateCartInserted(false); +} + +void MainWindow::onEjectCart() +{ + emuThread->emuPause(); + + ROMManager::EjectCart(); + + emuThread->emuUnpause(); + + updateCartInserted(false); +} + +void MainWindow::onInsertGBACart() +{ + emuThread->emuPause(); + + QStringList file = pickROM(true); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadGBAROM(file)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; } + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onInsertGBAAddon() +{ + QAction* act = (QAction*)sender(); + int type = act->data().toInt(); + + emuThread->emuPause(); + + ROMManager::LoadGBAAddon(type); + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onEjectGBACart() +{ + emuThread->emuPause(); + + ROMManager::EjectGBACart(); + + emuThread->emuUnpause(); + + updateCartInserted(true); } void MainWindow::onSaveState() @@ -2188,17 +2313,17 @@ void MainWindow::onSaveState() emuThread->emuPause(); - char filename[1024]; + std::string filename; if (slot > 0) { - Frontend::GetSavestateName(slot, filename, 1024); + filename = ROMManager::GetSavestateName(slot); } else { // TODO: specific 'last directory' for savestate files? QString qfilename = QFileDialog::getSaveFileName(this, "Save state", - Config::LastROMFolder, + QString::fromStdString(Config::LastROMFolder), "melonDS savestates (*.mln);;Any file (*.*)"); if (qfilename.isEmpty()) { @@ -2206,10 +2331,10 @@ void MainWindow::onSaveState() return; } - strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0'; + filename = qfilename.toStdString(); } - if (Frontend::SaveState(filename)) + if (ROMManager::SaveState(filename)) { char msg[64]; if (slot > 0) sprintf(msg, "State saved to slot %d", slot); @@ -2232,17 +2357,17 @@ void MainWindow::onLoadState() emuThread->emuPause(); - char filename[1024]; + std::string filename; if (slot > 0) { - Frontend::GetSavestateName(slot, filename, 1024); + filename = ROMManager::GetSavestateName(slot); } else { // TODO: specific 'last directory' for savestate files? QString qfilename = QFileDialog::getOpenFileName(this, "Load state", - Config::LastROMFolder, + QString::fromStdString(Config::LastROMFolder), "melonDS savestates (*.ml*);;Any file (*.*)"); if (qfilename.isEmpty()) { @@ -2250,7 +2375,7 @@ void MainWindow::onLoadState() return; } - strncpy(filename, qfilename.toStdString().c_str(), 1023); filename[1023] = '\0'; + filename = qfilename.toStdString(); } if (!Platform::FileExists(filename)) @@ -2264,7 +2389,7 @@ void MainWindow::onLoadState() return; } - if (Frontend::LoadState(filename)) + if (ROMManager::LoadState(filename)) { char msg[64]; if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); @@ -2284,7 +2409,7 @@ void MainWindow::onLoadState() void MainWindow::onUndoStateLoad() { emuThread->emuPause(); - Frontend::UndoStateLoad(); + ROMManager::UndoStateLoad(); emuThread->emuUnpause(); OSD::AddMessage(0, "State load undone"); @@ -2292,36 +2417,52 @@ void MainWindow::onUndoStateLoad() void MainWindow::onImportSavefile() { - if (!RunningSomething) return; - emuThread->emuPause(); QString path = QFileDialog::getOpenFileName(this, "Select savefile", - Config::LastROMFolder, + QString::fromStdString(Config::LastROMFolder), "Savefiles (*.sav *.bin *.dsv);;Any file (*.*)"); - if (!path.isEmpty()) + if (path.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + FILE* f = Platform::OpenFile(path.toStdString(), "rb", true); + if (!f) + { + QMessageBox::critical(this, "melonDS", "Could not open the given savefile."); + emuThread->emuUnpause(); + return; + } + + if (RunningSomething) { if (QMessageBox::warning(this, - "Emulation will be reset and data overwritten", + "melonDS", "The emulation will be reset and the current savefile overwritten.", - QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Ok) + QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) { - int res = Frontend::Reset(); - if (res != Frontend::Load_OK) - { - QMessageBox::critical(this, "melonDS", "Reset failed\n" + loadErrorStr(res)); - } - else - { - int diff = Frontend::ImportSRAM(path.toStdString().c_str()); - if (diff > 0) - OSD::AddMessage(0, "Trimmed savefile"); - else if (diff < 0) - OSD::AddMessage(0, "Savefile shorter than SRAM"); - } + emuThread->emuUnpause(); + return; } + + ROMManager::Reset(); } + + u32 len; + fseek(f, 0, SEEK_END); + len = (u32)ftell(f); + + u8* data = new u8[len]; + fseek(f, 0, SEEK_SET); + fread(data, len, 1, f); + + NDS::LoadSave(data, len); + delete[] data; + + fclose(f); emuThread->emuUnpause(); } @@ -2360,19 +2501,10 @@ void MainWindow::onReset() actUndoStateLoad->setEnabled(false); - int res = Frontend::Reset(); - if (res != Frontend::Load_OK) - { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else - { - OSD::AddMessage(0, "Reset"); - emuThread->emuRun(); - } + ROMManager::Reset(); + + OSD::AddMessage(0, "Reset"); + emuThread->emuRun(); } void MainWindow::onStop() @@ -2393,7 +2525,7 @@ void MainWindow::onFrameStep() void MainWindow::onEnableCheats(bool checked) { Config::EnableCheats = checked?1:0; - Frontend::EnableCheats(Config::EnableCheats != 0); + ROMManager::EnableCheats(Config::EnableCheats != 0); } void MainWindow::onSetupCheats() @@ -2431,11 +2563,28 @@ void MainWindow::onEmuSettingsDialogFinished(int res) { emuThread->emuUnpause(); + if (Config::ConsoleType == 1) + { + actInsertGBACart->setEnabled(false); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(false); + actEjectGBACart->setEnabled(false); + } + else + { + actInsertGBACart->setEnabled(true); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(true); + actEjectGBACart->setEnabled(ROMManager::GBACartInserted()); + } + if (EmuSettingsDialog::needsReset) onReset(); + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + if (!RunningSomething) - actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); } void MainWindow::onOpenInputConfig() @@ -2480,6 +2629,22 @@ void MainWindow::onFirmwareSettingsFinished(int res) emuThread->emuUnpause(); } +void MainWindow::onOpenPathSettings() +{ + emuThread->emuPause(); + + PathSettingsDialog* dlg = PathSettingsDialog::openDlg(this); + connect(dlg, &PathSettingsDialog::finished, this, &MainWindow::onPathSettingsFinished); +} + +void MainWindow::onPathSettingsFinished(int res) +{ + if (PathSettingsDialog::needsReset) + onReset(); + + emuThread->emuUnpause(); +} + void MainWindow::onUpdateAudioSettings() { SPU::SetInterpolation(Config::AudioInterp); @@ -2678,39 +2843,22 @@ void MainWindow::onFullscreenToggled() void MainWindow::onEmuStart() { - // TODO: make savestates work in DSi mode!! - if (Config::ConsoleType == 1) - { - for (int i = 0; i < 9; i++) - { - actSaveState[i]->setEnabled(false); - actLoadState[i]->setEnabled(false); - } - actUndoStateLoad->setEnabled(false); - } - else + for (int i = 1; i < 9; i++) { - for (int i = 1; i < 9; i++) - { - actSaveState[i]->setEnabled(true); - actLoadState[i]->setEnabled(Frontend::SavestateExists(i)); - } - actSaveState[0]->setEnabled(true); - actLoadState[0]->setEnabled(true); - actUndoStateLoad->setEnabled(false); + actSaveState[i]->setEnabled(true); + actLoadState[i]->setEnabled(ROMManager::SavestateExists(i)); } + actSaveState[0]->setEnabled(true); + actLoadState[0]->setEnabled(true); + actUndoStateLoad->setEnabled(false); actPause->setEnabled(true); actPause->setChecked(false); actReset->setEnabled(true); actStop->setEnabled(true); actFrameStep->setEnabled(true); - actImportSavefile->setEnabled(true); - actSetupCheats->setEnabled(true); actTitleManager->setEnabled(false); - - actROMInfo->setEnabled(true); } void MainWindow::onEmuStop() @@ -2723,17 +2871,13 @@ void MainWindow::onEmuStop() actLoadState[i]->setEnabled(false); } actUndoStateLoad->setEnabled(false); - actImportSavefile->setEnabled(false); actPause->setEnabled(false); actReset->setEnabled(false); actStop->setEnabled(false); actFrameStep->setEnabled(false); - actSetupCheats->setEnabled(false); - actTitleManager->setEnabled(strlen(Config::DSiNANDPath) > 0); - - actROMInfo->setEnabled(false); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); } void MainWindow::onUpdateVideoSettings(bool glchange) @@ -2767,9 +2911,6 @@ void emuStop() { RunningSomething = false; - Frontend::UnloadROM(Frontend::ROMSlot_NDS); - Frontend::UnloadROM(Frontend::ROMSlot_GBA); - emit emuThread->windowEmuStop(); OSD::AddMessage(0xFFC040, "Shutdown"); @@ -2788,7 +2929,8 @@ bool MelonApplication::event(QEvent *event) QFileOpenEvent *openEvent = static_cast<QFileOpenEvent*>(event); emuThread->emuPause(); - mainWindow->loadROM(openEvent->file()); + if (!mainWindow->preloadROMs(openEvent->file(), "")) + emuThread->emuUnpause(); } return QApplication::event(event); @@ -2886,8 +3028,7 @@ int main(int argc, char** argv) micExtBufferWritePos = 0; micWavBuffer = nullptr; - Frontend::Init_ROM(); - Frontend::EnableCheats(Config::EnableCheats != 0); + ROMManager::EnableCheats(Config::EnableCheats != 0); Frontend::Init_Audio(audioFreq); @@ -2914,29 +3055,11 @@ int main(int argc, char** argv) if (argc > 1) { - char* file = argv[1]; - char* ext = &file[strlen(file)-3]; - - if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl") || !strcasecmp(ext, "dsi")) - { - int res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); + QString file = argv[1]; + QString gbafile = ""; + if (argc > 2) gbafile = argv[2]; - if (res == Frontend::Load_OK) - { - if (argc > 2) - { - file = argv[2]; - ext = &file[strlen(file)-3]; - - if (!strcasecmp(ext, "gba")) - { - Frontend::LoadROM(file, Frontend::ROMSlot_GBA); - } - } - - emuThread->emuRun(); - } - } + mainWindow->preloadROMs(file, gbafile); } int ret = melon.exec(); @@ -2947,8 +3070,6 @@ int main(int argc, char** argv) Input::CloseJoystick(); - Frontend::DeInit_ROM(); - if (audioDevice) SDL_CloseAudioDevice(audioDevice); micClose(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 0b5e917..a0ee9c5 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -211,8 +211,7 @@ public: bool hasOGL; QOpenGLContext* getOGLContext(); - void loadROM(QString filename); - void loadROM(QByteArray *romData, QString archiveFileName, QString romFileName); + bool preloadROMs(QString filename, QString gbafilename); void onAppStateChanged(Qt::ApplicationState state); @@ -231,10 +230,14 @@ signals: private slots: void onOpenFile(); - void onOpenFileArchive(); void onClickRecentFile(); void onClearRecentFiles(); void onBootFirmware(); + void onInsertCart(); + void onEjectCart(); + void onInsertGBACart(); + void onInsertGBAAddon(); + void onEjectGBACart(); void onSaveState(); void onLoadState(); void onUndoStateLoad(); @@ -258,11 +261,13 @@ private slots: void onOpenVideoSettings(); void onOpenAudioSettings(); void onOpenFirmwareSettings(); + void onOpenPathSettings(); void onUpdateAudioSettings(); void onAudioSettingsFinished(int res); void onOpenWifiSettings(); void onWifiSettingsFinished(int res); void onFirmwareSettingsFinished(int res); + void onPathSettingsFinished(int res); void onOpenInterfaceSettings(); void onInterfaceSettingsFinished(int res); void onUpdateMouseTimer(); @@ -291,16 +296,19 @@ private slots: void onFullscreenToggled(); private: + QStringList currentROM; + QStringList currentGBAROM; QList<QString> recentFileList; QMenu *recentMenu; void updateRecentFilesMenu(); - QString pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer); + bool verifySetup(); + QString pickFileFromArchive(QString archiveFileName); + QStringList pickROM(bool gba); + void updateCartInserted(bool gba); void createScreenPanel(); - QString loadErrorStr(int error); - bool pausedManually = false; int oldW, oldH; @@ -312,12 +320,18 @@ public: ScreenPanelNative* panelNative; QAction* actOpenROM; - QAction* actOpenROMArchive; QAction* actBootFirmware; + QAction* actCurrentCart; + QAction* actInsertCart; + QAction* actEjectCart; + QAction* actCurrentGBACart; + QAction* actInsertGBACart; + QAction* actInsertGBAAddon[1]; + QAction* actEjectGBACart; + QAction* actImportSavefile; QAction* actSaveState[9]; QAction* actLoadState[9]; QAction* actUndoStateLoad; - QAction* actImportSavefile; QAction* actQuit; QAction* actPause; @@ -335,6 +349,7 @@ public: QAction* actAudioSettings; QAction* actWifiSettings; QAction* actFirmwareSettings; + QAction* actPathSettings; QAction* actInterfaceSettings; QAction* actSavestateSRAMReloc; QAction* actScreenSize[4]; |