From 1e4c0c9d7280aa34c06269867970a4a9eeddb9eb Mon Sep 17 00:00:00 2001 From: Madhav Kanbur Date: Fri, 22 Jan 2021 15:52:32 +0530 Subject: Polish up archive support (#930) * Fix directory path when extracting from archive * Don't create new dir in execution dir of melonds * Create it beside the archive instead Signed-off-by: Madhav Kanbur * ArchiveUtil : Use QT functions for I/O * Make it more platform independent, cleaner * Fixes permission related crash on linux Signed-off-by: Madhav Kanbur * NDSCart : Abstract out common code in LoadROM() Signed-off-by: Madhav Kanbur * Extract nds roms to memory * Some stuff is still broken in the frontend Signed-off-by: Madhav Kanbur * GBACart : Abstract out common code in LoadROM() Signed-off-by: Madhav Kanbur * Extract gba roms to memory Signed-off-by: Madhav Kanbur * Integrate archive support with recent files Signed-off-by: Madhav Kanbur * onClickRecentFile : Pause emu thread conditionally * Don't pause at start of the function * If user opens an archive and hits cancel, it won't pause Signed-off-by: Madhav Kanbur * Handle Resets when loading from archives * Ask user to pick the rom(s) again (i.e. GBA & NDS) when there are multiple files in the archive(s) * Directly load if only 1 file Signed-off-by: Madhav Kanbur * Archive support for drag-n-drop * Also recent files support for drag-n-drop Signed-off-by: Madhav Kanbur * main : Allocate rombuffer objects on stack * Less messy, decreases chances of memory leaks * Underlying implementation of qbytearray uses heap (hopefully?) Signed-off-by: Madhav Kanbur * GetSavestateName : Archive support * Construct ssname from srampath (since rompath has archive name) NOTE: In general, archive name != rom file name !!!!!!!!!! Signed-off-by: Madhav Kanbur * Add srl and dsi as "direct-load" formats * Direct-load = anything not in an archive Signed-off-by: Madhav Kanbur * Don't use static functions Signed-off-by: Madhav Kanbur * Remove QT stuff from Util_ROM * Also, during reset, directly load file from archive (no rom picker) Signed-off-by: Madhav Kanbur * Remove QT includes from FrontendUtil.h Signed-off-by: Madhav Kanbur * Util_ROM/LoadROM() : Use SetupDSiNAND() Signed-off-by: Madhav Kanbur * Util_ROM/Reset() : Use strrchr() Signed-off-by: Madhav Kanbur * Util_ROM : Put Archive stuff behind ifdefs Signed-off-by: Madhav Kanbur * main: Set parent widget for archive dialog boxes Signed-off-by: Madhav Kanbur * Revert "Util_ROM/Reset() : Use strrchr()" This reverts commit c8af6f066f6aa15e5557e478417edb125cad0809. --- src/GBACart.cpp | 61 ++++++++----- src/GBACart.h | 1 + src/NDS.cpp | 27 ++++++ src/NDS.h | 2 + src/NDSCart.cpp | 86 ++++++++++-------- src/NDSCart.h | 1 + src/frontend/FrontendUtil.h | 3 + src/frontend/Util_ROM.cpp | 170 ++++++++++++++++++++++++++++++++++-- src/frontend/qt_sdl/ArchiveUtil.cpp | 47 +++++----- src/frontend/qt_sdl/ArchiveUtil.h | 3 +- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/main.cpp | 152 ++++++++++++++++++++++++++++---- src/frontend/qt_sdl/main.h | 3 + 13 files changed, 449 insertions(+), 108 deletions(-) (limited to 'src') diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 4d44d3a..9188408 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -620,6 +620,32 @@ void DoSavestate(Savestate* file) if (HasSolarSensor) GBACart_SolarSensor::DoSavestate(file); } +void LoadROMCommon(const char *sram) +{ + char gamecode[5] = { '\0' }; + memcpy(&gamecode, CartROM + 0xAC, 4); + printf("Game code: %s\n", gamecode); + + for (int i = 0; i < sizeof(SOLAR_SENSOR_GAMECODES)/sizeof(SOLAR_SENSOR_GAMECODES[0]); i++) + { + if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) HasSolarSensor = true; + } + + if (HasSolarSensor) + { + printf("GBA solar sensor support detected!\n"); + } + + CartCRC = CRC32(CartROM, CartROMSize); + printf("ROM CRC32: %08X\n", CartCRC); + + CartInserted = true; + + // save + printf("Save file: %s\n", sram); + GBACart_SRAM::LoadSave(sram); +} + bool LoadROM(const char* path, const char* sram) { FILE* f = Platform::OpenFile(path, "rb"); @@ -640,36 +666,27 @@ bool LoadROM(const char* path, const char* sram) while (CartROMSize < len) CartROMSize <<= 1; - char gamecode[5] = { '\0' }; - fseek(f, 0xAC, SEEK_SET); - fread(&gamecode, 1, 4, f); - printf("Game code: %s\n", gamecode); - - for (int i = 0; i < sizeof(SOLAR_SENSOR_GAMECODES)/sizeof(SOLAR_SENSOR_GAMECODES[0]); i++) - { - if (strcmp(gamecode, SOLAR_SENSOR_GAMECODES[i]) == 0) HasSolarSensor = true; - } - - if (HasSolarSensor) - { - printf("GBA solar sensor support detected!\n"); - } - CartROM = new u8[CartROMSize]; memset(CartROM, 0, CartROMSize); fseek(f, 0, SEEK_SET); fread(CartROM, 1, len, f); - fclose(f); - CartCRC = CRC32(CartROM, CartROMSize); - printf("ROM CRC32: %08X\n", CartCRC); + LoadROMCommon(sram); - CartInserted = true; + return true; +} - // save - printf("Save file: %s\n", sram); - GBACart_SRAM::LoadSave(sram); +bool LoadROM(const u8* romdata, u32 filelength, const char *sram) +{ + CartROMSize = 0x200; + while (CartROMSize < filelength) + CartROMSize <<= 1; + + CartROM = new u8[CartROMSize]; + memcpy(CartROM, romdata, filelength); + + LoadROMCommon(sram); return true; } diff --git a/src/GBACart.h b/src/GBACart.h index 96a05b8..f976e6d 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -66,6 +66,7 @@ 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); void WriteGPIO(u32 addr, u16 val); diff --git a/src/NDS.cpp b/src/NDS.cpp index df3c103..d0346a5 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -830,6 +830,20 @@ void SetConsoleType(int type) ConsoleType = type; } +bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct) +{ + if (NDSCart::LoadROM(romdata, filelength, sram, direct)) + { + Running = true; + return true; + } + else + { + printf("Failed to load ROM from archive\n"); + return false; + } +} + bool LoadROM(const char* path, const char* sram, bool direct) { if (NDSCart::LoadROM(path, sram, direct)) @@ -857,6 +871,19 @@ bool LoadGBAROM(const char* path, const char* sram) } } +bool LoadGBAROM(const u8* romdata, u32 filelength, const char *filename, const char *sram) +{ + if (GBACart::LoadROM(romdata, filelength, sram)) + { + return true; + } + else + { + printf("Failed to load ROM %s from archive\n", filename); + return false; + } +} + void LoadBIOS() { Reset(); diff --git a/src/NDS.h b/src/NDS.h index f3b4be0..9f64784 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -197,7 +197,9 @@ void SetARM7RegionTimings(u32 addrstart, u32 addrend, int buswidth, int nonseq, 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); diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 0765288..ae051bd 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -885,46 +885,15 @@ void DecryptSecureArea(u8* out) } } - -bool LoadROM(const char* path, const char* sram, bool direct) +bool LoadROMCommon(u32 filelength, const char *sram, bool direct) { - // 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(); - - fseek(f, 0, SEEK_END); - u32 len = (u32)ftell(f); - - CartROMSize = 0x200; - while (CartROMSize < len) - CartROMSize <<= 1; - u32 gamecode; - fseek(f, 0x0C, SEEK_SET); - fread(&gamecode, 4, 1, f); + memcpy(&gamecode, CartROM + 0x0C, 4); printf("Game code: %c%c%c%c\n", gamecode&0xFF, (gamecode>>8)&0xFF, (gamecode>>16)&0xFF, gamecode>>24); - u8 unitcode; - fseek(f, 0x12, SEEK_SET); - fread(&unitcode, 1, 1, f); + u8 unitcode = CartROM[0x12]; CartIsDSi = (unitcode & 0x02) != 0; - CartROM = new u8[CartROMSize]; - memset(CartROM, 0, CartROMSize); - fseek(f, 0, SEEK_SET); - fread(CartROM, 1, len, f); - - fclose(f); - //CartROM = f; - ROMListEntry romparams; if (!ReadROMParams(gamecode, &romparams)) { @@ -941,7 +910,7 @@ bool LoadROM(const char* path, const char* sram, bool direct) else printf("ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType); - if (romparams.ROMSize != len) printf("!! bad ROM size %d (expected %d) rounded to %d\n", len, romparams.ROMSize, CartROMSize); + if (romparams.ROMSize != filelength) printf("!! bad ROM size %d (expected %d) rounded to %d\n", filelength, romparams.ROMSize, CartROMSize); // generate a ROM ID // note: most games don't check the actual value @@ -1026,6 +995,53 @@ bool LoadROM(const char* path, const char* sram, bool direct) return true; } +bool LoadROM(const char* path, const char* sram, bool direct) +{ + // 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(); + + 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); +} + +bool LoadROM(const u8* romdata, u32 filelength, const char *sram, bool direct) +{ + NDS::Reset(); + + 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); +} + void RelocateSave(const char* path, bool write) { // herp derp diff --git a/src/NDSCart.h b/src/NDSCart.h index 7d3f4a1..a108eb9 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -46,6 +46,7 @@ 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(); diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 584250b..83749d3 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -63,6 +63,8 @@ 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(); @@ -76,6 +78,7 @@ 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 diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index 065b319..5da7330 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -19,6 +19,9 @@ #include #include +#ifdef ARCHIVE_SUPPORT_ENABLED +#include "ArchiveUtil.h" +#endif #include "FrontendUtil.h" #include "Config.h" #include "SharedConfig.h" @@ -38,6 +41,8 @@ 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; @@ -295,6 +300,84 @@ int LoadBIOS() 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(); @@ -440,16 +523,76 @@ int Reset() } else { - SetupSRAMPath(0); - if (!NDS::LoadROM(ROMPath[ROMSlot_NDS], SRAMPath[ROMSlot_NDS], directboot)) - return Load_ROMLoadError; + char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_NDS]); + strncpy(ext, ROMPath[ROMSlot_NDS] + _len - 4, 4); + + if(!strncmp(ext, ".nds", 4) || !strncmp(ext, ".srl", 4) || !strncmp(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') { - SetupSRAMPath(1); - if (!NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA])) - return Load_ROMLoadError; + char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_GBA]); + strncpy(ext, ROMPath[ROMSlot_NDS] + _len - 4, 4); + + if(!strncmp(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(); @@ -472,15 +615,24 @@ void GetSavestateName(int slot, char* filename, int len) } else { - int l = strlen(ROMPath[ROMSlot_NDS]); + char *rompath; + char ext[5] = {0}; int _len = strlen(ROMPath[ROMSlot_NDS]); + strncpy(ext, ROMPath[ROMSlot_NDS] + _len - 4, 4); + + if(!strncmp(ext, ".nds", 4) || !strncmp(ext, ".srl", 4) || !strncmp(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[ROMSlot_NDS][pos] != '.' && pos > 0) pos--; + 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[ROMSlot_NDS], pos); + strncpy(&filename[0], rompath, pos); } strcpy(&filename[pos], ".ml"); filename[pos+3] = '0'+slot; diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index ba6e4b6..c1f2fc1 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -18,11 +18,6 @@ #include "ArchiveUtil.h" -#ifdef _WIN32 - #include - #define mkdir(dir, mode) _mkdir(dir) -#endif - namespace Archive { @@ -58,7 +53,7 @@ QVector ListArchive(const char* path) return fileList; } -QVector ExtractFileFromArchive(const char* path, const char* wantedFile) +QVector ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer) { struct archive *a = archive_read_new(); struct archive_entry *entry; @@ -72,38 +67,46 @@ QVector ExtractFileFromArchive(const char* path, const char* wantedFile { return QVector {"Err"}; } - while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { - if (wantedFile == nullptr) - { - break; - } + + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) + { if (strcmp(wantedFile, archive_entry_pathname(entry)) == 0) { break; } } + size_t bytesToWrite = archive_entry_size(entry); - auto archiveBuffer = std::make_unique(bytesToWrite); - ssize_t bytesRead = archive_read_data(a, archiveBuffer.get(), bytesToWrite); + romBuffer->fill(0, bytesToWrite); + ssize_t bytesRead = archive_read_data(a, romBuffer->data(), bytesToWrite); + if (bytesRead < 0) { printf(archive_error_string(a)); - archiveBuffer.reset(nullptr); return QVector {"Err", archive_error_string(a)}; } - QString nameToWrite = QFileInfo(path).absolutePath() + "/" + QFileInfo(path).baseName() + "/" + archive_entry_pathname(entry); - mkdir(QFileInfo(path).baseName().toUtf8().constData(), 600); // Create directory otherwise fopen will not open the file - FILE* fileToWrite = fopen(nameToWrite.toUtf8().constData(), "wb"); - fwrite((char*)archiveBuffer.get(), bytesToWrite, 1, fileToWrite); - fclose(fileToWrite); - - archiveBuffer.reset(nullptr); archive_read_close(a); archive_read_free(a); - return QVector {nameToWrite}; + return QVector {wantedFile}; } +u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata) +{ + QByteArray romBuffer; + QVector extractResult = ExtractFileFromArchive(path, wantedFile, &romBuffer); + + if(extractResult[0] == "Err") + { + return 0; + } + + u32 len = romBuffer.size(); + *romdata = new u8[romBuffer.size()]; + memcpy(*romdata, romBuffer.data(), len); + + return len; +} } diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index a6f404a..3095f07 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -18,7 +18,8 @@ namespace Archive { QVector ListArchive(const char* path); -QVector ExtractFileFromArchive(const char* path, const char* wantedFile); +QVector ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer); +u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata); } diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 237ccca..612c3fd 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -63,6 +63,7 @@ if (APPLE) list(APPEND CMAKE_PREFIX_PATH "${LIBARCHIVE_DIR}") endif() pkg_check_modules(LIBARCHIVE REQUIRED libarchive) +add_compile_definitions(ARCHIVE_SUPPORT_ENABLED) if (WIN32 AND (CMAKE_BUILD_TYPE STREQUAL Release)) add_executable(melonDS WIN32 ${SOURCES_QT_SDL}) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index f64494d..c5ce692 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1453,10 +1453,15 @@ void MainWindow::dragEnterEvent(QDragEnterEvent* event) if (urls.count() > 1) return; // not handling more than one file at once QString filename = urls.at(0).toLocalFile(); - QString ext = filename.right(3); - if (ext == "nds" || ext == "srl" || ext == "dsi" || ext == "gba") - event->acceptProposedAction(); + QStringList acceptedExts{".nds", ".srl", ".dsi", ".gba", ".rar", + ".zip", ".7z", ".tar", ".tar.gz", ".tar.xz", ".tar.bz2"}; + + for(const QString &ext : acceptedExts) + { + if(filename.endsWith(ext)) + event->acceptProposedAction(); + } } void MainWindow::dropEvent(QDropEvent* event) @@ -1471,6 +1476,10 @@ void MainWindow::dropEvent(QDropEvent* event) QString filename = urls.at(0).toLocalFile(); QString ext = filename.right(3); + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + char _filename[1024]; strncpy(_filename, filename.toStdString().c_str(), 1023); _filename[1023] = '\0'; @@ -1480,11 +1489,32 @@ void MainWindow::dropEvent(QDropEvent* event) slot = 1; res = Frontend::LoadROM(_filename, Frontend::ROMSlot_GBA); } - else + 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") ? 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); + + res = Frontend::LoadROM((const u8*)romBuffer.constData(), romBuffer.size(), + _filename, romFileName.toStdString().c_str(), sramFileName.toStdString().c_str(), + slot); + } + } if (res != Frontend::Load_OK) { @@ -1548,6 +1578,54 @@ QString MainWindow::loadErrorStr(int error) } } +void MainWindow::loadROM(QByteArray *romData, QString archiveFileName, QString romFileName) +{ + 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")) + { + 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); + } + + if (res != Frontend::Load_OK) + { + QMessageBox::critical(this, + "melonDS", + loadErrorStr(res)); + emuThread->emuUnpause(); + } + else if (slot == 1) + { + // checkme + emuThread->emuUnpause(); + } + else + { + emuThread->emuRun(); + } +} + void MainWindow::loadROM(QString filename) { recentFileList.removeAll(filename); @@ -1620,43 +1698,62 @@ void MainWindow::onOpenFileArchive() { emuThread->emuPause(); - QString filename = QFileDialog::getOpenFileName(this, + 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 (filename.isEmpty()) + 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(filename.toUtf8().constData()); + QVector archiveROMList = Archive::ListArchive(archiveFileName.toUtf8().constData()); + + + QString romFileName; // file name inside archive + if (archiveROMList.size() > 2) { archiveROMList.removeFirst(); + + 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); + "The archive was found to have multiple files. Select which ROM you want to load.", archiveROMList.toList(), 0, false, &ok); + if(!ok) // User clicked on cancel + return QString(); + printf("Extracting '%s'\n", toLoad.toUtf8().constData()); - QVector extractResult = Archive::ExtractFileFromArchive(filename.toUtf8().constData(), toLoad.toUtf8().constData()); + QVector extractResult = Archive::ExtractFileFromArchive(archiveFileName.toUtf8().constData(), toLoad.toUtf8().constData(), romBuffer); if (extractResult[0] != QString("Err")) { - filename = extractResult[0]; + romFileName = extractResult[0]; } - else + else { QMessageBox::critical(this, "melonDS", QString("There was an error while trying to extract the ROM from the archive: ") + extractResult[1]); } - } + } else if (archiveROMList.size() == 2) - { + { printf("Extracting the only ROM in archive\n"); - QVector extractResult = Archive::ExtractFileFromArchive(filename.toUtf8().constData(), nullptr); + QVector extractResult = Archive::ExtractFileFromArchive(archiveFileName.toUtf8().constData(), archiveROMList.at(1).toUtf8().constData(), romBuffer); if (extractResult[0] != QString("Err")) { - filename = extractResult[0]; + romFileName = extractResult[0]; } - else + else { QMessageBox::critical(this, "melonDS", QString("There was an error while trying to extract the ROM from the archive: ") + extractResult[1]); } @@ -1670,7 +1767,7 @@ void MainWindow::onOpenFileArchive() QMessageBox::critical(this, "melonDS", "The archive could not be read. It may be corrupt or you don't have the permissions."); } - loadROM(filename); + return romFileName; } void MainWindow::onClearRecentFiles() @@ -1705,9 +1802,26 @@ void MainWindow::updateRecentFilesMenu() void MainWindow::onClickRecentFile() { - emuThread->emuPause(); QAction *act = (QAction *)sender(); - loadROM(act->data().toString()); + QString fileName = act->data().toString(); + + if(fileName.endsWith(".gba") || fileName.endsWith(".nds") || fileName.endsWith(".srl") || fileName.endsWith(".dsi")) + { + emuThread->emuPause(); + loadROM(fileName); + } + else + { + // Archives + QString archiveFileName = fileName; + QByteArray romBuffer; + QString romFileName = MainWindow::pickAndExtractFileFromArchive(archiveFileName, &romBuffer); + if(!romFileName.isEmpty()) + { + emuThread->emuPause(); + loadROM(&romBuffer, archiveFileName, romFileName); + } + } } void MainWindow::onBootFirmware() diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index ba67401..9ab4c2a 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -255,6 +255,9 @@ private: QMenu *recentMenu; void updateRecentFilesMenu(); void loadROM(QString filename); + void loadROM(QByteArray *romData, QString archiveFileName, QString romFileName); + + QString pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer); void createScreenPanel(); -- cgit v1.2.3