diff options
Diffstat (limited to 'src/frontend/qt_sdl/ROMManager.cpp')
-rw-r--r-- | src/frontend/qt_sdl/ROMManager.cpp | 843 |
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); + } +} + +} |