diff options
author | Jesse Talavera <jesse@jesse.tg> | 2023-12-04 11:57:22 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-04 17:57:22 +0100 |
commit | bb42c8b6392e4e99634dc52137d2974781192482 (patch) | |
tree | 6b2de3d553aa331567e7ab0efa89ed841732f943 /src/frontend/qt_sdl | |
parent | da8d413ad9e339c40178be18da20aee1435c8cd4 (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.cpp | 7 | ||||
-rw-r--r-- | src/frontend/qt_sdl/ArchiveUtil.h | 2 | ||||
-rw-r--r-- | src/frontend/qt_sdl/Platform.cpp | 28 | ||||
-rw-r--r-- | src/frontend/qt_sdl/ROMManager.cpp | 647 | ||||
-rw-r--r-- | src/frontend/qt_sdl/ROMManager.h | 19 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.cpp | 166 | ||||
-rw-r--r-- | src/frontend/qt_sdl/main.h | 21 |
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(); |