aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/Util_ROM.cpp
diff options
context:
space:
mode:
authorRSDuck <RSDuck@users.noreply.github.com>2020-05-12 16:07:28 +0200
committerRSDuck <rsduck@users.noreply.github.com>2020-06-16 12:06:42 +0200
commite7d076403df7afd6dc8304196211b49e3ed7f464 (patch)
tree1d5ff1e743839f271de77f8bd312c985033c6a89 /src/frontend/Util_ROM.cpp
parent4cff4b52286a7d1a7e40817d52a5d271a937ddc2 (diff)
parentc17f7b100e36edb1c728dbf21c77f9484d1820c6 (diff)
Merge branch 'generic_jit' of https://github.com/Arisotura/melonDS into generic_jit
Diffstat (limited to 'src/frontend/Util_ROM.cpp')
-rw-r--r--src/frontend/Util_ROM.cpp542
1 files changed, 542 insertions, 0 deletions
diff --git a/src/frontend/Util_ROM.cpp b/src/frontend/Util_ROM.cpp
new file mode 100644
index 0000000..8116a93
--- /dev/null
+++ b/src/frontend/Util_ROM.cpp
@@ -0,0 +1,542 @@
+/*
+ Copyright 2016-2020 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 "FrontendUtil.h"
+#include "Config.h"
+#include "qt_sdl/PlatformConfig.h" // FIXME!!!
+#include "Platform.h"
+
+#include "NDS.h"
+#include "GBACart.h"
+
+
+namespace Frontend
+{
+
+char ROMPath [ROMSlot_MAX][1024];
+char SRAMPath [ROMSlot_MAX][1024];
+char PrevSRAMPath[ROMSlot_MAX][1024]; // for savestate 'undo load'
+
+bool SavestateLoaded;
+
+
+void Init_ROM()
+{
+ SavestateLoaded = false;
+
+ memset(ROMPath[ROMSlot_NDS], 0, 1024);
+ memset(ROMPath[ROMSlot_GBA], 0, 1024);
+ memset(SRAMPath[ROMSlot_NDS], 0, 1024);
+ memset(SRAMPath[ROMSlot_GBA], 0, 1024);
+ memset(PrevSRAMPath[ROMSlot_NDS], 0, 1024);
+ memset(PrevSRAMPath[ROMSlot_GBA], 0, 1024);
+}
+
+// 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)
+{
+ strncpy(SRAMPath[slot], ROMPath[slot], 1023);
+ SRAMPath[slot][1023] = '\0';
+ strncpy(SRAMPath[slot] + strlen(ROMPath[slot]) - 3, "sav", 3);
+}
+
+int VerifyDSBIOS()
+{
+ FILE* f;
+ long len;
+
+ 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;
+
+ f = Platform::OpenLocalFile(Config::FirmwarePath, "rb");
+ if (!f) return Load_FirmwareMissing;
+
+ 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 VerifyDSiNAND()
+{
+ FILE* f;
+ long len;
+
+ f = Platform::OpenLocalFile(Config::DSiNANDPath, "rb");
+ if (!f) return Load_DSiNANDMissing;
+
+ // TODO: some basic checks
+ // check that it has the nocash footer, and all
+
+ fclose(f);
+
+ return Load_OK;
+}
+
+int LoadBIOS()
+{
+ 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 = VerifyDSiNAND();
+ 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][0] = '\0';
+ SRAMPath[ROMSlot_NDS][0] = '\0';
+
+ NDS::SetConsoleType(Config::ConsoleType);
+ NDS::LoadBIOS();
+
+ SavestateLoaded = false;
+
+ return Load_OK;
+}
+
+int LoadROM(const char* file, 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 = VerifyDSiNAND();
+ 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(ROMPath[slot], file, 1023);
+ ROMPath[slot][1023] = '\0';
+
+ SetupSRAMPath(0);
+ SetupSRAMPath(1);
+
+ NDS::SetConsoleType(Config::ConsoleType);
+
+ if (slot == ROMSlot_NDS && NDS::LoadROM(ROMPath[slot], SRAMPath[slot], directboot))
+ {
+ SavestateLoaded = false;
+
+ // 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(ROMPath[slot], 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;
+ }
+}
+
+void UnloadROM(int slot)
+{
+ if (slot == ROMSlot_NDS)
+ {
+ // TODO!
+ }
+ else if (slot == ROMSlot_GBA)
+ {
+ GBACart::Eject();
+ }
+
+ ROMPath[slot][0] = '\0';
+}
+
+int Reset()
+{
+ 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 = VerifyDSiNAND();
+ 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][0] == '\0')
+ {
+ NDS::LoadBIOS();
+ }
+ else
+ {
+ SetupSRAMPath(0);
+ if (!NDS::LoadROM(ROMPath[ROMSlot_NDS], SRAMPath[ROMSlot_NDS], directboot))
+ return Load_ROMLoadError;
+ }
+
+ if (ROMPath[ROMSlot_GBA][0] != '\0')
+ {
+ SetupSRAMPath(1);
+ if (!NDS::LoadGBAROM(ROMPath[ROMSlot_GBA], SRAMPath[ROMSlot_GBA]))
+ return Load_ROMLoadError;
+ }
+
+ return Load_OK;
+}
+
+
+// SAVESTATE TODO
+// * configurable paths. not everyone wants their ROM directory to be polluted, I guess.
+
+void GetSavestateName(int slot, char* filename, int len)
+{
+ int pos;
+
+ if (ROMPath[ROMSlot_NDS][0] == '\0') // running firmware, no ROM
+ {
+ strcpy(filename, "firmware");
+ pos = 8;
+ }
+ else
+ {
+ int l = strlen(ROMPath[ROMSlot_NDS]);
+ pos = l;
+ while (ROMPath[ROMSlot_NDS][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);
+ }
+ strcpy(&filename[pos], ".ml");
+ filename[pos+3] = '0'+slot;
+ filename[pos+4] = '\0';
+}
+
+bool SavestateExists(int slot)
+{
+ char ssfile[1024];
+ GetSavestateName(slot, ssfile, 1024);
+ return Platform::FileExists(ssfile);
+}
+
+bool LoadState(const char* 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][0]!='\0')
+ {
+ strncpy(PrevSRAMPath[ROMSlot_NDS], SRAMPath[0], 1024);
+
+ strncpy(SRAMPath[ROMSlot_NDS], filename, 1019);
+ int len = strlen(SRAMPath[ROMSlot_NDS]);
+ strcpy(&SRAMPath[ROMSlot_NDS][len], ".sav");
+ SRAMPath[ROMSlot_NDS][len+4] = '\0';
+
+ NDS::RelocateSave(SRAMPath[ROMSlot_NDS], 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][0] = '\0';
+ SRAMPath[ROMSlot_GBA][0] = '\0';
+ 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(const char* 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][0]!='\0')
+ {
+ strncpy(SRAMPath[ROMSlot_NDS], filename, 1019);
+ int len = strlen(SRAMPath[ROMSlot_NDS]);
+ strcpy(&SRAMPath[ROMSlot_NDS][len], ".sav");
+ SRAMPath[ROMSlot_NDS][len+4] = '\0';
+
+ NDS::RelocateSave(SRAMPath[ROMSlot_NDS], 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][0]!='\0')
+ {
+ strncpy(SRAMPath[ROMSlot_NDS], PrevSRAMPath[ROMSlot_NDS], 1024);
+ NDS::RelocateSave(SRAMPath[ROMSlot_NDS], false);
+ }
+}
+
+}