aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/qt_sdl
diff options
context:
space:
mode:
authorJesse Talavera <jesse@jesse.tg>2023-12-04 11:57:22 -0500
committerGitHub <noreply@github.com>2023-12-04 17:57:22 +0100
commitbb42c8b6392e4e99634dc52137d2974781192482 (patch)
tree6b2de3d553aa331567e7ab0efa89ed841732f943 /src/frontend/qt_sdl
parentda8d413ad9e339c40178be18da20aee1435c8cd4 (diff)
Refactor how save data (including SD cards) is initialized (#1898)
* Remove `FATStorage::Open` and `FATStorage::Close` - That's what the constructor and destructor are for, respectively * Add `FATStorage::IsReadOnly` * Slight cleanup of `FATStorage` - Make it move-constructible and move-assignable - Represent the absence of a sync directory with `std::optional`, not an empty string - Add `FATStorageArgs` for later use * Refactor `CartHomebrew` to accept an optional `FATStorageArgs` - `CartHomebrew` uses it to load an SD card image - Not passing a `FATStorage` directly because we won't know if we need to load the card until we parse the ROM - Store the `FATStorage` inside a `std::optional` instead of a pointer - `CartHomebrew::Reset` no longer reloads the SD card; the frontend needs to set it with the `SetSDCard` method * Close `NANDImage::CurFile` when move-assigning - Whoops * Add `Args.h` - To construct a `NDS` or `DSi` with arguments - Mostly intended for system files * Fix incorrect `final` placement * Refactor how `DSi`'s NAND and SD card are set - Provide them via a `DSiArgs` argument in the constructor - Give `DSi_MMCStorage` ownership of the `NANDImage` or `FATStorage` as needed, and expose getters/setters - Replace `DSi_SDHost::Ports` with a `array<unique_ptr, 2>` to reduce the risk of leaks - Store `DSi_MMCStorage`'s disk images in a `std::variant` - The SD card and NAND image are no longer reset in `Reset()`; the frontend will need to do that itself * Add getters/setters on `DSi` itself for its storage media * Remove newly-unused `Platform::ConfigEntry`s * Use `DSi::SetNAND` in the frontend * Add `EmuThread::NeedToRecreateConsole` * Document `NDSArgs` and give its fields default values * Refactor how system files are loaded upon construction - Pass `NDSArgs&&` into `NDS`'s constructor - Use `std::array` for the emulator's BIOS images and the built-in FreeBIOS, to simplify copying and comparison - Initialize the BIOS, firmware, and SD cards from `NDSArgs` or `DSiArgs` - Add a new default constructor for `NDS` (not `DSi`) that initializes the DS with default system files - Embed `FirmwareMem::Firmware` directly instead of in a `unique_ptr` - `SPIHost` now takes a `Firmware&&` that it forwards to `FirmwareMem` - Add `Firmware` getters/setters plus `const` variants for `NDS`, `Firmware`, and `FirmwareMem` - Simplify installation of firmware * Initialize the DSi BIOS in the constructor - Change `DSi::ARM9iBIOS` and `ARM7iBIOS` to `std::array` * Update the frontend to reflect the core's changes * Remove `DSi_SDHost::CloseHandles` * Pass `nullopt` instead of the empty string when folder sync is off * Deduplicate ROM extraction logic - `LoadGBAROM` and `LoadROM` now delegate to `LoadROMData` - Also use `unique_ptr` instead of `new[]` * Oops, missed some `get()`'s * Move `NDS::IsLoadedARM9BIOSBuiltIn` to the header - So it's likelier to be inlined - Same for the ARM7 version * Remove `NDS::SetConsoleType` * Add `NDS::SetFirmware` * Move `GBACart::SetupSave` to be `protected` - It was only ever used inside the class * Rename `GBACart::LoadSave` to `SetSaveMemory` - Same for the cart slot * Declare `GBACartSlot` as a friend of `GBACart::CartCommon` * Revise `GBACartSlot`'s getters and setters - Rename `InsertROM` and `LoadROM` to `SetCart` - Add a `GetCart` method * Clean up getters and setters for NDS and GBA carts * Clean up how carts are inserted into the slots - Remove setters that operate directly on pointers, to simplify error-handling (use ParseROM instead) - Add overloads for all carts that accept a `const u8*` (to copy the ROM data) and a `unique_ptr<u8[]>` (to move the ROM data) - Store all ROM and RAM data in `unique_ptr` - Default-initialize all fields - Simplify constructors and destructors, inheriting where applicable * Refactor GBA save data insertion - Make `SetupSave` private and non-virtual and move its logic to be in `SetSaveMemory` - Add overloads for setting save data in the constructor - Update the SRAM completely in `SetSaveMemory` * Clean up `NDSCart::CartCommon::SetSaveMemory` - Move its declaration next to the other `SaveMemory` methods - Move its (empty) implementation to the header * Add some comments * Add Utils.cpp and Utils.h * Rename some functions in Utils for clarity * Add `GBACart::ParseROM` and `NDSCart::ParseROM` overloads that accept `unique_ptr<u8[]>` - The `u8*` overloads delegate to these new overloads - Also move `SetupSave` for both kinds of carts to be private non-virtual methods * Finalize the `NDSCart` refactor - Add `NDSCartArgs` to pass to `ParseROM` - Add SRAM arguments for all retail carts - Initialize SRAM inside the constructor - Delegate to other constructors where possible * Replace `ROMManager::NDSSave` and `GBASave` with `unique_ptr` * Make both cart slots return the previously-inserted cart in `EjectCart` - Primarily intended for reusing carts when resetting the console * Make `NDS::EjectCart` return the old cart * Initialize both cart slots with the provided ROM (if any) * Make `NDS::EjectGBACart` return the ejected cart * Clean up some comments in Args.h * Rename `ROMManager::LoadBIOS` to `BootToMenu` - Clarifies the intent * Add `ROMManager::LoadDLDISDCard` * Add a doc comment * Refactor how the `NDS` is created or updated - Rewrite `CreateConsole` to read from `Config` and load system files, but accept carts as arguments - Fail without creating an `NDS` if any required system file doesn't load - Add `UpdateConsole`, which delegates to `CreateConsole` if switching modes or starting the app - Use `std::variant` to indicate whether a cart should be removed, inserted, or reused - Load all system files (plus SD cards) in `UpdateConsole` - Eject the cart and reinsert it into the new console if applicable * Respect some more `Config` settings in the `Load*` functions * Remove `InstallNAND` in favor of `LoadNAND` * Fix some potential bugs in `LoadROMData` * Oops, forgot to delete the definition of `InstallNAND` * Add functions to get `FATStorageArgs` - Not the cards themselves, but to get the arguments you _would_ use to load the cards * Refactor `ROMManager::LoadROM` - Load the ROM and save data before trying to initialize the console * Clean up `ROMManager::Reset` and `BootToMenu` - Let `EmuThread::UpdateConsole` do the heavy lifting * Clean up `LoadGBAROM` * Remove some unused functions * Set the default DSi BIOS to be broken in `DSiArgs` * Respect `Config::DSiFullBIOSBoot` when loading DSi BIOS files * Remove some more unused functions * Remove redundant `virtual` specifiers * Refactor `NDSCart::CartCommon::Type()` to return a member instead of a constant - One less virtual dispatch - The cart type is read in `NDSCartSlot::DoSavestate`, which is a path downstream (due to rewinding) * Remove a hash that I computed for debugging purposes * Make `ByteSwap` `constexpr` * Remove an unused `#include` * Remove unnecessary functions from the NDS carts - Mostly overrides that added nothing * Default-initialize all NDSCart fields * Make `GBACart::Type()` not rely on virtual dispatch - `GBACartSlot::DoSavestate` calls it, and savestates can be a hot path downstream * Don't forget to reset the base class in `CartGameSolarSensor::Reset()` * Remove redundant `virtual` specifiers * Default-initialize some fields in `GBACart` * Fix ROMs not loading from archives in the frontend - Whoops * Change how the `Firmware` member is declared * Forgot an include in Utils.cpp * Rename `FirmwareMem::Firmware` to `FirmwareData` to fix a build error on Linux - One of these days I'll convince you people to let me use `camelCaseMemberNames` * Add `override` to places in `DSi_MMCStorage` that warrant it * Fix firmware saving on the frontend - Remove `GetConfigString` and `ConfigEntry::WifiSettingsPath` while I'm at it * Add a non-const `GetNAND()`
Diffstat (limited to 'src/frontend/qt_sdl')
-rw-r--r--src/frontend/qt_sdl/ArchiveUtil.cpp7
-rw-r--r--src/frontend/qt_sdl/ArchiveUtil.h2
-rw-r--r--src/frontend/qt_sdl/Platform.cpp28
-rw-r--r--src/frontend/qt_sdl/ROMManager.cpp647
-rw-r--r--src/frontend/qt_sdl/ROMManager.h19
-rw-r--r--src/frontend/qt_sdl/main.cpp166
-rw-r--r--src/frontend/qt_sdl/main.h21
7 files changed, 387 insertions, 503 deletions
diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp
index 7d1eca9..aa508e8 100644
--- a/src/frontend/qt_sdl/ArchiveUtil.cpp
+++ b/src/frontend/qt_sdl/ArchiveUtil.cpp
@@ -120,13 +120,12 @@ QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteA
}
-s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize)
+s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr<u8[]>& filedata, u32* filesize)
{
struct archive *a = archive_read_new();
struct archive_entry *entry;
int r;
- if (!filedata) return -1;
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
@@ -148,8 +147,8 @@ s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32*
size_t bytesToRead = archive_entry_size(entry);
if (filesize) *filesize = bytesToRead;
- *filedata = new u8[bytesToRead];
- ssize_t bytesRead = archive_read_data(a, *filedata, bytesToRead);
+ filedata = std::make_unique<u8[]>(bytesToRead);
+ ssize_t bytesRead = archive_read_data(a, filedata.get(), bytesToRead);
archive_read_close(a);
archive_read_free(a);
diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h
index eaffb0d..246670e 100644
--- a/src/frontend/qt_sdl/ArchiveUtil.h
+++ b/src/frontend/qt_sdl/ArchiveUtil.h
@@ -37,7 +37,7 @@ namespace Archive
using namespace melonDS;
QVector<QString> ListArchive(QString path);
-s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize);
+s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr<u8[]>& filedata, u32* filesize);
//QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer);
//u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata);
diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp
index c2e2f47..d410d4f 100644
--- a/src/frontend/qt_sdl/Platform.cpp
+++ b/src/frontend/qt_sdl/Platform.cpp
@@ -204,10 +204,6 @@ int GetConfigInt(ConfigEntry entry)
case JIT_MaxBlockSize: return Config::JIT_MaxBlockSize;
#endif
- case DLDI_ImageSize: return imgsizes[Config::DLDISize];
-
- case DSiSD_ImageSize: return imgsizes[Config::DSiSDSize];
-
case AudioBitDepth: return Config::AudioBitDepth;
#ifdef GDBSTUB_ENABLED
@@ -232,14 +228,6 @@ bool GetConfigBool(ConfigEntry entry)
case ExternalBIOSEnable: return Config::ExternalBIOSEnable != 0;
- case DLDI_Enable: return Config::DLDIEnable != 0;
- case DLDI_ReadOnly: return Config::DLDIReadOnly != 0;
- case DLDI_FolderSync: return Config::DLDIFolderSync != 0;
-
- case DSiSD_Enable: return Config::DSiSDEnable != 0;
- case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0;
- case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0;
-
case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0;
#ifdef GDBSTUB_ENABLED
@@ -252,22 +240,6 @@ bool GetConfigBool(ConfigEntry entry)
return false;
}
-std::string GetConfigString(ConfigEntry entry)
-{
- switch (entry)
- {
- case DLDI_ImagePath: return Config::DLDISDPath;
- case DLDI_FolderPath: return Config::DLDIFolderPath;
-
- case DSiSD_ImagePath: return Config::DSiSDPath;
- case DSiSD_FolderPath: return Config::DSiSDFolderPath;
-
- case WifiSettingsPath: return Config::WifiSettingsPath;
- }
-
- return "";
-}
-
bool GetConfigArray(ConfigEntry entry, void* data)
{
switch (entry)
diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp
index 2c0cee2..fda043a 100644
--- a/src/frontend/qt_sdl/ROMManager.cpp
+++ b/src/frontend/qt_sdl/ROMManager.cpp
@@ -68,8 +68,8 @@ std::string BaseGBAROMDir = "";
std::string BaseGBAROMName = "";
std::string BaseGBAAssetName = "";
-SaveManager* NDSSave = nullptr;
-SaveManager* GBASave = nullptr;
+std::unique_ptr<SaveManager> NDSSave = nullptr;
+std::unique_ptr<SaveManager> GBASave = nullptr;
std::unique_ptr<SaveManager> FirmwareSave = nullptr;
std::unique_ptr<Savestate> BackupState = nullptr;
@@ -303,6 +303,28 @@ QString VerifySetup()
return "";
}
+std::string GetEffectiveFirmwareSavePath(EmuThread* thread)
+{
+ if (!Config::ExternalBIOSEnable)
+ {
+ return Config::WifiSettingsPath;
+ }
+ if (thread->NDS->ConsoleType == 1)
+ {
+ return Config::DSiFirmwarePath;
+ }
+ else
+ {
+ return Config::FirmwarePath;
+ }
+}
+
+// Initializes the firmware save manager with the selected firmware image's path
+// OR the path to the wi-fi settings.
+void InitFirmwareSaveManager(EmuThread* thread) noexcept
+{
+ FirmwareSave = std::make_unique<SaveManager>(GetEffectiveFirmwareSavePath(thread));
+}
std::string GetSavestateName(int slot)
{
@@ -482,6 +504,11 @@ void LoadCheats(NDS& nds)
std::optional<std::array<u8, ARM9BIOSSize>> LoadARM9BIOS() noexcept
{
+ if (!Config::ExternalBIOSEnable)
+ {
+ return Config::ConsoleType == 0 ? std::make_optional(bios_arm9_bin) : std::nullopt;
+ }
+
if (FileHandle* f = OpenLocalFile(Config::BIOS9Path, Read))
{
std::array<u8, ARM9BIOSSize> bios {};
@@ -498,6 +525,11 @@ std::optional<std::array<u8, ARM9BIOSSize>> LoadARM9BIOS() noexcept
std::optional<std::array<u8, ARM7BIOSSize>> LoadARM7BIOS() noexcept
{
+ if (!Config::ExternalBIOSEnable)
+ {
+ return Config::ConsoleType == 0 ? std::make_optional(bios_arm7_bin) : std::nullopt;
+ }
+
if (FileHandle* f = OpenLocalFile(Config::BIOS7Path, Read))
{
std::array<u8, ARM7BIOSSize> bios {};
@@ -518,6 +550,16 @@ std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM9BIOS() noexcept
std::array<u8, DSiBIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(f);
+
+ if (!Config::DSiFullBIOSBoot)
+ {
+ // herp
+ *(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector
+
+ // TODO!!!!
+ // hax the upper 32K out of the goddamn DSi
+ // done that :) -pcy
+ }
Log(Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str());
return bios;
}
@@ -533,6 +575,16 @@ std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM7BIOS() noexcept
std::array<u8, DSiBIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(f);
+
+ if (!Config::DSiFullBIOSBoot)
+ {
+ // herp
+ *(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector
+
+ // TODO!!!!
+ // hax the upper 32K out of the goddamn DSi
+ // done that :) -pcy
+ }
Log(Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str());
return bios;
}
@@ -589,6 +641,16 @@ Firmware GenerateFirmware(int type) noexcept
std::optional<Firmware> LoadFirmware(int type) noexcept
{
+ if (!Config::ExternalBIOSEnable)
+ { // If we're using built-in firmware...
+ if (type == 1)
+ {
+ Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n");
+ return std::nullopt;
+ }
+
+ return GenerateFirmware(type);
+ }
const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath;
Log(Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str());
@@ -609,7 +671,10 @@ std::optional<Firmware> LoadFirmware(int type) noexcept
return std::nullopt;
}
- CustomizeFirmware(firmware);
+ if (Config::FirmwareOverrideSettings)
+ {
+ CustomizeFirmware(firmware);
+ }
return firmware;
}
@@ -694,7 +759,20 @@ std::optional<DSi_NAND::NANDImage> LoadNAND(const std::array<u8, DSiBIOSSize>& a
return nandImage;
}
-constexpr int imgsizes[] = {0, 256, 512, 1024, 2048, 4096};
+constexpr u64 imgsizes[] = {0, 256, 512, 1024, 2048, 4096};
+std::optional<FATStorageArgs> GetDSiSDCardArgs() noexcept
+{
+ if (!Config::DSiSDEnable)
+ return std::nullopt;
+
+ return FATStorageArgs {
+ Config::DSiSDPath,
+ imgsizes[Config::DSiSDSize],
+ Config::DSiSDReadOnly,
+ Config::DSiSDFolderSync ? std::make_optional(Config::DSiSDFolderPath) : std::nullopt
+ };
+}
+
std::optional<FATStorage> LoadDSiSDCard() noexcept
{
if (!Config::DSiSDEnable)
@@ -704,97 +782,34 @@ std::optional<FATStorage> LoadDSiSDCard() noexcept
Config::DSiSDPath,
imgsizes[Config::DSiSDSize],
Config::DSiSDReadOnly,
- Config::DSiSDFolderSync ? Config::DSiSDFolderPath : ""
+ Config::DSiSDFolderSync ? std::make_optional(Config::DSiSDFolderPath) : std::nullopt
);
}
-void LoadBIOSFiles(NDS& nds)
+std::optional<FATStorageArgs> GetDLDISDCardArgs() noexcept
{
- if (Config::ExternalBIOSEnable)
- {
- if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS9Path, FileMode::Read))
- {
- FileRewind(f);
- FileRead(nds.ARM9BIOS, sizeof(NDS::ARM9BIOS), 1, f);
-
- Log(LogLevel::Info, "ARM9 BIOS loaded from %s\n", Config::BIOS9Path.c_str());
- Platform::CloseFile(f);
- }
- else
- {
- Log(LogLevel::Warn, "ARM9 BIOS not found\n");
-
- for (int i = 0; i < 16; i++)
- ((u32*)nds.ARM9BIOS)[i] = 0xE7FFDEFF;
- }
-
- if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS7Path, FileMode::Read))
- {
- FileRead(nds.ARM7BIOS, sizeof(NDS::ARM7BIOS), 1, f);
-
- Log(LogLevel::Info, "ARM7 BIOS loaded from\n", Config::BIOS7Path.c_str());
- Platform::CloseFile(f);
- }
- else
- {
- Log(LogLevel::Warn, "ARM7 BIOS not found\n");
-
- for (int i = 0; i < 16; i++)
- ((u32*)nds.ARM7BIOS)[i] = 0xE7FFDEFF;
- }
- }
- else
- {
- Log(LogLevel::Info, "Using built-in ARM7 and ARM9 BIOSes\n");
- memcpy(nds.ARM9BIOS, bios_arm9_bin, sizeof(bios_arm9_bin));
- memcpy(nds.ARM7BIOS, bios_arm7_bin, sizeof(bios_arm7_bin));
- }
-
- if (Config::ConsoleType == 1)
- {
- DSi& dsi = static_cast<DSi&>(nds);
- if (FileHandle* f = Platform::OpenLocalFile(Config::DSiBIOS9Path, FileMode::Read))
- {
- FileRead(dsi.ARM9iBIOS, sizeof(DSi::ARM9iBIOS), 1, f);
-
- Log(LogLevel::Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str());
- Platform::CloseFile(f);
- }
- else
- {
- Log(LogLevel::Warn, "ARM9i BIOS not found\n");
-
- for (int i = 0; i < 16; i++)
- ((u32*)dsi.ARM9iBIOS)[i] = 0xE7FFDEFF;
- }
-
- if (FileHandle* f = Platform::OpenLocalFile(Config::DSiBIOS7Path, FileMode::Read))
- {
- // TODO: check if the first 32 bytes are crapoed
- FileRead(dsi.ARM7iBIOS, sizeof(DSi::ARM7iBIOS), 1, f);
-
- Log(LogLevel::Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str());
- CloseFile(f);
- }
- else
- {
- Log(LogLevel::Warn, "ARM7i BIOS not found\n");
+ if (!Config::DLDIEnable)
+ return std::nullopt;
- for (int i = 0; i < 16; i++)
- ((u32*)dsi.ARM7iBIOS)[i] = 0xE7FFDEFF;
- }
+ return FATStorageArgs{
+ Config::DLDISDPath,
+ imgsizes[Config::DLDISize],
+ Config::DLDIReadOnly,
+ Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt
+ };
+}
- if (!Config::DSiFullBIOSBoot)
- {
- // herp
- *(u32*)&dsi.ARM9iBIOS[0] = 0xEAFFFFFE;
- *(u32*)&dsi.ARM7iBIOS[0] = 0xEAFFFFFE;
+std::optional<FATStorage> LoadDLDISDCard() noexcept
+{
+ if (!Config::DLDIEnable)
+ return std::nullopt;
- // TODO!!!!
- // hax the upper 32K out of the goddamn DSi
- // done that :) -pcy
- }
- }
+ return FATStorage(
+ Config::DLDISDPath,
+ imgsizes[Config::DLDISize],
+ Config::DLDIReadOnly,
+ Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt
+ );
}
void EnableCheats(NDS& nds, bool enable)
@@ -835,16 +850,10 @@ void SetDateTime(NDS& nds)
void Reset(EmuThread* thread)
{
- thread->RecreateConsole();
+ thread->UpdateConsole(Keep {}, Keep {});
if (Config::ConsoleType == 1) EjectGBACart(*thread->NDS);
- LoadBIOSFiles(*thread->NDS);
- InstallFirmware(*thread->NDS);
- if (Config::ConsoleType == 1)
- {
- InstallNAND(static_cast<DSi&>(*thread->NDS));
- }
thread->NDS->Reset();
SetBatteryLevels(*thread->NDS);
SetDateTime(*thread->NDS);
@@ -867,6 +876,7 @@ void Reset(EmuThread* thread)
GBASave->SetPath(newsave, false);
}
+ InitFirmwareSaveManager(thread);
if (FirmwareSave)
{
std::string oldsave = FirmwareSave->GetPath();
@@ -899,36 +909,25 @@ void Reset(EmuThread* thread)
}
-bool LoadBIOS(EmuThread* thread)
+bool BootToMenu(EmuThread* thread)
{
- thread->RecreateConsole();
-
- LoadBIOSFiles(*thread->NDS);
-
- if (!InstallFirmware(*thread->NDS))
- return false;
-
- if (Config::ConsoleType == 1 && !InstallNAND(static_cast<DSi&>(*thread->NDS)))
+ // Keep whatever cart is in the console, if any.
+ if (!thread->UpdateConsole(Keep {}, Keep {}))
+ // Try to update the console, but keep the existing cart. If that fails...
return false;
+ // BIOS and firmware files are loaded, patched, and installed in UpdateConsole
if (thread->NDS->NeedsDirectBoot())
return false;
- /*if (NDSSave) delete NDSSave;
- NDSSave = nullptr;
-
- CartType = -1;
- BaseROMDir = "";
- BaseROMName = "";
- BaseAssetName = "";*/
-
+ InitFirmwareSaveManager(thread);
thread->NDS->Reset();
SetBatteryLevels(*thread->NDS);
SetDateTime(*thread->NDS);
return true;
}
-u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
+u32 DecompressROM(const u8* inContent, const u32 inSize, unique_ptr<u8[]>& outContent)
{
u64 realSize = ZSTD_getFrameContentSize(inContent, inSize);
const u32 maxSize = 0x40000000;
@@ -940,16 +939,15 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
if (realSize != ZSTD_CONTENTSIZE_UNKNOWN)
{
- u8* realContent = new u8[realSize];
- u64 decompressed = ZSTD_decompress(realContent, realSize, inContent, inSize);
+ outContent = make_unique<u8[]>(realSize);
+ u64 decompressed = ZSTD_decompress(outContent.get(), realSize, inContent, inSize);
if (ZSTD_isError(decompressed))
{
- delete[] realContent;
+ outContent = nullptr;
return 0;
}
- *outContent = realContent;
return realSize;
}
else
@@ -1005,8 +1003,8 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
} while (inBuf.pos < inBuf.size);
ZSTD_freeDStream(dStream);
- *outContent = new u8[outBuf.pos];
- memcpy(*outContent, outBuf.dst, outBuf.pos);
+ outContent = make_unique<u8[]>(outBuf.pos);
+ memcpy(outContent.get(), outBuf.dst, outBuf.pos);
ZSTD_freeDStream(dStream);
free(outBuf.dst);
@@ -1023,42 +1021,6 @@ void ClearBackupState()
}
}
-// We want both the firmware object and the path that was used to load it,
-// since we'll need to give it to the save manager later
-pair<unique_ptr<Firmware>, string> LoadFirmwareFromFile()
-{
- string loadedpath;
- unique_ptr<Firmware> firmware = nullptr;
- string firmwarepath = Config::ConsoleType == 0 ? Config::FirmwarePath : Config::DSiFirmwarePath;
-
- Log(LogLevel::Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str());
-
- string firmwareinstancepath = firmwarepath + Platform::InstanceFileSuffix();
-
- loadedpath = firmwareinstancepath;
- FileHandle* f = Platform::OpenLocalFile(firmwareinstancepath, FileMode::Read);
- if (!f)
- {
- loadedpath = firmwarepath;
- f = Platform::OpenLocalFile(firmwarepath, FileMode::Read);
- }
-
- if (f)
- {
- firmware = make_unique<Firmware>(f);
- if (!firmware->Buffer())
- {
- Log(LogLevel::Warn, "Couldn't read firmware file!\n");
- firmware = nullptr;
- loadedpath = "";
- }
-
- CloseFile(f);
- }
-
- return std::make_pair(std::move(firmware), loadedpath);
-}
-
pair<unique_ptr<Firmware>, string> GenerateDefaultFirmware()
{
// Construct the default firmware...
@@ -1068,7 +1030,7 @@ pair<unique_ptr<Firmware>, string> GenerateDefaultFirmware()
// Try to open the instanced Wi-fi settings, falling back to the regular Wi-fi settings if they don't exist.
// We don't need to save the whole firmware, just the part that may actually change.
- std::string wfcsettingspath = Platform::GetConfigString(ConfigEntry::WifiSettingsPath);
+ std::string wfcsettingspath = Config::WifiSettingsPath;
settingspath = wfcsettingspath + Platform::InstanceFileSuffix();
FileHandle* f = Platform::OpenLocalFile(settingspath, FileMode::Read);
if (!f)
@@ -1201,155 +1163,12 @@ void CustomizeFirmware(Firmware& firmware) noexcept
firmware.UpdateChecksums();
}
-static Platform::FileHandle* OpenNANDFile() noexcept
-{
- std::string nandpath = Config::DSiNANDPath;
- std::string instnand = nandpath + Platform::InstanceFileSuffix();
-
- FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting);
- if ((!nandfile) && (Platform::InstanceID() > 0))
- {
- FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read);
- if (!orig)
- {
- Log(LogLevel::Error, "Failed to open DSi NAND from %s\n", nandpath.c_str());
- return nullptr;
- }
-
- QFile::copy(QString::fromStdString(nandpath), QString::fromStdString(instnand));
-
- nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting);
- }
-
- return nandfile;
-}
-
-bool InstallNAND(DSi& dsi)
-{
- Platform::FileHandle* nandfile = OpenNANDFile();
- if (!nandfile)
- return false;
-
- DSi_NAND::NANDImage nandImage(nandfile, &dsi.ARM7iBIOS[0x8308]);
- if (!nandImage)
- {
- Log(LogLevel::Error, "Failed to parse DSi NAND\n");
- return false;
- }
-
- // scoped so that mount isn't alive when we move the NAND image to DSi::NANDImage
- {
- auto mount = DSi_NAND::NANDMount(nandImage);
- if (!mount)
- {
- Log(LogLevel::Error, "Failed to mount DSi NAND\n");
- return false;
- }
-
- DSi_NAND::DSiFirmwareSystemSettings settings {};
- if (!mount.ReadUserData(settings))
- {
- Log(LogLevel::Error, "Failed to read DSi NAND user data\n");
- return false;
- }
-
- // override user settings, if needed
- if (Config::FirmwareOverrideSettings)
- {
- // we store relevant strings as UTF-8, so we need to convert them to UTF-16
- auto converter = wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{};
-
- // setting up username
- std::u16string username = converter.from_bytes(Config::FirmwareUsername);
- size_t usernameLength = std::min(username.length(), (size_t) 10);
- memset(&settings.Nickname, 0, sizeof(settings.Nickname));
- memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t));
-
- // setting language
- settings.Language = static_cast<Firmware::Language>(Config::FirmwareLanguage);
-
- // setting up color
- settings.FavoriteColor = Config::FirmwareFavouriteColour;
-
- // setting up birthday
- settings.BirthdayMonth = Config::FirmwareBirthdayMonth;
- settings.BirthdayDay = Config::FirmwareBirthdayDay;
-
- // setup message
- std::u16string message = converter.from_bytes(Config::FirmwareMessage);
- size_t messageLength = std::min(message.length(), (size_t) 26);
- memset(&settings.Message, 0, sizeof(settings.Message));
- memcpy(&settings.Message, message.data(), messageLength * sizeof(char16_t));
-
- // TODO: make other items configurable?
- }
-
- // fix touchscreen coords
- settings.TouchCalibrationADC1 = {0, 0};
- settings.TouchCalibrationPixel1 = {0, 0};
- settings.TouchCalibrationADC2 = {255 << 4, 191 << 4};
- settings.TouchCalibrationPixel2 = {255, 191};
-
- settings.UpdateHash();
-
- if (!mount.ApplyUserData(settings))
- {
- Log(LogLevel::Error, "Failed to write patched DSi NAND user data\n");
- return false;
- }
- }
-
- dsi.NANDImage = std::make_unique<DSi_NAND::NANDImage>(std::move(nandImage));
- return true;
-}
-
-bool InstallFirmware(NDS& nds)
-{
- FirmwareSave.reset();
- unique_ptr<Firmware> firmware;
- string firmwarepath;
- bool generated = false;
-
- if (Config::ExternalBIOSEnable)
- { // If we want to try loading a firmware dump...
-
- tie(firmware, firmwarepath) = LoadFirmwareFromFile();
- if (!firmware)
- { // Try to load the configured firmware dump. If that fails...
- Log(LogLevel::Warn, "Firmware not found! Generating default firmware.\n");
- }
- }
-
- if (!firmware)
- { // If we haven't yet loaded firmware (either because the load failed or we want to use the default...)
- tie(firmware, firmwarepath) = GenerateDefaultFirmware();
- }
-
- if (!firmware)
- return false;
-
- if (Config::FirmwareOverrideSettings)
- {
- CustomizeFirmware(*firmware);
- }
-
- FirmwareSave = std::make_unique<SaveManager>(firmwarepath);
-
- return nds.SPI.GetFirmwareMem()->InstallFirmware(std::move(firmware));
-}
-
-bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
+// Loads ROM data without parsing it. Works for GBA and NDS ROMs.
+bool LoadROMData(const QStringList& filepath, std::unique_ptr<u8[]>& filedata, u32& filelen, string& basepath, string& romname) noexcept
{
if (filepath.empty()) return false;
- u8* filedata = nullptr;
- u32 filelen;
-
- std::string basepath;
- std::string romname;
-
- int num = filepath.count();
- if (num == 1)
+ if (int num = filepath.count(); num == 1)
{
// regular file
@@ -1361,38 +1180,35 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
if (len > 0x40000000)
{
Platform::CloseFile(f);
- delete[] filedata;
return false;
}
Platform::FileRewind(f);
- filedata = new u8[len];
- size_t nread = Platform::FileRead(filedata, (size_t)len, 1, f);
+ filedata = make_unique<u8[]>(len);
+ size_t nread = Platform::FileRead(filedata.get(), (size_t)len, 1, f);
+ Platform::CloseFile(f);
if (nread != 1)
{
- Platform::CloseFile(f);
- delete[] filedata;
+ filedata = nullptr;
return false;
}
- Platform::CloseFile(f);
filelen = (u32)len;
if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst")
{
- u8* outContent = nullptr;
- u32 decompressed = DecompressROM(filedata, len, &outContent);
+ filelen = DecompressROM(filedata.get(), len, filedata);
- if (decompressed > 0)
+ if (filelen > 0)
{
- delete[] filedata;
- filedata = outContent;
- filelen = decompressed;
filename = filename.substr(0, filename.length() - 4);
}
else
{
- delete[] filedata;
+ filedata = nullptr;
+ filelen = 0;
+ basepath = "";
+ romname = "";
return false;
}
}
@@ -1400,19 +1216,21 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
int pos = LastSep(filename);
if(pos != -1)
basepath = filename.substr(0, pos);
+
romname = filename.substr(pos+1);
+ return true;
}
#ifdef ARCHIVE_SUPPORT_ENABLED
else if (num == 2)
{
// file inside archive
- s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
+ s32 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;
+ filedata = nullptr;
return false;
}
@@ -1421,38 +1239,31 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
std::string std_romname = filepath.at(1).toStdString();
romname = std_romname.substr(LastSep(std_romname)+1);
+ return true;
}
#endif
else
return false;
+}
+
+bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
+{
+ unique_ptr<u8[]> filedata = nullptr;
+ u32 filelen;
+ std::string basepath;
+ std::string romname;
+
+ if (!LoadROMData(filepath, filedata, filelen, basepath, romname))
+ return false;
- if (NDSSave) delete NDSSave;
NDSSave = nullptr;
BaseROMDir = basepath;
BaseROMName = romname;
BaseAssetName = romname.substr(0, romname.rfind('.'));
- emuthread->RecreateConsole();
- if (!InstallFirmware(*emuthread->NDS))
- {
- return false;
- }
-
- if (reset)
- {
- emuthread->NDS->EjectCart();
- LoadBIOSFiles(*emuthread->NDS);
- if (Config::ConsoleType == 1)
- InstallNAND(static_cast<DSi&>(*emuthread->NDS));
-
- emuthread->NDS->Reset();
- SetBatteryLevels(*emuthread->NDS);
- SetDateTime(*emuthread->NDS);
- }
-
u32 savelen = 0;
- u8* savedata = nullptr;
+ std::unique_ptr<u8[]> savedata = nullptr;
std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav");
std::string origsav = savname;
@@ -1465,36 +1276,56 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
savelen = (u32)Platform::FileLength(sav);
FileRewind(sav);
- savedata = new u8[savelen];
- FileRead(savedata, savelen, 1, sav);
+ savedata = std::make_unique<u8[]>(savelen);
+ FileRead(savedata.get(), savelen, 1, sav);
CloseFile(sav);
}
- bool res = emuthread->NDS->LoadCart(filedata, filelen, savedata, savelen);
- if (res && reset)
+ NDSCart::NDSCartArgs cartargs {
+ // Don't load the SD card itself yet, because we don't know if
+ // the ROM is homebrew or not.
+ // So this is the card we *would* load if the ROM were homebrew.
+ .SDCard = GetDLDISDCardArgs(),
+
+ .SRAM = std::make_pair(std::move(savedata), savelen),
+ };
+
+ auto cart = NDSCart::ParseROM(std::move(filedata), filelen, std::move(cartargs));
+ if (!cart)
+ // If we couldn't parse the ROM...
+ return false;
+
+ if (reset)
{
+ if (!emuthread->UpdateConsole(std::move(cart), Keep {}))
+ return false;
+
+ InitFirmwareSaveManager(emuthread);
+ emuthread->NDS->Reset();
+
if (Config::DirectBoot || emuthread->NDS->NeedsDirectBoot())
- {
+ { // If direct boot is enabled or forced...
emuthread->NDS->SetupDirectBoot(romname);
}
- }
- if (res)
+ SetBatteryLevels(*emuthread->NDS);
+ SetDateTime(*emuthread->NDS);
+ }
+ else
{
- CartType = 0;
- NDSSave = new SaveManager(savname);
-
- LoadCheats(*emuthread->NDS);
+ assert(emuthread->NDS != nullptr);
+ emuthread->NDS->SetNDSCart(std::move(cart));
}
- if (savedata) delete[] savedata;
- delete[] filedata;
- return res;
+ CartType = 0;
+ NDSSave = std::make_unique<SaveManager>(savname);
+ LoadCheats(*emuthread->NDS);
+
+ return true;
}
void EjectCart(NDS& nds)
{
- if (NDSSave) delete NDSSave;
NDSSave = nullptr;
UnloadCheats(nds);
@@ -1529,92 +1360,16 @@ QString CartLabel()
bool LoadGBAROM(NDS& nds, QStringList filepath)
{
- if (Config::ConsoleType == 1) return false;
- if (filepath.empty()) return false;
+ if (nds.ConsoleType == 1) return false; // DSi doesn't have a GBA slot
- u8* filedata;
+ unique_ptr<u8[]> filedata = nullptr;
u32 filelen;
-
std::string basepath;
std::string romname;
- int num = filepath.count();
- if (num == 1)
- {
- // regular file
-
- std::string filename = filepath.at(0).toStdString();
- FileHandle* f = Platform::OpenFile(filename, FileMode::Read);
- if (!f) return false;
-
- long len = FileLength(f);
- if (len > 0x40000000)
- {
- CloseFile(f);
- return false;
- }
-
- FileRewind(f);
- filedata = new u8[len];
- size_t nread = FileRead(filedata, (size_t)len, 1, f);
- if (nread != 1)
- {
- CloseFile(f);
- delete[] filedata;
- return false;
- }
-
- CloseFile(f);
- filelen = (u32)len;
-
- if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst")
- {
- u8* outContent = nullptr;
- u32 decompressed = DecompressROM(filedata, len, &outContent);
-
- if (decompressed > 0)
- {
- delete[] filedata;
- filedata = outContent;
- filelen = decompressed;
- filename = filename.substr(0, filename.length() - 4);
- }
- else
- {
- delete[] filedata;
- return false;
- }
- }
-
- 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
-
- s32 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
+ if (!LoadROMData(filepath, filedata, filelen, basepath, romname))
return false;
- if (GBASave) delete GBASave;
GBASave = nullptr;
BaseGBAROMDir = basepath;
@@ -1622,7 +1377,7 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
BaseGBAAssetName = romname.substr(0, romname.rfind('.'));
u32 savelen = 0;
- u8* savedata = nullptr;
+ std::unique_ptr<u8[]> savedata = nullptr;
std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav");
std::string origsav = savname;
@@ -1634,30 +1389,29 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
{
savelen = (u32)FileLength(sav);
- FileRewind(sav);
- savedata = new u8[savelen];
- FileRead(savedata, savelen, 1, sav);
+ if (savelen > 0)
+ {
+ FileRewind(sav);
+ savedata = std::make_unique<u8[]>(savelen);
+ FileRead(savedata.get(), savelen, 1, sav);
+ }
CloseFile(sav);
}
- bool res = nds.LoadGBACart(filedata, filelen, savedata, savelen);
-
- if (res)
- {
- GBACartType = 0;
- GBASave = new SaveManager(savname);
- }
+ auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen);
+ if (!cart)
+ return false;
- if (savedata) delete[] savedata;
- delete[] filedata;
- return res;
+ nds.SetGBACart(std::move(cart));
+ GBACartType = 0;
+ GBASave = std::make_unique<SaveManager>(savname);
+ return true;
}
void LoadGBAAddon(NDS& nds, int type)
{
if (Config::ConsoleType == 1) return;
- if (GBASave) delete GBASave;
GBASave = nullptr;
nds.LoadGBAAddon(type);
@@ -1670,7 +1424,6 @@ void LoadGBAAddon(NDS& nds, int type)
void EjectGBACart(NDS& nds)
{
- if (GBASave) delete GBASave;
GBASave = nullptr;
nds.EjectGBACart();
diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h
index 2163a68..0b640c8 100644
--- a/src/frontend/qt_sdl/ROMManager.h
+++ b/src/frontend/qt_sdl/ROMManager.h
@@ -35,34 +35,43 @@ namespace melonDS
class NDS;
class DSi;
class FATStorage;
+class FATStorageArgs;
}
class EmuThread;
namespace ROMManager
{
using namespace melonDS;
-extern SaveManager* NDSSave;
-extern SaveManager* GBASave;
+extern std::unique_ptr<SaveManager> NDSSave;
+extern std::unique_ptr<SaveManager> GBASave;
extern std::unique_ptr<SaveManager> FirmwareSave;
QString VerifySetup();
void Reset(EmuThread* thread);
-bool LoadBIOS(EmuThread* thread);
+
+/// Boots the emulated console into its system menu without starting a game.
+bool BootToMenu(EmuThread* thread);
void ClearBackupState();
+/// Returns the configured ARM9 BIOS loaded from disk,
+/// the FreeBIOS if external BIOS is disabled and we're in NDS mode,
+/// or nullopt if loading failed.
std::optional<std::array<u8, ARM9BIOSSize>> LoadARM9BIOS() noexcept;
std::optional<std::array<u8, ARM7BIOSSize>> LoadARM7BIOS() noexcept;
std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM9BIOS() noexcept;
std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM7BIOS() noexcept;
+std::optional<FATStorageArgs> GetDSiSDCardArgs() noexcept;
std::optional<FATStorage> LoadDSiSDCard() noexcept;
+std::optional<FATStorageArgs> GetDLDISDCardArgs() noexcept;
+std::optional<FATStorage> LoadDLDISDCard() noexcept;
void CustomizeFirmware(Firmware& firmware) noexcept;
Firmware GenerateFirmware(int type) noexcept;
/// Loads and customizes a firmware image based on the values in Config
std::optional<Firmware> LoadFirmware(int type) noexcept;
/// Loads and customizes a NAND image based on the values in Config
std::optional<DSi_NAND::NANDImage> LoadNAND(const std::array<u8, DSiBIOSSize>& arm7ibios) noexcept;
-bool InstallFirmware(NDS& nds);
-bool InstallNAND(DSi& dsi);
+
+/// Inserts a ROM into the emulated console.
bool LoadROM(EmuThread*, QStringList filepath, bool reset);
void EjectCart(NDS& nds);
bool CartInserted();
diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp
index a0ac086..5bd4d1b 100644
--- a/src/frontend/qt_sdl/main.cpp
+++ b/src/frontend/qt_sdl/main.cpp
@@ -81,6 +81,7 @@
#include "FrontendUtil.h"
#include "OSD.h"
+#include "Args.h"
#include "NDS.h"
#include "NDSCart.h"
#include "GBACart.h"
@@ -204,29 +205,164 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
static_cast<ScreenPanelGL*>(mainWindow->panel)->transferLayout(this);
}
-std::unique_ptr<NDS> EmuThread::CreateConsole()
+std::unique_ptr<NDS> EmuThread::CreateConsole(
+ std::unique_ptr<melonDS::NDSCart::CartCommon>&& ndscart,
+ std::unique_ptr<melonDS::GBACart::CartCommon>&& gbacart
+) noexcept
{
+ auto arm7bios = ROMManager::LoadARM7BIOS();
+ if (!arm7bios)
+ return nullptr;
+
+ auto arm9bios = ROMManager::LoadARM9BIOS();
+ if (!arm9bios)
+ return nullptr;
+
+ auto firmware = ROMManager::LoadFirmware(Config::ConsoleType);
+ if (!firmware)
+ return nullptr;
+
+ NDSArgs ndsargs {
+ std::move(ndscart),
+ std::move(gbacart),
+ *arm9bios,
+ *arm7bios,
+ std::move(*firmware),
+ };
+
if (Config::ConsoleType == 1)
{
- return std::make_unique<melonDS::DSi>();
+ auto arm7ibios = ROMManager::LoadDSiARM7BIOS();
+ if (!arm7ibios)
+ return nullptr;
+
+ auto arm9ibios = ROMManager::LoadDSiARM9BIOS();
+ if (!arm9ibios)
+ return nullptr;
+
+ auto nand = ROMManager::LoadNAND(*arm7ibios);
+ if (!nand)
+ return nullptr;
+
+ auto sdcard = ROMManager::LoadDSiSDCard();
+ DSiArgs args {
+ std::move(ndsargs),
+ *arm9ibios,
+ *arm7ibios,
+ std::move(*nand),
+ std::move(sdcard),
+ };
+
+ args.GBAROM = nullptr;
+
+ return std::make_unique<melonDS::DSi>(std::move(args));
}
- return std::make_unique<melonDS::NDS>();
+ return std::make_unique<melonDS::NDS>(std::move(ndsargs));
}
-void EmuThread::RecreateConsole()
+bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept
{
- if (!NDS || NDS->ConsoleType != Config::ConsoleType)
+ // Let's get the cart we want to use;
+ // if we wnat to keep the cart, we'll eject it from the existing console first.
+ std::unique_ptr<NDSCart::CartCommon> nextndscart;
+ if (std::holds_alternative<Keep>(ndsargs))
+ { // If we want to keep the existing cart (if any)...
+ nextndscart = NDS ? NDS->EjectCart() : nullptr;
+ ndsargs = {};
+ }
+ else if (const auto ptr = std::get_if<std::unique_ptr<NDSCart::CartCommon>>(&ndsargs))
{
- NDS = nullptr; // To ensure the destructor is called before a new one is created
+ nextndscart = std::move(*ptr);
+ ndsargs = {};
+ }
+
+ if (nextndscart && nextndscart->Type() == NDSCart::Homebrew)
+ {
+ // Load DLDISDCard will return nullopt if the SD card is disabled;
+ // SetSDCard will accept nullopt, which means no SD card
+ auto& homebrew = static_cast<NDSCart::CartHomebrew&>(*nextndscart);
+ homebrew.SetSDCard(ROMManager::LoadDLDISDCard());
+ }
+
+ std::unique_ptr<GBACart::CartCommon> nextgbacart;
+ if (std::holds_alternative<Keep>(gbaargs))
+ {
+ nextgbacart = NDS ? NDS->EjectGBACart() : nullptr;
+ }
+ else if (const auto ptr = std::get_if<std::unique_ptr<GBACart::CartCommon>>(&gbaargs))
+ {
+ nextgbacart = std::move(*ptr);
+ gbaargs = {};
+ }
+
+ if (!NDS || NDS->ConsoleType != Config::ConsoleType)
+ { // If we're switching between DS and DSi mode, or there's no console...
+ // To ensure the destructor is called before a new one is created,
+ // as the presence of global signal handlers still complicates things a bit
+ NDS = nullptr;
NDS::Current = nullptr;
- NDS = CreateConsole();
- // TODO: Insert ROMs
+ NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart));
NDS::Current = NDS.get();
+
+ return NDS != nullptr;
}
-}
+ auto arm9bios = ROMManager::LoadARM9BIOS();
+ if (!arm9bios)
+ return false;
+
+ auto arm7bios = ROMManager::LoadARM7BIOS();
+ if (!arm7bios)
+ return false;
+
+ auto firmware = ROMManager::LoadFirmware(NDS->ConsoleType);
+ if (!firmware)
+ return false;
+
+ if (NDS->ConsoleType == 1)
+ { // If the console we're updating is a DSi...
+ DSi& dsi = static_cast<DSi&>(*NDS);
+
+ auto arm9ibios = ROMManager::LoadDSiARM9BIOS();
+ if (!arm9ibios)
+ return false;
+
+ auto arm7ibios = ROMManager::LoadDSiARM7BIOS();
+ if (!arm7ibios)
+ return false;
+
+ auto nandimage = ROMManager::LoadNAND(*arm7ibios);
+ if (!nandimage)
+ return false;
+
+ auto dsisdcard = ROMManager::LoadDSiSDCard();
+
+ dsi.ARM7iBIOS = *arm7ibios;
+ dsi.ARM9iBIOS = *arm9ibios;
+ dsi.SetNAND(std::move(*nandimage));
+ dsi.SetSDCard(std::move(dsisdcard));
+ // We're moving the optional, not the card
+ // (inserting std::nullopt here is okay, it means no card)
+
+ dsi.EjectGBACart();
+ }
+
+ if (NDS->ConsoleType == 0)
+ {
+ NDS->SetGBACart(std::move(nextgbacart));
+ }
+
+ NDS->ARM7BIOS = *arm7bios;
+ NDS->ARM9BIOS = *arm9bios;
+ NDS->SetFirmware(std::move(*firmware));
+ NDS->SetNDSCart(std::move(nextndscart));
+
+ NDS::Current = NDS.get();
+
+ return true;
+}
void EmuThread::updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix)
{
@@ -343,7 +479,8 @@ void EmuThread::run()
u32 mainScreenPos[3];
Platform::FileHandle* file;
- RecreateConsole();
+ UpdateConsole(nullptr, nullptr);
+ // No carts are inserted when melonDS first boots
mainScreenPos[0] = 0;
mainScreenPos[1] = 0;
@@ -2507,7 +2644,7 @@ void MainWindow::onBootFirmware()
return;
}
- if (!ROMManager::LoadBIOS(emuThread))
+ if (!ROMManager::BootToMenu(emuThread))
{
// TODO: better error reporting?
QMessageBox::critical(this, "melonDS", "This firmware is not bootable.");
@@ -2750,13 +2887,12 @@ void MainWindow::onImportSavefile()
u32 len = FileLength(f);
- u8* data = new u8[len];
+ std::unique_ptr<u8[]> data = std::make_unique<u8[]>(len);
Platform::FileRewind(f);
- Platform::FileRead(data, len, 1, f);
+ Platform::FileRead(data.get(), len, 1, f);
assert(emuThread->NDS != nullptr);
- emuThread->NDS->LoadSave(data, len);
- delete[] data;
+ emuThread->NDS->SetNDSSave(data.get(), len);
CloseFile(f);
emuThread->emuUnpause();
diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h
index 72ebfb1..ee2f720 100644
--- a/src/frontend/qt_sdl/main.h
+++ b/src/frontend/qt_sdl/main.h
@@ -34,12 +34,18 @@
#include <QCloseEvent>
#include <atomic>
-
+#include <variant>
#include <optional>
#include "FrontendUtil.h"
#include "duckstation/gl/context.h"
+#include "NDSCart.h"
+#include "GBACart.h"
+
+using Keep = std::monostate;
+using UpdateConsoleNDSArgs = std::variant<Keep, std::unique_ptr<melonDS::NDSCart::CartCommon>>;
+using UpdateConsoleGBAArgs = std::variant<Keep, std::unique_ptr<melonDS::GBACart::CartCommon>>;
namespace melonDS
{
class NDS;
@@ -72,7 +78,13 @@ public:
QMutex FrontBufferLock;
void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix);
- void RecreateConsole();
+
+ /// Applies the config in args.
+ /// Creates a new NDS console if needed,
+ /// modifies the existing one if possible.
+ /// @return \c true if the console was updated.
+ /// If this returns \c false, then the existing NDS console is not modified.
+ bool UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept;
std::unique_ptr<melonDS::NDS> NDS; // TODO: Proper encapsulation and synchronization
signals:
void windowUpdate();
@@ -96,7 +108,10 @@ signals:
void syncVolumeLevel();
private:
- std::unique_ptr<melonDS::NDS> CreateConsole();
+ std::unique_ptr<melonDS::NDS> CreateConsole(
+ std::unique_ptr<melonDS::NDSCart::CartCommon>&& ndscart,
+ std::unique_ptr<melonDS::GBACart::CartCommon>&& gbacart
+ ) noexcept;
void drawScreenGL();
void initOpenGL();
void deinitOpenGL();