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 <abcdjdj@gmail.com>

* ArchiveUtil : Use QT functions for I/O

* Make it more platform independent, cleaner

* Fixes permission related crash on linux

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* NDSCart : Abstract out common code in LoadROM()

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Extract nds roms to memory

* Some stuff is still broken in the frontend

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* GBACart : Abstract out common code in LoadROM()

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Extract gba roms to memory

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Integrate archive support with recent files

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* 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 <abcdjdj@gmail.com>

* 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 <abcdjdj@gmail.com>

* Archive support for drag-n-drop

* Also recent files support for drag-n-drop

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* 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 <abcdjdj@gmail.com>

* 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 <abcdjdj@gmail.com>

* Add srl and dsi as "direct-load" formats

* Direct-load = anything not in an archive

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Don't use static functions

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Remove QT stuff from Util_ROM

* Also, during reset, directly load file from archive (no rom picker)

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Remove QT includes from FrontendUtil.h

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Util_ROM/LoadROM() : Use SetupDSiNAND()

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Util_ROM/Reset() : Use strrchr()

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Util_ROM : Put Archive stuff behind ifdefs

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* main: Set parent widget for archive dialog boxes

Signed-off-by: Madhav Kanbur <abcdjdj@gmail.com>

* Revert "Util_ROM/Reset() : Use strrchr()"

This reverts commit c8af6f066f.
This commit is contained in:
Madhav Kanbur 2021-01-22 15:52:32 +05:30 committed by GitHub
parent d42ca1ec4b
commit 1e4c0c9d72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 449 additions and 108 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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();

View File

@ -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

View File

@ -19,6 +19,9 @@
#include <stdio.h>
#include <string.h>
#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;

View File

@ -18,11 +18,6 @@
#include "ArchiveUtil.h"
#ifdef _WIN32
#include <direct.h>
#define mkdir(dir, mode) _mkdir(dir)
#endif
namespace Archive
{
@ -58,7 +53,7 @@ QVector<QString> ListArchive(const char* path)
return fileList;
}
QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile)
QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer)
{
struct archive *a = archive_read_new();
struct archive_entry *entry;
@ -72,38 +67,46 @@ QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile
{
return QVector<QString> {"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<u8[]>(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<QString> {"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<QString> {nameToWrite};
return QVector<QString> {wantedFile};
}
u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata)
{
QByteArray romBuffer;
QVector<QString> 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;
}
}

View File

@ -18,7 +18,8 @@ namespace Archive
{
QVector<QString> ListArchive(const char* path);
QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile);
QVector<QString> ExtractFileFromArchive(const char* path, const char* wantedFile, QByteArray *romBuffer);
u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata);
}

View File

@ -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})

View File

@ -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<QString> archiveROMList = Archive::ListArchive(filename.toUtf8().constData());
QVector<QString> 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<QString> extractResult = Archive::ExtractFileFromArchive(filename.toUtf8().constData(), toLoad.toUtf8().constData());
QVector<QString> 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<QString> extractResult = Archive::ExtractFileFromArchive(filename.toUtf8().constData(), nullptr);
QVector<QString> 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()

View File

@ -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();