aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/qt_sdl/ROMManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/frontend/qt_sdl/ROMManager.cpp')
-rw-r--r--src/frontend/qt_sdl/ROMManager.cpp843
1 files changed, 843 insertions, 0 deletions
diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp
new file mode 100644
index 0000000..2b9bbd3
--- /dev/null
+++ b/src/frontend/qt_sdl/ROMManager.cpp
@@ -0,0 +1,843 @@
+/*
+ Copyright 2016-2021 Arisotura
+
+ This file is part of melonDS.
+
+ melonDS is free software: you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation, either version 3 of the License, or (at your option)
+ any later version.
+
+ melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with melonDS. If not, see http://www.gnu.org/licenses/.
+*/
+
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <utility>
+
+#ifdef ARCHIVE_SUPPORT_ENABLED
+#include "ArchiveUtil.h"
+#endif
+#include "ROMManager.h"
+#include "Config.h"
+#include "Platform.h"
+
+#include "NDS.h"
+#include "DSi.h"
+
+
+namespace ROMManager
+{
+
+int CartType = -1;
+std::string BaseROMDir = "";
+std::string BaseROMName = "";
+std::string BaseAssetName = "";
+
+int GBACartType = -1;
+std::string BaseGBAROMDir = "";
+std::string BaseGBAROMName = "";
+std::string BaseGBAAssetName = "";
+
+SaveManager* NDSSave = nullptr;
+SaveManager* GBASave = nullptr;
+
+bool SavestateLoaded = false;
+std::string PreviousSaveFile = "";
+
+ARCodeFile* CheatFile = nullptr;
+bool CheatsOn = false;
+
+
+int LastSep(std::string path)
+{
+ int i = path.length() - 1;
+ while (i >= 0)
+ {
+ if (path[i] == '/' || path[i] == '\\')
+ return i;
+
+ i--;
+ }
+
+ return -1;
+}
+
+std::string GetAssetPath(bool gba, std::string configpath, std::string ext, std::string file="")
+{
+ if (configpath.empty())
+ configpath = gba ? BaseGBAROMDir : BaseROMDir;
+
+ if (file.empty())
+ {
+ file = gba ? BaseGBAAssetName : BaseAssetName;
+ if (file.empty())
+ file = "firmware";
+ }
+
+ for (;;)
+ {
+ int i = configpath.length() - 1;
+ if (configpath[i] == '/' || configpath[i] == '\\')
+ configpath = configpath.substr(0, i);
+ else
+ break;
+ }
+
+ if (!configpath.empty())
+ configpath += "/";
+
+ return configpath + file + ext;
+}
+
+
+QString VerifyDSBIOS()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::BIOS9Path, "rb");
+ if (!f) return "DS ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x1000)
+ {
+ fclose(f);
+ return "DS ARM9 BIOS is not a valid BIOS dump.";
+ }
+
+ fclose(f);
+
+ f = Platform::OpenLocalFile(Config::BIOS7Path, "rb");
+ if (!f) return "DS ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x4000)
+ {
+ fclose(f);
+ return "DS ARM7 BIOS is not a valid BIOS dump.";
+ }
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifyDSiBIOS()
+{
+ FILE* f;
+ long len;
+
+ // TODO: check the first 32 bytes
+
+ f = Platform::OpenLocalFile(Config::DSiBIOS9Path, "rb");
+ if (!f) return "DSi ARM9 BIOS was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x10000)
+ {
+ fclose(f);
+ return "DSi ARM9 BIOS is not a valid BIOS dump.";
+ }
+
+ fclose(f);
+
+ f = Platform::OpenLocalFile(Config::DSiBIOS7Path, "rb");
+ if (!f) return "DSi ARM7 BIOS was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x10000)
+ {
+ fclose(f);
+ return "DSi ARM7 BIOS is not a valid BIOS dump.";
+ }
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifyDSFirmware()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::FirmwarePath, "rb");
+ if (!f) return "DS firmware was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len == 0x20000)
+ {
+ // 128KB firmware, not bootable
+ fclose(f);
+ // TODO report it somehow? detect in core?
+ return "";
+ }
+ else if (len != 0x40000 && len != 0x80000)
+ {
+ fclose(f);
+ return "DS firmware is not a valid firmware dump.";
+ }
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifyDSiFirmware()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::DSiFirmwarePath, "rb");
+ if (!f) return "DSi firmware was not found or could not be accessed. Check your emu settings.";
+
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ if (len != 0x20000)
+ {
+ // not 128KB
+ // TODO: check whether those work
+ fclose(f);
+ return "DSi firmware is not a valid firmware dump.";
+ }
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifyDSiNAND()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::DSiNANDPath, "r+b");
+ if (!f) return "DSi NAND was not found or could not be accessed. Check your emu settings.";
+
+ // TODO: some basic checks
+ // check that it has the nocash footer, and all
+
+ fclose(f);
+
+ return "";
+}
+
+QString VerifySetup()
+{
+ QString res;
+
+ if (Config::ExternalBIOSEnable)
+ {
+ res = VerifyDSBIOS();
+ if (!res.isEmpty()) return res;
+ }
+
+ if (Config::ConsoleType == 1)
+ {
+ res = VerifyDSiBIOS();
+ if (!res.isEmpty()) return res;
+
+ if (Config::ExternalBIOSEnable)
+ {
+ res = VerifyDSiFirmware();
+ if (!res.isEmpty()) return res;
+ }
+
+ res = VerifyDSiNAND();
+ if (!res.isEmpty()) return res;
+ }
+ else
+ {
+ if (Config::ExternalBIOSEnable)
+ {
+ res = VerifyDSFirmware();
+ if (!res.isEmpty()) return res;
+ }
+ }
+
+ return "";
+}
+
+
+std::string GetSavestateName(int slot)
+{
+ std::string ext = ".ml";
+ ext += (char)('0'+slot);
+ return GetAssetPath(false, Config::SavestatePath, ext);
+}
+
+bool SavestateExists(int slot)
+{
+ std::string ssfile = GetSavestateName(slot);
+ return Platform::FileExists(ssfile);
+}
+
+bool LoadState(std::string filename)
+{
+ // backup
+ Savestate* backup = new Savestate("timewarp.mln", true);
+ NDS::DoSavestate(backup);
+ delete backup;
+
+ bool failed = false;
+
+ Savestate* state = new Savestate(filename, false);
+ if (state->Error)
+ {
+ delete state;
+
+ // current state might be crapoed, so restore from sane backup
+ state = new Savestate("timewarp.mln", false);
+ failed = true;
+ }
+
+ bool res = NDS::DoSavestate(state);
+ delete state;
+
+ if (!res)
+ {
+ failed = true;
+ state = new Savestate("timewarp.mln", false);
+ NDS::DoSavestate(state);
+ delete state;
+ }
+
+ if (failed) return false;
+
+ if (Config::SavestateRelocSRAM && NDSSave)
+ {
+ PreviousSaveFile = NDSSave->GetPath();
+
+ std::string savefile = filename.substr(LastSep(filename)+1);
+ savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
+ NDSSave->SetPath(savefile, true);
+ }
+
+ SavestateLoaded = true;
+
+ return true;
+}
+
+bool SaveState(std::string filename)
+{
+ Savestate* state = new Savestate(filename, true);
+ if (state->Error)
+ {
+ delete state;
+ return false;
+ }
+
+ NDS::DoSavestate(state);
+ delete state;
+
+ if (Config::SavestateRelocSRAM && NDSSave)
+ {
+ std::string savefile = filename.substr(LastSep(filename)+1);
+ savefile = GetAssetPath(false, Config::SaveFilePath, ".sav", savefile);
+ NDSSave->SetPath(savefile, false);
+ }
+
+ return true;
+}
+
+void UndoStateLoad()
+{
+ if (!SavestateLoaded) return;
+
+ // pray that this works
+ // what do we do if it doesn't???
+ // but it should work.
+ Savestate* backup = new Savestate("timewarp.mln", false);
+ NDS::DoSavestate(backup);
+ delete backup;
+
+ if (NDSSave && (!PreviousSaveFile.empty()))
+ {
+ NDSSave->SetPath(PreviousSaveFile, true);
+ }
+}
+
+
+void UnloadCheats()
+{
+ if (CheatFile)
+ {
+ delete CheatFile;
+ CheatFile = nullptr;
+ }
+}
+
+void LoadCheats()
+{
+ UnloadCheats();
+
+ std::string filename = GetAssetPath(false, Config::CheatFilePath, ".mch");
+
+ // TODO: check for error (malformed cheat file, ...)
+ CheatFile = new ARCodeFile(filename);
+
+ AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
+}
+
+void EnableCheats(bool enable)
+{
+ CheatsOn = enable;
+ if (CheatFile)
+ AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
+}
+
+ARCodeFile* GetCheatFile()
+{
+ return CheatFile;
+}
+
+
+void Reset()
+{
+ NDS::SetConsoleType(Config::ConsoleType);
+ if (Config::ConsoleType == 1) EjectGBACart();
+ NDS::Reset();
+
+ if ((CartType != -1) && NDSSave)
+ {
+ std::string oldsave = NDSSave->GetPath();
+ std::string newsave = GetAssetPath(false, Config::SaveFilePath, ".sav");
+ if (oldsave != newsave)
+ NDSSave->SetPath(newsave, false);
+ }
+
+ if ((GBACartType != -1) && GBASave)
+ {
+ std::string oldsave = GBASave->GetPath();
+ std::string newsave = GetAssetPath(true, Config::SaveFilePath, ".sav");
+ if (oldsave != newsave)
+ GBASave->SetPath(newsave, false);
+ }
+
+ if (!BaseROMName.empty())
+ {
+ if (Config::DirectBoot || NDS::NeedsDirectBoot())
+ {
+ NDS::SetupDirectBoot(BaseROMName);
+ }
+ }
+}
+
+
+bool LoadBIOS()
+{
+ NDS::SetConsoleType(Config::ConsoleType);
+
+ if (NDS::NeedsDirectBoot())
+ return false;
+
+ /*if (NDSSave) delete NDSSave;
+ NDSSave = nullptr;
+
+ CartType = -1;
+ BaseROMDir = "";
+ BaseROMName = "";
+ BaseAssetName = "";*/
+
+ NDS::Reset();
+ return true;
+}
+
+
+bool LoadROM(QStringList filepath, bool reset)
+{
+ if (filepath.empty()) return false;
+
+ u8* filedata;
+ u32 filelen;
+
+ std::string basepath;
+ std::string romname;
+
+ int num = filepath.count();
+ if (num == 1)
+ {
+ // regular file
+
+ std::string filename = filepath.at(0).toStdString();
+ FILE* f = Platform::OpenFile(filename, "rb", true);
+ if (!f) return false;
+
+ fseek(f, 0, SEEK_END);
+ long len = ftell(f);
+ if (len > 0x40000000)
+ {
+ fclose(f);
+ return false;
+ }
+
+ fseek(f, 0, SEEK_SET);
+ filedata = new u8[len];
+ size_t nread = fread(filedata, (size_t)len, 1, f);
+ if (nread != 1)
+ {
+ fclose(f);
+ delete[] filedata;
+ return false;
+ }
+
+ fclose(f);
+ filelen = (u32)len;
+
+ int pos = LastSep(filename);
+ basepath = filename.substr(0, pos);
+ romname = filename.substr(pos+1);
+ }
+#ifdef ARCHIVE_SUPPORT_ENABLED
+ else if (num == 2)
+ {
+ // file inside archive
+
+ u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
+ if (lenread < 0) return false;
+ if (!filedata) return false;
+ if (lenread != filelen)
+ {
+ delete[] filedata;
+ return false;
+ }
+
+ std::string std_archivepath = filepath.at(0).toStdString();
+ basepath = std_archivepath.substr(0, LastSep(std_archivepath));
+
+ std::string std_romname = filepath.at(1).toStdString();
+ romname = std_romname.substr(LastSep(std_romname)+1);
+ }
+#endif
+ else
+ return false;
+
+ if (NDSSave) delete NDSSave;
+ NDSSave = nullptr;
+
+ BaseROMDir = basepath;
+ BaseROMName = romname;
+ BaseAssetName = romname.substr(0, romname.rfind('.'));
+
+ if (reset)
+ {
+ NDS::SetConsoleType(Config::ConsoleType);
+ NDS::Reset();
+ }
+
+ u32 savelen = 0;
+ u8* savedata = nullptr;
+
+ std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav");
+ FILE* sav = Platform::OpenFile(savname, "rb", true);
+ if (sav)
+ {
+ fseek(sav, 0, SEEK_END);
+ savelen = (u32)ftell(sav);
+
+ fseek(sav, 0, SEEK_SET);
+ savedata = new u8[savelen];
+ fread(savedata, savelen, 1, sav);
+ fclose(sav);
+ }
+
+ bool res = NDS::LoadCart(filedata, filelen, savedata, savelen);
+ if (res && reset)
+ {
+ if (Config::DirectBoot || NDS::NeedsDirectBoot())
+ {
+ NDS::SetupDirectBoot(romname);
+ }
+ }
+
+ if (res)
+ {
+ CartType = 0;
+ NDSSave = new SaveManager(savname);
+
+ LoadCheats();
+ }
+
+ if (savedata) delete[] savedata;
+ delete[] filedata;
+ return res;
+}
+
+void EjectCart()
+{
+ if (NDSSave) delete NDSSave;
+ NDSSave = nullptr;
+
+ UnloadCheats();
+
+ NDS::EjectCart();
+
+ CartType = -1;
+ BaseROMDir = "";
+ BaseROMName = "";
+ BaseAssetName = "";
+}
+
+bool CartInserted()
+{
+ return CartType != -1;
+}
+
+QString CartLabel()
+{
+ if (CartType == -1)
+ return "(none)";
+
+ QString ret = QString::fromStdString(BaseROMName);
+
+ int maxlen = 32;
+ if (ret.length() > maxlen)
+ ret = ret.left(maxlen-6) + "..." + ret.right(3);
+
+ return ret;
+}
+
+
+bool LoadGBAROM(QStringList filepath)
+{
+ if (Config::ConsoleType == 1) return false;
+ if (filepath.empty()) return false;
+
+ u8* filedata;
+ u32 filelen;
+
+ std::string basepath;
+ std::string romname;
+
+ int num = filepath.count();
+ if (num == 1)
+ {
+ // regular file
+
+ std::string filename = filepath.at(0).toStdString();
+ FILE* f = Platform::OpenFile(filename, "rb", true);
+ if (!f) return false;
+
+ fseek(f, 0, SEEK_END);
+ long len = ftell(f);
+ if (len > 0x40000000)
+ {
+ fclose(f);
+ return false;
+ }
+
+ fseek(f, 0, SEEK_SET);
+ filedata = new u8[len];
+ size_t nread = fread(filedata, (size_t)len, 1, f);
+ if (nread != 1)
+ {
+ fclose(f);
+ delete[] filedata;
+ return false;
+ }
+
+ fclose(f);
+ filelen = (u32)len;
+
+ int pos = LastSep(filename);
+ basepath = filename.substr(0, pos);
+ romname = filename.substr(pos+1);
+ }
+#ifdef ARCHIVE_SUPPORT_ENABLED
+ else if (num == 2)
+ {
+ // file inside archive
+
+ u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
+ if (lenread < 0) return false;
+ if (!filedata) return false;
+ if (lenread != filelen)
+ {
+ delete[] filedata;
+ return false;
+ }
+
+ std::string std_archivepath = filepath.at(0).toStdString();
+ basepath = std_archivepath.substr(0, LastSep(std_archivepath));
+
+ std::string std_romname = filepath.at(1).toStdString();
+ romname = std_romname.substr(LastSep(std_romname)+1);
+ }
+#endif
+ else
+ return false;
+
+ if (GBASave) delete GBASave;
+ GBASave = nullptr;
+
+ BaseGBAROMDir = basepath;
+ BaseGBAROMName = romname;
+ BaseGBAAssetName = romname.substr(0, romname.rfind('.'));
+
+ u32 savelen = 0;
+ u8* savedata = nullptr;
+
+ std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav");
+ FILE* sav = Platform::OpenFile(savname, "rb", true);
+ if (sav)
+ {
+ fseek(sav, 0, SEEK_END);
+ savelen = (u32)ftell(sav);
+
+ fseek(sav, 0, SEEK_SET);
+ savedata = new u8[savelen];
+ fread(savedata, savelen, 1, sav);
+ fclose(sav);
+ }
+
+ bool res = NDS::LoadGBACart(filedata, filelen, savedata, savelen);
+
+ if (res)
+ {
+ GBACartType = 0;
+ GBASave = new SaveManager(savname);
+ }
+
+ if (savedata) delete[] savedata;
+ delete[] filedata;
+ return res;
+}
+
+void LoadGBAAddon(int type)
+{
+ if (Config::ConsoleType == 1) return;
+
+ if (GBASave) delete GBASave;
+ GBASave = nullptr;
+
+ NDS::LoadGBAAddon(type);
+
+ GBACartType = type;
+ BaseGBAROMDir = "";
+ BaseGBAROMName = "";
+ BaseGBAAssetName = "";
+}
+
+void EjectGBACart()
+{
+ if (GBASave) delete GBASave;
+ GBASave = nullptr;
+
+ NDS::EjectGBACart();
+
+ GBACartType = -1;
+ BaseGBAROMDir = "";
+ BaseGBAROMName = "";
+ BaseGBAAssetName = "";
+}
+
+bool GBACartInserted()
+{
+ return GBACartType != -1;
+}
+
+QString GBACartLabel()
+{
+ if (Config::ConsoleType == 1) return "none (DSi)";
+
+ switch (GBACartType)
+ {
+ case 0:
+ {
+ QString ret = QString::fromStdString(BaseGBAROMName);
+
+ int maxlen = 32;
+ if (ret.length() > maxlen)
+ ret = ret.left(maxlen-6) + "..." + ret.right(3);
+
+ return ret;
+ }
+
+ case NDS::GBAAddon_RAMExpansion:
+ return "Memory expansion";
+ }
+
+ return "(none)";
+}
+
+
+void ROMIcon(u8 (&data)[512], u16 (&palette)[16], u32* iconRef)
+{
+ int index = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ for (int j = 0; j < 4; j++)
+ {
+ for (int k = 0; k < 8; k++)
+ {
+ for (int l = 0; l < 8; l++)
+ {
+ u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F;
+ u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31;
+ u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31;
+ u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31;
+ u8 a = pal_index ? 255: 0;
+ u32* row = &iconRef[256 * i + 32 * k + 8 * j];
+ row[l] = (a << 24) | (r << 16) | (g << 8) | b;
+ index++;
+ }
+ }
+ }
+ }
+}
+
+#define SEQ_FLIPV(i) ((i & 0b1000000000000000) >> 15)
+#define SEQ_FLIPH(i) ((i & 0b0100000000000000) >> 14)
+#define SEQ_PAL(i) ((i & 0b0011100000000000) >> 11)
+#define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8)
+#define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0)
+
+void AnimatedROMIcon(u8 (&data)[8][512], u16 (&palette)[8][16], u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector<int> &animatedSequenceRef)
+{
+ for (int i = 0; i < 64; i++)
+ {
+ if (!sequence[i])
+ break;
+ u32* frame = &animatedTexRef[32 * 32 * i];
+ ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame);
+
+ if (SEQ_FLIPH(sequence[i]))
+ {
+ for (int x = 0; x < 32; x++)
+ {
+ for (int y = 0; y < 32/2; y++)
+ {
+ std::swap(frame[x * 32 + y], frame[x * 32 + (32 - 1 - y)]);
+ }
+ }
+ }
+ if (SEQ_FLIPV(sequence[i]))
+ {
+ for (int x = 0; x < 32/2; x++)
+ {
+ for (int y = 0; y < 32; y++)
+ {
+ std::swap(frame[x * 32 + y], frame[(32 - 1 - x) * 32 + y]);
+ }
+ }
+ }
+
+ for (int j = 0; j < SEQ_DUR(sequence[i]); j++)
+ animatedSequenceRef.push_back(i);
+ }
+}
+
+}