diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 7ba07d1a..f1ac392d 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -479,11 +479,11 @@ void CartGame::SRAMWrite_FLASH(u32 addr, u8 val) u32 start_addr = addr + 0x10000 * SRAMFlashState.bank; memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); - if (SRAMFile) + /*if (SRAMFile) { fseek(SRAMFile, start_addr, SEEK_SET); fwrite((u8*)&SRAM[start_addr], 1, 0x1000, SRAMFile); - } + }*/ } SRAMFlashState.state = 0; SRAMFlashState.cmd = 0; @@ -541,11 +541,11 @@ void CartGame::SRAMWrite_SRAM(u32 addr, u8 val) { *(u8*)&SRAM[addr] = val; - if (SRAMFile) + /*if (SRAMFile) { fseek(SRAMFile, addr, SEEK_SET); fwrite((u8*)&SRAM[addr], 1, 1, SRAMFile); - } + }*/ } } diff --git a/src/NDS.cpp b/src/NDS.cpp index e12f6ba6..3d74c919 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1134,7 +1134,7 @@ u32 RunFrame() #endif SPU::TransferOutput(); - NDSCart::FlushSRAMFile(); + //NDSCart::FlushSRAMFile(); } // In the context of TASes, frame count is traditionally the primary measure of emulated time, @@ -1273,10 +1273,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 2ea26d4d..a7299ecd 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -259,7 +259,7 @@ void SetLidClosed(bool closed); void MicInputFrame(s16* data, int samples); -int ImportSRAM(u8* data, u32 length); +//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 09c260f6..c40ad2a0 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -934,18 +934,18 @@ 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 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) { diff --git a/src/NDSCart_SRAMManager.cpp b/src/NDSCart_SRAMManager.cpp index addd1225..d0a2325c 100644 --- a/src/NDSCart_SRAMManager.cpp +++ b/src/NDSCart_SRAMManager.cpp @@ -84,7 +84,7 @@ void Setup(const char* path, u8* buffer, u32 length) Buffer = buffer; Length = length; - if(SecondaryBuffer) delete[] SecondaryBuffer; // Delete secondary buffer, there might be previous state. + if (SecondaryBuffer) delete[] SecondaryBuffer; // Delete secondary buffer, there might be previous state. SecondaryBuffer = new u8[length]; SecondaryBufferLength = length; @@ -125,7 +125,7 @@ void FlushThreadFunc() 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) { diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp index f50f4869..a40d981a 100644 --- a/src/frontend/Util_ROM.cpp +++ b/src/frontend/Util_ROM.cpp @@ -343,7 +343,7 @@ int LoadROM(const u8 *romdata, u32 romlength, const char *archivefilename, const NDS::SetConsoleType(Config::ConsoleType); - if (slot == ROMSlot_NDS && NDS::LoadROM(romdata, romlength, SRAMPath[slot].c_str(), directboot)) +/* if (slot == ROMSlot_NDS && NDS::LoadROM(romdata, romlength, SRAMPath[slot].c_str(), directboot)) { SavestateLoaded = false; @@ -363,7 +363,7 @@ int LoadROM(const u8 *romdata, u32 romlength, const char *archivefilename, const PrevSRAMPath[slot] = SRAMPath[slot]; // safety return Load_OK; } - else + else*/ { ROMPath[slot] = oldpath; SRAMPath[slot] = oldsram; @@ -423,7 +423,7 @@ int LoadROM(const char* file, int slot) NDS::SetConsoleType(Config::ConsoleType); - if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot].c_str(), SRAMPath[slot].c_str(), directboot)) + /*if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot].c_str(), SRAMPath[slot].c_str(), directboot)) { SavestateLoaded = false; @@ -443,7 +443,7 @@ int LoadROM(const char* file, int slot) PrevSRAMPath[slot] = SRAMPath[slot]; // safety return Load_OK; } - else + else*/ { ROMPath[slot] = oldpath; SRAMPath[slot] = oldsram; @@ -577,7 +577,7 @@ int Reset() { NDS::LoadBIOS(); } - else + /*else { std::string ext = ROMPath[ROMSlot_NDS].substr(ROMPath[ROMSlot_NDS].length() - 4); std::transform(ext.begin(), ext.end(), ext.begin(), tolower); @@ -611,7 +611,7 @@ int Reset() bool ok = NDS::LoadROM(romdata, romlen, sramfilename, directboot); delete romdata; - if (!ok)*/ + if (!ok)*-/ return Load_ROMLoadError; } #endif @@ -650,11 +650,11 @@ int Reset() bool ok = NDS::LoadGBAROM(romdata, romlen, romfilename, SRAMPath[ROMSlot_GBA]); delete romdata; - if (!ok)*/ + if (!ok)*-/ return Load_ROMLoadError; } #endif - } + }*/ LoadCheats(); @@ -735,7 +735,7 @@ bool LoadState(std::string filename) // TODO: how should this interact with custom paths? SRAMPath[ROMSlot_NDS] = filename + ".sav"; - NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), false); +// NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), false); } bool loadedPartialGBAROM = false; @@ -783,7 +783,7 @@ bool SaveState(std::string filename) // TODO: how should this interact with custom paths? SRAMPath[ROMSlot_NDS] = filename + ".sav"; - NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), true); + // NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), true); } } @@ -804,13 +804,13 @@ void UndoStateLoad() if (!ROMPath[ROMSlot_NDS].empty()) { SRAMPath[ROMSlot_NDS] = PrevSRAMPath[ROMSlot_NDS]; - NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), false); +// NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), false); } } int ImportSRAM(const char* filename) { - FILE* file = fopen(filename, "rb"); + /*FILE* file = fopen(filename, "rb"); fseek(file, 0, SEEK_END); u32 size = ftell(file); u8* importData = new u8[size]; @@ -820,7 +820,8 @@ int ImportSRAM(const char* filename) int diff = NDS::ImportSRAM(importData, size); delete[] importData; - return diff; + return diff;*/ + return 0; } void EnableCheats(bool enable) diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index 6919d483..b02d1503 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; } diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index a8a4a147..906041bc 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -34,10 +34,10 @@ 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); +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/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 7fb0aa7a..634a2c88 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -25,6 +25,7 @@ SET(SOURCES_QT_SDL font.h Platform.cpp QPathInput.h + ROMLoader.cpp SaveManager.cpp ArchiveUtil.h diff --git a/src/frontend/qt_sdl/ROMLoader.cpp b/src/frontend/qt_sdl/ROMLoader.cpp new file mode 100644 index 00000000..18e14b3c --- /dev/null +++ b/src/frontend/qt_sdl/ROMLoader.cpp @@ -0,0 +1,834 @@ +/* + 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 "ROMLoader.h" +#include "SharedConfig.h" +#include "Platform.h" + +#include "NDS.h" +#include "DSi.h" +#include "GBACart.h" + +#include "AREngine.h" + + +namespace ROMLoader +{ + +std::string ROMPath [ROMSlot_MAX]; +std::string SRAMPath [ROMSlot_MAX]; +std::string PrevSRAMPath[ROMSlot_MAX]; // for savestate 'undo load' + +std::string NDSROMExtension; + +bool SavestateLoaded; + +ARCodeFile* CheatFile; +bool CheatsOn; + + +void Init_ROM() +{ + SavestateLoaded = false; + + ROMPath[ROMSlot_NDS] = ""; + ROMPath[ROMSlot_GBA] = ""; + SRAMPath[ROMSlot_NDS] = ""; + SRAMPath[ROMSlot_GBA] = ""; + PrevSRAMPath[ROMSlot_NDS] = ""; + PrevSRAMPath[ROMSlot_GBA] = ""; + + 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) +{ + SRAMPath[slot] = ROMPath[slot].substr(0, ROMPath[slot].length() - 3) + "sav"; +} + +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; + } + + std::string filename; + if (!ROMPath[ROMSlot_NDS].empty()) + { + filename = ROMPath[ROMSlot_NDS].substr(0, ROMPath[ROMSlot_NDS].length() - 3) + "mch"; + } + else + { + filename = "firmware.mch"; + } + + // TODO: add custom path here + + // 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] = ""; + SRAMPath[ROMSlot_NDS] = ""; + + 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; + + 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] = ""; + } + else + { + res = VerifyDSFirmware(); + if (res != Load_OK) + { + if (res == Load_FirmwareNotBootable) + directboot = true; + else + return res; + } + } + + std::string oldpath = ROMPath[slot]; + std::string oldsram = SRAMPath[slot]; + + SRAMPath[slot] = sramfilename; + ROMPath[slot] = archivefilename; + + NDS::SetConsoleType(Config::ConsoleType); + +/* if (slot == ROMSlot_NDS && NDS::LoadROM(romdata, romlength, SRAMPath[slot].c_str(), directboot)) + { + SavestateLoaded = false; + + LoadCheats(); + + // Reload the inserted GBA cartridge (if any) + // TODO: report failure there?? + //if (!ROMPath[ROMSlot_GBA].empty()) NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]); + + PrevSRAMPath[slot] = SRAMPath[slot]; // safety + return Load_OK; + } + else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(romdata, romlength, romfilename, SRAMPath[slot].c_str())) + { + SavestateLoaded = false; // checkme?? + + PrevSRAMPath[slot] = SRAMPath[slot]; // safety + return Load_OK; + } + else*/ + { + ROMPath[slot] = oldpath; + SRAMPath[slot] = oldsram; + 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] = ""; + } + else + { + res = VerifyDSFirmware(); + if (res != Load_OK) + { + if (res == Load_FirmwareNotBootable) + directboot = true; + else + return res; + } + } + + std::string oldpath = ROMPath[slot]; + std::string oldsram = SRAMPath[slot]; + + ROMPath[slot] = file; + + SetupSRAMPath(0); + SetupSRAMPath(1); + + NDS::SetConsoleType(Config::ConsoleType); + + /*if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot].c_str(), SRAMPath[slot].c_str(), directboot)) + { + SavestateLoaded = false; + + LoadCheats(); + + // Reload the inserted GBA cartridge (if any) + // TODO: report failure there?? + if (!ROMPath[ROMSlot_GBA].empty()) NDS::LoadGBAROM(ROMPath[ROMSlot_GBA].c_str(), SRAMPath[ROMSlot_GBA].c_str()); + + PrevSRAMPath[slot] = SRAMPath[slot]; // safety + return Load_OK; + } + else if (slot == ROMSlot_GBA && NDS::LoadGBAROM(ROMPath[slot].c_str(), SRAMPath[slot].c_str())) + { + SavestateLoaded = false; // checkme?? + + PrevSRAMPath[slot] = SRAMPath[slot]; // safety + return Load_OK; + } + else*/ + { + ROMPath[slot] = oldpath; + SRAMPath[slot] = oldsram; + 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] = ""; + + 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].empty()) + { + NDS::LoadBIOS(); + } + else + { + std::string ext = ROMPath[ROMSlot_NDS].substr(ROMPath[ROMSlot_NDS].length() - 4); + std::transform(ext.begin(), ext.end(), ext.begin(), tolower); + + if (ext == ".nds" || ext == ".srl" || ext == ".dsi") + { + SetupSRAMPath(0); + //if (!NDS::LoadROM(ROMPath[ROMSlot_NDS].c_str(), SRAMPath[ROMSlot_NDS].c_str(), directboot)) + // return Load_ROMLoadError; + } +#ifdef ARCHIVE_SUPPORT_ENABLED + else + { + // TODO!!! + // THIS WILL BREAK IF CUSTOM SRAM PATHS ARE ADDED + + /*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].empty()) + { + std::string ext = ROMPath[ROMSlot_GBA].substr(ROMPath[ROMSlot_GBA].length() - 4); + std::transform(ext.begin(), ext.end(), ext.begin(), tolower); + + if (ext == ".gba") + { + SetupSRAMPath(1); + //if (!NDS::LoadGBAROM(ROMPath[ROMSlot_GBA].c_str(), SRAMPath[ROMSlot_GBA].c_str())) + // return Load_ROMLoadError; + } +#ifdef ARCHIVE_SUPPORT_ENABLED + else + { + // TODO!! SAME AS ABOVE + + /*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. + +std::string GetSavestateName(int slot) +{ + std::string filename; + + if (ROMPath[ROMSlot_NDS].empty()) // running firmware, no ROM + { + filename = "firmware"; + } + else + { + std::string rompath; + std::string ext = ROMPath[ROMSlot_NDS].substr(ROMPath[ROMSlot_NDS].length() - 4); + std::transform(ext.begin(), ext.end(), ext.begin(), tolower); + + // TODO!!! MORE SHIT THAT IS GONNA ASPLODE + if (ext == ".nds" || ext == ".srl" || ext == ".dsi") + rompath = ROMPath[ROMSlot_NDS]; + else + rompath = SRAMPath[ROMSlot_NDS]; // If archive, construct ssname from sram file + + filename = rompath.substr(0, rompath.rfind('.')); + } + + filename += ".ml"; + filename += ('0'+slot); + + return filename; +} + +bool SavestateExists(int slot) +{ + std::string ssfile = GetSavestateName(slot); + return Platform::FileExists(ssfile); +} + +bool LoadState(std::string 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].empty()) + { + PrevSRAMPath[ROMSlot_NDS] = SRAMPath[ROMSlot_NDS]; + + // TODO: how should this interact with custom paths? + SRAMPath[ROMSlot_NDS] = filename + ".sav"; + + //NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), 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] = ""; + SRAMPath[ROMSlot_GBA] = ""; + 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(std::string 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].empty()) + { + // TODO: how should this interact with custom paths? + SRAMPath[ROMSlot_NDS] = filename + ".sav"; + + //NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), 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].empty()) + { + SRAMPath[ROMSlot_NDS] = PrevSRAMPath[ROMSlot_NDS]; + //NDS::RelocateSave(SRAMPath[ROMSlot_NDS].c_str(), 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;*/ + return 0; +} + +void EnableCheats(bool enable) +{ + CheatsOn = enable; + if (CheatFile) + AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); +} + +} diff --git a/src/frontend/qt_sdl/ROMLoader.h b/src/frontend/qt_sdl/ROMLoader.h new file mode 100644 index 00000000..33a73fef --- /dev/null +++ b/src/frontend/qt_sdl/ROMLoader.h @@ -0,0 +1,122 @@ +/* + 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 ROMLOADER_H +#define ROMLOADER_H + +#include "types.h" + +#include +#include + +namespace ROMLoader +{ + +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 std::string ROMPath [ROMSlot_MAX]; +extern std::string SRAMPath[ROMSlot_MAX]; +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 std::string NDSROMExtension; + +// 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) +std::string GetSavestateName(int slot); + +// 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(std::string filename); + +// save the current emulator state to the given file +bool SaveState(std::string 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); + + + +} + +#endif // ROMLOADER_H diff --git a/src/frontend/qt_sdl/SaveManager.cpp b/src/frontend/qt_sdl/SaveManager.cpp index c78c801e..968797c8 100644 --- a/src/frontend/qt_sdl/SaveManager.cpp +++ b/src/frontend/qt_sdl/SaveManager.cpp @@ -23,7 +23,7 @@ #include "Platform.h" -SaveManager() +SaveManager::SaveManager() { SecondaryBuffer = nullptr; SecondaryBufferLock = new QMutex(); @@ -31,7 +31,7 @@ SaveManager() Running = false; } -~SaveManager() +SaveManager::~SaveManager() { if (Running) { @@ -73,7 +73,7 @@ void SaveManager::Setup(std::string path, u8* buffer, u32 length) Running = true; start(); } - else if (path.empty && Running) + else if (path.empty() && Running) { Running = false; wait(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index f3b79978..ce3a871d 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1288,9 +1288,9 @@ 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) @@ -1307,6 +1307,43 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) menu->addSeparator(); + actCurrentCart = menu->addAction("Slot 1: princessbourf.nds"); + actCurrentCart->setEnabled(false); + + actInsertCart = menu->addAction("Insert cart..."); + + actEjectCart = menu->addAction("Eject cart"); + + menu->addSeparator(); + + actCurrentGBACart = menu->addAction("Slot 2: Fartslapper Mk. II"); + actCurrentGBACart->setEnabled(false); + + actInsertGBACart = menu->addAction("Insert ROM cart..."); + + //actInsertGBAAddon = menu->addAction("Insert add-on cart"); + { + QMenu* submenu = menu->addMenu("Insert add-on cart"); + + actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); + actInsertGBAAddon[1] = submenu->addAction("Vibrator Pak"); + actInsertGBAAddon[2] = submenu->addAction("Guitar Hero grip"); + actInsertGBAAddon[3] = submenu->addAction("Fartslapper"); + actInsertGBAAddon[4] = submenu->addAction("Fartslapper Mk. II"); + actInsertGBAAddon[5] = submenu->addAction("Ghostbusters ray"); + actInsertGBAAddon[6] = submenu->addAction("Fridge Pak"); + actInsertGBAAddon[7] = submenu->addAction("Fazil"); + } + + actEjectGBACart = menu->addAction("Eject cart"); + + menu->addSeparator(); + + actImportSavefile = menu->addAction("Import savefile"); + connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); + + menu->addSeparator(); + { QMenu* submenu = menu->addMenu("Save state"); @@ -1344,9 +1381,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"); @@ -1974,26 +2008,69 @@ void MainWindow::loadROM(QString filename) } } -void MainWindow::onOpenFile() +void MainWindow::pickAndLoadROM(bool gba) { + QString console; + QStringList romexts; + QStringList arcexts{"*.zip", "*.7z", "*.rar", "*.tar", "*.gz", "*.xz", "*.bz2"}; + + if (gba) + { + console = "GBA"; + romexts.append("*.gba"); + } + else + { + console = "DS"; + romexts.append({"*.nds", "*.dsi", "*.srl"}); + } + emuThread->emuPause(); + QString filter = romexts.join(' ') + " " + arcexts.join(' '); + filter = console + " ROMs (" + filter + ");;Any file (*.*)"; + QString filename = QFileDialog::getOpenFileName(this, - "Open ROM", + "Open "+console+" ROM", QString::fromStdString(Config::LastROMFolder), - "DS ROMs (*.nds *.dsi *.srl);;GBA ROMs (*.gba *.zip);;Any file (*.*)"); + filter); if (filename.isEmpty()) { emuThread->emuUnpause(); return; } - loadROM(filename); + 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); + QString path_ext = path_file.mid(path_file.lastIndexOf('.')).toLower(); + + Config::LastROMFolder = path_dir.toStdString(); + + printf("LOADING ROM: %s\n", filename.toStdString().c_str()); + printf("- %s\n", path_dir.toStdString().c_str()); + printf("- %s\n", path_file.toStdString().c_str()); + printf("- %s\n", path_ext.toStdString().c_str()); + + if (arcexts.contains("*"+path_ext)) + { + QByteArray romBuffer; + QString arcfile = pickAndExtractFileFromArchive(filename, &romBuffer); + + printf("ARCHIVE: %s\n", arcfile.toStdString().c_str()); + printf("SIZE=%d\n", romBuffer.size()); + } +} + +void MainWindow::onOpenFile() +{ + pickAndLoadROM(false); } void MainWindow::onOpenFileArchive() { - emuThread->emuPause(); + /*emuThread->emuPause(); QString archiveFileName = QFileDialog::getOpenFileName(this, "Open ROM Archive", @@ -2010,7 +2087,7 @@ void MainWindow::onOpenFileArchive() if(!romFileName.isEmpty()) { loadROM(&romBuffer, archiveFileName, romFileName); - } + }*/ } QString MainWindow::pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer) @@ -2019,7 +2096,7 @@ QString MainWindow::pickAndExtractFileFromArchive(QString archiveFileName, QByte QVector archiveROMList = Archive::ListArchive(archiveFileName.toUtf8().constData()); - QString romFileName; // file name inside archive + QString romFileName = ""; // file name inside archive if (archiveROMList.size() > 2) { @@ -2028,7 +2105,7 @@ 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 + if (!ok) // User clicked on cancel return QString(); printf("Extracting '%s'\n", toLoad.toUtf8().constData()); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 423c4e50..a580560d 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -298,6 +298,8 @@ private: QString pickAndExtractFileFromArchive(QString archiveFileName, QByteArray *romBuffer); + void pickAndLoadROM(bool gba); + void createScreenPanel(); QString loadErrorStr(int error); @@ -313,12 +315,19 @@ public: ScreenPanelNative* panelNative; QAction* actOpenROM; - QAction* actOpenROMArchive; + //QAction* actOpenROMArchive; QAction* actBootFirmware; + QAction* actCurrentCart; + QAction* actInsertCart; + QAction* actEjectCart; + QAction* actCurrentGBACart; + QAction* actInsertGBACart; + QAction* actInsertGBAAddon[16]; + QAction* actEjectGBACart; + QAction* actImportSavefile; QAction* actSaveState[9]; QAction* actLoadState[9]; QAction* actUndoStateLoad; - QAction* actImportSavefile; QAction* actQuit; QAction* actPause;