From e665e25bd3ea4b9af932e182b1c93e77b762ccb0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 7 Jan 2022 14:00:43 +0100 Subject: [PATCH] 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) --- src/ARCodeFile.cpp | 13 +- src/ARCodeFile.h | 9 +- src/AREngine.cpp | 7 +- src/AREngine.h | 1 + src/CMakeLists.txt | 1 - src/CRC32.cpp | 4 +- src/CRC32.h | 2 +- src/DSi.cpp | 233 +++-- src/DSi.h | 6 +- src/DSi_AES.cpp | 37 + src/DSi_AES.h | 2 + src/DSi_Camera.cpp | 39 +- src/DSi_Camera.h | 4 + src/DSi_DSP.cpp | 2 + src/DSi_DSP.h | 4 +- src/DSi_I2C.cpp | 20 + src/DSi_I2C.h | 2 +- src/DSi_NDMA.cpp | 26 +- src/DSi_NWifi.cpp | 39 + src/DSi_NWifi.h | 2 + src/DSi_SD.cpp | 86 +- src/DSi_SD.h | 7 +- src/DSi_SPI_TSC.cpp | 9 +- src/GBACart.cpp | 384 ++++---- src/GBACart.h | 62 +- src/NDS.cpp | 176 ++-- src/NDS.h | 31 +- src/NDSCart.cpp | 460 ++++----- src/NDSCart.h | 49 +- src/NDSCart_SRAMManager.cpp | 184 ---- src/NDSCart_SRAMManager.h | 39 - src/Platform.h | 7 + src/SPI.cpp | 1 + src/SPI.h | 1 + src/Savestate.cpp | 9 +- src/Savestate.h | 3 +- src/frontend/FrontendUtil.h | 90 +- src/frontend/SharedConfig.h | 42 - src/frontend/Util_ROM.cpp | 845 ---------------- src/frontend/qt_sdl/ArchiveUtil.cpp | 134 ++- src/frontend/qt_sdl/ArchiveUtil.h | 9 +- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 4 +- src/frontend/qt_sdl/CMakeLists.txt | 5 +- src/frontend/qt_sdl/CheatsDialog.cpp | 29 +- src/frontend/qt_sdl/Config.cpp | 397 ++++---- src/frontend/qt_sdl/Config.h | 113 +-- src/frontend/qt_sdl/EmuSettingsDialog.cpp | 117 ++- .../qt_sdl/FirmwareSettingsDialog.cpp | 24 +- src/frontend/qt_sdl/FirmwareSettingsDialog.h | 2 +- src/frontend/qt_sdl/LAN_PCap.cpp | 2 +- src/frontend/qt_sdl/PathSettingsDialog.cpp | 119 +++ src/frontend/qt_sdl/PathSettingsDialog.h | 67 ++ src/frontend/qt_sdl/PathSettingsDialog.ui | 149 +++ src/frontend/qt_sdl/Platform.cpp | 16 +- src/frontend/qt_sdl/ROMInfoDialog.cpp | 6 +- src/frontend/qt_sdl/ROMInfoDialog.h | 4 +- src/frontend/qt_sdl/ROMManager.cpp | 843 ++++++++++++++++ src/frontend/qt_sdl/ROMManager.h | 66 ++ src/frontend/qt_sdl/SaveManager.cpp | 194 ++++ src/frontend/qt_sdl/SaveManager.h | 70 ++ src/frontend/qt_sdl/TitleManagerDialog.cpp | 4 +- src/frontend/qt_sdl/WifiSettingsDialog.cpp | 18 +- src/frontend/qt_sdl/main.cpp | 907 ++++++++++-------- src/frontend/qt_sdl/main.h | 31 +- 64 files changed, 3606 insertions(+), 2662 deletions(-) delete mode 100644 src/NDSCart_SRAMManager.cpp delete mode 100644 src/NDSCart_SRAMManager.h delete mode 100644 src/frontend/SharedConfig.h delete mode 100644 src/frontend/Util_ROM.cpp create mode 100644 src/frontend/qt_sdl/PathSettingsDialog.cpp create mode 100644 src/frontend/qt_sdl/PathSettingsDialog.h create mode 100644 src/frontend/qt_sdl/PathSettingsDialog.ui create mode 100644 src/frontend/qt_sdl/ROMManager.cpp create mode 100644 src/frontend/qt_sdl/ROMManager.h create mode 100644 src/frontend/qt_sdl/SaveManager.cpp create mode 100644 src/frontend/qt_sdl/SaveManager.h diff --git a/src/ARCodeFile.cpp b/src/ARCodeFile.cpp index d79cd395..4e44f06a 100644 --- a/src/ARCodeFile.cpp +++ b/src/ARCodeFile.cpp @@ -26,10 +26,9 @@ // TODO: more user-friendly error reporting -ARCodeFile::ARCodeFile(const char* filename) +ARCodeFile::ARCodeFile(std::string filename) { - memset(Filename, 0, sizeof(Filename)); - strncpy(Filename, filename, 1023); + Filename = filename; Error = false; @@ -91,7 +90,7 @@ bool ARCodeFile::Load() if (isincat) Categories.push_back(curcat); isincat = true; - memcpy(curcat.Name, catname, 128); + curcat.Name = catname; curcat.Codes.clear(); } else if (!strncasecmp(start, "CODE", 4)) @@ -118,7 +117,7 @@ bool ARCodeFile::Load() if (isincode) curcat.Codes.push_back(curcode); isincode = true; - memcpy(curcode.Name, codename, 128); + curcode.Name = codename; curcode.Enabled = enable!=0; curcode.CodeLen = 0; } @@ -172,12 +171,12 @@ bool ARCodeFile::Save() ARCodeCat& cat = *it; if (it != Categories.begin()) fprintf(f, "\r\n"); - fprintf(f, "CAT %s\r\n\r\n", cat.Name); + fprintf(f, "CAT %s\r\n\r\n", cat.Name.c_str()); for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++) { ARCode& code = *jt; - fprintf(f, "CODE %d %s\r\n", code.Enabled, code.Name); + fprintf(f, "CODE %d %s\r\n", code.Enabled, code.Name.c_str()); for (u32 i = 0; i < code.CodeLen; i+=2) { diff --git a/src/ARCodeFile.h b/src/ARCodeFile.h index a3c36e42..16149080 100644 --- a/src/ARCodeFile.h +++ b/src/ARCodeFile.h @@ -19,13 +19,14 @@ #ifndef ARCODEFILE_H #define ARCODEFILE_H +#include #include #include "types.h" struct ARCode { - char Name[128]; + std::string Name; bool Enabled; u32 CodeLen; u32 Code[2*64]; @@ -35,7 +36,7 @@ typedef std::list ARCodeList; struct ARCodeCat { - char Name[128]; + std::string Name; ARCodeList Codes; }; @@ -45,7 +46,7 @@ typedef std::list ARCodeCatList; class ARCodeFile { public: - ARCodeFile(const char* filename); + ARCodeFile(std::string filename); ~ARCodeFile(); bool Error; @@ -56,7 +57,7 @@ public: ARCodeCatList Categories; private: - char Filename[1024]; + std::string Filename; }; #endif // ARCODEFILE_H diff --git a/src/AREngine.cpp b/src/AREngine.cpp index ec3b70ea..eb5ab343 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -50,8 +50,6 @@ void DeInit() void Reset() { - CodeFile = nullptr; - if (NDS::ConsoleType == 1) { BusRead8 = DSi::ARM7Read8; @@ -73,6 +71,11 @@ void Reset() } +ARCodeFile* GetCodeFile() +{ + return CodeFile; +} + void SetCodeFile(ARCodeFile* file) { CodeFile = file; diff --git a/src/AREngine.h b/src/AREngine.h index c1427110..362b3207 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -28,6 +28,7 @@ bool Init(); void DeInit(); void Reset(); +ARCodeFile* GetCodeFile(); void SetCodeFile(ARCodeFile* file); void RunCheats(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d1f47dc3..7288b540 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -36,7 +36,6 @@ add_library(core STATIC melonDLDI.h NDS.cpp NDSCart.cpp - NDSCart_SRAMManager.cpp Platform.h ROMList.h FreeBIOS.h diff --git a/src/CRC32.cpp b/src/CRC32.cpp index b51b1713..eabdf33f 100644 --- a/src/CRC32.cpp +++ b/src/CRC32.cpp @@ -52,7 +52,7 @@ void _inittable() } } -u32 CRC32(u8 *data, int len) +u32 CRC32(u8 *data, int len, u32 start) { if (!tableinited) { @@ -60,7 +60,7 @@ u32 CRC32(u8 *data, int len) tableinited = true; } - u32 crc = 0xFFFFFFFF; + u32 crc = start ^ 0xFFFFFFFF; while (len--) crc = (crc >> 8) ^ crctable[(crc & 0xFF) ^ *data++]; diff --git a/src/CRC32.h b/src/CRC32.h index 2107533a..5600a637 100644 --- a/src/CRC32.h +++ b/src/CRC32.h @@ -21,6 +21,6 @@ #include "types.h" -u32 CRC32(u8* data, int len); +u32 CRC32(u8* data, int len, u32 start=0); #endif // CRC32_H diff --git a/src/DSi.cpp b/src/DSi.cpp index 9267f148..2ac1032e 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -47,8 +47,6 @@ namespace DSi { -u32 BootAddr[2]; - u16 SCFG_BIOS; u16 SCFG_Clock9; u16 SCFG_Clock7; @@ -79,16 +77,12 @@ DSi_NDMA* NDMAs[8]; DSi_SDHost* SDMMC; DSi_SDHost* SDIO; -FILE* SDMMCFile = nullptr; - u64 ConsoleID; u8 eMMC_CID[16]; -u8 ITCMInit[0x8000]; -u8 ARM7Init[0x3C00]; - void Set_SCFG_Clock9(u16 val); +void Set_SCFG_MC(u32 val); bool Init() @@ -134,8 +128,6 @@ void DeInit() delete SDMMC; delete SDIO; - - CloseDSiNAND(); } void Reset() @@ -144,21 +136,24 @@ void Reset() //NDS::ARM9->CP15Write(0x911, 0x00000020); //NDS::ARM9->CP15Write(0x100, NDS::ARM9->CP15Read(0x100) | 0x00050000); - NDS::ARM9->JumpTo(BootAddr[0]); - NDS::ARM7->JumpTo(BootAddr[1]); + NDS::MapSharedWRAM(3); NDMACnt[0] = 0; NDMACnt[1] = 0; for (int i = 0; i < 8; i++) NDMAs[i]->Reset(); - memcpy(NDS::ARM9->ITCM, ITCMInit, 0x8000); - DSi_I2C::Reset(); - DSi_AES::Reset(); DSi_DSP::Reset(); + SDMMC->CloseHandles(); + SDIO->CloseHandles(); + + LoadNAND(); + SDMMC->Reset(); SDIO->Reset(); + DSi_AES::Reset(); + SCFG_BIOS = 0x0101; // TODO: should be zero when booting from BIOS SCFG_Clock9 = 0x0187; // CHECKME SCFG_Clock7 = 0x0187; @@ -172,22 +167,85 @@ void Reset() // LCD init flag GPU::DispStat[0] |= (1<<6); GPU::DispStat[1] |= (1<<6); +} - NDS::MapSharedWRAM(3); +void DoSavestate(Savestate* file) +{ + file->Section("DSIG"); - for (u32 i = 0; i < 0x3C00; i+=4) - ARM7Write32(0x03FFC400+i, *(u32*)&ARM7Init[i]); + file->Var16(&SCFG_BIOS); + file->Var16(&SCFG_Clock9); + file->Var16(&SCFG_Clock7); + file->VarArray(&SCFG_EXT[0], sizeof(u32)*2); + file->Var32(&SCFG_MC); + file->Var16(&SCFG_RST); - u32 eaddr = 0x03FFE6E4; - ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]); - ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]); - ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]); - ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]); - ARM7Write16(eaddr+0x2C, 0x0001); - ARM7Write16(eaddr+0x2E, 0x0001); - ARM7Write16(eaddr+0x3C, 0x0100); - ARM7Write16(eaddr+0x3E, 0x40E0); - ARM7Write16(eaddr+0x42, 0x0001); + //file->VarArray(ARM9iBIOS, 0x10000); + //file->VarArray(ARM7iBIOS, 0x10000); + + if (file->Saving) + { + file->VarArray(&MBK[0][0], sizeof(u32)*8); + file->VarArray(&MBK[1][5], sizeof(u32)*3); + file->Var32(&MBK[0][8]); + } + else + { + Set_SCFG_Clock9(SCFG_Clock9); + Set_SCFG_MC(SCFG_MC); + DSi_DSP::SetRstLine(SCFG_RST & 0x0001); + + MBK[0][8] = 0; + MBK[1][8] = 0; + + u32 mbk[12]; + file->VarArray(&mbk, sizeof(u32)*12); + + MapNWRAM_A(0, mbk[0] & 0xFF); + MapNWRAM_A(1, (mbk[0] >> 8) & 0xFF); + MapNWRAM_A(2, (mbk[0] >> 16) & 0xFF); + MapNWRAM_A(3, mbk[0] >> 24); + + MapNWRAM_B(0, mbk[1] & 0xFF); + MapNWRAM_B(1, (mbk[1] >> 8) & 0xFF); + MapNWRAM_B(2, (mbk[1] >> 16) & 0xFF); + MapNWRAM_B(3, mbk[1] >> 24); + MapNWRAM_B(4, mbk[2] & 0xFF); + MapNWRAM_B(5, (mbk[2] >> 8) & 0xFF); + MapNWRAM_B(6, (mbk[2] >> 16) & 0xFF); + MapNWRAM_B(7, mbk[2] >> 24); + + MapNWRAM_C(0, mbk[3] & 0xFF); + MapNWRAM_C(1, (mbk[3] >> 8) & 0xFF); + MapNWRAM_C(2, (mbk[3] >> 16) & 0xFF); + MapNWRAM_C(3, mbk[3] >> 24); + MapNWRAM_C(4, mbk[4] & 0xFF); + MapNWRAM_C(5, (mbk[4] >> 8) & 0xFF); + MapNWRAM_C(6, (mbk[4] >> 16) & 0xFF); + MapNWRAM_C(7, mbk[4] >> 24); + + MapNWRAMRange(0, 0, mbk[5]); + MapNWRAMRange(0, 1, mbk[6]); + MapNWRAMRange(0, 2, mbk[7]); + + MapNWRAMRange(1, 0, mbk[8]); + MapNWRAMRange(1, 1, mbk[9]); + MapNWRAMRange(1, 2, mbk[10]); + + mbk[11] &= 0x00FFFF0F; + MBK[0][8] = mbk[11]; + MBK[1][8] = mbk[11]; + } + + for (int i = 0; i < 8; i++) + NDMAs[i]->DoSavestate(file); + + DSi_AES::DoSavestate(file); + DSi_Camera::DoSavestate(file); + DSi_DSP::DoSavestate(file); + DSi_I2C::DoSavestate(file); + SDMMC->DoSavestate(file); + SDIO->DoSavestate(file); } void DecryptModcryptArea(u32 offset, u32 size, u8* iv) @@ -445,24 +503,30 @@ void SetupDirectBoot() ARM9Write32(0x02FFE000+i, tmp); } - if (DSi_NAND::Init(SDMMCFile, &DSi::ARM7iBIOS[0x8308])) + FILE* nand = Platform::OpenLocalFile(Platform::GetConfigString(Platform::DSi_NANDPath), "r+b"); + if (nand) { - u8 userdata[0x1B0]; - DSi_NAND::ReadUserData(userdata); - for (u32 i = 0; i < 0x128; i+=4) - ARM9Write32(0x02000400+i, *(u32*)&userdata[0x88+i]); + if (DSi_NAND::Init(nand, &DSi::ARM7iBIOS[0x8308])) + { + u8 userdata[0x1B0]; + DSi_NAND::ReadUserData(userdata); + for (u32 i = 0; i < 0x128; i+=4) + ARM9Write32(0x02000400+i, *(u32*)&userdata[0x88+i]); - u8 hwinfoS[0xA4]; - u8 hwinfoN[0x9C]; - DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN); + u8 hwinfoS[0xA4]; + u8 hwinfoN[0x9C]; + DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN); - for (u32 i = 0; i < 0x14; i+=4) - ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); + for (u32 i = 0; i < 0x14; i+=4) + ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); - for (u32 i = 0; i < 0x18; i+=4) - ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS[0x88+i]); + for (u32 i = 0; i < 0x18; i+=4) + ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS[0x88+i]); - DSi_NAND::DeInit(); + DSi_NAND::DeInit(); + } + + fclose(nand); } u8 nwifiver = SPI_Firmware::GetNWifiVersion(); @@ -544,21 +608,22 @@ void SoftReset() NDS::ARM9->CP15Reset(); - memcpy(NDS::ARM9->ITCM, ITCMInit, 0x8000); + NDS::MapSharedWRAM(3); - DSi_AES::Reset(); // TODO: does the DSP get reset? NWRAM doesn't, so I'm assuming no // *HOWEVER*, the bootrom (which does get rerun) does remap NWRAM, and thus // the DSP most likely gets reset DSi_DSP::Reset(); + SDMMC->CloseHandles(); + SDIO->CloseHandles(); + LoadNAND(); SDMMC->Reset(); SDIO->Reset(); - NDS::ARM9->JumpTo(BootAddr[0]); - NDS::ARM7->JumpTo(BootAddr[1]); + DSi_AES::Reset(); SCFG_BIOS = 0x0101; // TODO: should be zero when booting from BIOS SCFG_Clock9 = 0x0187; // CHECKME @@ -574,22 +639,6 @@ void SoftReset() // LCD init flag GPU::DispStat[0] |= (1<<6); GPU::DispStat[1] |= (1<<6); - - NDS::MapSharedWRAM(3); - - for (u32 i = 0; i < 0x3C00; i+=4) - ARM7Write32(0x03FFC400+i, *(u32*)&ARM7Init[i]); - - u32 eaddr = 0x03FFE6E4; - ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]); - ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]); - ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]); - ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]); - ARM7Write16(eaddr+0x2C, 0x0001); - ARM7Write16(eaddr+0x2E, 0x0001); - ARM7Write16(eaddr+0x3C, 0x0100); - ARM7Write16(eaddr+0x3E, 0x40E0); - ARM7Write16(eaddr+0x42, 0x0001); } bool LoadBIOS() @@ -650,7 +699,14 @@ bool LoadNAND() { printf("Loading DSi NAND\n"); - if (!DSi_NAND::Init(SDMMCFile, &DSi::ARM7iBIOS[0x8308])) + FILE* nand = Platform::OpenLocalFile(Platform::GetConfigString(Platform::DSi_NANDPath), "r+b"); + if (!nand) + { + printf("Failed to open DSi NAND\n"); + return false; + } + + if (!DSi_NAND::Init(nand, &DSi::ARM7iBIOS[0x8308])) { printf("Failed to load DSi NAND\n"); return false; @@ -676,8 +732,8 @@ bool LoadNAND() memset(NWRAMMask, 0, sizeof(NWRAMMask)); u32 bootparams[8]; - fseek(SDMMCFile, 0x220, SEEK_SET); - fread(bootparams, 4, 8, SDMMCFile); + fseek(nand, 0x220, SEEK_SET); + fread(bootparams, 4, 8, nand); printf("ARM9: offset=%08X size=%08X RAM=%08X size_aligned=%08X\n", bootparams[0], bootparams[1], bootparams[2], bootparams[3]); @@ -690,8 +746,8 @@ bool LoadNAND() MBK[1][8] = 0; u32 mbk[12]; - fseek(SDMMCFile, 0x380, SEEK_SET); - fread(mbk, 4, 12, SDMMCFile); + fseek(nand, 0x380, SEEK_SET); + fread(mbk, 4, 12, nand); MapNWRAM_A(0, mbk[0] & 0xFF); MapNWRAM_A(1, (mbk[0] >> 8) & 0xFF); @@ -745,12 +801,12 @@ bool LoadNAND() AES_init_ctx_iv(&ctx, boot2key, boot2iv); - fseek(SDMMCFile, bootparams[0], SEEK_SET); + fseek(nand, bootparams[0], SEEK_SET); dstaddr = bootparams[2]; for (u32 i = 0; i < bootparams[3]; i += 16) { u8 data[16]; - fread(data, 16, 1, SDMMCFile); + fread(data, 16, 1, nand); for (int j = 0; j < 16; j++) tmp[j] = data[15-j]; AES_CTR_xcrypt_buffer(&ctx, tmp, 16); @@ -770,12 +826,12 @@ bool LoadNAND() AES_init_ctx_iv(&ctx, boot2key, boot2iv); - fseek(SDMMCFile, bootparams[4], SEEK_SET); + fseek(nand, bootparams[4], SEEK_SET); dstaddr = bootparams[6]; for (u32 i = 0; i < bootparams[7]; i += 16) { u8 data[16]; - fread(data, 16, 1, SDMMCFile); + fread(data, 16, 1, nand); for (int j = 0; j < 16; j++) tmp[j] = data[15-j]; AES_CTR_xcrypt_buffer(&ctx, tmp, 16); @@ -787,11 +843,6 @@ bool LoadNAND() ARM7Write32(dstaddr, *(u32*)&data[12]); dstaddr += 4; } - // repoint the CPUs to the boot2 binaries - - BootAddr[0] = bootparams[2]; - BootAddr[1] = bootparams[6]; - #define printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); } #define printhex_rev(str, size) { for (int z = (size)-1; z >= 0; z--) printf("%02X", (str)[z]); printf("\n"); } @@ -800,18 +851,36 @@ bool LoadNAND() printf("eMMC CID: "); printhex(eMMC_CID, 16); printf("Console ID: %" PRIx64 "\n", ConsoleID); - memset(ITCMInit, 0, 0x8000); - memcpy(&ITCMInit[0x4400], &ARM9iBIOS[0x87F4], 0x400); - memcpy(&ITCMInit[0x4800], &ARM9iBIOS[0x9920], 0x80); - memcpy(&ITCMInit[0x4894], &ARM9iBIOS[0x99A0], 0x1048); - memcpy(&ITCMInit[0x58DC], &ARM9iBIOS[0xA9E8], 0x1048); + u32 eaddr = 0x03FFE6E4; + ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]); + ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]); + ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]); + ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]); + ARM7Write16(eaddr+0x2C, 0x0001); + ARM7Write16(eaddr+0x2E, 0x0001); + ARM7Write16(eaddr+0x3C, 0x0100); + ARM7Write16(eaddr+0x3E, 0x40E0); + ARM7Write16(eaddr+0x42, 0x0001); + memcpy(&NDS::ARM9->ITCM[0x4400], &ARM9iBIOS[0x87F4], 0x400); + memcpy(&NDS::ARM9->ITCM[0x4800], &ARM9iBIOS[0x9920], 0x80); + memcpy(&NDS::ARM9->ITCM[0x4894], &ARM9iBIOS[0x99A0], 0x1048); + memcpy(&NDS::ARM9->ITCM[0x58DC], &ARM9iBIOS[0xA9E8], 0x1048); + + u8 ARM7Init[0x3C00]; memset(ARM7Init, 0, 0x3C00); memcpy(&ARM7Init[0x0000], &ARM7iBIOS[0x8188], 0x200); memcpy(&ARM7Init[0x0200], &ARM7iBIOS[0xB5D8], 0x40); memcpy(&ARM7Init[0x0254], &ARM7iBIOS[0xC6D0], 0x1048); memcpy(&ARM7Init[0x129C], &ARM7iBIOS[0xD718], 0x1048); + for (u32 i = 0; i < 0x3C00; i+=4) + ARM7Write32(0x03FFC400+i, *(u32*)&ARM7Init[i]); + + // repoint the CPUs to the boot2 binaries + NDS::ARM9->JumpTo(bootparams[2]); + NDS::ARM7->JumpTo(bootparams[6]); + DSi_NAND::PatchUserData(); DSi_NAND::DeInit(); @@ -819,12 +888,6 @@ bool LoadNAND() return true; } -void CloseDSiNAND() -{ - if (DSi::SDMMCFile) - fclose(DSi::SDMMCFile); - DSi::SDMMCFile = nullptr; -} void RunNDMAs(u32 cpu) { diff --git a/src/DSi.h b/src/DSi.h index 12365277..93f46eb1 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -39,8 +39,6 @@ extern u64 ConsoleID; extern DSi_SDHost* SDMMC; extern DSi_SDHost* SDIO; -extern FILE* SDMMCFile; - const u32 NWRAMSize = 0x40000; extern u8* NWRAM_A; @@ -59,14 +57,14 @@ bool Init(); void DeInit(); void Reset(); +void DoSavestate(Savestate* file); + void SetupDirectBoot(); void SoftReset(); bool LoadBIOS(); bool LoadNAND(); -void CloseDSiNAND(); - void RunNDMAs(u32 cpu); void StallNDMAs(); bool NDMAsInMode(u32 cpu, u32 mode); diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 02e4f1c0..ec7b0e6c 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -155,6 +155,43 @@ void Reset() *(u32*)&KeyY[3][8] = 0x202DDD1D; } +void DoSavestate(Savestate* file) +{ + file->Section("AESi"); + + file->Var32(&Cnt); + + file->Var32(&BlkCnt); + file->Var32(&RemExtra); + file->Var32(&RemBlocks); + + file->Bool32(&OutputFlush); + + file->Var32(&InputDMASize); + file->Var32(&OutputDMASize); + file->Var32(&AESMode); + + InputFIFO.DoSavestate(file); + OutputFIFO.DoSavestate(file); + + file->VarArray(IV, 16); + + file->VarArray(MAC, 16); + + file->VarArray(KeyNormal, 4*16); + file->VarArray(KeyX, 4*16); + file->VarArray(KeyY, 4*16); + + file->VarArray(CurKey, 16); + file->VarArray(CurMAC, 16); + + file->VarArray(OutputMAC, 16); + file->Bool32(&OutputMACDue); + + file->VarArray(Ctx.RoundKey, AES_keyExpSize); + file->VarArray(Ctx.Iv, AES_BLOCKLEN); +} + void ProcessBlock_CCM_Extra() { diff --git a/src/DSi_AES.h b/src/DSi_AES.h index 27f8e642..021879eb 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -30,6 +30,8 @@ bool Init(); void DeInit(); void Reset(); +void DoSavestate(Savestate* file); + u32 ReadCnt(); void WriteCnt(u32 val); void WriteBlkCnt(u32 val); diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index d8cc3350..10acdcae 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -70,6 +70,21 @@ void DSi_Camera::Reset() NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0); } +void DSi_Camera::DoSavestate(Savestate* file) +{ + file->Section("CAMi"); + + file->Var16(&ModuleCnt); + file->Var16(&Cnt); + + file->VarArray(FrameBuffer, sizeof(FrameBuffer)); + file->Var32(&TransferPos); + file->Var32(&FrameLength); + + DSi_Camera0->DoCamSavestate(file); + DSi_Camera1->DoCamSavestate(file); +} + void DSi_Camera::IRQ(u32 param) { @@ -150,7 +165,29 @@ DSi_Camera::DSi_Camera(u32 num) DSi_Camera::~DSi_Camera() { - // +} + +void DSi_Camera::DoCamSavestate(Savestate* file) +{ + char magic[5] = "CAMx"; + magic[3] = '0' + Num; + file->Section(magic); + + file->Var32(&DataPos); + file->Var32(&RegAddr); + file->Var16(&RegData); + + file->Var16(&PLLDiv); + file->Var16(&PLLPDiv); + file->Var16(&PLLCnt); + file->Var16(&ClocksCnt); + file->Var16(&StandbyCnt); + file->Var16(&MiscCnt); + + file->Var16(&MCUAddr); + // TODO: MCUData?? + + file->VarArray(MCURegs, 0x8000); } void DSi_Camera::ResetCam() diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index 3f39e3a5..5dc037d2 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -28,6 +28,8 @@ public: static void DeInit(); static void Reset(); + static void DoSavestate(Savestate* file); + static void IRQ(u32 param); static void RequestFrame(u32 cam); @@ -36,6 +38,8 @@ public: DSi_Camera(u32 num); ~DSi_Camera(); + void DoCamSavestate(Savestate* file); + void ResetCam(); bool IsActivated(); diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index 328f01d8..c889aff9 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -587,6 +587,8 @@ void DoSavestate(Savestate* file) file->Var16(&DSP_REP[1]); file->Var16(&DSP_REP[2]); file->Var8((u8*)&SCFG_RST); + + // TODO: save the Teakra state!!! } } diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h index f5264b46..ade88bac 100644 --- a/src/DSi_DSP.h +++ b/src/DSi_DSP.h @@ -41,10 +41,10 @@ bool Init(); void DeInit(); void Reset(); -// TODO: needs to be called! -// however, no DSi savestate stuff seems to be actually implemented?! void DoSavestate(Savestate* file); +void DSPCatchUpU32(u32 _); + // SCFG_RST bit0 bool IsRstReleased(); void SetRstLine(bool release); diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index 6f5f2e58..9e865b12 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -72,6 +72,14 @@ void Reset() Registers[0x81] = 0x64; } +void DoSavestate(Savestate* file) +{ + file->Section("I2BP"); + + file->VarArray(Registers, 0x100); + file->Var32(&CurPos); +} + u8 GetBootFlag() { return Registers[0x70]; } void Start() @@ -169,6 +177,18 @@ void Reset() DSi_Camera::Reset(); } +void DoSavestate(Savestate* file) +{ + file->Section("I2Ci"); + + file->Var8(&Cnt); + file->Var8(&Data); + file->Var32(&Device); + + DSi_BPTWL::DoSavestate(file); + // cameras are savestated from the DSi_Camera module +} + void WriteCnt(u8 val) { //printf("I2C: write CNT %02X, %08X\n", val, NDS::GetPC(1)); diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index c0643667..55313999 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -34,7 +34,7 @@ extern u8 Cnt; bool Init(); void DeInit(); void Reset(); -//void DoSavestate(Savestate* file); +void DoSavestate(Savestate* file); void WriteCnt(u8 val); diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index 37d50184..1c3b94a3 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -63,7 +63,31 @@ void DSi_NDMA::Reset() void DSi_NDMA::DoSavestate(Savestate* file) { - // TODO! + char magic[5] = "NDMx"; + magic[3] = '0' + Num + (CPU*4); + file->Section(magic); + + file->Var32(&SrcAddr); + file->Var32(&DstAddr); + file->Var32(&TotalLength); + file->Var32(&BlockLength); + file->Var32(&SubblockTimer); + file->Var32(&FillData); + file->Var32(&Cnt); + + file->Var32(&StartMode); + file->Var32(&CurSrcAddr); + file->Var32(&CurDstAddr); + file->Var32(&SubblockLength); + file->Var32(&RemCount); + file->Var32(&IterCount); + file->Var32(&TotalRemCount); + file->Var32(&SrcAddrInc); + file->Var32(&DstAddrInc); + + file->Var32(&Running); + file->Bool32(&InProgress); + file->Bool32(&IsGXFIFODMA); } void DSi_NDMA::WriteCnt(u32 val) diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 57bd93c3..fde48ecf 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -223,6 +223,45 @@ void DSi_NWifi::Reset() NDS::CancelEvent(NDS::Event_DSi_NWifi); } +void DSi_NWifi::DoSavestate(Savestate* file) +{ + file->Section("NWFi"); + + for (int i = 0; i < 9; i++) + Mailbox[i].DoSavestate(file); + + file->Var8(&F0_IRQEnable); + file->Var8(&F0_IRQStatus); + + file->Var8(&F1_IRQEnable); + file->Var8(&F1_IRQEnable_CPU); + file->Var8(&F1_IRQEnable_Error); + file->Var8(&F1_IRQEnable_Counter); + file->Var8(&F1_IRQStatus); + file->Var8(&F1_IRQStatus_CPU); + file->Var8(&F1_IRQStatus_Error); + file->Var8(&F1_IRQStatus_Counter); + + file->Var32(&WindowData); + file->Var32(&WindowReadAddr); + file->Var32(&WindowWriteAddr); + + file->Var32(&ROMID); + file->Var32(&ChipID); + file->Var32(&HostIntAddr); + + file->VarArray(EEPROM, 0x400); + file->Var32(&EEPROMReady); + + file->Var32(&BootPhase); + + file->Var32(&ErrorMask); + file->Var32(&ScanTimer); + + file->Var64(&BeaconTimer); + file->Var32(&ConnectionStatus); +} + // CHECKME // can IRQ status bits be set when the corresponding IRQs are disabled in the enable register? diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h index da6597d7..ef337fcd 100644 --- a/src/DSi_NWifi.h +++ b/src/DSi_NWifi.h @@ -30,6 +30,8 @@ public: void Reset(); + void DoSavestate(Savestate* file); + void SendCMD(u8 cmd, u32 param); void SendACMD(u8 cmd, u32 param); diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 4c2b0855..6c5979b0 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -51,8 +51,8 @@ DSi_SDHost::DSi_SDHost(u32 num) { Num = num; - Ports[0] = NULL; - Ports[1] = NULL; + Ports[0] = nullptr; + Ports[1] = nullptr; } DSi_SDHost::~DSi_SDHost() @@ -61,6 +61,14 @@ DSi_SDHost::~DSi_SDHost() if (Ports[1]) delete Ports[1]; } +void DSi_SDHost::CloseHandles() +{ + if (Ports[0]) delete Ports[0]; + if (Ports[1]) delete Ports[1]; + Ports[0] = nullptr; + Ports[1] = nullptr; +} + void DSi_SDHost::Reset() { if (Num == 0) @@ -101,10 +109,7 @@ void DSi_SDHost::Reset() TXReq = false; - if (Ports[0]) delete Ports[0]; - if (Ports[1]) delete Ports[1]; - Ports[0] = nullptr; - Ports[1] = nullptr; + CloseHandles(); if (Num == 0) { @@ -131,7 +136,7 @@ void DSi_SDHost::Reset() else sd = nullptr; - mmc = new DSi_MMCStorage(this, true, DSi::SDMMCFile); + mmc = new DSi_MMCStorage(this, true, Platform::GetConfigString(Platform::DSi_NANDPath)); mmc->SetCID(DSi::eMMC_CID); Ports[0] = sd; @@ -150,7 +155,41 @@ void DSi_SDHost::Reset() void DSi_SDHost::DoSavestate(Savestate* file) { - // TODO! + file->Section(Num ? "SDIO" : "SDMM"); + + file->Var16(&PortSelect); + file->Var16(&SoftReset); + file->Var16(&SDClock); + file->Var16(&SDOption); + + file->Var32(&IRQStatus); + file->Var32(&IRQMask); + + file->Var16(&CardIRQStatus); + file->Var16(&CardIRQMask); + file->Var16(&CardIRQCtl); + + file->Var16(&DataCtl); + file->Var16(&Data32IRQ); + file->Var32(&DataMode); + file->Var16(&BlockCount16); + file->Var16(&BlockCount32); + file->Var16(&BlockCountInternal); + file->Var16(&BlockLen16); + file->Var16(&BlockLen32); + file->Var16(&StopAction); + + file->Var16(&Command); + file->Var32(&Param); + file->VarArray(ResponseBuffer, 8); + + file->Var32(&CurFIFO); + DataFIFO[0].DoSavestate(file); + DataFIFO[1].DoSavestate(file); + DataFIFO32.DoSavestate(file); + + if (Ports[0]) Ports[0]->DoSavestate(file); + if (Ports[1]) Ports[1]->DoSavestate(file); } @@ -727,12 +766,15 @@ void DSi_SDHost::CheckSwapFIFO() #define MMC_DESC (Internal?"NAND":"SDcard") -DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file) +DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename) : DSi_SDDevice(host) { Internal = internal; - File = file; + File = Platform::OpenLocalFile(filename, "r+b"); + SD = nullptr; + + ReadOnly = false; } DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename, u64 size, bool readonly, std::string sourcedir) @@ -754,6 +796,10 @@ DSi_MMCStorage::~DSi_MMCStorage() SD->Close(); delete SD; } + if (File) + { + fclose(File); + } } void DSi_MMCStorage::Reset() @@ -781,6 +827,26 @@ void DSi_MMCStorage::Reset() RWCommand = 0; } +void DSi_MMCStorage::DoSavestate(Savestate* file) +{ + file->Section(Internal ? "NAND" : "SDCR"); + + file->VarArray(CID, 16); + file->VarArray(CSD, 16); + + file->Var32(&CSR); + file->Var32(&OCR); + file->Var32(&RCA); + file->VarArray(SCR, 8); + file->VarArray(SSR, 64); + + file->Var32(&BlockSize); + file->Var64(&RWAddress); + file->Var32(&RWCommand); + + // TODO: what about the file contents? +} + void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) { if (CSR & (1<<5)) diff --git a/src/DSi_SD.h b/src/DSi_SD.h index 1c0f7ce1..2e63a631 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -33,6 +33,7 @@ public: DSi_SDHost(u32 num); ~DSi_SDHost(); + void CloseHandles(); void Reset(); void DoSavestate(Savestate* file); @@ -108,6 +109,8 @@ public: virtual void Reset() = 0; + virtual void DoSavestate(Savestate* file) = 0; + virtual void SendCMD(u8 cmd, u32 param) = 0; virtual void ContinueTransfer() = 0; @@ -122,12 +125,14 @@ protected: class DSi_MMCStorage : public DSi_SDDevice { public: - DSi_MMCStorage(DSi_SDHost* host, bool internal, FILE* file); + DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename); DSi_MMCStorage(DSi_SDHost* host, bool internal, std::string filename, u64 size, bool readonly, std::string sourcedir); ~DSi_MMCStorage(); void Reset(); + void DoSavestate(Savestate* file); + void SetCID(u8* cid) { memcpy(CID, cid, 16); } void SendCMD(u8 cmd, u32 param); diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index 061feb1a..134d9def 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -71,14 +71,15 @@ void Reset() void DoSavestate(Savestate* file) { - /*file->Section("SPTi"); + file->Section("SPTi"); file->Var32(&DataPos); - file->Var8(&ControlByte); + file->Var8(&Index); + file->Var8(&Bank); file->Var8(&Data); - file->Var16(&ConvResult);*/ - // TODO!! + file->VarArray(Bank3Regs, 0x80); + file->Var8(&TSCMode); } void SetMode(u8 mode) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index e5b6e4fc..ca092f4f 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -18,6 +18,7 @@ #include #include +#include "NDS.h" #include "GBACart.h" #include "CRC32.h" #include "Platform.h" @@ -42,7 +43,6 @@ const char SOLAR_SENSOR_GAMECODES[10][5] = bool CartInserted; u8* CartROM; u32 CartROMSize; -u32 CartCRC; u32 CartID; CartCommon* Cart; @@ -58,16 +58,20 @@ CartCommon::~CartCommon() { } +void CartCommon::Reset() +{ +} + void CartCommon::DoSavestate(Savestate* file) { file->Section("GBCS"); } -void CartCommon::LoadSave(const char* path, u32 type) +void CartCommon::SetupSave(u32 type) { } -void CartCommon::RelocateSave(const char* path, bool write) +void CartCommon::LoadSave(const u8* savedata, u32 savelen) { } @@ -99,22 +103,32 @@ CartGame::CartGame(u8* rom, u32 len) : CartCommon() { ROM = rom; ROMLength = len; - - memset(&GPIO, 0, sizeof(GPIO)); - - SRAM = nullptr; - SRAMFile = nullptr; - SRAMLength = 0; - SRAMType = S_NULL; - SRAMFlashState = {}; } CartGame::~CartGame() { - if (SRAMFile) fclose(SRAMFile); if (SRAM) delete[] SRAM; } +u32 CartGame::Checksum() +{ + u32 crc = CRC32(ROM, 0xC0, 0); + + // TODO: hash more contents? + + return crc; +} + +void CartGame::Reset() +{ + memset(&GPIO, 0, sizeof(GPIO)); + + SRAM = nullptr; + SRAMLength = 0; + SRAMType = S_NULL; + SRAMFlashState = {}; +} + void CartGame::DoSavestate(Savestate* file) { CartCommon::DoSavestate(file); @@ -123,8 +137,6 @@ void CartGame::DoSavestate(Savestate* file) file->Var16(&GPIO.data); file->Var16(&GPIO.direction); - // logic mostly copied from NDSCart_SRAM - u32 oldlen = SRAMLength; file->Var32(&SRAMLength); @@ -133,6 +145,7 @@ void CartGame::DoSavestate(Savestate* file) { // reallocate save memory if (oldlen) delete[] SRAM; + SRAM = nullptr; if (SRAMLength) SRAM = new u8[SRAMLength]; } if (SRAMLength) @@ -144,9 +157,7 @@ void CartGame::DoSavestate(Savestate* file) { // no save data, clear the current state SRAMType = SaveType::S_NULL; - if (SRAMFile) fclose(SRAMFile); SRAM = nullptr; - SRAMFile = nullptr; return; } @@ -158,27 +169,24 @@ void CartGame::DoSavestate(Savestate* file) file->Var8(&SRAMFlashState.state); file->Var8((u8*)&SRAMType); + + if ((!file->Saving) && SRAM) + Platform::WriteGBASave(SRAM, SRAMLength, 0, SRAMLength); } -void CartGame::LoadSave(const char* path, u32 type) +void CartGame::SetupSave(u32 type) { if (SRAM) delete[] SRAM; + SRAM = nullptr; - strncpy(SRAMPath, path, 1023); - SRAMPath[1023] = '\0'; - SRAMLength = 0; + // TODO: have type be determined from some list, like in NDSCart + // and not this gross hack!! + SRAMLength = type; - FILE* f = Platform::OpenFile(SRAMPath, "r+b"); - if (f) + if (SRAMLength) { - fseek(f, 0, SEEK_END); - SRAMLength = (u32)ftell(f); SRAM = new u8[SRAMLength]; - - fseek(f, 0, SEEK_SET); - fread(SRAM, SRAMLength, 1, f); - - SRAMFile = f; + memset(SRAM, 0xFF, SRAMLength); } switch (SRAMLength) @@ -219,26 +227,13 @@ void CartGame::LoadSave(const char* path, u32 type) } } -void CartGame::RelocateSave(const char* path, bool write) +void CartGame::LoadSave(const u8* savedata, u32 savelen) { - if (!write) - { - LoadSave(path, 0); // lazy - return; - } + if (!SRAM) return; - strncpy(SRAMPath, path, 1023); - SRAMPath[1023] = '\0'; - - FILE *f = Platform::OpenFile(path, "r+b"); - if (!f) - { - printf("GBACart_SRAM::RelocateSave: failed to create new file. fuck\n"); - return; - } - - SRAMFile = f; - fwrite(SRAM, SRAMLength, 1, SRAMFile); + u32 len = std::min(savelen, SRAMLength); + memcpy(SRAM, savedata, len); + Platform::WriteGBASave(savedata, len, 0, len); } u16 CartGame::ROMRead(u32 addr) @@ -469,11 +464,7 @@ void CartGame::SRAMWrite_FLASH(u32 addr, u8 val) u32 start_addr = addr + 0x10000 * SRAMFlashState.bank; memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); - if (SRAMFile) - { - fseek(SRAMFile, start_addr, SEEK_SET); - fwrite((u8*)&SRAM[start_addr], 1, 0x1000, SRAMFile); - } + Platform::WriteGBASave(SRAM, SRAMLength, start_addr, 0x1000); } SRAMFlashState.state = 0; SRAMFlashState.cmd = 0; @@ -531,11 +522,8 @@ void CartGame::SRAMWrite_SRAM(u32 addr, u8 val) { *(u8*)&SRAM[addr] = val; - if (SRAMFile) - { - fseek(SRAMFile, addr, SEEK_SET); - fwrite((u8*)&SRAM[addr], 1, 1, SRAMFile); - } + // TODO: optimize this!! + Platform::WriteGBASave(SRAM, SRAMLength, addr, 1); } } @@ -544,16 +532,20 @@ const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 1 CartGameSolarSensor::CartGameSolarSensor(u8* rom, u32 len) : CartGame(rom, len) { - LightEdge = false; - LightCounter = 0; - LightSample = 0xFF; - LightLevel = 0; } CartGameSolarSensor::~CartGameSolarSensor() { } +void CartGameSolarSensor::Reset() +{ + LightEdge = false; + LightCounter = 0; + LightSample = 0xFF; + LightLevel = 0; +} + void CartGameSolarSensor::DoSavestate(Savestate* file) { CartGame::DoSavestate(file); @@ -608,6 +600,86 @@ void CartGameSolarSensor::ProcessGPIO() } +CartRAMExpansion::CartRAMExpansion() : CartCommon() +{ +} + +CartRAMExpansion::~CartRAMExpansion() +{ +} + +void CartRAMExpansion::Reset() +{ + memset(RAM, 0xFF, sizeof(RAM)); + RAMEnable = 1; +} + +void CartRAMExpansion::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + file->VarArray(RAM, sizeof(RAM)); + file->Var16(&RAMEnable); +} + +u16 CartRAMExpansion::ROMRead(u32 addr) +{ + addr &= 0x01FFFFFF; + + if (addr < 0x01000000) + { + switch (addr) + { + case 0xB0: return 0xFFFF; + case 0xB2: return 0x0000; + case 0xB4: return 0x2400; + case 0xB6: return 0x2424; + case 0xB8: return 0xFFFF; + case 0xBA: return 0xFFFF; + case 0xBC: return 0xFFFF; + case 0xBE: return 0x7FFF; + + case 0x1FFFC: return 0xFFFF; + case 0x1FFFE: return 0x7FFF; + + case 0x240000: return RAMEnable; + case 0x240002: return 0x0000; + } + + return 0xFFFF; + } + else if (addr < 0x01800000) + { + if (!RAMEnable) return 0xFFFF; + + return *(u16*)&RAM[addr & 0x7FFFFF]; + } + + return 0xFFFF; +} + +void CartRAMExpansion::ROMWrite(u32 addr, u16 val) +{ + addr &= 0x01FFFFFF; + + if (addr < 0x01000000) + { + switch (addr) + { + case 0x240000: + RAMEnable = val & 0x0001; + return; + } + } + else if (addr < 0x01800000) + { + if (!RAMEnable) return; + + *(u16*)&RAM[addr & 0x7FFFFF] = val; + } +} + + bool Init() { CartROM = nullptr; @@ -626,86 +698,65 @@ void DeInit() void Reset() { - // Do not reset cartridge ROM. - // Prefer keeping the inserted cartridge on reset. - // This allows resetting a DS game without losing GBA state, - // and resetting to firmware without the slot being emptied. - // The Stop function will clear the cartridge state via Eject(). - - // OpenBusDecay doesn't need to be reset, either, as it will be set - // through NDS::SetGBASlotTimings(). -} - -void Eject() -{ - if (CartROM) delete[] CartROM; - - CartInserted = false; - CartROM = NULL; - CartROMSize = 0; - CartCRC = 0; - CartID = 0; - - if (Cart) delete Cart; - Cart = nullptr; - - Reset(); + if (Cart) Cart->Reset(); } void DoSavestate(Savestate* file) { file->Section("GBAC"); // Game Boy Advance Cartridge - // logic mostly copied from NDSCart + // little state here + // no need to save OpenBusDecay, it will be set later - // first we need to reload the cart itself, - // since unlike with DS, it's not loaded in advance - - file->Var32(&CartROMSize); - if (!CartROMSize) // no GBA cartridge state? nothing to do here + u32 carttype = 0; + u32 cartchk = 0; + if (Cart) { - // do eject the cartridge if something is inserted - Eject(); - return; + carttype = Cart->Type(); + cartchk = Cart->Checksum(); } - u32 oldCRC = CartCRC; - file->Var32(&CartCRC); - - if (CartCRC != oldCRC) + if (file->Saving) { - // delete and reallocate ROM so that it is zero-padded to its full length - if (CartROM) delete[] CartROM; - CartROM = new u8[CartROMSize]; + file->Var32(&carttype); + file->Var32(&cartchk); } + else + { + u32 savetype; + file->Var32(&savetype); + if (savetype != carttype) return; - // only save/load the cartridge header - // - // GBA connectivity on DS mainly involves identifying the title currently - // inserted, reading save data, and issuing commands intercepted here - // (e.g. solar sensor signals). we don't know of any case where GBA ROM is - // read directly from DS software. therefore, it is more practical, both - // from the development and user experience perspectives, to avoid dealing - // with file dependencies, and store a small portion of ROM data that should - // satisfy the needs of all known software that reads from the GBA slot. - // - // note: in case of a state load, only the cartridge header is restored, but - // the rest of the ROM data is only cleared (zero-initialized) if the CRC - // differs. Therefore, loading the GBA cartridge associated with the save state - // in advance will maintain access to the full ROM contents. - file->VarArray(CartROM, 192); - - CartInserted = true; // known, because CartROMSize > 0 - file->Var32(&CartCRC); - file->Var32(&CartID); - - // now do the rest + u32 savechk; + file->Var32(&savechk); + if (savechk != cartchk) return; + } if (Cart) Cart->DoSavestate(file); } -void LoadROMCommon(const char *sram) +bool LoadROM(const u8* romdata, u32 romlen) { + if (CartInserted) + EjectCart(); + + CartROMSize = 0x200; + while (CartROMSize < romlen) + CartROMSize <<= 1; + + try + { + CartROM = new u8[CartROMSize]; + } + catch (const std::bad_alloc& e) + { + printf("GBACart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize); + return false; + } + + memset(CartROM, 0, CartROMSize); + memcpy(CartROM, romdata, romlen); + char gamecode[5] = { '\0' }; memcpy(&gamecode, CartROM + 0xAC, 4); printf("GBA game code: %s\n", gamecode); @@ -722,9 +773,6 @@ void LoadROMCommon(const char *sram) printf("GBA solar sensor support detected!\n"); } - CartCRC = CRC32(CartROM, CartROMSize); - printf("GBA ROM CRC32: %08X\n", CartCRC); - CartInserted = true; if (solarsensor) @@ -732,61 +780,61 @@ void LoadROMCommon(const char *sram) else Cart = new CartGame(CartROM, CartROMSize); + if (Cart) + Cart->Reset(); + // save - printf("GBA save file: %s\n", sram); + //printf("GBA save file: %s\n", sram); // TODO: have a list of sorts like in NDSCart? to determine the savemem type - if (Cart) Cart->LoadSave(sram, 0); -} + //if (Cart) Cart->LoadSave(sram, 0); -bool LoadROM(const char* path, const char* sram) -{ - FILE* f = Platform::OpenFile(path, "rb"); - if (!f) - { - return false; - } - - if (CartInserted) - { - Reset(); - } - - fseek(f, 0, SEEK_END); - u32 len = (u32)ftell(f); - - CartROMSize = 0x200; - while (CartROMSize < len) - CartROMSize <<= 1; - - CartROM = new u8[CartROMSize]; - memset(CartROM, 0, CartROMSize); - fseek(f, 0, SEEK_SET); - fread(CartROM, 1, len, f); - fclose(f); - - LoadROMCommon(sram); + // TODO: setup cart save here! from a list or something return true; } -bool LoadROM(const u8* romdata, u32 filelength, const char *sram) +void LoadSave(const u8* savedata, u32 savelen) { - CartROMSize = 0x200; - while (CartROMSize < filelength) - CartROMSize <<= 1; + if (Cart) + { + // gross hack + Cart->SetupSave(savelen); - CartROM = new u8[CartROMSize]; - memcpy(CartROM, romdata, filelength); - - LoadROMCommon(sram); - - return true; + Cart->LoadSave(savedata, savelen); + } } -void RelocateSave(const char* path, bool write) +void LoadAddon(int type) { - if (Cart) Cart->RelocateSave(path, write); + CartROMSize = 0; + CartROM = nullptr; + + switch (type) + { + case NDS::GBAAddon_RAMExpansion: + Cart = new CartRAMExpansion(); + break; + + default: + printf("GBACart: !! invalid addon type %d\n", type); + return; + } + + CartInserted = true; +} + +void EjectCart() +{ + if (Cart) delete Cart; + Cart = nullptr; + + if (CartROM) delete[] CartROM; + + CartInserted = false; + CartROM = nullptr; + CartROMSize = 0; + CartID = 0; } diff --git a/src/GBACart.h b/src/GBACart.h index 8698e257..13c6100c 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -32,10 +32,15 @@ public: CartCommon(); virtual ~CartCommon(); + virtual u32 Type() { return 0x001; } + virtual u32 Checksum() { return 0; } + + virtual void Reset(); + virtual void DoSavestate(Savestate* file); - virtual void LoadSave(const char* path, u32 type); - virtual void RelocateSave(const char* path, bool write); + virtual void SetupSave(u32 type); + virtual void LoadSave(const u8* savedata, u32 savelen); virtual int SetInput(int num, bool pressed); @@ -53,10 +58,15 @@ public: CartGame(u8* rom, u32 len); virtual ~CartGame() override; + virtual u32 Type() override { return 0x101; } + virtual u32 Checksum() override; + + virtual void Reset() override; + virtual void DoSavestate(Savestate* file) override; - virtual void LoadSave(const char* path, u32 type) override; - virtual void RelocateSave(const char* path, bool write) override; + virtual void SetupSave(u32 type) override; + virtual void LoadSave(const u8* savedata, u32 savelen) override; virtual u16 ROMRead(u32 addr) override; virtual void ROMWrite(u32 addr, u16 val) override; @@ -107,11 +117,8 @@ protected: } SRAMFlashState; u8* SRAM; - FILE* SRAMFile; u32 SRAMLength; SaveType SRAMType; - - char SRAMPath[1024]; }; // CartGameSolarSensor -- Boktai game cart @@ -121,6 +128,10 @@ public: CartGameSolarSensor(u8* rom, u32 len); virtual ~CartGameSolarSensor() override; + virtual u32 Type() override { return 0x102; } + + virtual void Reset() override; + virtual void DoSavestate(Savestate* file) override; virtual int SetInput(int num, bool pressed) override; @@ -136,6 +147,27 @@ private: u8 LightLevel; }; +// CartRAMExpansion -- RAM expansion cart (DS browser, ...) +class CartRAMExpansion : public CartCommon +{ +public: + CartRAMExpansion(); + ~CartRAMExpansion() override; + + virtual u32 Type() override { return 0x201; } + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) override; + void ROMWrite(u32 addr, u16 val) override; + +private: + u8 RAM[0x800000]; + u16 RAMEnable; +}; + // possible inputs for GBA carts that might accept user input enum { @@ -146,17 +178,23 @@ enum extern bool CartInserted; extern u8* CartROM; extern u32 CartROMSize; -extern u32 CartCRC; bool Init(); void DeInit(); void Reset(); -void Eject(); void DoSavestate(Savestate* file); -bool LoadROM(const char* path, const char* sram); -bool LoadROM(const u8* romdata, u32 filelength, const char *sram); -void RelocateSave(const char* path, bool write); + +bool LoadROM(const u8* romdata, u32 romlen); +void LoadSave(const u8* savedata, u32 savelen); + +void LoadAddon(int type); + +void EjectCart(); + +//bool LoadROM(const char* path, const char* sram); +//bool LoadROM(const u8* romdata, u32 filelength, const char *sram); +//void RelocateSave(const char* path, bool write); // TODO: make more flexible, support nonbinary inputs int SetInput(int num, bool pressed); diff --git a/src/NDS.cpp b/src/NDS.cpp index e8093137..b5c8ee96 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -32,7 +32,6 @@ #include "Wifi.h" #include "AREngine.h" #include "Platform.h" -#include "NDSCart_SRAMManager.h" #include "FreeBIOS.h" #ifdef JIT_ENABLED @@ -42,6 +41,9 @@ #include "DSi.h" #include "DSi_SPI_TSC.h" +#include "DSi_NWifi.h" +#include "DSi_Camera.h" +#include "DSi_DSP.h" namespace NDS @@ -200,7 +202,6 @@ bool Init() DMAs[6] = new DMA(1, 2); DMAs[7] = new DMA(1, 3); - if (!NDSCart_SRAMManager::Init()) return false; if (!NDSCart::Init()) return false; if (!GBACart::Init()) return false; if (!GPU::Init()) return false; @@ -228,7 +229,6 @@ void DeInit() for (int i = 0; i < 8; i++) delete DMAs[i]; - NDSCart_SRAMManager::DeInit(); NDSCart::DeInit(); GBACart::DeInit(); GPU::DeInit(); @@ -353,7 +353,28 @@ void InitTimings() // handled later: GBA slot, wifi } -void SetupDirectBoot() +bool NeedsDirectBoot() +{ + if (ConsoleType == 1) + { + // for now, DSi mode requires original BIOS/NAND + return false; + } + else + { + // internal BIOS does not support direct boot + if (!Platform::GetConfigBool(Platform::ExternalBIOSEnable)) + return true; + + // DSi/3DS firmwares aren't bootable + if (SPI_Firmware::GetFirmwareLength() == 0x20000) + return true; + + return false; + } +} + +void SetupDirectBoot(std::string romname) { if (ConsoleType == 1) { @@ -444,6 +465,8 @@ void SetupDirectBoot() ARM9->CP15Write(0x911, 0x00000020); } + NDSCart::SetupDirectBoot(romname); + ARM9->R[12] = NDSCart::Header.ARM9EntryAddress; ARM9->R[13] = 0x03002F7C; ARM9->R[14] = NDSCart::Header.ARM9EntryAddress; @@ -542,7 +565,6 @@ void Reset() if (ConsoleType == 1) { DSi::LoadBIOS(); - DSi::LoadNAND(); ARM9ClockShift = 2; MainRAMMask = 0xFFFFFF; @@ -658,6 +680,11 @@ void Reset() AREngine::Reset(); } +void Start() +{ + Running = true; +} + void Stop() { printf("Stopping: shutdown\n"); @@ -692,7 +719,14 @@ bool DoSavestate_Scheduler(Savestate* file) DivDone, SqrtDone, - NULL + DSi_SDHost::FinishRX, + DSi_SDHost::FinishTX, + DSi_NWifi::MSTimer, + DSi_Camera::IRQ, + DSi_Camera::Transfer, + DSi_DSP::DSPCatchUpU32, + + nullptr }; int len = Event_MAX; @@ -702,7 +736,7 @@ bool DoSavestate_Scheduler(Savestate* file) { SchedEvent* evt = &SchedList[i]; - u32 funcid = -1; + u32 funcid = 0xFFFFFFFF; if (evt->Func) { for (int j = 0; eventfuncs[j]; j++) @@ -749,7 +783,7 @@ bool DoSavestate_Scheduler(Savestate* file) evt->Func = eventfuncs[funcid]; } else - evt->Func = NULL; + evt->Func = nullptr; file->Var64(&evt->Timestamp); file->Var32(&evt->Param); @@ -763,15 +797,26 @@ bool DoSavestate(Savestate* file) { file->Section("NDSG"); - // TODO: - // * do something for bool's (sizeof=1) - // * do something for 'loading DSi-mode savestate in DS mode' and vice-versa - // * add IE2/IF2 there + if (file->Saving) + { + u32 console = ConsoleType; + file->Var32(&console); + } + else + { + u32 console; + file->Var32(&console); + if (console != ConsoleType) + return false; + } - file->VarArray(MainRAM, 0x400000); - file->VarArray(SharedWRAM, 0x8000); + file->VarArray(MainRAM, MainRAMMaxSize); + file->VarArray(SharedWRAM, SharedWRAMSize); file->VarArray(ARM7WRAM, ARM7WRAMSize); + //file->VarArray(ARM9BIOS, 0x1000); + //file->VarArray(ARM7BIOS, 0x4000); + file->VarArray(ExMemCnt, 2*sizeof(u16)); file->VarArray(ROMSeed0, 2*8); file->VarArray(ROMSeed1, 2*8); @@ -781,6 +826,8 @@ bool DoSavestate(Savestate* file) file->VarArray(IME, 2*sizeof(u32)); file->VarArray(IE, 2*sizeof(u32)); file->VarArray(IF, 2*sizeof(u32)); + file->Var32(&IE2); + file->Var32(&IF2); file->Var8(&PostFlag9); file->Var8(&PostFlag7); @@ -825,11 +872,8 @@ bool DoSavestate(Savestate* file) file->Var64(&LastSysClockCycles); file->Var64(&FrameStartTimestamp); file->Var32(&NumFrames); - if (file->IsAtleastVersion(7, 1)) - { - file->Var32(&NumLagFrames); - file->Bool32(&LagFrameFlag); - } + file->Var32(&NumLagFrames); + file->Bool32(&LagFrameFlag); // TODO: save KeyInput???? file->Var16(&KeyCnt); @@ -860,13 +904,17 @@ bool DoSavestate(Savestate* file) ARM7->DoSavestate(file); NDSCart::DoSavestate(file); - GBACart::DoSavestate(file); + if (ConsoleType == 0) + GBACart::DoSavestate(file); GPU::DoSavestate(file); SPU::DoSavestate(file); SPI::DoSavestate(file); RTC::DoSavestate(file); Wifi::DoSavestate(file); + if (ConsoleType == 1) + DSi::DoSavestate(file); + if (!file->Saving) { GPU::SetPowerCnt(PowerControl9); @@ -888,73 +936,59 @@ void SetConsoleType(int type) ConsoleType = type; } -bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct) +bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) { - if (NDSCart::LoadROM(romdata, filelength, sram, direct)) - { - Running = true; - return true; - } - else - { - printf("Failed to load ROM from archive\n"); + if (!NDSCart::LoadROM(romdata, romlen)) return false; - } + + if (savedata && savelen) + NDSCart::LoadSave(savedata, savelen); + + return true; } -bool LoadROM(const char* path, const char* sram, bool direct) +void LoadSave(const u8* savedata, u32 savelen) { - if (NDSCart::LoadROM(path, sram, direct)) - { - Running = true; - return true; - } - else - { - printf("Failed to load ROM %s\n", path); - return false; - } + if (savedata && savelen) + NDSCart::LoadSave(savedata, savelen); } -bool LoadGBAROM(const char* path, const char* sram) +void EjectCart() { - if (GBACart::LoadROM(path, sram)) - { - return true; - } - else - { - printf("Failed to load ROM %s\n", path); - return false; - } + NDSCart::EjectCart(); } -bool LoadGBAROM(const u8* romdata, u32 filelength, const char *filename, const char *sram) +bool CartInserted() { - if (GBACart::LoadROM(romdata, filelength, sram)) - { - return true; - } - else - { - printf("Failed to load ROM %s from archive\n", filename); + return NDSCart::CartInserted; +} + +bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) +{ + if (!GBACart::LoadROM(romdata, romlen)) return false; - } + + if (savedata && savelen) + GBACart::LoadSave(savedata, savelen); + + return true; +} + +void LoadGBAAddon(int type) +{ + GBACart::LoadAddon(type); +} + +void EjectGBACart() +{ + GBACart::EjectCart(); } void LoadBIOS() { Reset(); - Running = true; } -void RelocateSave(const char* path, bool write) -{ - printf("SRAM: relocating to %s (write=%s)\n", path, write?"true":"false"); - NDSCart::RelocateSave(path, write); -} - - u64 NextTarget() { @@ -1094,8 +1128,6 @@ u32 RunFrame() GPU3D::Timestamp-SysTimestamp); #endif SPU::TransferOutput(); - - NDSCart::FlushSRAMFile(); } // In the context of TASes, frame count is traditionally the primary measure of emulated time, @@ -1234,10 +1266,10 @@ void MicInputFrame(s16* data, int samples) return SPI_TSC::MicInputFrame(data, samples); } -int ImportSRAM(u8* data, u32 length) +/*int ImportSRAM(u8* data, u32 length) { return NDSCart::ImportSRAM(data, length); -} +}*/ void Halt() diff --git a/src/NDS.h b/src/NDS.h index 29fc4ef5..c3764d1f 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -19,6 +19,8 @@ #ifndef NDS_H #define NDS_H +#include + #include "Savestate.h" #include "types.h" @@ -48,8 +50,6 @@ enum Event_DSi_NWifi, Event_DSi_CamIRQ, Event_DSi_CamTransfer, - - Event_DSi_RAMSizeChange, Event_DSi_DSP, Event_MAX @@ -162,6 +162,12 @@ struct MemRegion u32 Mask; }; +// supported GBA slot addon types +enum +{ + GBAAddon_RAMExpansion = 1, +}; + #ifdef JIT_ENABLED extern bool EnableJIT; #endif @@ -219,6 +225,7 @@ extern u8* ARM7WRAM; bool Init(); void DeInit(); void Reset(); +void Start(); void Stop(); bool DoSavestate(Savestate* file); @@ -229,13 +236,19 @@ void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, // 0=DS 1=DSi void SetConsoleType(int type); -bool LoadROM(const char* path, const char* sram, bool direct); -bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct); -bool LoadGBAROM(const char* path, const char* sram); -bool LoadGBAROM(const u8* romdata, u32 filelength, const char *filename, const char *sram); void LoadBIOS(); -void SetupDirectBoot(); -void RelocateSave(const char* path, bool write); + +bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); +void LoadSave(const u8* savedata, u32 savelen); +void EjectCart(); +bool CartInserted(); + +bool NeedsDirectBoot(); +void SetupDirectBoot(std::string romname); + +bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); +void LoadGBAAddon(int type); +void EjectGBACart(); u32 RunFrame(); @@ -249,8 +262,6 @@ void SetLidClosed(bool closed); void MicInputFrame(s16* data, int samples); -int ImportSRAM(u8* data, u32 length); - void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param); void CancelEvent(u32 id); diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 3d231acc..940827f9 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -22,11 +22,11 @@ #include "DSi.h" #include "NDSCart.h" #include "ARM.h" +#include "CRC32.h" #include "DSi_AES.h" #include "Platform.h" #include "ROMList.h" #include "melonDLDI.h" -#include "NDSCart_SRAMManager.h" namespace NDSCart @@ -51,12 +51,9 @@ u32 TransferDir; u8 TransferCmd[8]; bool CartInserted; -char CartName[256]; u8* CartROM; u32 CartROMSize; u32 CartID; -bool CartIsHomebrew; -bool CartIsDSi; NDSHeader Header; NDSBanner Banner; @@ -135,13 +132,24 @@ void Key1_ApplyKeycode(u32* keycode, u32 mod) } } +void Key1_LoadKeyBuf(bool dsi) +{ + // it is possible that this gets called before the BIOSes are loaded + // so we will read from the BIOS files directly + + std::string path = Platform::GetConfigString(dsi ? Platform::DSi_BIOS7Path : Platform::BIOS7Path); + FILE* f = Platform::OpenLocalFile(path, "rb"); + if (f) + { + fseek(f, dsi ? 0xC6D0 : 0x0030, SEEK_SET); + fread(Key1_KeyBuf, 0x1048, 1, f); + fclose(f); + } +} + void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod) { - // TODO: source the key data from different possible places - if (dsi && NDS::ConsoleType==1) - memcpy(Key1_KeyBuf, &DSi::ARM7iBIOS[0xC6D0], 0x1048); // hax - else - memcpy(Key1_KeyBuf, &NDS::ARM7BIOS[0x30], 0x1048); // hax + Key1_LoadKeyBuf(dsi); u32 keycode[3] = {idcode, idcode>>1, idcode<<1}; if (level >= 1) Key1_ApplyKeycode(keycode, mod); @@ -191,6 +199,22 @@ CartCommon::~CartCommon() { } +u32 CartCommon::Checksum() +{ + u32 crc = CRC32(ROM, 0x40); + + crc = CRC32(&ROM[Header.ARM9ROMOffset], Header.ARM9Size, crc); + crc = CRC32(&ROM[Header.ARM7ROMOffset], Header.ARM7Size, crc); + + if (IsDSi) + { + crc = CRC32(&ROM[Header.DSiARM9iROMOffset], Header.DSiARM9iSize, crc); + crc = CRC32(&ROM[Header.DSiARM7iROMOffset], Header.DSiARM7iSize, crc); + } + + return crc; +} + void CartCommon::Reset() { CmdEncMode = 0; @@ -198,11 +222,11 @@ void CartCommon::Reset() DSiMode = false; } -void CartCommon::SetupDirectBoot() +void CartCommon::SetupDirectBoot(std::string romname) { CmdEncMode = 2; DataEncMode = 2; - DSiMode = IsDSi && NDS::ConsoleType==1; + DSiMode = IsDSi && (NDS::ConsoleType==1); } void CartCommon::DoSavestate(Savestate* file) @@ -214,20 +238,11 @@ void CartCommon::DoSavestate(Savestate* file) file->Bool32(&DSiMode); } -void CartCommon::LoadSave(const char* path, u32 type) +void CartCommon::SetupSave(u32 type) { } -void CartCommon::RelocateSave(const char* path, bool write) -{ -} - -int CartCommon::ImportSRAM(const u8* data, u32 length) -{ - return 0; -} - -void CartCommon::FlushSRAMFile() +void CartCommon::LoadSave(const u8* savedata, u32 savelen) { } @@ -392,11 +407,7 @@ void CartRetail::DoSavestate(Savestate* file) CartCommon::DoSavestate(file); // we reload the SRAM contents. - // it should be the same file (as it should be the same ROM, duh) - // but the contents may change - - //if (!file->Saving && SRAMLength) - // delete[] SRAM; + // it should be the same file, but the contents may change u32 oldlen = SRAMLength; @@ -407,13 +418,11 @@ void CartRetail::DoSavestate(Savestate* file) printf("oh well. loading it anyway. adsfgdsf\n"); if (oldlen) delete[] SRAM; + SRAM = nullptr; if (SRAMLength) SRAM = new u8[SRAMLength]; } if (SRAMLength) { - //if (!file->Saving) - // SRAM = new u8[SRAMLength]; - file->VarArray(SRAM, SRAMLength); } @@ -423,20 +432,14 @@ void CartRetail::DoSavestate(Savestate* file) file->Var32(&SRAMAddr); file->Var8(&SRAMStatus); - // SRAMManager might now have an old buffer (or one from the future or alternate timeline!) - if (!file->Saving) - { - SRAMFileDirty = false; - NDSCart_SRAMManager::RequestFlush(); - } + if ((!file->Saving) && SRAM) + Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength); } -void CartRetail::LoadSave(const char* path, u32 type) +void CartRetail::SetupSave(u32 type) { if (SRAM) delete[] SRAM; - - strncpy(SRAMPath, path, 1023); - SRAMPath[1023] = '\0'; + SRAM = nullptr; if (type > 10) type = 0; int sramlen[] = @@ -455,18 +458,6 @@ void CartRetail::LoadSave(const char* path, u32 type) memset(SRAM, 0xFF, SRAMLength); } - FILE* f = Platform::OpenFile(path, "rb"); - if (f) - { - fseek(f, 0, SEEK_SET); - fread(SRAM, 1, SRAMLength, f); - - fclose(f); - } - - SRAMFileDirty = false; - NDSCart_SRAMManager::Setup(path, SRAM, SRAMLength); - switch (type) { case 1: SRAMType = 1; break; // EEPROM, small @@ -483,47 +474,13 @@ void CartRetail::LoadSave(const char* path, u32 type) } } -void CartRetail::RelocateSave(const char* path, bool write) +void CartRetail::LoadSave(const u8* savedata, u32 savelen) { - if (!write) - { - LoadSave(path, 0); // lazy - return; - } + if (!SRAM) return; - strncpy(SRAMPath, path, 1023); - SRAMPath[1023] = '\0'; - - FILE* f = Platform::OpenFile(path, "wb"); - if (!f) - { - printf("NDSCart_SRAM::RelocateSave: failed to create new file. fuck\n"); - return; - } - - fwrite(SRAM, SRAMLength, 1, f); - fclose(f); -} - -int CartRetail::ImportSRAM(const u8* data, u32 length) -{ - memcpy(SRAM, data, std::min(length, SRAMLength)); - FILE* f = Platform::OpenFile(SRAMPath, "wb"); - if (f) - { - fwrite(SRAM, SRAMLength, 1, f); - fclose(f); - } - - return length - SRAMLength; -} - -void CartRetail::FlushSRAMFile() -{ - if (!SRAMFileDirty) return; - - SRAMFileDirty = false; - NDSCart_SRAMManager::RequestFlush(); + u32 len = std::min(savelen, SRAMLength); + memcpy(SRAM, savedata, len); + Platform::WriteNDSSave(savedata, len, 0, len); } int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len) @@ -624,6 +581,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) if (pos < 2) { SRAMAddr = val; + SRAMFirstAddr = SRAMAddr; } else { @@ -631,11 +589,15 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) if (SRAMStatus & (1<<1)) { SRAM[(SRAMAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF] = val; - SRAMFileDirty |= last; } SRAMAddr++; } - if (last) SRAMStatus &= ~(1<<1); + if (last) + { + SRAMStatus &= ~(1<<1); + Platform::WriteNDSSave(SRAM, SRAMLength, + (SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr); + } return 0; case 0x03: // read low @@ -683,6 +645,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last) { SRAMAddr <<= 8; SRAMAddr |= val; + SRAMFirstAddr = SRAMAddr; } else { @@ -690,11 +653,15 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last) if (SRAMStatus & (1<<1)) { SRAM[SRAMAddr & (SRAMLength-1)] = val; - SRAMFileDirty |= last; } SRAMAddr++; } - if (last) SRAMStatus &= ~(1<<1); + if (last) + { + SRAMStatus &= ~(1<<1); + Platform::WriteNDSSave(SRAM, SRAMLength, + SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); + } return 0; case 0x03: // read @@ -735,6 +702,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) { SRAMAddr <<= 8; SRAMAddr |= val; + SRAMFirstAddr = SRAMAddr; } else { @@ -742,11 +710,15 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) { // CHECKME: should it be &=~val ?? SRAM[SRAMAddr & (SRAMLength-1)] = 0; - SRAMFileDirty |= last; } SRAMAddr++; } - if (last) SRAMStatus &= ~(1<<1); + if (last) + { + SRAMStatus &= ~(1<<1); + Platform::WriteNDSSave(SRAM, SRAMLength, + SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); + } return 0; case 0x03: // read @@ -768,17 +740,22 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) { SRAMAddr <<= 8; SRAMAddr |= val; + SRAMFirstAddr = SRAMAddr; } else { if (SRAMStatus & (1<<1)) { SRAM[SRAMAddr & (SRAMLength-1)] = val; - SRAMFileDirty |= last; } SRAMAddr++; } - if (last) SRAMStatus &= ~(1<<1); + if (last) + { + SRAMStatus &= ~(1<<1); + Platform::WriteNDSSave(SRAM, SRAMLength, + SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); + } return 0; case 0x0B: // fast read @@ -809,6 +786,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) { SRAMAddr <<= 8; SRAMAddr |= val; + SRAMFirstAddr = SRAMAddr; } if ((pos == 3) && (SRAMStatus & (1<<1))) { @@ -817,9 +795,13 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) SRAM[SRAMAddr & (SRAMLength-1)] = 0; SRAMAddr++; } - SRAMFileDirty = true; } - if (last) SRAMStatus &= ~(1<<1); + if (last) + { + SRAMStatus &= ~(1<<1); + Platform::WriteNDSSave(SRAM, SRAMLength, + SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); + } return 0; case 0xDB: // page erase @@ -827,6 +809,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) { SRAMAddr <<= 8; SRAMAddr |= val; + SRAMFirstAddr = SRAMAddr; } if ((pos == 3) && (SRAMStatus & (1<<1))) { @@ -835,9 +818,13 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) SRAM[SRAMAddr & (SRAMLength-1)] = 0; SRAMAddr++; } - SRAMFileDirty = true; } - if (last) SRAMStatus &= ~(1<<1); + if (last) + { + SRAMStatus &= ~(1<<1); + Platform::WriteNDSSave(SRAM, SRAMLength, + SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); + } return 0; default: @@ -884,19 +871,12 @@ void CartRetailNAND::DoSavestate(Savestate* file) BuildSRAMID(); } -void CartRetailNAND::LoadSave(const char* path, u32 type) +void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen) { - CartRetail::LoadSave(path, type); + CartRetail::LoadSave(savedata, savelen); BuildSRAMID(); } -int CartRetailNAND::ImportSRAM(const u8* data, u32 length) -{ - int ret = CartRetail::ImportSRAM(data, length); - BuildSRAMID(); - return ret; -} - int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len) { if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len); @@ -926,7 +906,7 @@ int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len) if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000)) { memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800); - SRAMFileDirty = true; + Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800); } SRAMAddr = 0; @@ -1164,8 +1144,30 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, chipid) { + SD = nullptr; +} + +CartHomebrew::~CartHomebrew() +{ + if (SD) + { + SD->Close(); + delete SD; + } +} + +void CartHomebrew::Reset() +{ + CartCommon::Reset(); + ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly); + if (SD) + { + SD->Close(); + delete SD; + } + if (Platform::GetConfigBool(Platform::DLDI_Enable)) { std::string folderpath; @@ -1185,29 +1187,15 @@ CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid) : CartCommon(rom, len, SD = nullptr; } -CartHomebrew::~CartHomebrew() +void CartHomebrew::SetupDirectBoot(std::string romname) { - if (SD) - { - SD->Close(); - delete SD; - } -} - -void CartHomebrew::Reset() -{ - CartCommon::Reset(); -} - -void CartHomebrew::SetupDirectBoot() -{ - CartCommon::SetupDirectBoot(); + CartCommon::SetupDirectBoot(romname); if (SD) { // add the ROM to the SD volume - if (!SD->InjectFile(CartName, CartROM, CartROMSize)) + if (!SD->InjectFile(romname, CartROM, CartROMSize)) return; // setup argv command line @@ -1216,7 +1204,7 @@ void CartHomebrew::SetupDirectBoot() int argvlen; strncpy(argv, "fat:/", 511); - strncat(argv, CartName, 511); + strncat(argv, romname.c_str(), 511); argvlen = strlen(argv); void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32; @@ -1441,6 +1429,7 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) bool Init() { + CartInserted = false; CartROM = nullptr; Cart = nullptr; @@ -1455,17 +1444,6 @@ void DeInit() void Reset() { - CartInserted = false; - if (CartROM) delete[] CartROM; - CartROM = nullptr; - CartROMSize = 0; - CartID = 0; - CartIsHomebrew = false; - CartIsDSi = false; - - if (Cart) delete Cart; - Cart = nullptr; - ResetCart(); } @@ -1494,6 +1472,30 @@ void DoSavestate(Savestate* file) // (TODO: system to verify that indeed the right ROM is loaded) // (what to CRC? whole ROM? code binaries? latter would be more convenient for ie. romhaxing) + u32 carttype = 0; + u32 cartchk = 0; + if (Cart) + { + carttype = Cart->Type(); + cartchk = Cart->Checksum(); + } + + if (file->Saving) + { + file->Var32(&carttype); + file->Var32(&cartchk); + } + else + { + u32 savetype; + file->Var32(&savetype); + if (savetype != carttype) return; + + u32 savechk; + file->Var32(&savechk); + if (savechk != cartchk) return; + } + if (Cart) Cart->DoSavestate(file); } @@ -1542,11 +1544,6 @@ bool ReadROMParams(u32 gamecode, ROMListEntry* params) void DecryptSecureArea(u8* out) { - // TODO: source decryption data from different possible sources - // * original DS-mode ARM7 BIOS has the key data at 0x30 - // * .srl ROMs (VC dumps) have encrypted secure areas but have precomputed - // decryption data at 0x1000 (and at the beginning of the DSi region if any) - u32 gamecode = (u32)Header.GameCode[3] << 24 | (u32)Header.GameCode[2] << 16 | (u32)Header.GameCode[1] << 8 | @@ -1576,8 +1573,28 @@ void DecryptSecureArea(u8* out) } } -bool LoadROMCommon(u32 filelength, const char *sram, bool direct) +bool LoadROM(const u8* romdata, u32 romlen) { + if (CartInserted) + EjectCart(); + + CartROMSize = 0x200; + while (CartROMSize < romlen) + CartROMSize <<= 1; + + try + { + CartROM = new u8[CartROMSize]; + } + catch (const std::bad_alloc& e) + { + printf("NDSCart: failed to allocate memory for ROM (%d bytes)\n", CartROMSize); + return false; + } + + memset(CartROM, 0, CartROMSize); + memcpy(CartROM, romdata, romlen); + memcpy(&Header, CartROM, sizeof(Header)); memcpy(&Banner, CartROM + Header.BannerOffset, sizeof(Banner)); @@ -1589,7 +1606,10 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) (u32)Header.GameCode[0]; u8 unitcode = Header.UnitCode; - CartIsDSi = (unitcode & 0x02) != 0; + bool dsi = (unitcode & 0x02) != 0; + + u32 arm9base = Header.ARM9ROMOffset; + bool homebrew = (arm9base < 0x4000) || (gamecode == 0x23232323); ROMListEntry romparams; if (!ReadROMParams(gamecode, &romparams)) @@ -1599,7 +1619,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) romparams.GameCode = gamecode; romparams.ROMSize = CartROMSize; - if (*(u32*)&CartROM[0x20] < 0x4000) + if (homebrew) romparams.SaveMemType = 0; // no saveRAM for homebrew else romparams.SaveMemType = 2; // assume EEPROM 64k (TODO FIXME) @@ -1607,7 +1627,8 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) else printf("ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType); - if (romparams.ROMSize != filelength) printf("!! bad ROM size %d (expected %d) rounded to %d\n", filelength, romparams.ROMSize, CartROMSize); + if (romparams.ROMSize != romlen) + printf("!! bad ROM size %d (expected %d) rounded to %d\n", romlen, romparams.ROMSize, CartROMSize); // generate a ROM ID // note: most games don't check the actual value @@ -1622,7 +1643,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) if (romparams.SaveMemType >= 8 && romparams.SaveMemType <= 10) CartID |= 0x08000000; // NAND flag - if (CartIsDSi) + if (dsi) CartID |= 0x40000000; // cart ID for Jam with the Band @@ -1633,34 +1654,24 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) printf("Cart ID: %08X\n", CartID); - u32 arm9base = *(u32*)&CartROM[0x20]; - - if (arm9base < 0x8000) + if (arm9base >= 0x4000 && arm9base < 0x8000) { - if (arm9base >= 0x4000) + // reencrypt secure area if needed + if (*(u32*)&CartROM[arm9base] == 0xE7FFDEFF && *(u32*)&CartROM[arm9base+0x10] != 0xE7FFDEFF) { - // reencrypt secure area if needed - if (*(u32*)&CartROM[arm9base] == 0xE7FFDEFF && *(u32*)&CartROM[arm9base+0x10] != 0xE7FFDEFF) - { - printf("Re-encrypting cart secure area\n"); + printf("Re-encrypting cart secure area\n"); - strncpy((char*)&CartROM[arm9base], "encryObj", 8); + strncpy((char*)&CartROM[arm9base], "encryObj", 8); - Key1_InitKeycode(false, gamecode, 3, 2); - for (u32 i = 0; i < 0x800; i += 8) - Key1_Encrypt((u32*)&CartROM[arm9base + i]); + Key1_InitKeycode(false, gamecode, 3, 2); + for (u32 i = 0; i < 0x800; i += 8) + Key1_Encrypt((u32*)&CartROM[arm9base + i]); - Key1_InitKeycode(false, gamecode, 2, 2); - Key1_Encrypt((u32*)&CartROM[arm9base]); - } + Key1_InitKeycode(false, gamecode, 2, 2); + Key1_Encrypt((u32*)&CartROM[arm9base]); } } - if ((arm9base < 0x4000) || (gamecode == 0x23232323)) - { - CartIsHomebrew = true; - } - CartInserted = true; u32 irversion = 0; @@ -1672,7 +1683,7 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) irversion = 2; // Pokémon HG/SS, B/W, B2/W2 } - if (CartIsHomebrew) + if (homebrew) Cart = new CartHomebrew(CartROM, CartROMSize, CartID); else if (CartID & 0x08000000) Cart = new CartRetailNAND(CartROM, CartROMSize, CartID); @@ -1684,99 +1695,44 @@ bool LoadROMCommon(u32 filelength, const char *sram, bool direct) Cart = new CartRetail(CartROM, CartROMSize, CartID); if (Cart) - { Cart->Reset(); - if (direct) - { - NDS::SetupDirectBoot(); - Cart->SetupDirectBoot(); - } - } - // encryption - Key1_InitKeycode(false, gamecode, 2, 2); - - // save - printf("Save file: %s\n", sram); - if (Cart) Cart->LoadSave(sram, romparams.SaveMemType); + if (Cart && romparams.SaveMemType > 0) + Cart->SetupSave(romparams.SaveMemType); return true; } -bool LoadROM(const char* path, const char* sram, bool direct) +void LoadSave(const u8* savedata, u32 savelen) { - // TODO: streaming mode? for really big ROMs or systems with limited RAM - // for now we're lazy - // also TODO: validate what we're loading!! - - FILE* f = Platform::OpenFile(path, "rb"); - if (!f) - { - return false; - } - - NDS::Reset(); - - char* romname = strrchr((char*)path, '/'); - if (!romname) - { - romname = strrchr((char*)path, '\\'); - if (!romname) - romname = (char*)&path[-1]; - } - romname++; - strncpy(CartName, romname, 255); CartName[255] = '\0'; - - fseek(f, 0, SEEK_END); - u32 len = (u32)ftell(f); - - CartROMSize = 0x200; - while (CartROMSize < len) - CartROMSize <<= 1; - - CartROM = new u8[CartROMSize]; - memset(CartROM, 0, CartROMSize); - fseek(f, 0, SEEK_SET); - fread(CartROM, 1, len, f); - - fclose(f); - - return LoadROMCommon(len, sram, direct); + if (Cart) + Cart->LoadSave(savedata, savelen); } -bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct) +void SetupDirectBoot(std::string romname) { - NDS::Reset(); - - // TODO: make it more meaningful? - strncpy(CartName, "rom.nds", 256); - - u32 len = filelength; - CartROMSize = 0x200; - while (CartROMSize < len) - CartROMSize <<= 1; - - CartROM = new u8[CartROMSize]; - memset(CartROM, 0, CartROMSize); - memcpy(CartROM, romdata, filelength); - - return LoadROMCommon(filelength, sram, direct); + if (Cart) + Cart->SetupDirectBoot(romname); } -void RelocateSave(const char* path, bool write) +void EjectCart() { - if (Cart) Cart->RelocateSave(path, write); -} + if (!CartInserted) return; -void FlushSRAMFile() -{ - if (Cart) Cart->FlushSRAMFile(); -} + // ejecting the cart triggers the gamecard IRQ + NDS::SetIRQ(0, NDS::IRQ_CartIREQMC); + NDS::SetIRQ(1, NDS::IRQ_CartIREQMC); -int ImportSRAM(const u8* data, u32 length) -{ - if (Cart) return Cart->ImportSRAM(data, length); - return 0; + if (Cart) delete Cart; + Cart = nullptr; + + CartInserted = false; + if (CartROM) delete[] CartROM; + CartROM = nullptr; + CartROMSize = 0; + CartID = 0; + + // CHECKME: does an eject imply anything for the ROM/SPI transfer registers? } void ResetCart() @@ -1880,6 +1836,8 @@ void WriteROMCnt(u32 val) *(u32*)&TransferCmd[0] = *(u32*)&ROMCommand[0]; *(u32*)&TransferCmd[4] = *(u32*)&ROMCommand[4]; + memset(TransferData, 0xFF, TransferLen); + /*printf("ROM COMMAND %04X %08X %02X%02X%02X%02X%02X%02X%02X%02X SIZE %04X\n", SPICnt, ROMCnt, TransferCmd[0], TransferCmd[1], TransferCmd[2], TransferCmd[3], diff --git a/src/NDSCart.h b/src/NDSCart.h index 9f399889..b5b5c3f0 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -19,6 +19,8 @@ #ifndef NDSCART_H #define NDSCART_H +#include + #include "types.h" #include "NDS_Header.h" #include "FATStorage.h" @@ -33,15 +35,16 @@ public: CartCommon(u8* rom, u32 len, u32 chipid); virtual ~CartCommon(); + virtual u32 Type() { return 0x001; } + virtual u32 Checksum(); + virtual void Reset(); - virtual void SetupDirectBoot(); + virtual void SetupDirectBoot(std::string romname); virtual void DoSavestate(Savestate* file); - virtual void LoadSave(const char* path, u32 type); - virtual void RelocateSave(const char* path, bool write); - virtual int ImportSRAM(const u8* data, u32 length); - virtual void FlushSRAMFile(); + virtual void SetupSave(u32 type); + virtual void LoadSave(const u8* savedata, u32 savelen); virtual int ROMCommandStart(u8* cmd, u8* data, u32 len); virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len); @@ -71,14 +74,14 @@ public: CartRetail(u8* rom, u32 len, u32 chipid); virtual ~CartRetail() override; + virtual u32 Type() override { return 0x101; } + virtual void Reset() override; virtual void DoSavestate(Savestate* file) override; - virtual void LoadSave(const char* path, u32 type) override; - virtual void RelocateSave(const char* path, bool write) override; - virtual int ImportSRAM(const u8* data, u32 length) override; - virtual void FlushSRAMFile() override; + virtual void SetupSave(u32 type) override; + virtual void LoadSave(const u8* savedata, u32 savelen) override; virtual int ROMCommandStart(u8* cmd, u8* data, u32 len) override; @@ -95,11 +98,9 @@ protected: u32 SRAMLength; u32 SRAMType; - char SRAMPath[1024]; - bool SRAMFileDirty; - u8 SRAMCmd; u32 SRAMAddr; + u32 SRAMFirstAddr; u8 SRAMStatus; }; @@ -110,12 +111,13 @@ public: CartRetailNAND(u8* rom, u32 len, u32 chipid); ~CartRetailNAND() override; + virtual u32 Type() override { return 0x102; } + void Reset() override; void DoSavestate(Savestate* file) override; - void LoadSave(const char* path, u32 type) override; - int ImportSRAM(const u8* data, u32 length) override; + void LoadSave(const u8* savedata, u32 savelen) override; int ROMCommandStart(u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; @@ -139,6 +141,8 @@ public: CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion); ~CartRetailIR() override; + virtual u32 Type() override { return 0x103; } + void Reset() override; void DoSavestate(Savestate* file) override; @@ -157,6 +161,8 @@ public: CartRetailBT(u8* rom, u32 len, u32 chipid); ~CartRetailBT() override; + virtual u32 Type() override { return 0x104; } + void Reset() override; void DoSavestate(Savestate* file) override; @@ -171,8 +177,10 @@ public: CartHomebrew(u8* rom, u32 len, u32 chipid); ~CartHomebrew() override; + virtual u32 Type() override { return 0x201; } + void Reset() override; - void SetupDirectBoot() override; + void SetupDirectBoot(std::string romname) override; void DoSavestate(Savestate* file) override; @@ -192,6 +200,7 @@ extern u32 ROMCnt; extern u8 ROMCommand[8]; +extern bool CartInserted; extern u8* CartROM; extern u32 CartROMSize; @@ -207,14 +216,12 @@ void Reset(); void DoSavestate(Savestate* file); void DecryptSecureArea(u8* out); -bool LoadROM(const char* path, const char* sram, bool direct); -bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct); -void FlushSRAMFile(); +bool LoadROM(const u8* romdata, u32 romlen); +void LoadSave(const u8* savedata, u32 savelen); +void SetupDirectBoot(std::string romname); -void RelocateSave(const char* path, bool write); - -int ImportSRAM(const u8* data, u32 length); +void EjectCart(); void ResetCart(); diff --git a/src/NDSCart_SRAMManager.cpp b/src/NDSCart_SRAMManager.cpp deleted file mode 100644 index addd1225..00000000 --- a/src/NDSCart_SRAMManager.cpp +++ /dev/null @@ -1,184 +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 -#include -#include -#include -#include -#include "NDSCart_SRAMManager.h" -#include "Platform.h" - -namespace NDSCart_SRAMManager -{ - -Platform::Thread* FlushThread; -std::atomic_bool FlushThreadRunning; -Platform::Mutex* SecondaryBufferLock; - -char Path[1024]; - -u8* Buffer; -u32 Length; - -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; - -void FlushThreadFunc(); - -bool Init() -{ - SecondaryBufferLock = Platform::Mutex_Create(); - - return true; -} - -void DeInit() -{ - if (FlushThreadRunning) - { - FlushThreadRunning = false; - Platform::Thread_Wait(FlushThread); - Platform::Thread_Free(FlushThread); - FlushSecondaryBuffer(); - } - - if (SecondaryBuffer) delete[] SecondaryBuffer; - SecondaryBuffer = NULL; - - Platform::Mutex_Free(SecondaryBufferLock); -} - -void Setup(const char* path, u8* buffer, u32 length) -{ - // Flush SRAM in case there is unflushed data from previous state. - FlushSecondaryBuffer(); - - Platform::Mutex_Lock(SecondaryBufferLock); - - strncpy(Path, path, 1023); - Path[1023] = '\0'; - - Buffer = buffer; - Length = length; - - if(SecondaryBuffer) delete[] SecondaryBuffer; // Delete secondary buffer, there might be previous state. - - SecondaryBuffer = new u8[length]; - SecondaryBufferLength = length; - - FlushVersion = 0; - PreviousFlushVersion = 0; - TimeAtLastFlushRequest = 0; - - Platform::Mutex_Unlock(SecondaryBufferLock); - - if (path[0] != '\0' && !FlushThreadRunning) - { - FlushThread = Platform::Thread_Create(FlushThreadFunc); - FlushThreadRunning = true; - } - else if (path[0] == '\0' && FlushThreadRunning) - { - FlushThreadRunning = false; - Platform::Thread_Wait(FlushThread); - Platform::Thread_Free(FlushThread); - } -} - -void RequestFlush() -{ - Platform::Mutex_Lock(SecondaryBufferLock); - printf("NDS SRAM: Flush requested\n"); - memcpy(SecondaryBuffer, Buffer, Length); - FlushVersion++; - TimeAtLastFlushRequest = time(NULL); - Platform::Mutex_Unlock(SecondaryBufferLock); -} - -void FlushThreadFunc() -{ - for (;;) - { - Platform::Sleep(100 * 1000); // 100ms - - if (!FlushThreadRunning) return; - - // We debounce for two seconds after last flush request to ensure that writing has finished. - if (TimeAtLastFlushRequest == 0 || difftime(time(NULL), TimeAtLastFlushRequest) < 2) - { - continue; - } - - FlushSecondaryBuffer(); - } -} - -void FlushSecondaryBuffer(u8* dst, s32 dstLength) -{ - // 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; - - Platform::Mutex_Lock(SecondaryBufferLock); - if (dst) - { - memcpy(dst, SecondaryBuffer, SecondaryBufferLength); - } - else - { - FILE* f = Platform::OpenFile(Path, "wb"); - if (f) - { - printf("NDS SRAM: Written\n"); - fwrite(SecondaryBuffer, SecondaryBufferLength, 1, f); - fclose(f); - } - } - PreviousFlushVersion = FlushVersion; - TimeAtLastFlushRequest = 0; - Platform::Mutex_Unlock(SecondaryBufferLock); -} - -bool NeedsFlush() -{ - return FlushVersion != PreviousFlushVersion; -} - -void UpdateBuffer(u8* src, s32 srcLength) -{ - if (!src || srcLength != Length) return; - - // should we create a lock for the primary buffer? this method is not intended to be called from a secondary thread in the way Flush is - memcpy(Buffer, src, srcLength); - Platform::Mutex_Lock(SecondaryBufferLock); - memcpy(SecondaryBuffer, src, srcLength); - Platform::Mutex_Unlock(SecondaryBufferLock); - - PreviousFlushVersion = FlushVersion; -} - -} diff --git a/src/NDSCart_SRAMManager.h b/src/NDSCart_SRAMManager.h deleted file mode 100644 index 89b65cef..00000000 --- a/src/NDSCart_SRAMManager.h +++ /dev/null @@ -1,39 +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 NDSCART_SRAMMANAGER_H -#define NDSCART_SRAMMANAGER_H - -#include "types.h" - -namespace NDSCart_SRAMManager -{ - extern u32 SecondaryBufferLength; - - bool Init(); - void DeInit(); - - void Setup(const char* path, u8* buffer, u32 length); - void RequestFlush(); - - bool NeedsFlush(); - void FlushSecondaryBuffer(u8* dst = NULL, s32 dstLength = 0); - void UpdateBuffer(u8* src, s32 srcLength); -} - -#endif // NDSCART_SRAMMANAGER_H \ No newline at end of file diff --git a/src/Platform.h b/src/Platform.h index bbdc245c..aefbf4b0 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -145,6 +145,13 @@ void Mutex_Unlock(Mutex* mutex); bool Mutex_TryLock(Mutex* mutex); +// functions called when the NDS or GBA save files need to be written back to storage +// savedata and savelen are always the entire save memory buffer and its full length +// writeoffset and writelen indicate which part of the memory was altered +void WriteNDSSave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen); +void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen); + + // local multiplayer comm interface // packet type: DS-style TX header (12 bytes) + original 802.11 frame bool MP_Init(); diff --git a/src/SPI.cpp b/src/SPI.cpp index 80ef336c..1641a267 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -463,6 +463,7 @@ void SetupDirectBoot(bool dsi) } } +u32 GetFirmwareLength() { return FirmwareLength; } u8 GetConsoleType() { return Firmware[0x1D]; } u8 GetWifiVersion() { return Firmware[0x2F]; } u8 GetNWifiVersion() { return Firmware[0x1FD]; } // for DSi; will return 0xFF on a DS diff --git a/src/SPI.h b/src/SPI.h index d82485b8..d98d3c25 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -28,6 +28,7 @@ void SetupDirectBoot(bool dsi); u32 FixFirmwareLength(u32 originalLength); +u32 GetFirmwareLength(); u8 GetConsoleType(); u8 GetWifiVersion(); u8 GetNWifiVersion(); diff --git a/src/Savestate.cpp b/src/Savestate.cpp index 1bfe937c..81d42eb3 100644 --- a/src/Savestate.cpp +++ b/src/Savestate.cpp @@ -43,7 +43,10 @@ * different minor means adjustments may have to be made */ -Savestate::Savestate(const char* filename, bool save) +// TODO: buffering system! or something of that sort +// repeated fread/fwrite is slow on Switch + +Savestate::Savestate(std::string filename, bool save) { const char* magic = "MELN"; @@ -55,7 +58,7 @@ Savestate::Savestate(const char* filename, bool save) file = Platform::OpenLocalFile(filename, "wb"); if (!file) { - printf("savestate: file %s doesn't exist\n", filename); + printf("savestate: file %s doesn't exist\n", filename.c_str()); Error = true; return; } @@ -74,7 +77,7 @@ Savestate::Savestate(const char* filename, bool save) file = Platform::OpenFile(filename, "rb"); if (!file) { - printf("savestate: file %s doesn't exist\n", filename); + printf("savestate: file %s doesn't exist\n", filename.c_str()); Error = true; return; } diff --git a/src/Savestate.h b/src/Savestate.h index 60d34ccf..5fe4ddcb 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -19,6 +19,7 @@ #ifndef SAVESTATE_H #define SAVESTATE_H +#include #include #include "types.h" @@ -28,7 +29,7 @@ class Savestate { public: - Savestate(const char* filename, bool save); + Savestate(std::string filename, bool save); ~Savestate(); bool Error; diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index f52dced6..a0085a8a 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -21,100 +21,12 @@ #include "types.h" +#include #include 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 &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 3598e744..00000000 --- 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 4d23c705..00000000 --- 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 -#include - -#include - -#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 &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 6919d483..af8e4a90 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -17,52 +17,37 @@ */ #include "ArchiveUtil.h" +#include "Platform.h" namespace Archive { -QVector 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 ListArchive(QString path) { struct archive *a; struct archive_entry *entry; int r; - QVector fileList = {"OK"}; - + QVector 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); - if (r != ARCHIVE_OK) - { - return QVector {"Err"}; - } - - while (archive_read_next_header(a, &entry) == ARCHIVE_OK) - { - fileList.push_back(archive_entry_pathname(entry)); - archive_read_data_skip(a); - } - archive_read_close(a); - archive_read_free(a); - if (r != ARCHIVE_OK) - { - return QVector {"Err"}; - } - - return fileList; -} -QVector ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer) -{ - struct archive *a = archive_read_new(); - struct archive_entry *entry; - int r; - - 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 {"Err"}; @@ -70,7 +55,46 @@ QVector 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 (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); + + if (r != ARCHIVE_OK) + { + return QVector {"Err"}; + } + + std::stable_sort(fileList.begin(), fileList.end(), compareCI); + fileList.prepend("OK"); + + return fileList; +} + +QVector ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer) +{ + struct archive *a = archive_read_new(); + struct archive_entry *entry; + int r; + + 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 QVector {"Err"}; + } + + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) + { + if (strcmp(wantedFile.toUtf8().constData(), archive_entry_pathname_utf8(entry)) == 0) { break; } @@ -92,7 +116,45 @@ QVector 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 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 a8a4a147..ce5e1923 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -34,10 +34,11 @@ namespace Archive { - -QVector ListArchive(const char* path); -QVector ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer); -u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata); + +QVector ListArchive(QString path); +u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize); +//QVector 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 d4ce678b..dff30558 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 ad38e379..b1dfd450 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 afa08059..28211ada 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()) @@ -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 30babafb..cf2e0d6d 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}, + {"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, 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}, + {"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, 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}, + {"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, 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}, + {"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, NULL, 0}, + {"JoystickID", 0, &JoystickID, 0}, - {"WindowWidth", 0, &WindowWidth, 256, NULL, 0}, - {"WindowHeight", 0, &WindowHeight, 384, NULL, 0}, - {"WindowMax", 0, &WindowMaximized, 0, NULL, 0}, + {"WindowWidth", 0, &WindowWidth, 256}, + {"WindowHeight", 0, &WindowHeight, 384}, + {"WindowMax", 1, &WindowMaximized, false}, - {"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}, + {"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", 0, &ScreenUseGL, 0, NULL, 0}, - {"ScreenVSync", 0, &ScreenVSync, 0, NULL, 0}, - {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1, NULL, 0}, + {"ScreenUseGL", 1, &ScreenUseGL, false}, + {"ScreenVSync", 1, &ScreenVSync, false}, + {"ScreenVSyncInterval", 0, &ScreenVSyncInterval, 1}, - {"3DRenderer", 0, &_3DRenderer, 0, NULL, 0}, - {"Threaded3D", 0, &Threaded3D, 1, NULL, 0}, + {"3DRenderer", 0, &_3DRenderer, 0}, + {"Threaded3D", 1, &Threaded3D, true}, - {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1, NULL, 0}, - {"GL_BetterPolygons", 0, &GL_BetterPolygons, 0, NULL, 0}, + {"GL_ScaleFactor", 0, &GL_ScaleFactor, 1}, + {"GL_BetterPolygons", 1, &GL_BetterPolygons, false}, - {"LimitFPS", 0, &LimitFPS, 1, NULL, 0}, - {"AudioSync", 0, &AudioSync, 0, NULL, 0}, - {"ShowOSD", 0, &ShowOSD, 1, NULL, 0}, + {"LimitFPS", 1, &LimitFPS, true}, + {"AudioSync", 1, &AudioSync, false}, + {"ShowOSD", 1, &ShowOSD, true}, - {"ConsoleType", 0, &ConsoleType, 0, NULL, 0}, - {"DirectBoot", 0, &DirectBoot, 1, NULL, 0}, + {"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}, + {"ExternalBIOSEnable", 1, &ExternalBIOSEnable, false}, - {"BIOS9Path", 1, BIOS9Path, 0, "", 1023}, - {"BIOS7Path", 1, BIOS7Path, 0, "", 1023}, - {"FirmwarePath", 1, FirmwarePath, 0, "", 1023}, + {"BIOS9Path", 2, &BIOS9Path, ""}, + {"BIOS7Path", 2, &BIOS7Path, ""}, + {"FirmwarePath", 2, &FirmwarePath, ""}, - {"DSiBIOS9Path", 1, DSiBIOS9Path, 0, "", 1023}, - {"DSiBIOS7Path", 1, DSiBIOS7Path, 0, "", 1023}, - {"DSiFirmwarePath", 1, DSiFirmwarePath, 0, "", 1023}, - {"DSiNANDPath", 1, DSiNANDPath, 0, "", 1023}, + {"DSiBIOS9Path", 2, &DSiBIOS9Path, ""}, + {"DSiBIOS7Path", 2, &DSiBIOS7Path, ""}, + {"DSiFirmwarePath", 2, &DSiFirmwarePath, ""}, + {"DSiNANDPath", 2, &DSiNANDPath, ""}, - {"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}, + {"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", 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}, + {"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", 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}, + {"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", 0, &SocketBindAnyAddr, 0, NULL, 0}, - {"LANDevice", 1, LANDevice, 0, "", 127}, - {"DirectLAN", 0, &DirectLAN, 0, NULL, 0}, + {"SockBindAnyAddr", 1, &SocketBindAnyAddr, false}, + {"LANDevice", 2, &LANDevice, ""}, + {"DirectLAN", 1, &DirectLAN, false}, - {"SavStaRelocSRAM", 0, &SavestateRelocSRAM, 0, NULL, 0}, + {"SavStaRelocSRAM", 1, &SavestateRelocSRAM, false}, - {"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}, + {"AudioInterp", 0, &AudioInterp, 0}, + {"AudioBitrate", 0, &AudioBitrate, 0}, + {"AudioVolume", 0, &AudioVolume, 256}, + {"MicInputType", 0, &MicInputType, 1}, + {"MicWavPath", 2, &MicWavPath, ""}, - {"LastROMFolder", 1, LastROMFolder, 0, "", 1023}, + {"LastROMFolder", 2, &LastROMFolder, ""}, - {"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}, + {"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], ""}, - {"EnableCheats", 0, &EnableCheats, 0, NULL, 0}, + {"SaveFilePath", 2, &SaveFilePath, ""}, + {"SavestatePath", 2, &SavestatePath, ""}, + {"CheatFilePath", 2, &CheatFilePath, ""}, - {"MouseHide", 0, &MouseHide, 0, NULL, 0}, - {"MouseHideSeconds", 0, &MouseHideSeconds, 5, NULL, 0}, - {"PauseLostFocus", 0, &PauseLostFocus, 0, NULL, 0}, + {"EnableCheats", 1, &EnableCheats, false}, - {"", -1, NULL, 0, NULL, 0} + {"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(entry->Default); break; + case 1: *(bool*)entry->Value = std::get(entry->Default); break; + case 2: *(std::string*)entry->Value = std::get(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 ad9b4c62..902ec6db 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 +#include + 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 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 char LastROMFolder[1024]; +extern std::string LastROMFolder; -extern char RecentROMList[10][1024]; +extern std::string RecentROMList[10]; -extern int EnableCheats; +extern std::string SaveFilePath; +extern std::string SavestatePath; +extern std::string CheatFilePath; -extern int MouseHide; +extern bool EnableCheats; + +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 fd2ca85c..75118815 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 0b2cad6c..976934f9 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 1ae409f6..7ed8b0bd 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 ed3eee94..dcc23103 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 00000000..5a2fa1a0 --- /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 +#include +#include + +#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 00000000..6a0fea2d --- /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 + +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 00000000..95f5acc9 --- /dev/null +++ b/src/frontend/qt_sdl/PathSettingsDialog.ui @@ -0,0 +1,149 @@ + + + PathSettingsDialog + + + + 0 + 0 + 439 + 166 + + + + Path settings - melonDS + + + + + + true + + + + + + + Browse... + + + + + + + Savestates path: + + + + + + + true + + + + + + + Leave a path blank to use the current ROM's path. + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Cheat files path: + + + + + + + Browse... + + + + + + + Browse... + + + + + + + true + + + + + + + Save files path: + + + + + + + + + + + + + + txtSaveFilePath + btnSaveFileBrowse + txtSavestatePath + btnSavestateBrowse + txtCheatFilePath + btnCheatFileBrowse + + + + + buttonBox + accepted() + PathSettingsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PathSettingsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 812c9532..0197264c 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 @@ -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 9166efe8..5fbca0f1 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(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 5193554e..8fdab740 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.h +++ b/src/frontend/qt_sdl/ROMInfoDialog.h @@ -25,7 +25,7 @@ #include #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 00000000..2b9bbd33 --- /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 +#include + +#include +#include + +#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 &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 00000000..8c97965e --- /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 +#include + +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 &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 00000000..20ac5431 --- /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 +#include + +#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 00000000..9ccb0e3e --- /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 +#include +#include +#include +#include +#include + +#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 0a5e65d8..5af36368 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 d438179e..e584b6ac 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 bf7c2618..5711a05f 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 urls = event->mimeData()->urls(); if (urls.count() > 1) return; // not handling more than one file at once + QString filename = urls.at(0).toLocalFile(); + QStringList arcexts{".zip", ".7z", ".rar", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"}; + emuThread->emuPause(); - QString filename = urls.at(0).toLocalFile(); - QString ext = filename.right(3).toLower(); - - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); - - char _filename[1024]; - strncpy(_filename, filename.toStdString().c_str(), 1023); _filename[1023] = '\0'; - - int slot; int res; - if (ext == "gba") + if (!verifySetup()) { - slot = 1; - res = Frontend::LoadROM(_filename, Frontend::ROMSlot_GBA); + emuThread->emuUnpause(); + return; } - else if(ext == "nds" || ext == "srl" || ext == "dsi") - { - slot = 0; - res = Frontend::LoadROM(_filename, Frontend::ROMSlot_NDS); - } - else - { - QByteArray romBuffer; - QString romFileName = pickAndExtractFileFromArchive(_filename, &romBuffer); - if(romFileName.isEmpty()) - { - 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); + for (const QString &ext : arcexts) + { + if (filename.endsWith(ext, Qt::CaseInsensitive)) + { + 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) + QStringList file = filename.split('|'); + + if (filename.endsWith(".gba", Qt::CaseInsensitive)) { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else if (slot == 1) - { - // 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,194 +1899,69 @@ 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."; - - 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."; - - 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."; - - 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."; - - 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."; - - 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."; - - case Frontend::Load_ROMLoadError: - return "Failed to load the ROM. Make sure the file is accessible and isn't used by another application."; - - default: return "Unknown error during launch; smack Arisotura."; + QMessageBox::critical(this, "melonDS", res); + return false; } + + return true; } -void MainWindow::loadROM(QByteArray *romData, QString archiveFileName, QString romFileName) +bool MainWindow::preloadROMs(QString filename, QString gbafilename) { - recentFileList.removeAll(archiveFileName); - recentFileList.prepend(archiveFileName); - updateRecentFilesMenu(); - - // Strip entire archive name and get folder path - strncpy(Config::LastROMFolder, QFileInfo(archiveFileName).absolutePath().toStdString().c_str(), 1024); - - QString sramFileName = QFileInfo(archiveFileName).absolutePath() + QDir::separator() + QFileInfo(romFileName).completeBaseName() + ".sav"; - - int slot; int res; - if (romFileName.endsWith("gba")) + if (!verifySetup()) { - 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); - } - else - { - 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); + return false; } - if (res != Frontend::Load_OK) + bool gbaloaded = false; + if (!gbafilename.isEmpty()) { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else if (slot == 1) - { - // checkme - emuThread->emuUnpause(); - } - else - { - emuThread->emuRun(); - } -} + QStringList gbafile = gbafilename.split('|'); + if (!ROMManager::LoadGBAROM(gbafile)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); + return false; + } + + gbaloaded = true; + } + + QStringList file = filename.split('|'); + if (!ROMManager::LoadROM(file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + return false; + } -void MainWindow::loadROM(QString filename) -{ recentFileList.removeAll(filename); recentFileList.prepend(filename); updateRecentFilesMenu(); - // TODO: validate the input file!! - // * check that it is a proper ROM - // * ensure the binary offsets are sane - // * etc + NDS::Start(); + emuThread->emuRun(); - // this shit is stupid - char file[1024]; - strncpy(file, filename.toStdString().c_str(), 1023); file[1023] = '\0'; + updateCartInserted(false); - int pos = strlen(file)-1; - while (file[pos] != '/' && file[pos] != '\\' && pos > 0) pos--; - strncpy(Config::LastROMFolder, file, pos); - Config::LastROMFolder[pos] = '\0'; - char* ext = &file[strlen(file)-3]; - - int slot; int res; - if (!strcasecmp(ext, "gba")) + if (gbaloaded) { - slot = 1; - res = Frontend::LoadROM(file, Frontend::ROMSlot_GBA); - } - else - { - slot = 0; - res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); + updateCartInserted(true); } - if (res != Frontend::Load_OK) - { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else if (slot == 1) - { - // checkme - emuThread->emuUnpause(); - } - else - { - emuThread->emuRun(); - } + return true; } -void MainWindow::onOpenFile() +QString MainWindow::pickFileFromArchive(QString archiveFileName) { - emuThread->emuPause(); + QVector archiveROMList = Archive::ListArchive(archiveFileName); - QString filename = QFileDialog::getOpenFileName(this, - "Open ROM", - Config::LastROMFolder, - "DS ROMs (*.nds *.dsi *.srl);;GBA ROMs (*.gba *.zip);;Any file (*.*)"); - if (filename.isEmpty()) - { - 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()) - { - emuThread->emuUnpause(); - return; - } - - QByteArray romBuffer; - QString romFileName = pickAndExtractFileFromArchive(archiveFileName, &romBuffer); - if(!romFileName.isEmpty()) - { - loadROM(&romBuffer, archiveFileName, romFileName); - } -} - -QString MainWindow::pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer) -{ - printf("Finding list of ROMs...\n"); - QVector archiveROMList = Archive::ListArchive(archiveFileName.toUtf8().constData()); - - - QString romFileName; // file name inside archive + QString romFileName = ""; // file name inside archive if (archiveROMList.size() > 2) { @@ -2041,50 +1969,152 @@ QString MainWindow::pickAndExtractFileFromArchive(QString archiveFileName, QByte 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 + "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(); - printf("Extracting '%s'\n", toLoad.toUtf8().constData()); - QVector 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]); - } + romFileName = toLoad; } else if (archiveROMList.size() == 2) { - printf("Extracting the only ROM in archive\n"); - QVector 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]); - } + romFileName = archiveROMList.at(1); } else if ((archiveROMList.size() == 1) && (archiveROMList[0] == QString("OK"))) { - QMessageBox::warning(this, "melonDS", "The archive is intact, but there are no files inside."); + QMessageBox::warning(this, "melonDS", "This archive is empty."); } else { - QMessageBox::critical(this, "melonDS", "The archive could not be read. It may be corrupt or you don't have the permissions."); + QMessageBox::critical(this, "melonDS", "This archive could not be read. It may be corrupt or you don't have the permissions."); } 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) + { + console = "GBA"; + romexts.append("*.gba"); + } + else + { + console = "DS"; + romexts.append({"*.nds", "*.dsi", "*.ids", "*.srl"}); + } + + QString filter = romexts.join(' ') + " " + arcexts.join(' '); + filter = console + " ROMs (" + filter + ");;Any file (*.*)"; + + QString filename = QFileDialog::getOpenFileName(this, + "Open "+console+" ROM", + QString::fromStdString(Config::LastROMFolder), + filter); + if (filename.isEmpty()) + return ret; + + 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); + + Config::LastROMFolder = path_dir.toStdString(); + + bool isarc = false; + for (const auto& ext : arcexts) + { + 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 + { + ret.append(filename); + } + + return ret; +} + +void MainWindow::updateCartInserted(bool gba) +{ + bool inserted; + if (gba) + { + inserted = ROMManager::GBACartInserted() && (Config::ConsoleType == 0); + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + actEjectGBACart->setEnabled(inserted); + } + else + { + inserted = ROMManager::CartInserted(); + actCurrentCart->setText("DS slot: " + ROMManager::CartLabel()); + actEjectCart->setEnabled(inserted); + actImportSavefile->setEnabled(inserted); + actSetupCheats->setEnabled(inserted); + actROMInfo->setEnabled(inserted); + } +} + +void MainWindow::onOpenFile() +{ + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + QStringList file = pickROM(false); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadROM(file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + QString filename = file.join('|'); + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + NDS::Start(); + emuThread->emuRun(); + + 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('|'); - if (fileName.endsWith(".gba", Qt::CaseInsensitive) || - fileName.endsWith(".nds", Qt::CaseInsensitive) || - fileName.endsWith(".srl", Qt::CaseInsensitive) || - fileName.endsWith(".dsi", Qt::CaseInsensitive)) + emuThread->emuPause(); + + 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(); + + QStringList file = pickROM(false); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadROM(file, false)) + { + // 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(); - int res = Frontend::LoadBIOS(); - if (res != Frontend::Load_OK) - { - QMessageBox::critical(this, - "melonDS", - loadErrorStr(res)); - emuThread->emuUnpause(); - } - else - { - emuThread->emuRun(); - } + 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 = 1; i < 9; i++) { - 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++) - { - 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(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]; + QString file = argv[1]; + QString gbafile = ""; + if (argc > 2) gbafile = argv[2]; - if (!strcasecmp(ext, "nds") || !strcasecmp(ext, "srl") || !strcasecmp(ext, "dsi")) - { - int res = Frontend::LoadROM(file, Frontend::ROMSlot_NDS); - - 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 0b5e917a..a0ee9c54 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 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];