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()`
This commit is contained in:
Jesse Talavera 2023-12-04 11:57:22 -05:00 committed by GitHub
parent da8d413ad9
commit bb42c8b639
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 2511 additions and 2362 deletions

100
src/Args.h Normal file
View File

@ -0,0 +1,100 @@
/*
Copyright 2016-2023 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef MELONDS_ARGS_H
#define MELONDS_ARGS_H
#include <array>
#include <optional>
#include <memory>
#include "types.h"
#include "MemConstants.h"
#include "DSi_NAND.h"
#include "FATStorage.h"
#include "FreeBIOS.h"
#include "SPI_Firmware.h"
namespace melonDS
{
namespace NDSCart { class CartCommon; }
namespace GBACart { class CartCommon; }
template<size_t N>
constexpr std::array<u8, N> BrokenBIOS = []() constexpr {
std::array<u8, N> broken {};
for (int i = 0; i < 16; i++)
{
broken[i*4+0] = 0xE7;
broken[i*4+1] = 0xFF;
broken[i*4+2] = 0xDE;
broken[i*4+3] = 0xFF;
}
return broken;
}();
/// Arguments to pass into the NDS constructor.
/// New fields here should have default values if possible.
struct NDSArgs
{
/// NDS ROM to install.
/// Defaults to nullptr, which means no cart.
/// Should be populated with the desired save data beforehand,
/// including an SD card if applicable.
std::unique_ptr<NDSCart::CartCommon> NDSROM = nullptr;
/// GBA ROM to install.
/// Defaults to nullptr, which means no cart.
/// Should be populated with the desired save data beforehand.
/// Ignored in DSi mode.
std::unique_ptr<GBACart::CartCommon> GBAROM = nullptr;
/// NDS ARM9 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::array<u8, ARM9BIOSSize> ARM9BIOS = bios_arm9_bin;
/// NDS ARM7 BIOS to install.
/// Defaults to FreeBIOS, which is not compatible with DSi mode.
std::array<u8, ARM7BIOSSize> ARM7BIOS = bios_arm7_bin;
/// Firmware image to install.
/// Defaults to generated NDS firmware.
/// Generated firmware is not compatible with DSi mode.
melonDS::Firmware Firmware {0};
};
/// Arguments to pass into the DSi constructor.
/// New fields here should have default values if possible.
/// Contains no virtual methods, so there's no vtable.
struct DSiArgs final : public NDSArgs
{
std::array<u8, DSiBIOSSize> ARM9iBIOS = BrokenBIOS<DSiBIOSSize>;
std::array<u8, DSiBIOSSize> ARM7iBIOS = BrokenBIOS<DSiBIOSSize>;
/// NAND image to install.
/// Required, there is no default value.
DSi_NAND::NANDImage NANDImage;
/// SD card to install.
/// Defaults to std::nullopt, which means no SD card.
std::optional<FATStorage> DSiSDCard;
};
}
#endif //MELONDS_ARGS_H

View File

@ -49,6 +49,8 @@ add_library(core STATIC
SPI_Firmware.cpp SPI_Firmware.cpp
SPU.cpp SPU.cpp
types.h types.h
Utils.cpp
Utils.h
version.h version.h
Wifi.cpp Wifi.cpp
WifiAP.cpp WifiAP.cpp

View File

@ -19,6 +19,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <inttypes.h> #include <inttypes.h>
#include "Args.h"
#include "NDS.h" #include "NDS.h"
#include "DSi.h" #include "DSi.h"
#include "ARM.h" #include "ARM.h"
@ -68,8 +69,8 @@ const u32 NDMAModes[] =
0xFF, // wifi / GBA cart slot (TODO) 0xFF, // wifi / GBA cart slot (TODO)
}; };
DSi::DSi() noexcept : DSi::DSi(DSiArgs&& args) noexcept :
NDS(1), NDS(std::move(args), 1),
NDMAs { NDMAs {
DSi_NDMA(0, 0, *this), DSi_NDMA(0, 0, *this),
DSi_NDMA(0, 1, *this), DSi_NDMA(0, 1, *this),
@ -80,9 +81,11 @@ DSi::DSi() noexcept :
DSi_NDMA(1, 2, *this), DSi_NDMA(1, 2, *this),
DSi_NDMA(1, 3, *this), DSi_NDMA(1, 3, *this),
}, },
ARM7iBIOS(args.ARM7iBIOS),
ARM9iBIOS(args.ARM9iBIOS),
DSP(*this), DSP(*this),
SDMMC(*this, 0), SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)),
SDIO(*this, 1), SDIO(*this),
I2C(*this), I2C(*this),
CamModule(*this), CamModule(*this),
AES(*this) AES(*this)
@ -118,9 +121,6 @@ void DSi::Reset()
CamModule.Reset(); CamModule.Reset();
DSP.Reset(); DSP.Reset();
SDMMC.CloseHandles();
SDIO.CloseHandles();
LoadNAND(); LoadNAND();
SDMMC.Reset(); SDMMC.Reset();
@ -162,24 +162,22 @@ void DSi::Stop(Platform::StopReason reason)
CamModule.Stop(); CamModule.Stop();
} }
bool DSi::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) void DSi::SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart)
{ {
if (NDS::LoadCart(romdata, romlen, savedata, savelen)) NDS::SetNDSCart(std::move(cart));
{ SetCartInserted(NDSCartSlot.GetCart() != nullptr);
SetCartInserted(true);
return true;
}
return false;
} }
void DSi::EjectCart() std::unique_ptr<NDSCart::CartCommon> DSi::EjectCart()
{ {
NDS::EjectCart(); auto oldcart = NDS::EjectCart();
SetCartInserted(false); SetCartInserted(false);
return oldcart;
} }
void DSi::CamInputFrame(int cam, u32* data, int width, int height, bool rgb) void DSi::CamInputFrame(int cam, u32* data, int width, int height, bool rgb)
{ {
switch (cam) switch (cam)
@ -509,9 +507,9 @@ void DSi::SetupDirectBoot()
ARM9Write32(0x02FFE000+i, tmp); ARM9Write32(0x02FFE000+i, tmp);
} }
if (NANDImage && *NANDImage) if (DSi_NAND::NANDImage* image = SDMMC.GetNAND(); image && *image)
{ // If a NAND image is installed, and it's valid... { // If a NAND image is installed, and it's valid...
if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*NANDImage)) if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*image))
{ {
DSi_NAND::DSiFirmwareSystemSettings userdata {}; DSi_NAND::DSiFirmwareSystemSettings userdata {};
nand.ReadUserData(userdata); nand.ReadUserData(userdata);
@ -531,7 +529,7 @@ void DSi::SetupDirectBoot()
} }
} }
Firmware::WifiBoard nwifiver = SPI.GetFirmware()->GetHeader().WifiBoard; Firmware::WifiBoard nwifiver = SPI.GetFirmware().GetHeader().WifiBoard;
ARM9Write8(0x020005E0, static_cast<u8>(nwifiver)); ARM9Write8(0x020005E0, static_cast<u8>(nwifiver));
// TODO: these should be taken from the wifi firmware in NAND // TODO: these should be taken from the wifi firmware in NAND
@ -674,9 +672,6 @@ void DSi::SoftReset()
// the DSP most likely gets reset // the DSP most likely gets reset
DSP.Reset(); DSP.Reset();
SDMMC.CloseHandles();
SDIO.CloseHandles();
LoadNAND(); LoadNAND();
SDMMC.Reset(); SDMMC.Reset();
@ -709,21 +704,22 @@ void DSi::SoftReset()
bool DSi::LoadNAND() bool DSi::LoadNAND()
{ {
if (!NANDImage) DSi_NAND::NANDImage* image = SDMMC.GetNAND();
if (!(image && *image))
{ {
Log(LogLevel::Error, "No NAND image loaded\n"); Log(LogLevel::Error, "No NAND image loaded\n");
return false; return false;
} }
Log(LogLevel::Info, "Loading DSi NAND\n"); Log(LogLevel::Info, "Loading DSi NAND\n");
DSi_NAND::NANDMount nandmount(*NANDImage); DSi_NAND::NANDMount nandmount(*SDMMC.GetNAND());
if (!nandmount) if (!nandmount)
{ {
Log(LogLevel::Error, "Failed to load DSi NAND\n"); Log(LogLevel::Error, "Failed to load DSi NAND\n");
return false; return false;
} }
FileHandle* nand = NANDImage->GetFile(); FileHandle* nand = image->GetFile();
// Make sure NWRAM is accessible. // Make sure NWRAM is accessible.
// The Bits are set to the startup values in Reset() and we might // The Bits are set to the startup values in Reset() and we might
@ -879,9 +875,9 @@ bool DSi::LoadNAND()
} }
} }
const DSi_NAND::DSiKey& emmccid = NANDImage->GetEMMCID(); const DSi_NAND::DSiKey& emmccid = image->GetEMMCID();
Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]); Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]);
Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", NANDImage->GetConsoleID()); Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", image->GetConsoleID());
if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
{ {
@ -1728,12 +1724,12 @@ bool DSi::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
return false; return false;
} }
region->Mem = ARM9BIOS; region->Mem = &ARM9BIOS[0];
region->Mask = 0xFFF; region->Mask = 0xFFF;
} }
else else
{ {
region->Mem = ARM9iBIOS; region->Mem = &ARM9iBIOS[0];
region->Mask = 0xFFFF; region->Mask = 0xFFFF;
} }
return true; return true;
@ -2678,14 +2674,14 @@ u8 DSi::ARM7IORead8(u32 addr)
case 0x04004500: return I2C.ReadData(); case 0x04004500: return I2C.ReadData();
case 0x04004501: return I2C.ReadCnt(); case 0x04004501: return I2C.ReadCnt();
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF; case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFF;
case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF; case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 8) & 0xFF;
case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFF; case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFF;
case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 24) & 0xFF; case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 24) & 0xFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFF; case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFF;
case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 40) & 0xFF; case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 40) & 0xFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 48) & 0xFF; case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 48) & 0xFF;
case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56; case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 56;
case 0x04004D08: return 0; case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF; case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF;
@ -2726,10 +2722,10 @@ u16 DSi::ARM7IORead16(u32 addr)
CASE_READ16_32BIT(0x0400405C, MBK[1][7]) CASE_READ16_32BIT(0x0400405C, MBK[1][7])
CASE_READ16_32BIT(0x04004060, MBK[1][8]) CASE_READ16_32BIT(0x04004060, MBK[1][8])
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFF; case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFF;
case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFFFF; case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFFFF; case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFFFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48; case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 48;
case 0x04004D08: return 0; case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt(); case 0x4004700: return DSP.ReadSNDExCnt();
@ -2806,8 +2802,8 @@ u32 DSi::ARM7IORead32(u32 addr)
case 0x04004400: return AES.ReadCnt(); case 0x04004400: return AES.ReadCnt();
case 0x0400440C: return AES.ReadOutputFIFO(); case 0x0400440C: return AES.ReadOutputFIFO();
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF; case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFFFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32; case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 32;
case 0x04004D08: return 0; case 0x04004D08: return 0;
case 0x4004700: case 0x4004700:

View File

@ -33,6 +33,7 @@ class DSi_I2CHost;
class DSi_CamModule; class DSi_CamModule;
class DSi_AES; class DSi_AES;
class DSi_DSP; class DSi_DSP;
class DSiArgs;
namespace DSi_NAND namespace DSi_NAND
{ {
@ -48,9 +49,8 @@ public:
u16 SCFG_Clock9; u16 SCFG_Clock9;
u32 SCFG_EXT[2]; u32 SCFG_EXT[2];
u8 ARM9iBIOS[0x10000]; std::array<u8, DSiBIOSSize> ARM9iBIOS;
u8 ARM7iBIOS[0x10000]; std::array<u8, DSiBIOSSize> ARM7iBIOS;
std::unique_ptr<DSi_NAND::NANDImage> NANDImage;
DSi_SDHost SDMMC; DSi_SDHost SDMMC;
DSi_SDHost SDIO; DSi_SDHost SDIO;
@ -130,19 +130,29 @@ public:
void ARM7IOWrite32(u32 addr, u32 val) override; void ARM7IOWrite32(u32 addr, u32 val) override;
public: public:
DSi() noexcept; DSi(DSiArgs&& args) noexcept;
~DSi() noexcept override; ~DSi() noexcept override;
DSi(const DSi&) = delete; DSi(const DSi&) = delete;
DSi& operator=(const DSi&) = delete; DSi& operator=(const DSi&) = delete;
DSi(DSi&&) = delete; DSi(DSi&&) = delete;
DSi& operator=(DSi&&) = delete; DSi& operator=(DSi&&) = delete;
bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) override; void SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart) override;
void EjectCart() override; std::unique_ptr<NDSCart::CartCommon> EjectCart() override;
bool NeedsDirectBoot() override bool NeedsDirectBoot() override
{ {
// for now, DSi mode requires original BIOS/NAND // for now, DSi mode requires original BIOS/NAND
return false; return false;
} }
[[nodiscard]] const DSi_NAND::NANDImage& GetNAND() const noexcept { return *SDMMC.GetNAND(); }
[[nodiscard]] DSi_NAND::NANDImage& GetNAND() noexcept { return *SDMMC.GetNAND(); }
void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { SDMMC.SetNAND(std::move(nand)); }
u64 GetConsoleID() const noexcept { return SDMMC.GetNAND()->GetConsoleID(); }
[[nodiscard]] const FATStorage* GetSDCard() const noexcept { return SDMMC.GetSDCard(); }
void SetSDCard(FATStorage&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); }
void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) override; void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) override;
bool DMAsInMode(u32 cpu, u32 mode) override; bool DMAsInMode(u32 cpu, u32 mode) override;
bool DMAsRunning(u32 cpu) override; bool DMAsRunning(u32 cpu) override;

View File

@ -78,7 +78,7 @@ void DSi_AES::Reset()
OutputMACDue = false; OutputMACDue = false;
// initialize keys // initialize keys
u64 consoleid = DSi.NANDImage->GetConsoleID(); u64 consoleid = DSi.SDMMC.GetNAND()->GetConsoleID();
// slot 0: modcrypt // slot 0: modcrypt
*(u32*)&KeyX[0][0] = 0x746E694E; *(u32*)&KeyX[0][0] = 0x746E694E;

View File

@ -131,6 +131,9 @@ NANDImage& NANDImage::operator=(NANDImage&& other) noexcept
{ {
if (this != &other) if (this != &other)
{ {
if (CurFile)
CloseFile(CurFile);
CurFile = other.CurFile; CurFile = other.CurFile;
eMMC_CID = other.eMMC_CID; eMMC_CID = other.eMMC_CID;
ConsoleID = other.ConsoleID; ConsoleID = other.ConsoleID;

View File

@ -165,13 +165,13 @@ void DSi_NWifi::Reset()
for (int i = 0; i < 9; i++) for (int i = 0; i < 9; i++)
Mailbox[i].Clear(); Mailbox[i].Clear();
const Firmware* fw = DSi.SPI.GetFirmware(); const Firmware& fw = DSi.SPI.GetFirmware();
MacAddress mac = fw->GetHeader().MacAddr; MacAddress mac = fw.GetHeader().MacAddr;
Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
Firmware::WifiBoard type = fw->GetHeader().WifiBoard; Firmware::WifiBoard type = fw.GetHeader().WifiBoard;
switch (type) switch (type)
{ {
case Firmware::WifiBoard::W015: // AR6002 case Firmware::WifiBoard::W015: // AR6002

View File

@ -18,6 +18,7 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "Args.h"
#include "DSi.h" #include "DSi.h"
#include "DSi_SD.h" #include "DSi_SD.h"
#include "DSi_NAND.h" #include "DSi_NAND.h"
@ -26,6 +27,10 @@
namespace melonDS namespace melonDS
{ {
using std::holds_alternative;
using std::unique_ptr;
using std::get_if;
using std::get;
using namespace Platform; using namespace Platform;
// observed IRQ behavior during transfers // observed IRQ behavior during transfers
@ -57,36 +62,38 @@ enum
}; };
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, u32 num) : DSi(dsi) DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard) noexcept : DSi(dsi), Num(0)
{ {
Num = num; DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
Ports[0] = nullptr; Ports[0] = sdcard ? std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard)) : nullptr;
sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object
Ports[1] = std::make_unique<DSi_MMCStorage>(DSi, this, std::move(nand));
}
// Creates an SDIO host
DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1)
{
DSi.RegisterEventFunc(Event_DSi_SDIOTransfer ,
Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX));
DSi.RegisterEventFunc(Event_DSi_SDIOTransfer,
Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX));
Ports[0] = std::make_unique<DSi_NWifi>(DSi, this);
Ports[1] = nullptr; Ports[1] = nullptr;
} }
DSi_SDHost::~DSi_SDHost() DSi_SDHost::~DSi_SDHost()
{ {
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_TX); Transfer_TX);
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_RX); Transfer_RX);
}
void DSi_SDHost::CloseHandles() // unique_ptr's destructor will clean up the ports
{
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
Ports[0] = nullptr;
Ports[1] = nullptr;
} }
void DSi_SDHost::Reset() void DSi_SDHost::Reset()
@ -129,48 +136,70 @@ void DSi_SDHost::Reset()
TXReq = false; TXReq = false;
CloseHandles(); if (Ports[0]) Ports[0]->Reset();
if (Ports[1]) Ports[1]->Reset();
}
if (Num == 0) FATStorage* DSi_SDHost::GetSDCard() noexcept
{
if (Num != 0) return nullptr;
return static_cast<DSi_MMCStorage*>(Ports[0].get())->GetSDCard();
}
const FATStorage* DSi_SDHost::GetSDCard() const noexcept
{
if (Num != 0) return nullptr;
return static_cast<const DSi_MMCStorage*>(Ports[0].get())->GetSDCard();
}
DSi_NAND::NANDImage* DSi_SDHost::GetNAND() noexcept
{
if (Num != 0) return nullptr;
return static_cast<DSi_MMCStorage*>(Ports[1].get())->GetNAND();
}
const DSi_NAND::NANDImage* DSi_SDHost::GetNAND() const noexcept
{
if (Num != 0) return nullptr;
return static_cast<const DSi_MMCStorage*>(Ports[1].get())->GetNAND();
}
void DSi_SDHost::SetSDCard(FATStorage&& sdcard) noexcept
{
if (Num != 0) return;
static_cast<DSi_MMCStorage*>(Ports[0].get())->SetSDCard(std::move(sdcard));
}
void DSi_SDHost::SetSDCard(std::optional<FATStorage>&& sdcard) noexcept
{
if (Num != 0) return;
if (sdcard)
{ {
DSi_MMCStorage* sd; if (!Ports[0])
DSi_MMCStorage* mmc;
if (Platform::GetConfigBool(Platform::DSiSD_Enable))
{ {
std::string folderpath; Ports[0] = std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard));
if (Platform::GetConfigBool(Platform::DSiSD_FolderSync))
folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath);
else
folderpath = "";
sd = new DSi_MMCStorage(this,
false,
Platform::GetConfigString(Platform::DSiSD_ImagePath),
(u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024,
Platform::GetConfigBool(Platform::DSiSD_ReadOnly),
folderpath);
u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
sd->SetCID(sd_cid);
} }
else else
sd = nullptr; {
static_cast<DSi_MMCStorage*>(Ports[0].get())->SetSDCard(std::move(*sdcard));
mmc = new DSi_MMCStorage(this, *DSi.NANDImage); }
mmc->SetCID(DSi.NANDImage->GetEMMCID().data());
Ports[0] = sd;
Ports[1] = mmc;
} }
else else
{ {
DSi_NWifi* nwifi = new DSi_NWifi(DSi, this); Ports[0] = nullptr;
Ports[0] = nwifi;
} }
if (Ports[0]) Ports[0]->Reset(); sdcard = std::nullopt;
if (Ports[1]) Ports[1]->Reset(); // a moved-from optional isn't empty, it contains a moved-from object
}
void DSi_SDHost::SetNAND(DSi_NAND::NANDImage&& nand) noexcept
{
if (Num != 0) return;
static_cast<DSi_MMCStorage*>(Ports[1].get())->SetNAND(std::move(nand));
} }
void DSi_SDHost::DoSavestate(Savestate* file) void DSi_SDHost::DoSavestate(Savestate* file)
@ -261,7 +290,7 @@ void DSi_SDHost::SetCardIRQ()
if (!(CardIRQCtl & (1<<0))) return; if (!(CardIRQCtl & (1<<0))) return;
u16 oldflags = CardIRQStatus & ~CardIRQMask; u16 oldflags = CardIRQStatus & ~CardIRQMask;
DSi_SDDevice* dev = Ports[PortSelect & 0x1]; DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev->IRQ) CardIRQStatus |= (1<<0); if (dev->IRQ) CardIRQStatus |= (1<<0);
else CardIRQStatus &= ~(1<<0); else CardIRQStatus &= ~(1<<0);
@ -332,7 +361,7 @@ u32 DSi_SDHost::DataRX(u8* data, u32 len)
void DSi_SDHost::FinishTX(u32 param) void DSi_SDHost::FinishTX(u32 param)
{ {
DSi_SDDevice* dev = Ports[PortSelect & 0x1]; DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (BlockCountInternal == 0) if (BlockCountInternal == 0)
{ {
@ -419,7 +448,7 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len)
void DSi_SDHost::CheckRX() void DSi_SDHost::CheckRX()
{ {
DSi_SDDevice* dev = Ports[PortSelect & 0x1]; DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
CheckSwapFIFO(); CheckSwapFIFO();
@ -459,7 +488,7 @@ void DSi_SDHost::CheckTX()
return; return;
} }
DSi_SDDevice* dev = Ports[PortSelect & 0x1]; DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev) dev->ContinueTransfer(); if (dev) dev->ContinueTransfer();
} }
@ -550,7 +579,6 @@ u16 DSi_SDHost::ReadFIFO16()
return 0; return 0;
} }
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u16 ret = DataFIFO[f].Read(); u16 ret = DataFIFO[f].Read();
if (DataFIFO[f].IsEmpty()) if (DataFIFO[f].IsEmpty())
@ -571,7 +599,6 @@ u32 DSi_SDHost::ReadFIFO32()
return 0; return 0;
} }
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 ret = DataFIFO32.Read(); u32 ret = DataFIFO32.Read();
if (DataFIFO32.IsEmpty()) if (DataFIFO32.IsEmpty())
@ -593,7 +620,7 @@ void DSi_SDHost::Write(u32 addr, u16 val)
Command = val; Command = val;
u8 cmd = Command & 0x3F; u8 cmd = Command & 0x3F;
DSi_SDDevice* dev = Ports[PortSelect & 0x1]; DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev) if (dev)
{ {
// CHECKME // CHECKME
@ -707,7 +734,6 @@ void DSi_SDHost::Write(u32 addr, u16 val)
void DSi_SDHost::WriteFIFO16(u16 val) void DSi_SDHost::WriteFIFO16(u16 val)
{ {
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 f = CurFIFO; u32 f = CurFIFO;
if (DataFIFO[f].IsFull()) if (DataFIFO[f].IsFull())
{ {
@ -780,34 +806,23 @@ void DSi_SDHost::CheckSwapFIFO()
#define MMC_DESC (Internal?"NAND":"SDcard") #define MMC_DESC (Internal?"NAND":"SDcard")
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand) DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept
: DSi_SDDevice(host), Internal(true), NAND(&nand), SD(nullptr) : DSi_SDDevice(host), DSi(dsi), Storage(std::move(nand))
{ {
ReadOnly = false; ReadOnly = false;
SetCID(get<DSi_NAND::NANDImage>(Storage).GetEMMCID().data());
} }
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir) DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept
: DSi_SDDevice(host) : DSi_SDDevice(host), DSi(dsi), Storage(std::move(sdcard))
{ {
Internal = internal; ReadOnly = get<FATStorage>(Storage).IsReadOnly();
NAND = nullptr; SetCID(DSiSDCardCID);
SD = new FATStorage(filename, size, readonly, sourcedir);
SD->Open();
ReadOnly = readonly;
} }
DSi_MMCStorage::~DSi_MMCStorage() // The FATStorage or NANDImage is owned by this object;
{ // std::variant's destructor will clean it up.
if (SD) DSi_MMCStorage::~DSi_MMCStorage() = default;
{
SD->Close();
delete SD;
}
// Do not close the NANDImage, it's not owned by this object
}
void DSi_MMCStorage::Reset() void DSi_MMCStorage::Reset()
{ {
@ -836,7 +851,7 @@ void DSi_MMCStorage::Reset()
void DSi_MMCStorage::DoSavestate(Savestate* file) void DSi_MMCStorage::DoSavestate(Savestate* file)
{ {
file->Section(Internal ? "NAND" : "SDCR"); file->Section(holds_alternative<DSi_NAND::NANDImage>(Storage) ? "NAND" : "SDCR");
file->VarArray(CID, 16); file->VarArray(CID, 16);
file->VarArray(CSD, 16); file->VarArray(CSD, 16);
@ -871,7 +886,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 1: // SEND_OP_COND case 1: // SEND_OP_COND
// CHECKME!! // CHECKME!!
// also TODO: it's different for the SD card // also TODO: it's different for the SD card
if (Internal) if (std::holds_alternative<DSi_NAND::NANDImage>(Storage))
{ {
param &= ~(1<<30); param &= ~(1<<30);
OCR &= 0xBF000000; OCR &= 0xBF000000;
@ -895,7 +910,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
return; return;
case 3: // get/set RCA case 3: // get/set RCA
if (Internal) if (holds_alternative<DSi_NAND::NANDImage>(Storage))
{ {
RCA = param >> 16; RCA = param >> 16;
Host->SendResponse(CSR|0x10000, true); // huh?? Host->SendResponse(CSR|0x10000, true); // huh??
@ -930,7 +945,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 12: // stop operation case 12: // stop operation
SetState(0x04); SetState(0x04);
if (NAND) FileFlush(NAND->GetFile()); if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
FileFlush(nand->GetFile());
RWCommand = 0; RWCommand = 0;
Host->SendResponse(CSR, true); Host->SendResponse(CSR, true);
return; return;
@ -1011,7 +1027,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
// DSi boot2 sets this to 0x40100000 (hardcoded) // DSi boot2 sets this to 0x40100000 (hardcoded)
// then has two codepaths depending on whether bit30 did get set // then has two codepaths depending on whether bit30 did get set
// is it settable at all on the MMC? probably not. // is it settable at all on the MMC? probably not.
if (Internal) param &= ~(1<<30); if (holds_alternative<DSi_NAND::NANDImage>(Storage)) param &= ~(1<<30);
OCR &= 0xBF000000; OCR &= 0xBF000000;
OCR |= (param & 0x40FFFFFF); OCR |= (param & 0x40FFFFFF);
Host->SendResponse(OCR, true); Host->SendResponse(OCR, true);
@ -1057,14 +1073,14 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
len = Host->GetTransferrableLen(len); len = Host->GetTransferrableLen(len);
u8 data[0x200]; u8 data[0x200];
if (SD) if (auto* sd = std::get_if<FATStorage>(&Storage))
{ {
SD->ReadSectors((u32)(addr >> 9), 1, data); sd->ReadSectors((u32)(addr >> 9), 1, data);
} }
else if (NAND) else if (auto* nand = std::get_if<DSi_NAND::NANDImage>(&Storage))
{ {
FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start); FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
FileRead(&data[addr & 0x1FF], 1, len, NAND->GetFile()); FileRead(&data[addr & 0x1FF], 1, len, nand->GetFile());
} }
return Host->DataRX(&data[addr & 0x1FF], len); return Host->DataRX(&data[addr & 0x1FF], len);
@ -1078,23 +1094,23 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr)
u8 data[0x200]; u8 data[0x200];
if (len < 0x200) if (len < 0x200)
{ {
if (SD) if (auto* sd = get_if<FATStorage>(&Storage))
{ {
SD->ReadSectors((u32)(addr >> 9), 1, data); sd->ReadSectors((u32)(addr >> 9), 1, data);
} }
} }
if ((len = Host->DataTX(&data[addr & 0x1FF], len))) if ((len = Host->DataTX(&data[addr & 0x1FF], len)))
{ {
if (!ReadOnly) if (!ReadOnly)
{ {
if (SD) if (auto* sd = get_if<FATStorage>(&Storage))
{ {
SD->WriteSectors((u32)(addr >> 9), 1, data); sd->WriteSectors((u32)(addr >> 9), 1, data);
} }
else if (NAND) else if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
{ {
FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start); FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
FileWrite(&data[addr & 0x1FF], 1, len, NAND->GetFile()); FileWrite(&data[addr & 0x1FF], 1, len, nand->GetFile());
} }
} }
} }

View File

@ -20,28 +20,30 @@
#define DSI_SD_H #define DSI_SD_H
#include <cstring> #include <cstring>
#include <variant>
#include "FIFO.h" #include "FIFO.h"
#include "FATStorage.h" #include "FATStorage.h"
#include "DSi_NAND.h"
#include "Savestate.h" #include "Savestate.h"
namespace melonDS namespace melonDS
{ {
namespace DSi_NAND
{
class NANDImage;
}
class DSi_SDDevice; class DSi_SDDevice;
class DSi; class DSi;
using Nothing = std::monostate;
using DSiStorage = std::variant<std::monostate, FATStorage, DSi_NAND::NANDImage>;
class DSi_SDHost class DSi_SDHost
{ {
public: public:
DSi_SDHost(melonDS::DSi& dsi, u32 num); /// Creates an SDMMC host.
DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional<FATStorage>&& sdcard = std::nullopt) noexcept;
/// Creates an SDIO host
explicit DSi_SDHost(melonDS::DSi& dsi) noexcept;
~DSi_SDHost(); ~DSi_SDHost();
void CloseHandles();
void Reset(); void Reset();
void DoSavestate(Savestate* file); void DoSavestate(Savestate* file);
@ -59,6 +61,15 @@ public:
void SetCardIRQ(); void SetCardIRQ();
[[nodiscard]] FATStorage* GetSDCard() noexcept;
[[nodiscard]] const FATStorage* GetSDCard() const noexcept;
[[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept;
[[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept;
void SetSDCard(FATStorage&& sdcard) noexcept;
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept;
void SetNAND(DSi_NAND::NANDImage&& nand) noexcept;
u16 Read(u32 addr); u16 Read(u32 addr);
void Write(u32 addr, u16 val); void Write(u32 addr, u16 val);
u16 ReadFIFO16(); u16 ReadFIFO16();
@ -96,7 +107,7 @@ private:
u32 Param; u32 Param;
u16 ResponseBuffer[8]; u16 ResponseBuffer[8];
DSi_SDDevice* Ports[2]; std::array<std::unique_ptr<DSi_SDDevice>, 2> Ports {};
u32 CurFIFO; // FIFO accessible for read/write u32 CurFIFO; // FIFO accessible for read/write
FIFO<u16, 0x100> DataFIFO[2]; FIFO<u16, 0x100> DataFIFO[2];
@ -134,25 +145,53 @@ protected:
class DSi_MMCStorage : public DSi_SDDevice class DSi_MMCStorage : public DSi_SDDevice
{ {
public: public:
DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand); DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept;
DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir); DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept;
~DSi_MMCStorage(); ~DSi_MMCStorage() override;
void Reset(); [[nodiscard]] FATStorage* GetSDCard() noexcept { return std::get_if<FATStorage>(&Storage); }
[[nodiscard]] const FATStorage* GetSDCard() const noexcept { return std::get_if<FATStorage>(&Storage); }
[[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept { return std::get_if<DSi_NAND::NANDImage>(&Storage); }
[[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept { return std::get_if<DSi_NAND::NANDImage>(&Storage); }
void DoSavestate(Savestate* file); void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { Storage = std::move(nand); }
void SetSDCard(FATStorage&& sdcard) noexcept { Storage = std::move(sdcard); }
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept
{
if (sdcard)
{ // If we're setting a new SD card...
Storage = std::move(*sdcard);
sdcard = std::nullopt;
}
else
{
Storage = Nothing();
}
}
void SetStorage(DSiStorage&& storage) noexcept
{
Storage = std::move(storage);
storage = Nothing();
// not sure if a moved-from variant is empty or contains a moved-from object;
// better to be safe than sorry
}
void Reset() override;
void DoSavestate(Savestate* file) override;
void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); } void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); }
void SendCMD(u8 cmd, u32 param); void SendCMD(u8 cmd, u32 param) override;
void SendACMD(u8 cmd, u32 param); void SendACMD(u8 cmd, u32 param);
void ContinueTransfer(); void ContinueTransfer() override;
private: private:
bool Internal; static constexpr u8 DSiSDCardCID[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
DSi_NAND::NANDImage* NAND; melonDS::DSi& DSi;
FATStorage* SD; DSiStorage Storage;
u8 CID[16]; u8 CID[16];
u8 CSD[16]; u8 CSD[16];

View File

@ -29,39 +29,79 @@ namespace melonDS
{ {
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace Platform; using namespace Platform;
using std::string;
FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir) FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional<string>& sourcedir) :
FilePath(filename),
FileSize(size),
ReadOnly(readonly),
SourceDir(sourcedir)
{ {
ReadOnly = readonly;
Load(filename, size, sourcedir); Load(filename, size, sourcedir);
File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
}
FATStorage::FATStorage(const FATStorageArgs& args) noexcept :
FATStorage(args.Filename, args.Size, args.ReadOnly, args.SourceDir)
{
}
FATStorage::FATStorage(FATStorageArgs&& args) noexcept :
FilePath(std::move(args.Filename)),
FileSize(args.Size),
ReadOnly(args.ReadOnly),
SourceDir(std::move(args.SourceDir))
{
Load(FilePath, FileSize, SourceDir);
File = nullptr; File = nullptr;
} }
FATStorage::FATStorage(FATStorage&& other) noexcept
{
FilePath = std::move(other.FilePath);
IndexPath = std::move(other.IndexPath);
SourceDir = std::move(other.SourceDir);
ReadOnly = other.ReadOnly;
File = other.File;
FileSize = other.FileSize;
DirIndex = std::move(other.DirIndex);
FileIndex = std::move(other.FileIndex);
other.File = nullptr;
}
FATStorage& FATStorage::operator=(FATStorage&& other) noexcept
{
if (this != &other)
{
if (File)
CloseFile(File);
FilePath = std::move(other.FilePath);
IndexPath = std::move(other.IndexPath);
SourceDir = std::move(other.SourceDir);
ReadOnly = other.ReadOnly;
File = other.File;
FileSize = other.FileSize;
DirIndex = std::move(other.DirIndex);
FileIndex = std::move(other.FileIndex);
other.File = nullptr;
}
return *this;
}
FATStorage::~FATStorage() FATStorage::~FATStorage()
{ {
if (!ReadOnly) Save(); if (!ReadOnly) Save();
}
bool FATStorage::Open()
{
File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
if (!File)
{
return false;
}
return true;
}
void FATStorage::Close()
{
if (File) CloseFile(File); if (File) CloseFile(File);
File = nullptr; File = nullptr;
} }
bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
{ {
if (!File) return false; if (!File) return false;
@ -930,19 +970,15 @@ u64 FATStorage::GetDirectorySize(fs::path sourcedir)
return ret; return ret;
} }
bool FATStorage::Load(const std::string& filename, u64 size, const std::string& sourcedir) bool FATStorage::Load(const std::string& filename, u64 size, const std::optional<string>& sourcedir)
{ {
FilePath = filename; bool hasdir = sourcedir && !sourcedir->empty();
FileSize = size; if (sourcedir)
SourceDir = sourcedir;
bool hasdir = !sourcedir.empty();
if (hasdir)
{ {
if (!fs::is_directory(fs::u8path(sourcedir))) if (!fs::is_directory(fs::u8path(*sourcedir)))
{ {
hasdir = false; hasdir = false;
SourceDir = ""; SourceDir = std::nullopt;
} }
} }
@ -1005,7 +1041,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
{ {
if (hasdir) if (hasdir)
{ {
FileSize = GetDirectorySize(fs::u8path(sourcedir)); FileSize = GetDirectorySize(fs::u8path(*sourcedir));
FileSize += 0x8000000ULL; // 128MB leeway FileSize += 0x8000000ULL; // 128MB leeway
// make it a power of two // make it a power of two
@ -1054,7 +1090,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
if (res == FR_OK) if (res == FR_OK)
{ {
if (hasdir) if (hasdir)
ImportDirectory(sourcedir); ImportDirectory(*sourcedir);
} }
f_unmount("0:"); f_unmount("0:");
@ -1068,9 +1104,9 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
bool FATStorage::Save() bool FATStorage::Save()
{ {
if (SourceDir.empty()) if (!SourceDir)
{ { // If we're not syncing the SD card image to a host directory...
return true; return true; // Not an error.
} }
FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
@ -1094,7 +1130,7 @@ bool FATStorage::Save()
return false; return false;
} }
ExportChanges(SourceDir); ExportChanges(*SourceDir);
SaveIndex(); SaveIndex();

View File

@ -22,6 +22,7 @@
#include <stdio.h> #include <stdio.h>
#include <string> #include <string>
#include <map> #include <map>
#include <optional>
#include <filesystem> #include <filesystem>
#include "Platform.h" #include "Platform.h"
@ -30,24 +31,41 @@
namespace melonDS namespace melonDS
{ {
/// Contains information necessary to load an SD card image.
/// The intended use case is for loading homebrew NDS ROMs;
/// you won't know that a ROM is homebrew until you parse it,
/// so if you load the SD card before the ROM
/// then you might end up discarding it.
struct FATStorageArgs
{
std::string Filename;
u64 Size;
bool ReadOnly;
std::optional<std::string> SourceDir;
};
class FATStorage class FATStorage
{ {
public: public:
FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir); FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional<std::string>& sourcedir = std::nullopt);
FATStorage(const FATStorageArgs& args) noexcept;
FATStorage(FATStorageArgs&& args) noexcept;
FATStorage(FATStorage&& other) noexcept;
FATStorage(const FATStorage& other) = delete;
FATStorage& operator=(const FATStorage& other) = delete;
FATStorage& operator=(FATStorage&& other) noexcept;
~FATStorage(); ~FATStorage();
bool Open();
void Close();
bool InjectFile(const std::string& path, u8* data, u32 len); bool InjectFile(const std::string& path, u8* data, u32 len);
u32 ReadSectors(u32 start, u32 num, u8* data); u32 ReadSectors(u32 start, u32 num, u8* data);
u32 WriteSectors(u32 start, u32 num, u8* data); u32 WriteSectors(u32 start, u32 num, u8* data);
[[nodiscard]] bool IsReadOnly() const noexcept { return ReadOnly; }
private: private:
std::string FilePath; std::string FilePath;
std::string IndexPath; std::string IndexPath;
std::string SourceDir; std::optional<std::string> SourceDir;
bool ReadOnly; bool ReadOnly;
Platform::FileHandle* File; Platform::FileHandle* File;
@ -76,7 +94,7 @@ private:
bool ImportDirectory(const std::string& sourcedir); bool ImportDirectory(const std::string& sourcedir);
u64 GetDirectorySize(std::filesystem::path sourcedir); u64 GetDirectorySize(std::filesystem::path sourcedir);
bool Load(const std::string& filename, u64 size, const std::string& sourcedir); bool Load(const std::string& filename, u64 size, const std::optional<std::string>& sourcedir);
bool Save(); bool Save();
typedef struct typedef struct

View File

@ -28,7 +28,7 @@
namespace melonDS namespace melonDS
{ {
unsigned char bios_arm7_bin[] = { std::array<u8, ARM7BIOSSize> bios_arm7_bin = {
0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea,
0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea, 0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea,
0xe3, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0xe3, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,
@ -1397,7 +1397,7 @@ unsigned char bios_arm7_bin[] = {
0x00, 0x00, 0x00, 0x00 0x00, 0x00, 0x00, 0x00
}; };
unsigned char bios_arm9_bin[] = { std::array<u8, ARM9BIOSSize> bios_arm9_bin = {
0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea,
0x3c, 0x00, 0x00, 0xea, 0x3b, 0x00, 0x00, 0xea, 0x3a, 0x00, 0x00, 0xea, 0x3c, 0x00, 0x00, 0xea, 0x3b, 0x00, 0x00, 0xea, 0x3a, 0x00, 0x00, 0xea,
0xad, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0xad, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00,

View File

@ -28,10 +28,13 @@
#ifndef FREEBIOS_H #ifndef FREEBIOS_H
#define FREEBIOS_H #define FREEBIOS_H
#include <array>
#include "MemConstants.h"
namespace melonDS namespace melonDS
{ {
extern unsigned char bios_arm7_bin[16384]; extern std::array<u8, ARM7BIOSSize> bios_arm7_bin;
extern unsigned char bios_arm9_bin[4096]; extern std::array<u8, ARM9BIOSSize> bios_arm9_bin;
} }
#endif // FREEBIOS_H #endif // FREEBIOS_H

File diff suppressed because it is too large Load Diff

View File

@ -1,257 +1,289 @@
/* /*
Copyright 2016-2023 melonDS team Copyright 2016-2023 melonDS team
This file is part of melonDS. This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option) Software Foundation, either version 3 of the License, or (at your option)
any later version. any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/. with melonDS. If not, see http://www.gnu.org/licenses/.
*/ */
#ifndef GBACART_H #ifndef GBACART_H
#define GBACART_H #define GBACART_H
#include <memory> #include <memory>
#include "types.h" #include "types.h"
#include "Savestate.h" #include "Savestate.h"
namespace melonDS::GBACart namespace melonDS::GBACart
{ {
enum CartType enum CartType
{ {
Default = 0x001, Default = 0x001,
Game = 0x101, Game = 0x101,
GameSolarSensor = 0x102, GameSolarSensor = 0x102,
RAMExpansion = 0x201, RAMExpansion = 0x201,
}; };
// CartCommon -- base code shared by all cart types // CartCommon -- base code shared by all cart types
class CartCommon class CartCommon
{ {
public: public:
CartCommon(); virtual ~CartCommon() = default;
virtual ~CartCommon();
[[nodiscard]] u32 Type() const { return CartType; }
virtual u32 Type() const = 0; virtual u32 Checksum() const { return 0; }
virtual u32 Checksum() const { return 0; }
virtual void Reset();
virtual void Reset();
virtual void DoSavestate(Savestate* file);
virtual void DoSavestate(Savestate* file);
virtual int SetInput(int num, bool pressed);
virtual void SetupSave(u32 type);
virtual void LoadSave(const u8* savedata, u32 savelen); virtual u16 ROMRead(u32 addr) const;
virtual void ROMWrite(u32 addr, u16 val);
virtual int SetInput(int num, bool pressed);
virtual u8 SRAMRead(u32 addr);
virtual u16 ROMRead(u32 addr) const; virtual void SRAMWrite(u32 addr, u8 val);
virtual void ROMWrite(u32 addr, u16 val);
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; }
virtual u8 SRAMRead(u32 addr); [[nodiscard]] virtual u32 GetROMLength() const { return 0; }
virtual void SRAMWrite(u32 addr, u8 val);
virtual u8* GetSaveMemory() const;
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; } virtual u32 GetSaveMemoryLength() const;
[[nodiscard]] virtual u32 GetROMLength() const { return 0; } virtual void SetSaveMemory(const u8* savedata, u32 savelen);
protected:
virtual u8* GetSaveMemory() const; CartCommon(GBACart::CartType type);
virtual u32 GetSaveMemoryLength() const; friend class GBACartSlot;
}; private:
GBACart::CartType CartType;
// CartGame -- regular retail game cart (ROM, SRAM) };
class CartGame : public CartCommon
{ // CartGame -- regular retail game cart (ROM, SRAM)
public: class CartGame : public CartCommon
CartGame(u8* rom, u32 len); {
virtual ~CartGame() override; public:
CartGame(const u8* rom, u32 len, const u8* sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game);
virtual u32 Type() const override { return CartType::Game; } CartGame(std::unique_ptr<u8[]>&& rom, u32 len, std::unique_ptr<u8[]>&& sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game);
virtual u32 Checksum() const override; ~CartGame() override;
virtual void Reset() override; u32 Checksum() const override;
virtual void DoSavestate(Savestate* file) override; void Reset() override;
virtual void SetupSave(u32 type) override; void DoSavestate(Savestate* file) override;
virtual void LoadSave(const u8* savedata, u32 savelen) override;
u16 ROMRead(u32 addr) const override;
virtual u16 ROMRead(u32 addr) const override; void ROMWrite(u32 addr, u16 val) override;
virtual void ROMWrite(u32 addr, u16 val) override;
u8 SRAMRead(u32 addr) override;
virtual u8 SRAMRead(u32 addr) override; void SRAMWrite(u32 addr, u8 val) override;
virtual void SRAMWrite(u32 addr, u8 val) override;
[[nodiscard]] const u8* GetROM() const override { return ROM.get(); }
[[nodiscard]] const u8* GetROM() const override { return ROM; } [[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
u8* GetSaveMemory() const override;
virtual u8* GetSaveMemory() const override; u32 GetSaveMemoryLength() const override;
virtual u32 GetSaveMemoryLength() const override; void SetSaveMemory(const u8* savedata, u32 savelen) override;
protected: protected:
virtual void ProcessGPIO(); virtual void ProcessGPIO();
u8 SRAMRead_EEPROM(u32 addr); u8 SRAMRead_EEPROM(u32 addr);
void SRAMWrite_EEPROM(u32 addr, u8 val); void SRAMWrite_EEPROM(u32 addr, u8 val);
u8 SRAMRead_FLASH(u32 addr); u8 SRAMRead_FLASH(u32 addr);
void SRAMWrite_FLASH(u32 addr, u8 val); void SRAMWrite_FLASH(u32 addr, u8 val);
u8 SRAMRead_SRAM(u32 addr); u8 SRAMRead_SRAM(u32 addr);
void SRAMWrite_SRAM(u32 addr, u8 val); void SRAMWrite_SRAM(u32 addr, u8 val);
u8* ROM; std::unique_ptr<u8[]> ROM;
u32 ROMLength; u32 ROMLength;
struct struct
{ {
u16 data; u16 data;
u16 direction; u16 direction;
u16 control; u16 control;
} GPIO; } GPIO {};
enum SaveType enum SaveType
{ {
S_NULL, S_NULL,
S_EEPROM4K, S_EEPROM4K,
S_EEPROM64K, S_EEPROM64K,
S_SRAM256K, S_SRAM256K,
S_FLASH512K, S_FLASH512K,
S_FLASH1M S_FLASH1M
}; };
// from DeSmuME // from DeSmuME
struct struct
{ {
u8 state; u8 state;
u8 cmd; u8 cmd;
u8 device; u8 device;
u8 manufacturer; u8 manufacturer;
u8 bank; u8 bank;
} SRAMFlashState; } SRAMFlashState {};
u8* SRAM; std::unique_ptr<u8[]> SRAM = nullptr;
u32 SRAMLength; u32 SRAMLength = 0;
SaveType SRAMType; SaveType SRAMType = S_NULL;
}; private:
void SetupSave(u32 type);
// CartGameSolarSensor -- Boktai game cart };
class CartGameSolarSensor : public CartGame
{ // CartGameSolarSensor -- Boktai game cart
public: class CartGameSolarSensor : public CartGame
CartGameSolarSensor(u8* rom, u32 len); {
virtual ~CartGameSolarSensor() override; public:
CartGameSolarSensor(const u8* rom, u32 len, const u8* sram, u32 sramlen);
virtual u32 Type() const override { return CartType::GameSolarSensor; } CartGameSolarSensor(std::unique_ptr<u8[]>&& rom, u32 len, std::unique_ptr<u8[]>&& sram, u32 sramlen);
virtual void Reset() override; void Reset() override;
virtual void DoSavestate(Savestate* file) override; void DoSavestate(Savestate* file) override;
virtual int SetInput(int num, bool pressed) override; int SetInput(int num, bool pressed) override;
private: protected:
virtual void ProcessGPIO() override; void ProcessGPIO() override;
static const int kLuxLevels[11]; private:
static const int kLuxLevels[11];
bool LightEdge;
u8 LightCounter; bool LightEdge = false;
u8 LightSample; u8 LightCounter = 0;
u8 LightLevel; u8 LightSample = 0;
}; u8 LightLevel = 0;
};
// CartRAMExpansion -- RAM expansion cart (DS browser, ...)
class CartRAMExpansion : public CartCommon // CartRAMExpansion -- RAM expansion cart (DS browser, ...)
{ class CartRAMExpansion : public CartCommon
public: {
CartRAMExpansion(); public:
~CartRAMExpansion() override; CartRAMExpansion();
~CartRAMExpansion() override;
virtual u32 Type() const override { return CartType::RAMExpansion; }
void Reset() override;
void Reset() override;
void DoSavestate(Savestate* file) override;
void DoSavestate(Savestate* file) override;
u16 ROMRead(u32 addr) const override;
u16 ROMRead(u32 addr) const override; void ROMWrite(u32 addr, u16 val) override;
void ROMWrite(u32 addr, u16 val) override;
private:
private: u8 RAM[0x800000] {};
u8 RAM[0x800000]; u16 RAMEnable = 0;
u16 RAMEnable; };
};
// possible inputs for GBA carts that might accept user input
// possible inputs for GBA carts that might accept user input enum
enum {
{ Input_SolarSensorDown = 0,
Input_SolarSensorDown = 0, Input_SolarSensorUp,
Input_SolarSensorUp, };
};
class GBACartSlot
class GBACartSlot {
{ public:
public: GBACartSlot(std::unique_ptr<CartCommon>&& cart = nullptr) noexcept;
GBACartSlot() noexcept = default; ~GBACartSlot() noexcept = default;
~GBACartSlot() noexcept = default; void Reset() noexcept;
void Reset() noexcept; void DoSavestate(Savestate* file) noexcept;
void DoSavestate(Savestate* file) noexcept;
/// Applies the GBACartData to the emulator state and unloads an existing ROM if any. /// Ejects the cart in the GBA slot (if any)
/// Upon successful insertion, \c cart will be nullptr and the global GBACart state /// and inserts the given one.
/// (\c CartROM, CartInserted, etc.) will be updated. ///
bool InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept; /// To insert a cart that does not require ROM data
bool LoadROM(const u8* romdata, u32 romlen) noexcept; /// (such as the RAM expansion pack),
void LoadSave(const u8* savedata, u32 savelen) noexcept; /// create it manually with std::make_unique and pass it here.
///
void LoadAddon(int type) noexcept; /// @param cart Movable \c unique_ptr to the GBA cart object.
/// May be \c nullptr, in which case the cart slot remains empty.
void EjectCart() noexcept; /// @post \c cart is \c nullptr and the underlying object
/// is moved into the cart slot.
// TODO: make more flexible, support nonbinary inputs void SetCart(std::unique_ptr<CartCommon>&& cart) noexcept;
int SetInput(int num, bool pressed) noexcept; [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; }
void LoadAddon(int type) noexcept;
u16 ROMRead(u32 addr) const noexcept;
void ROMWrite(u32 addr, u16 val) noexcept; /// @return The cart that was in the cart slot if any,
/// or \c nullptr if the cart slot was empty.
u8 SRAMRead(u32 addr) noexcept; std::unique_ptr<CartCommon> EjectCart() noexcept;
void SRAMWrite(u32 addr, u8 val) noexcept;
// TODO: make more flexible, support nonbinary inputs
/// This function is intended to allow frontends to save and load SRAM int SetInput(int num, bool pressed) noexcept;
/// without using melonDS APIs.
/// Modifying the emulated SRAM for any other reason is strongly discouraged. void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; }
/// The returned pointer may be invalidated if the emulator is reset,
/// or when a new game is loaded. u16 ROMRead(u32 addr) const noexcept;
/// Consequently, don't store the returned pointer for any longer than necessary. void ROMWrite(u32 addr, u16 val) noexcept;
/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr.
[[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } u8 SRAMRead(u32 addr) noexcept;
[[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } void SRAMWrite(u32 addr, u8 val) noexcept;
/// @returns The length of the buffer returned by ::GetSaveMemory() /// This function is intended to allow frontends to save and load SRAM
/// if a cart is loaded and supports SRAM, otherwise zero. /// without using melonDS APIs.
[[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } /// Modifying the emulated SRAM for any other reason is strongly discouraged.
private: /// The returned pointer may be invalidated if the emulator is reset,
std::unique_ptr<CartCommon> Cart = nullptr; /// or when a new game is loaded.
u16 OpenBusDecay = 0; /// Consequently, don't store the returned pointer for any longer than necessary.
}; /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr.
[[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
/// that can be inserted into the emulator or used to extract information about the cart beforehand.
/// @param romdata The ROM data to parse. /// Sets the loaded cart's SRAM.
/// The returned cartridge will contain a copy of this data, /// Does nothing if no cart is inserted
/// so the caller may deallocate \c romdata after this function returns. /// or the inserted cart doesn't support SRAM.
/// @param romlen The length of the ROM data in bytes. ///
/// @returns A \c GBACart::CartCommon object representing the parsed ROM, /// @param savedata Buffer containing the raw contents of the SRAM.
/// or \c nullptr if the ROM data couldn't be parsed. /// The contents of this buffer are copied into the cart slot,
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen); /// so the caller may dispose of it after this method returns.
/// @param savelen The length of the buffer in \c savedata, in bytes.
} void SetSaveMemory(const u8* savedata, u32 savelen) noexcept;
#endif // GBACART_H /// @returns The length of the buffer returned by ::GetSaveMemory()
/// if a cart is loaded and supports SRAM, otherwise zero.
[[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; }
private:
std::unique_ptr<CartCommon> Cart = nullptr;
u16 OpenBusDecay = 0;
};
/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass
/// that can be inserted into the emulator or used to extract information about the cart beforehand.
/// @param romdata The ROM data to parse.
/// The returned cartridge will contain a copy of this data,
/// so the caller may deallocate \c romdata after this function returns.
/// @param romlen The length of the ROM data in bytes.
/// @returns A \c GBACart::CartCommon object representing the parsed ROM,
/// or \c nullptr if the ROM data couldn't be parsed.
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen);
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen);
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, const u8* sramdata, u32 sramlen);
/// @param romdata The ROM data to parse. Will be moved-from.
/// @param romlen Length of romdata in bytes.
/// @param sramdata The save data to add to the cart.
/// May be \c nullptr, in which case the cart will have no save data.
/// @param sramlen Length of sramdata in bytes.
/// May be zero, in which case the cart will have no save data.
/// @return Unique pointer to the parsed GBA cart,
/// or \c nullptr if there was an error.
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::unique_ptr<u8[]>&& sramdata, u32 sramlen);
}
#endif // GBACART_H

View File

@ -34,6 +34,7 @@
#include "AREngine.h" #include "AREngine.h"
#include "Platform.h" #include "Platform.h"
#include "FreeBIOS.h" #include "FreeBIOS.h"
#include "Args.h"
#include "DSi.h" #include "DSi.h"
#include "DSi_SPI_TSC.h" #include "DSi_SPI_TSC.h"
@ -74,16 +75,31 @@ const s32 kIterationCycleMargin = 8;
NDS* NDS::Current = nullptr; NDS* NDS::Current = nullptr;
NDS::NDS(int type) noexcept : NDS::NDS() noexcept :
NDS(
NDSArgs {
nullptr,
nullptr,
bios_arm9_bin,
bios_arm7_bin,
Firmware(0),
}
)
{
}
NDS::NDS(NDSArgs&& args, int type) noexcept :
ConsoleType(type), ConsoleType(type),
ARM7BIOS(args.ARM7BIOS),
ARM9BIOS(args.ARM9BIOS),
JIT(*this), JIT(*this),
SPU(*this), SPU(*this),
GPU(*this), GPU(*this),
SPI(*this), SPI(*this, std::move(args.Firmware)),
RTC(*this), RTC(*this),
Wifi(*this), Wifi(*this),
NDSCartSlot(*this), NDSCartSlot(*this, std::move(args.NDSROM)),
GBACartSlot(), GBACartSlot(type == 1 ? nullptr : std::move(args.GBAROM)),
AREngine(*this), AREngine(*this),
ARM9(*this), ARM9(*this),
ARM7(*this), ARM7(*this),
@ -238,7 +254,7 @@ bool NDS::NeedsDirectBoot()
return true; return true;
// DSi/3DS firmwares aren't bootable // DSi/3DS firmwares aren't bootable
if (!SPI.GetFirmware()->IsBootable()) if (!SPI.GetFirmware().IsBootable())
return true; return true;
return false; return false;
@ -710,42 +726,27 @@ bool NDS::DoSavestate(Savestate* file)
return true; return true;
} }
bool NDS::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) void NDS::SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart)
{ {
if (!NDSCartSlot.LoadROM(romdata, romlen)) NDSCartSlot.SetCart(std::move(cart));
return false; // The existing cart will always be ejected;
// if cart is null, then that's equivalent to ejecting a cart
if (savedata && savelen) // without inserting a new one.
NDSCartSlot.LoadSave(savedata, savelen);
return true;
} }
void NDS::LoadSave(const u8* savedata, u32 savelen) void NDS::SetNDSSave(const u8* savedata, u32 savelen)
{ {
if (savedata && savelen) if (savedata && savelen)
NDSCartSlot.LoadSave(savedata, savelen); NDSCartSlot.SetSaveMemory(savedata, savelen);
} }
void NDS::EjectCart() void NDS::SetGBASave(const u8* savedata, u32 savelen)
{ {
NDSCartSlot.EjectCart(); if (ConsoleType == 0 && savedata && savelen)
} {
GBACartSlot.SetSaveMemory(savedata, savelen);
}
bool NDS::CartInserted()
{
return NDSCartSlot.GetCart() != nullptr;
}
bool NDS::LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen)
{
if (!GBACartSlot.LoadROM(romdata, romlen))
return false;
if (savedata && savelen)
GBACartSlot.LoadSave(savedata, savelen);
return true;
} }
void NDS::LoadGBAAddon(int type) void NDS::LoadGBAAddon(int type)
@ -753,26 +754,11 @@ void NDS::LoadGBAAddon(int type)
GBACartSlot.LoadAddon(type); GBACartSlot.LoadAddon(type);
} }
void NDS::EjectGBACart()
{
GBACartSlot.EjectCart();
}
void NDS::LoadBIOS() void NDS::LoadBIOS()
{ {
Reset(); Reset();
} }
bool NDS::IsLoadedARM9BIOSBuiltIn()
{
return memcmp(ARM9BIOS, bios_arm9_bin, sizeof(NDS::ARM9BIOS)) == 0;
}
bool NDS::IsLoadedARM7BIOSBuiltIn()
{
return memcmp(ARM7BIOS, bios_arm7_bin, sizeof(NDS::ARM7BIOS)) == 0;
}
u64 NDS::NextTarget() u64 NDS::NextTarget()
{ {
u64 minEvent = UINT64_MAX; u64 minEvent = UINT64_MAX;
@ -2252,7 +2238,7 @@ bool NDS::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
if ((addr & 0xFFFFF000) == 0xFFFF0000 && !write) if ((addr & 0xFFFFF000) == 0xFFFF0000 && !write)
{ {
region->Mem = ARM9BIOS; region->Mem = &ARM9BIOS[0];
region->Mask = 0xFFF; region->Mask = 0xFFF;
return true; return true;
} }
@ -2700,7 +2686,7 @@ bool NDS::ARM7GetMemRegion(u32 addr, bool write, MemRegion* region)
{ {
if (ARM7.R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7.R[15] < ARM7BIOSProt)) if (ARM7.R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7.R[15] < ARM7BIOSProt))
{ {
region->Mem = ARM7BIOS; region->Mem = &ARM7BIOS[0];
region->Mask = 0x3FFF; region->Mask = 0x3FFF;
return true; return true;
} }

View File

@ -21,7 +21,7 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include <memory> #include <optional>
#include <functional> #include <functional>
#include "Platform.h" #include "Platform.h"
@ -37,6 +37,7 @@
#include "GPU.h" #include "GPU.h"
#include "ARMJIT.h" #include "ARMJIT.h"
#include "DMA.h" #include "DMA.h"
#include "FreeBIOS.h"
// when touching the main loop/timing code, pls test a lot of shit // when touching the main loop/timing code, pls test a lot of shit
// with this enabled, to make sure it doesn't desync // with this enabled, to make sure it doesn't desync
@ -44,7 +45,8 @@
namespace melonDS namespace melonDS
{ {
struct NDSArgs;
class Firmware;
enum enum
{ {
Event_LCD = 0, Event_LCD = 0,
@ -255,8 +257,8 @@ public:
u8 ROMSeed0[2*8]; u8 ROMSeed0[2*8];
u8 ROMSeed1[2*8]; u8 ROMSeed1[2*8];
u8 ARM9BIOS[0x1000]; std::array<u8, ARM9BIOSSize> ARM9BIOS;
u8 ARM7BIOS[0x4000]; std::array<u8, ARM7BIOSSize> ARM7BIOS;
u16 ARM7BIOSProt; u16 ARM7BIOSProt;
u8* MainRAM; u8* MainRAM;
@ -303,25 +305,51 @@ public:
void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq);
void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq);
// 0=DS 1=DSi
void SetConsoleType(int type);
void LoadBIOS(); void LoadBIOS();
bool IsLoadedARM9BIOSBuiltIn(); [[nodiscard]] bool IsLoadedARM9BIOSBuiltIn() const noexcept { return ARM9BIOS == bios_arm9_bin; }
bool IsLoadedARM7BIOSBuiltIn(); [[nodiscard]] bool IsLoadedARM7BIOSBuiltIn() const noexcept { return ARM7BIOS == bios_arm7_bin; }
virtual bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); [[nodiscard]] NDSCart::CartCommon* GetNDSCart() { return NDSCartSlot.GetCart(); }
void LoadSave(const u8* savedata, u32 savelen); [[nodiscard]] const NDSCart::CartCommon* GetNDSCart() const { return NDSCartSlot.GetCart(); }
virtual void EjectCart(); virtual void SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart);
bool CartInserted(); [[nodiscard]] bool CartInserted() const noexcept { return NDSCartSlot.GetCart() != nullptr; }
virtual std::unique_ptr<NDSCart::CartCommon> EjectCart() { return NDSCartSlot.EjectCart(); }
[[nodiscard]] u8* GetNDSSave() { return NDSCartSlot.GetSaveMemory(); }
[[nodiscard]] const u8* GetNDSSave() const { return NDSCartSlot.GetSaveMemory(); }
[[nodiscard]] u32 GetNDSSaveLength() const { return NDSCartSlot.GetSaveMemoryLength(); }
void SetNDSSave(const u8* savedata, u32 savelen);
const Firmware& GetFirmware() const { return SPI.GetFirmwareMem()->GetFirmware(); }
Firmware& GetFirmware() { return SPI.GetFirmwareMem()->GetFirmware(); }
void SetFirmware(Firmware&& firmware) { SPI.GetFirmwareMem()->SetFirmware(std::move(firmware)); }
virtual bool NeedsDirectBoot(); virtual bool NeedsDirectBoot();
void SetupDirectBoot(const std::string& romname); void SetupDirectBoot(const std::string& romname);
virtual void SetupDirectBoot(); virtual void SetupDirectBoot();
bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); [[nodiscard]] GBACart::CartCommon* GetGBACart() { return (ConsoleType == 1) ? nullptr : GBACartSlot.GetCart(); }
[[nodiscard]] const GBACart::CartCommon* GetGBACart() const { return (ConsoleType == 1) ? nullptr : GBACartSlot.GetCart(); }
/// Inserts a GBA cart into the emulated console's Slot-2.
///
/// @param cart The GBA cart, most likely (but not necessarily) returned from GBACart::ParseROM.
/// To insert an accessory that doesn't use a ROM image
/// (e.g. the Expansion Pak), create it manually and pass it here.
/// If \c nullptr, the existing cart is ejected.
/// If this is a DSi, this method does nothing.
///
/// @post \c cart is \c nullptr and this NDS takes ownership
/// of the cart object it held, if any.
void SetGBACart(std::unique_ptr<GBACart::CartCommon>&& cart) { if (ConsoleType == 0) GBACartSlot.SetCart(std::move(cart)); }
u8* GetGBASave() { return GBACartSlot.GetSaveMemory(); }
const u8* GetGBASave() const { return GBACartSlot.GetSaveMemory(); }
u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); }
void SetGBASave(const u8* savedata, u32 savelen);
void LoadGBAAddon(int type); void LoadGBAAddon(int type);
void EjectGBACart(); std::unique_ptr<GBACart::CartCommon> EjectGBACart() { return GBACartSlot.EjectCart(); }
u32 RunFrame(); u32 RunFrame();
@ -456,7 +484,8 @@ private:
template <bool EnableJIT> template <bool EnableJIT>
u32 RunFrame(); u32 RunFrame();
public: public:
NDS() noexcept : NDS(0) {} NDS(NDSArgs&& args) noexcept : NDS(std::move(args), 0) {}
NDS() noexcept;
virtual ~NDS() noexcept; virtual ~NDS() noexcept;
NDS(const NDS&) = delete; NDS(const NDS&) = delete;
NDS& operator=(const NDS&) = delete; NDS& operator=(const NDS&) = delete;
@ -465,7 +494,7 @@ public:
// The frontend should set and unset this manually after creating and destroying the NDS object. // The frontend should set and unset this manually after creating and destroying the NDS object.
[[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current; [[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current;
protected: protected:
explicit NDS(int type) noexcept; explicit NDS(NDSArgs&& args, int type) noexcept;
virtual void DoSavestateExtra(Savestate* file) {} virtual void DoSavestateExtra(Savestate* file) {}
}; };

View File

@ -20,12 +20,12 @@
#include "NDS.h" #include "NDS.h"
#include "DSi.h" #include "DSi.h"
#include "NDSCart.h" #include "NDSCart.h"
#include "ARM.h"
#include "CRC32.h" #include "CRC32.h"
#include "Platform.h" #include "Platform.h"
#include "ROMList.h" #include "ROMList.h"
#include "melonDLDI.h" #include "melonDLDI.h"
#include "xxhash/xxhash.h" #include "FATStorage.h"
#include "Utils.h"
namespace melonDS namespace melonDS
{ {
@ -43,7 +43,7 @@ enum
// SRAM TODO: emulate write delays??? // SRAM TODO: emulate write delays???
u32 ByteSwap(u32 val) constexpr u32 ByteSwap(u32 val)
{ {
return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24);
} }
@ -173,27 +173,29 @@ void NDSCartSlot::Key2_Encrypt(u8* data, u32 len) noexcept
} }
CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) CartCommon::CartCommon(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, melonDS::NDSCart::CartType type) :
CartCommon(CopyToUnique(rom, len), len, chipid, badDSiDump, romparams, type)
{ {
ROM = rom; }
ROMLength = len;
ChipID = chipid;
ROMParams = romparams;
memcpy(&Header, rom, sizeof(Header)); CartCommon::CartCommon(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, melonDS::NDSCart::CartType type) :
ROM(std::move(rom)),
ROMLength(len),
ChipID(chipid),
ROMParams(romparams),
CartType(type)
{
memcpy(&Header, ROM.get(), sizeof(Header));
IsDSi = Header.IsDSi() && !badDSiDump; IsDSi = Header.IsDSi() && !badDSiDump;
DSiBase = Header.DSiRegionStart << 19; DSiBase = Header.DSiRegionStart << 19;
} }
CartCommon::~CartCommon() CartCommon::~CartCommon() = default;
{
delete[] ROM;
}
u32 CartCommon::Checksum() const u32 CartCommon::Checksum() const
{ {
const NDSHeader& header = GetHeader(); const NDSHeader& header = GetHeader();
u32 crc = CRC32(ROM, 0x40); u32 crc = CRC32(ROM.get(), 0x40);
crc = CRC32(&ROM[header.ARM9ROMOffset], header.ARM9Size, crc); crc = CRC32(&ROM[header.ARM9ROMOffset], header.ARM9Size, crc);
crc = CRC32(&ROM[header.ARM7ROMOffset], header.ARM7Size, crc); crc = CRC32(&ROM[header.ARM7ROMOffset], header.ARM7Size, crc);
@ -230,14 +232,6 @@ void CartCommon::DoSavestate(Savestate* file)
file->Bool32(&DSiMode); file->Bool32(&DSiMode);
} }
void CartCommon::SetupSave(u32 type)
{
}
void CartCommon::LoadSave(const u8* savedata, u32 savelen)
{
}
int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len)
{ {
if (CmdEncMode == 0) if (CmdEncMode == 0)
@ -267,7 +261,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da
case 0x3C: case 0x3C:
CmdEncMode = 1; CmdEncMode = 1;
cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.ARM7BIOS, sizeof(NDS::ARM7BIOS)); cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, &nds.ARM7BIOS[0], sizeof(NDS::ARM7BIOS));
DSiMode = false; DSiMode = false;
return 0; return 0;
@ -276,7 +270,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da
{ {
auto& dsi = static_cast<DSi&>(nds); auto& dsi = static_cast<DSi&>(nds);
CmdEncMode = 1; CmdEncMode = 1;
cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, dsi.ARM7iBIOS, sizeof(DSi::ARM7iBIOS)); cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, &dsi.ARM7iBIOS[0], sizeof(DSi::ARM7iBIOS));
DSiMode = true; DSiMode = true;
} }
return 0; return 0;
@ -360,23 +354,13 @@ u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last)
return 0xFF; return 0xFF;
} }
u8 *CartCommon::GetSaveMemory() const
{
return nullptr;
}
u32 CartCommon::GetSaveMemoryLength() const
{
return 0;
}
void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset)
{ {
if (addr >= ROMLength) return; if (addr >= ROMLength) return;
if ((addr+len) > ROMLength) if ((addr+len) > ROMLength)
len = ROMLength - addr; len = ROMLength - addr;
memcpy(data+offset, ROM+addr, len); memcpy(data+offset, ROM.get()+addr, len);
} }
const NDSBanner* CartCommon::Banner() const const NDSBanner* CartCommon::Banner() const
@ -385,22 +369,64 @@ const NDSBanner* CartCommon::Banner() const
size_t bannersize = header.IsDSi() ? 0x23C0 : 0xA40; size_t bannersize = header.IsDSi() ? 0x23C0 : 0xA40;
if (header.BannerOffset >= 0x200 && header.BannerOffset < (ROMLength - bannersize)) if (header.BannerOffset >= 0x200 && header.BannerOffset < (ROMLength - bannersize))
{ {
return reinterpret_cast<const NDSBanner*>(ROM + header.BannerOffset); return reinterpret_cast<const NDSBanner*>(ROM.get() + header.BannerOffset);
} }
return nullptr; return nullptr;
} }
CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) : CartCommon(rom, len, chipid, badDSiDump, romparams) CartRetail::CartRetail(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen, melonDS::NDSCart::CartType type) :
CartRetail(CopyToUnique(rom, len), len, chipid, badDSiDump, romparams, std::move(sram), sramlen, type)
{ {
SRAM = nullptr;
} }
CartRetail::~CartRetail() CartRetail::CartRetail(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen, melonDS::NDSCart::CartType type) :
CartCommon(std::move(rom), len, chipid, badDSiDump, romparams, type)
{ {
if (SRAM) delete[] SRAM; u32 savememtype = ROMParams.SaveMemType <= 10 ? ROMParams.SaveMemType : 0;
constexpr int sramlengths[] =
{
0,
512,
8192, 65536, 128*1024,
256*1024, 512*1024, 1024*1024,
8192*1024, 16384*1024, 65536*1024
};
SRAMLength = sramlengths[savememtype];
if (SRAMLength)
{ // If this cart should have any save data...
if (sram && sramlen == SRAMLength)
{ // If we were given save data that already has the correct length...
SRAM = std::move(sram);
}
else
{ // Copy in what we can, truncate the rest.
SRAM = std::make_unique<u8[]>(SRAMLength);
memset(SRAM.get(), 0xFF, SRAMLength);
memcpy(SRAM.get(), sram.get(), std::min(sramlen, SRAMLength));
}
}
switch (savememtype)
{
case 1: SRAMType = 1; break; // EEPROM, small
case 2:
case 3:
case 4: SRAMType = 2; break; // EEPROM, regular
case 5:
case 6:
case 7: SRAMType = 3; break; // FLASH
case 8:
case 9:
case 10: SRAMType = 4; break; // NAND
default: SRAMType = 0; break; // ...whatever else
}
} }
CartRetail::~CartRetail() = default;
// std::unique_ptr cleans up the SRAM and ROM
void CartRetail::Reset() void CartRetail::Reset()
{ {
CartCommon::Reset(); CartCommon::Reset();
@ -425,13 +451,11 @@ void CartRetail::DoSavestate(Savestate* file)
Log(LogLevel::Warn, "savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength); Log(LogLevel::Warn, "savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength);
Log(LogLevel::Warn, "oh well. loading it anyway. adsfgdsf\n"); Log(LogLevel::Warn, "oh well. loading it anyway. adsfgdsf\n");
if (oldlen) delete[] SRAM; SRAM = SRAMLength ? std::make_unique<u8[]>(SRAMLength) : nullptr;
SRAM = nullptr;
if (SRAMLength) SRAM = new u8[SRAMLength];
} }
if (SRAMLength) if (SRAMLength)
{ {
file->VarArray(SRAM, SRAMLength); file->VarArray(SRAM.get(), SRAMLength);
} }
// SPI status shito // SPI status shito
@ -441,53 +465,15 @@ void CartRetail::DoSavestate(Savestate* file)
file->Var8(&SRAMStatus); file->Var8(&SRAMStatus);
if ((!file->Saving) && SRAM) if ((!file->Saving) && SRAM)
Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength); Platform::WriteNDSSave(SRAM.get(), SRAMLength, 0, SRAMLength);
} }
void CartRetail::SetupSave(u32 type) void CartRetail::SetSaveMemory(const u8* savedata, u32 savelen)
{
if (SRAM) delete[] SRAM;
SRAM = nullptr;
if (type > 10) type = 0;
int sramlen[] =
{
0,
512,
8192, 65536, 128*1024,
256*1024, 512*1024, 1024*1024,
8192*1024, 16384*1024, 65536*1024
};
SRAMLength = sramlen[type];
if (SRAMLength)
{
SRAM = new u8[SRAMLength];
memset(SRAM, 0xFF, SRAMLength);
}
switch (type)
{
case 1: SRAMType = 1; break; // EEPROM, small
case 2:
case 3:
case 4: SRAMType = 2; break; // EEPROM, regular
case 5:
case 6:
case 7: SRAMType = 3; break; // FLASH
case 8:
case 9:
case 10: SRAMType = 4; break; // NAND
default: SRAMType = 0; break; // ...whatever else
}
}
void CartRetail::LoadSave(const u8* savedata, u32 savelen)
{ {
if (!SRAM) return; if (!SRAM) return;
u32 len = std::min(savelen, SRAMLength); u32 len = std::min(savelen, SRAMLength);
memcpy(SRAM, savedata, len); memcpy(SRAM.get(), savedata, len);
Platform::WriteNDSSave(savedata, len, 0, len); Platform::WriteNDSSave(savedata, len, 0, len);
} }
@ -551,16 +537,6 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last)
} }
} }
u8 *CartRetail::GetSaveMemory() const
{
return SRAM;
}
u32 CartRetail::GetSaveMemoryLength() const
{
return SRAMLength;
}
void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
{ {
addr &= (ROMLength-1); addr &= (ROMLength-1);
@ -578,7 +554,7 @@ void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
addr = 0x8000 + (addr & 0x1FF); addr = 0x8000 + (addr & 0x1FF);
} }
memcpy(data+offset, ROM+addr, len); memcpy(data+offset, ROM.get()+addr, len);
} }
u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
@ -613,7 +589,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
if (last) if (last)
{ {
SRAMStatus &= ~(1<<1); SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength, Platform::WriteNDSSave(SRAM.get(), SRAMLength,
(SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr); (SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr);
} }
return 0; return 0;
@ -677,7 +653,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)
if (last) if (last)
{ {
SRAMStatus &= ~(1<<1); SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength, Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
} }
return 0; return 0;
@ -734,7 +710,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
if (last) if (last)
{ {
SRAMStatus &= ~(1<<1); SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength, Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
} }
return 0; return 0;
@ -771,7 +747,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
if (last) if (last)
{ {
SRAMStatus &= ~(1<<1); SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength, Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
} }
return 0; return 0;
@ -817,7 +793,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
if (last) if (last)
{ {
SRAMStatus &= ~(1<<1); SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength, Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
} }
return 0; return 0;
@ -840,7 +816,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
if (last) if (last)
{ {
SRAMStatus &= ~(1<<1); SRAMStatus &= ~(1<<1);
Platform::WriteNDSSave(SRAM, SRAMLength, Platform::WriteNDSSave(SRAM.get(), SRAMLength,
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
} }
return 0; return 0;
@ -852,15 +828,19 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
} }
} }
CartRetailNAND::CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams) CartRetailNAND(CopyToUnique(rom, len), len, chipid, romparams, std::move(sram), sramlen)
{ {
} }
CartRetailNAND::~CartRetailNAND() CartRetailNAND::CartRetailNAND(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetail(std::move(rom), len, chipid, false, romparams, std::move(sram), sramlen, CartType::RetailNAND)
{ {
BuildSRAMID();
} }
CartRetailNAND::~CartRetailNAND() = default;
void CartRetailNAND::Reset() void CartRetailNAND::Reset()
{ {
CartRetail::Reset(); CartRetail::Reset();
@ -889,9 +869,9 @@ void CartRetailNAND::DoSavestate(Savestate* file)
BuildSRAMID(); BuildSRAMID();
} }
void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen) void CartRetailNAND::SetSaveMemory(const u8* savedata, u32 savelen)
{ {
CartRetail::LoadSave(savedata, savelen); CartRetail::SetSaveMemory(savedata, savelen);
BuildSRAMID(); BuildSRAMID();
} }
@ -924,7 +904,7 @@ int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8
if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000)) if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000))
{ {
memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800); memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800);
Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800); Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMAddr - SRAMBase, 0x800);
} }
SRAMAddr = 0; SRAMAddr = 0;
@ -1080,15 +1060,28 @@ void CartRetailNAND::BuildSRAMID()
} }
CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams) : CartRetail(rom, len, chipid, badDSiDump, romparams) CartRetailIR::CartRetailIR(const u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetailIR(CopyToUnique(rom, len), len, chipid, irversion, badDSiDump, romparams, std::move(sram), sramlen)
{ {
IRVersion = irversion;
} }
CartRetailIR::~CartRetailIR() CartRetailIR::CartRetailIR(
std::unique_ptr<u8[]>&& rom,
u32 len,
u32 chipid,
u32 irversion,
bool badDSiDump,
ROMListEntry romparams,
std::unique_ptr<u8[]>&& sram,
u32 sramlen
) :
CartRetail(std::move(rom), len, chipid, badDSiDump, romparams, std::move(sram), sramlen, CartType::RetailIR),
IRVersion(irversion)
{ {
} }
CartRetailIR::~CartRetailIR() = default;
void CartRetailIR::Reset() void CartRetailIR::Reset()
{ {
CartRetail::Reset(); CartRetail::Reset();
@ -1125,25 +1118,18 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last)
return 0; return 0;
} }
CartRetailBT::CartRetailBT(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetailBT(CopyToUnique(rom, len), len, chipid, romparams, std::move(sram), sramlen)
{
}
CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams) CartRetailBT::CartRetailBT(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
CartRetail(std::move(rom), len, chipid, false, romparams, std::move(sram), sramlen, CartType::RetailBT)
{ {
Log(LogLevel::Info,"POKETYPE CART\n"); Log(LogLevel::Info,"POKETYPE CART\n");
} }
CartRetailBT::~CartRetailBT() CartRetailBT::~CartRetailBT() = default;
{
}
void CartRetailBT::Reset()
{
CartRetail::Reset();
}
void CartRetailBT::DoSavestate(Savestate* file)
{
CartRetail::DoSavestate(file);
}
u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
{ {
@ -1159,50 +1145,30 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
return 0; return 0;
} }
CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartCommon(rom, len, chipid, false, romparams) CartHomebrew(CopyToUnique(rom, len), len, chipid, romparams, std::move(sdcard))
{ {
SD = nullptr;
} }
CartHomebrew::~CartHomebrew() CartHomebrew::CartHomebrew(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
CartCommon(std::move(rom), len, chipid, false, romparams, CartType::Homebrew),
SD(std::move(sdcard))
{ {
if (SD) sdcard = std::nullopt;
{ // std::move on optionals usually results in an optional with a moved-from object
SD->Close();
delete SD;
}
} }
CartHomebrew::~CartHomebrew() = default;
// The SD card is destroyed by the optional's destructor
void CartHomebrew::Reset() void CartHomebrew::Reset()
{ {
CartCommon::Reset(); CartCommon::Reset();
ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly);
if (SD) if (SD)
{ {
SD->Close(); ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly());
delete SD;
} }
if (Platform::GetConfigBool(Platform::DLDI_Enable))
{
std::string folderpath;
if (Platform::GetConfigBool(Platform::DLDI_FolderSync))
folderpath = Platform::GetConfigString(Platform::DLDI_FolderPath);
else
folderpath = "";
ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), ReadOnly);
SD = new FATStorage(Platform::GetConfigString(Platform::DLDI_ImagePath),
(u64)Platform::GetConfigInt(Platform::DLDI_ImageSize) * 1024 * 1024,
ReadOnly,
folderpath);
SD->Open();
}
else
SD = nullptr;
} }
void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds)
@ -1213,7 +1179,7 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds)
{ {
// add the ROM to the SD volume // add the ROM to the SD volume
if (!SD->InjectFile(romname, ROM, ROMLength)) if (!SD->InjectFile(romname, ROM.get(), ROMLength))
return; return;
// setup argv command line // setup argv command line
@ -1240,11 +1206,6 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds)
} }
} }
void CartHomebrew::DoSavestate(Savestate* file)
{
CartCommon::DoSavestate(file);
}
int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len)
{ {
if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len);
@ -1293,7 +1254,7 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len)
case 0xC1: case 0xC1:
{ {
u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4];
if (SD && (!ReadOnly)) SD->WriteSectors(sector, len>>9, data); if (SD && !SD->IsReadOnly()) SD->WriteSectors(sector, len>>9, data);
} }
break; break;
@ -1439,17 +1400,20 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
addr &= (ROMLength-1); addr &= (ROMLength-1);
memcpy(data+offset, ROM+addr, len); memcpy(data+offset, ROM.get()+addr, len);
} }
NDSCartSlot::NDSCartSlot(melonDS::NDS& nds) noexcept : NDS(nds) NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom) noexcept : NDS(nds)
{ {
NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData)); NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData));
NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer)); NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer));
NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone)); NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone));
// All fields are default-constructed because they're listed as such in the class declaration // All fields are default-constructed because they're listed as such in the class declaration
if (rom)
SetCart(std::move(rom));
} }
NDSCartSlot::~NDSCartSlot() noexcept NDSCartSlot::~NDSCartSlot() noexcept
@ -1569,16 +1533,13 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept
memcpy(out, &cartrom[arm9base], 0x800); memcpy(out, &cartrom[arm9base], 0x800);
Key1_InitKeycode(false, gamecode, 2, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); Key1_InitKeycode(false, gamecode, 2, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS));
Key1_Decrypt((u32*)&out[0]); Key1_Decrypt((u32*)&out[0]);
Key1_InitKeycode(false, gamecode, 3, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); Key1_InitKeycode(false, gamecode, 3, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS));
for (u32 i = 0; i < 0x800; i += 8) for (u32 i = 0; i < 0x800; i += 8)
Key1_Decrypt((u32*)&out[i]); Key1_Decrypt((u32*)&out[i]);
XXH64_hash_t hash = XXH64(out, 0x800, 0);
Log(LogLevel::Debug, "Secure area post-decryption xxh64 hash: %zx\n", hash);
if (!strncmp((const char*)out, "encryObj", 8)) if (!strncmp((const char*)out, "encryObj", 8))
{ {
Log(LogLevel::Info, "Secure area decryption OK\n"); Log(LogLevel::Info, "Secure area decryption OK\n");
@ -1593,7 +1554,12 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept
} }
} }
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen) std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, std::optional<NDSCartArgs>&& args)
{
return ParseROM(CopyToUnique(romdata, romlen), romlen, std::move(args));
}
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::optional<NDSCartArgs>&& args)
{ {
if (romdata == nullptr) if (romdata == nullptr)
{ {
@ -1607,28 +1573,10 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
return nullptr; return nullptr;
} }
u32 cartromsize = 0x200; auto [cartrom, cartromsize] = PadToPowerOf2(std::move(romdata), romlen);
while (cartromsize < romlen)
cartromsize <<= 1; // ROM size must be a power of 2
u8* cartrom = nullptr;
try
{
cartrom = new u8[cartromsize];
}
catch (const std::bad_alloc& e)
{
Log(LogLevel::Error, "NDSCart: failed to allocate memory for ROM (%d bytes)\n", cartromsize);
return nullptr;
}
// copy romdata into cartrom then zero out the remaining space
memcpy(cartrom, romdata, romlen);
memset(cartrom + romlen, 0, cartromsize - romlen);
NDSHeader header {}; NDSHeader header {};
memcpy(&header, cartrom, sizeof(header)); memcpy(&header, cartrom.get(), sizeof(header));
bool dsi = header.IsDSi(); bool dsi = header.IsDSi();
bool badDSiDump = false; bool badDSiDump = false;
@ -1694,30 +1642,24 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
} }
std::unique_ptr<CartCommon> cart; std::unique_ptr<CartCommon> cart;
auto [sram, sramlen] = args ? std::move(*args->SRAM) : std::make_pair(nullptr, 0);
if (homebrew) if (homebrew)
cart = std::make_unique<CartHomebrew>(cartrom, cartromsize, cartid, romparams); cart = std::make_unique<CartHomebrew>(std::move(cartrom), cartromsize, cartid, romparams, args ? std::move(args->SDCard) : std::nullopt);
else if (cartid & 0x08000000) else if (cartid & 0x08000000)
cart = std::make_unique<CartRetailNAND>(cartrom, cartromsize, cartid, romparams); cart = std::make_unique<CartRetailNAND>(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen);
else if (irversion != 0) else if (irversion != 0)
cart = std::make_unique<CartRetailIR>(cartrom, cartromsize, cartid, irversion, badDSiDump, romparams); cart = std::make_unique<CartRetailIR>(std::move(cartrom), cartromsize, cartid, irversion, badDSiDump, romparams, std::move(sram), sramlen);
else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx
cart = std::make_unique<CartRetailBT>(cartrom, cartromsize, cartid, romparams); cart = std::make_unique<CartRetailBT>(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen);
else else
cart = std::make_unique<CartRetail>(cartrom, cartromsize, cartid, badDSiDump, romparams); cart = std::make_unique<CartRetail>(std::move(cartrom), cartromsize, cartid, badDSiDump, romparams, std::move(sram), sramlen);
if (romparams.SaveMemType > 0)
cart->SetupSave(romparams.SaveMemType);
args = std::nullopt;
return cart; return cart;
} }
bool NDSCartSlot::InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept void NDSCartSlot::SetCart(std::unique_ptr<CartCommon>&& cart) noexcept
{ {
if (!cart) {
Log(LogLevel::Error, "Failed to insert invalid cart; existing cart (if any) was not ejected.\n");
return false;
}
if (Cart) if (Cart)
EjectCart(); EjectCart();
@ -1725,6 +1667,10 @@ bool NDSCartSlot::InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept
// and cloning polymorphic objects without knowing the underlying type is annoying. // and cloning polymorphic objects without knowing the underlying type is annoying.
Cart = std::move(cart); Cart = std::move(cart);
if (!Cart)
// If we're ejecting an existing cart without inserting a new one...
return;
Cart->Reset(); Cart->Reset();
const NDSHeader& header = Cart->GetHeader(); const NDSHeader& header = Cart->GetHeader();
@ -1739,11 +1685,11 @@ bool NDSCartSlot::InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept
strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8); strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8);
Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); Key1_InitKeycode(false, romparams.GameCode, 3, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS));
for (u32 i = 0; i < 0x800; i += 8) for (u32 i = 0; i < 0x800; i += 8)
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]); Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]);
Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); Key1_InitKeycode(false, romparams.GameCode, 2, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS));
Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]); Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]);
Log(LogLevel::Debug, "Re-encrypted cart secure area\n"); Log(LogLevel::Debug, "Re-encrypted cart secure area\n");
@ -1757,21 +1703,12 @@ bool NDSCartSlot::InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept
Log(LogLevel::Info, "Inserted cart with game code: %.4s\n", header.GameCode); Log(LogLevel::Info, "Inserted cart with game code: %.4s\n", header.GameCode);
Log(LogLevel::Info, "Inserted cart with ID: %08X\n", Cart->ID()); Log(LogLevel::Info, "Inserted cart with ID: %08X\n", Cart->ID());
Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType); Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType);
return true;
} }
bool NDSCartSlot::LoadROM(const u8* romdata, u32 romlen) noexcept void NDSCartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept
{
std::unique_ptr<CartCommon> cart = ParseROM(romdata, romlen);
return InsertROM(std::move(cart));
}
void NDSCartSlot::LoadSave(const u8* savedata, u32 savelen) noexcept
{ {
if (Cart) if (Cart)
Cart->LoadSave(savedata, savelen); Cart->SetSaveMemory(savedata, savelen);
} }
void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept
@ -1780,15 +1717,15 @@ void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept
Cart->SetupDirectBoot(romname, NDS); Cart->SetupDirectBoot(romname, NDS);
} }
void NDSCartSlot::EjectCart() noexcept std::unique_ptr<CartCommon> NDSCartSlot::EjectCart() noexcept
{ {
if (!Cart) return; if (!Cart) return nullptr;
// ejecting the cart triggers the gamecard IRQ // ejecting the cart triggers the gamecard IRQ
NDS.SetIRQ(0, IRQ_CartIREQMC); NDS.SetIRQ(0, IRQ_CartIREQMC);
NDS.SetIRQ(1, IRQ_CartIREQMC); NDS.SetIRQ(1, IRQ_CartIREQMC);
Cart = nullptr; return std::move(Cart);
// CHECKME: does an eject imply anything for the ROM/SPI transfer registers? // CHECKME: does an eject imply anything for the ROM/SPI transfer registers?
} }

View File

@ -22,7 +22,7 @@
#include <array> #include <array>
#include <string> #include <string>
#include <memory> #include <memory>
#include <array> #include <variant>
#include "types.h" #include "types.h"
#include "Savestate.h" #include "Savestate.h"
@ -49,14 +49,32 @@ enum CartType
class NDSCartSlot; class NDSCartSlot;
/// Arguments used to create and populate an NDS cart of unknown type.
/// Different carts take different subsets of these arguments,
/// but we won't know which ones to use
/// until we parse the header at runtime.
struct NDSCartArgs
{
/// The arguments used to load a homebrew SD card image.
/// If \c nullopt, then the cart will not have an SD card.
/// Ignored for retail ROMs.
std::optional<FATStorageArgs> SDCard = std::nullopt;
/// Save RAM to load into the cartridge.
/// If \c nullopt, then the cart's SRAM buffer will be empty.
/// Ignored for homebrew ROMs.
std::optional<std::pair<std::unique_ptr<u8[]>, u32>> SRAM = std::nullopt;
};
// CartCommon -- base code shared by all cart types // CartCommon -- base code shared by all cart types
class CartCommon class CartCommon
{ {
public: public:
CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams); CartCommon(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, CartType type);
CartCommon(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, CartType type);
virtual ~CartCommon(); virtual ~CartCommon();
virtual u32 Type() const = 0; [[nodiscard]] u32 Type() const { return CartType; };
[[nodiscard]] u32 Checksum() const; [[nodiscard]] u32 Checksum() const;
virtual void Reset(); virtual void Reset();
@ -64,16 +82,16 @@ public:
virtual void DoSavestate(Savestate* file); virtual void DoSavestate(Savestate* file);
virtual void SetupSave(u32 type);
virtual void LoadSave(const u8* savedata, u32 savelen);
virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len); virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len);
virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len); virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len);
virtual u8 SPIWrite(u8 val, u32 pos, bool last); virtual u8 SPIWrite(u8 val, u32 pos, bool last);
virtual u8* GetSaveMemory() const; virtual u8* GetSaveMemory() { return nullptr; }
virtual u32 GetSaveMemoryLength() const; virtual const u8* GetSaveMemory() const { return nullptr; }
virtual u32 GetSaveMemoryLength() const { return 0; }
virtual void SetSaveMemory(const u8* savedata, u32 savelen) {};
[[nodiscard]] const NDSHeader& GetHeader() const { return Header; } [[nodiscard]] const NDSHeader& GetHeader() const { return Header; }
[[nodiscard]] NDSHeader& GetHeader() { return Header; } [[nodiscard]] NDSHeader& GetHeader() { return Header; }
@ -82,48 +100,65 @@ public:
[[nodiscard]] const NDSBanner* Banner() const; [[nodiscard]] const NDSBanner* Banner() const;
[[nodiscard]] const ROMListEntry& GetROMParams() const { return ROMParams; }; [[nodiscard]] const ROMListEntry& GetROMParams() const { return ROMParams; };
[[nodiscard]] u32 ID() const { return ChipID; } [[nodiscard]] u32 ID() const { return ChipID; }
[[nodiscard]] const u8* GetROM() const { return ROM; } [[nodiscard]] const u8* GetROM() const { return ROM.get(); }
[[nodiscard]] u32 GetROMLength() const { return ROMLength; } [[nodiscard]] u32 GetROMLength() const { return ROMLength; }
protected: protected:
void ReadROM(u32 addr, u32 len, u8* data, u32 offset); void ReadROM(u32 addr, u32 len, u8* data, u32 offset);
u8* ROM; std::unique_ptr<u8[]> ROM = nullptr;
u32 ROMLength; u32 ROMLength = 0;
u32 ChipID; u32 ChipID = 0;
bool IsDSi; bool IsDSi = false;
bool DSiMode; bool DSiMode = false;
u32 DSiBase; u32 DSiBase = 0;
u32 CmdEncMode; u32 CmdEncMode = 0;
u32 DataEncMode; u32 DataEncMode = 0;
// Kept separate from the ROM data so we can decrypt the modcrypt area // Kept separate from the ROM data so we can decrypt the modcrypt area
// without touching the overall ROM data // without touching the overall ROM data
NDSHeader Header; NDSHeader Header {};
ROMListEntry ROMParams; ROMListEntry ROMParams {};
const melonDS::NDSCart::CartType CartType = Default;
}; };
// CartRetail -- regular retail cart (ROM, SPI SRAM) // CartRetail -- regular retail cart (ROM, SPI SRAM)
class CartRetail : public CartCommon class CartRetail : public CartCommon
{ {
public: public:
CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams); CartRetail(
virtual ~CartRetail() override; const u8* rom,
u32 len,
u32 chipid,
bool badDSiDump,
ROMListEntry romparams,
std::unique_ptr<u8[]>&& sram,
u32 sramlen,
melonDS::NDSCart::CartType type = CartType::Retail
);
CartRetail(
std::unique_ptr<u8[]>&& rom,
u32 len, u32 chipid,
bool badDSiDump,
ROMListEntry romparams,
std::unique_ptr<u8[]>&& sram,
u32 sramlen,
melonDS::NDSCart::CartType type = CartType::Retail
);
~CartRetail() override;
virtual u32 Type() const override { return CartType::Retail; } void Reset() override;
virtual void Reset() override; void DoSavestate(Savestate* file) override;
virtual void DoSavestate(Savestate* file) override; void SetSaveMemory(const u8* savedata, u32 savelen) override;
virtual void SetupSave(u32 type) override; int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override;
virtual void LoadSave(const u8* savedata, u32 savelen) override;
virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; u8 SPIWrite(u8 val, u32 pos, bool last) override;
virtual u8 SPIWrite(u8 val, u32 pos, bool last) override; u8* GetSaveMemory() override { return SRAM.get(); }
const u8* GetSaveMemory() const override { return SRAM.get(); }
virtual u8* GetSaveMemory() const override; u32 GetSaveMemoryLength() const override { return SRAMLength; }
virtual u32 GetSaveMemoryLength() const override;
protected: protected:
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
@ -132,30 +167,29 @@ protected:
u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last); u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last);
u8 SRAMWrite_FLASH(u8 val, u32 pos, bool last); u8 SRAMWrite_FLASH(u8 val, u32 pos, bool last);
u8* SRAM; std::unique_ptr<u8[]> SRAM = nullptr;
u32 SRAMLength; u32 SRAMLength = 0;
u32 SRAMType; u32 SRAMType = 0;
u8 SRAMCmd; u8 SRAMCmd = 0;
u32 SRAMAddr; u32 SRAMAddr = 0;
u32 SRAMFirstAddr; u32 SRAMFirstAddr = 0;
u8 SRAMStatus; u8 SRAMStatus = 0;
}; };
// CartRetailNAND -- retail cart with NAND SRAM (WarioWare DIY, Jam with the Band, ...) // CartRetailNAND -- retail cart with NAND SRAM (WarioWare DIY, Jam with the Band, ...)
class CartRetailNAND : public CartRetail class CartRetailNAND : public CartRetail
{ {
public: public:
CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
CartRetailNAND(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
~CartRetailNAND() override; ~CartRetailNAND() override;
virtual u32 Type() const override { return CartType::RetailNAND; }
void Reset() override; void Reset() override;
void DoSavestate(Savestate* file) override; void DoSavestate(Savestate* file) override;
void LoadSave(const u8* savedata, u32 savelen) override; void SetSaveMemory(const u8* savedata, u32 savelen) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override;
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
@ -165,22 +199,21 @@ public:
private: private:
void BuildSRAMID(); void BuildSRAMID();
u32 SRAMBase; u32 SRAMBase = 0;
u32 SRAMWindow; u32 SRAMWindow = 0;
u8 SRAMWriteBuffer[0x800]; u8 SRAMWriteBuffer[0x800] {};
u32 SRAMWritePos; u32 SRAMWritePos = 0;
}; };
// CartRetailIR -- SPI IR device and SRAM // CartRetailIR -- SPI IR device and SRAM
class CartRetailIR : public CartRetail class CartRetailIR : public CartRetail
{ {
public: public:
CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams); CartRetailIR(const u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
CartRetailIR(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
~CartRetailIR() override; ~CartRetailIR() override;
virtual u32 Type() const override { return CartType::RetailIR; }
void Reset() override; void Reset() override;
void DoSavestate(Savestate* file) override; void DoSavestate(Savestate* file) override;
@ -188,23 +221,18 @@ public:
u8 SPIWrite(u8 val, u32 pos, bool last) override; u8 SPIWrite(u8 val, u32 pos, bool last) override;
private: private:
u32 IRVersion; u32 IRVersion = 0;
u8 IRCmd; u8 IRCmd = 0;
}; };
// CartRetailBT - Pok<6F>mon Typing Adventure (SPI BT controller) // CartRetailBT - Pok<6F>mon Typing Adventure (SPI BT controller)
class CartRetailBT : public CartRetail class CartRetailBT : public CartRetail
{ {
public: public:
CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); CartRetailBT(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
CartRetailBT(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen);
~CartRetailBT() override; ~CartRetailBT() override;
virtual u32 Type() const override { return CartType::RetailBT; }
void Reset() override;
void DoSavestate(Savestate* file) override;
u8 SPIWrite(u8 val, u32 pos, bool last) override; u8 SPIWrite(u8 val, u32 pos, bool last) override;
}; };
@ -212,32 +240,38 @@ public:
class CartHomebrew : public CartCommon class CartHomebrew : public CartCommon
{ {
public: public:
CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard = std::nullopt);
CartHomebrew(std::unique_ptr<u8[]>&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard = std::nullopt);
~CartHomebrew() override; ~CartHomebrew() override;
virtual u32 Type() const override { return CartType::Homebrew; }
void Reset() override; void Reset() override;
void SetupDirectBoot(const std::string& romname, NDS& nds) override; void SetupDirectBoot(const std::string& romname, NDS& nds) override;
void DoSavestate(Savestate* file) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override;
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
[[nodiscard]] const std::optional<FATStorage>& GetSDCard() const noexcept { return SD; }
void SetSDCard(FATStorage&& sdcard) noexcept { SD = std::move(sdcard); }
void SetSDCard(std::optional<FATStorage>&& sdcard) noexcept
{
SD = std::move(sdcard);
sdcard = std::nullopt;
// moving from an optional doesn't set it to nullopt,
// it just leaves behind an optional with a moved-from value
}
private: private:
void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly); void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly);
void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly); void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly);
void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset);
FATStorage* SD; std::optional<FATStorage> SD {};
bool ReadOnly;
}; };
class NDSCartSlot class NDSCartSlot
{ {
public: public:
NDSCartSlot(melonDS::NDS& nds) noexcept; explicit NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom = nullptr) noexcept;
~NDSCartSlot() noexcept; ~NDSCartSlot() noexcept;
void Reset() noexcept; void Reset() noexcept;
void ResetCart() noexcept; void ResetCart() noexcept;
@ -252,25 +286,16 @@ public:
/// If the provided cart is not valid, /// If the provided cart is not valid,
/// then the currently-loaded ROM will not be ejected. /// then the currently-loaded ROM will not be ejected.
/// ///
/// @param cart Movable reference to the cart. /// @param cart Movable reference to the cart,
/// @returns \c true if the cart was successfully loaded, /// or \c nullptr to eject the cart.
/// \c false otherwise.
/// @post If the cart was successfully loaded, /// @post If the cart was successfully loaded,
/// then \c cart will be \c nullptr /// then \c cart will be \c nullptr
/// and \c Cart will contain the object that \c cart previously pointed to. /// and \c Cart will contain the object that \c cart previously pointed to.
/// Otherwise, \c cart and \c Cart will be both be unchanged. /// Otherwise, \c cart and \c Cart will be both be unchanged.
bool InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept; void SetCart(std::unique_ptr<CartCommon>&& cart) noexcept;
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
/// Parses a ROM image and loads it into the emulator.
/// This function is equivalent to calling ::ParseROM() and ::InsertROM() in sequence.
/// @param romdata Pointer to the ROM image.
/// The cart emulator maintains its own copy of this data,
/// so the caller is free to discard this data after calling this function.
/// @param romlen The length of the ROM image, in bytes.
/// @returns \c true if the ROM image was successfully loaded,
/// \c false if not.
bool LoadROM(const u8* romdata, u32 romlen) noexcept;
void LoadSave(const u8* savedata, u32 savelen) noexcept;
void SetupDirectBoot(const std::string& romname) noexcept; void SetupDirectBoot(const std::string& romname) noexcept;
/// This function is intended to allow frontends to save and load SRAM /// This function is intended to allow frontends to save and load SRAM
@ -282,11 +307,15 @@ public:
/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr.
[[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
[[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } [[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
void SetSaveMemory(const u8* savedata, u32 savelen) noexcept;
/// @returns The length of the buffer returned by ::GetSaveMemory() /// @returns The length of the buffer returned by ::GetSaveMemory()
/// if a cart is loaded and supports SRAM, otherwise zero. /// if a cart is loaded and supports SRAM, otherwise zero.
[[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } [[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; }
void EjectCart() noexcept;
/// @return The cart that was in the slot before it was ejected,
/// or \c nullptr if the slot was already empty.
std::unique_ptr<CartCommon> EjectCart() noexcept;
u32 ReadROMData() noexcept; u32 ReadROMData() noexcept;
void WriteROMData(u32 val) noexcept; void WriteROMData(u32 val) noexcept;
void WriteSPICnt(u16 val) noexcept; void WriteSPICnt(u16 val) noexcept;
@ -294,9 +323,6 @@ public:
[[nodiscard]] u8 ReadSPIData() const noexcept; [[nodiscard]] u8 ReadSPIData() const noexcept;
void WriteSPIData(u8 val) noexcept; void WriteSPIData(u8 val) noexcept;
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
[[nodiscard]] u8 GetROMCommand(u8 index) const noexcept { return ROMCommand[index]; } [[nodiscard]] u8 GetROMCommand(u8 index) const noexcept { return ROMCommand[index]; }
void SetROMCommand(u8 index, u8 val) noexcept { ROMCommand[index] = val; } void SetROMCommand(u8 index, u8 val) noexcept { ROMCommand[index] = val; }
@ -306,27 +332,27 @@ public:
private: private:
friend class CartCommon; friend class CartCommon;
melonDS::NDS& NDS; melonDS::NDS& NDS;
u16 SPICnt {}; u16 SPICnt = 0;
u32 ROMCnt {}; u32 ROMCnt = 0;
std::array<u8, 8> ROMCommand {}; std::array<u8, 8> ROMCommand {};
u8 SPIData; u8 SPIData = 0;
u32 SPIDataPos; u32 SPIDataPos = 0;
bool SPIHold; bool SPIHold = false;
u32 ROMData; u32 ROMData = 0;
std::array<u8, 0x4000> TransferData; std::array<u8, 0x4000> TransferData {};
u32 TransferPos; u32 TransferPos = 0;
u32 TransferLen; u32 TransferLen = 0;
u32 TransferDir; u32 TransferDir = 0;
std::array<u8, 8> TransferCmd; std::array<u8, 8> TransferCmd {};
std::unique_ptr<CartCommon> Cart; std::unique_ptr<CartCommon> Cart = nullptr;
std::array<u32, 0x412> Key1_KeyBuf; std::array<u32, 0x412> Key1_KeyBuf {};
u64 Key2_X; u64 Key2_X = 0;
u64 Key2_Y; u64 Key2_Y = 0;
void Key1_Encrypt(u32* data) noexcept; void Key1_Encrypt(u32* data) noexcept;
void Key1_Decrypt(u32* data) noexcept; void Key1_Decrypt(u32* data) noexcept;
@ -346,9 +372,13 @@ private:
/// The returned cartridge will contain a copy of this data, /// The returned cartridge will contain a copy of this data,
/// so the caller may deallocate \c romdata after this function returns. /// so the caller may deallocate \c romdata after this function returns.
/// @param romlen The length of the ROM data in bytes. /// @param romlen The length of the ROM data in bytes.
/// @param sdcard The arguments to use for initializing the SD card.
/// Ignored if the parsed ROM is not homebrew.
/// If not given, the cart will not have an SD card.
/// @returns A \c NDSCart::CartCommon object representing the parsed ROM, /// @returns A \c NDSCart::CartCommon object representing the parsed ROM,
/// or \c nullptr if the ROM data couldn't be parsed. /// or \c nullptr if the ROM data couldn't be parsed.
std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen); std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen, std::optional<NDSCartArgs>&& args = std::nullopt);
std::unique_ptr<CartCommon> ParseROM(std::unique_ptr<u8[]>&& romdata, u32 romlen, std::optional<NDSCartArgs>&& args = std::nullopt);
} }
#endif #endif

View File

@ -106,24 +106,8 @@ enum ConfigEntry
ExternalBIOSEnable, ExternalBIOSEnable,
DLDI_Enable,
DLDI_ImagePath,
DLDI_ImageSize,
DLDI_ReadOnly,
DLDI_FolderSync,
DLDI_FolderPath,
DSiSD_Enable,
DSiSD_ImagePath,
DSiSD_ImageSize,
DSiSD_ReadOnly,
DSiSD_FolderSync,
DSiSD_FolderPath,
Firm_MAC, Firm_MAC,
WifiSettingsPath,
AudioBitDepth, AudioBitDepth,
DSi_FullBIOSBoot, DSi_FullBIOSBoot,
@ -139,7 +123,6 @@ enum ConfigEntry
int GetConfigInt(ConfigEntry entry); int GetConfigInt(ConfigEntry entry);
bool GetConfigBool(ConfigEntry entry); bool GetConfigBool(ConfigEntry entry);
std::string GetConfigString(ConfigEntry entry);
bool GetConfigArray(ConfigEntry entry, void* data); bool GetConfigArray(ConfigEntry entry, void* data);
/** /**

View File

@ -59,31 +59,22 @@ u16 CRC16(const u8* data, u32 len, u32 start)
bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
{ {
u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset]; u16 crc_stored = *(u16*)&FirmwareData.Buffer()[crcoffset];
u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start); u16 crc_calced = CRC16(&FirmwareData.Buffer()[offset], len, start);
return (crc_stored == crc_calced); return (crc_stored == crc_calced);
} }
FirmwareMem::FirmwareMem(melonDS::NDS& nds) : SPIDevice(nds) FirmwareMem::FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware) : SPIDevice(nds), FirmwareData(std::move(firmware))
{ {
} }
FirmwareMem::~FirmwareMem() FirmwareMem::~FirmwareMem() = default;
{
RemoveFirmware();
}
void FirmwareMem::Reset() void FirmwareMem::Reset()
{ {
if (!Firmware)
{
Log(LogLevel::Warn, "SPI firmware: no firmware loaded! Using default\n");
Firmware = std::make_unique<class Firmware>(NDS.ConsoleType);
}
// fix touchscreen coords // fix touchscreen coords
for (auto& u : Firmware->GetUserData()) for (auto& u : FirmwareData.GetUserData())
{ {
u.TouchCalibrationADC1[0] = 0; u.TouchCalibrationADC1[0] = 0;
u.TouchCalibrationADC1[1] = 0; u.TouchCalibrationADC1[1] = 0;
@ -95,17 +86,17 @@ void FirmwareMem::Reset()
u.TouchCalibrationPixel2[1] = 191; u.TouchCalibrationPixel2[1] = 191;
} }
Firmware->UpdateChecksums(); FirmwareData.UpdateChecksums();
// disable autoboot // disable autoboot
//Firmware[userdata+0x64] &= 0xBF; //Firmware[userdata+0x64] &= 0xBF;
MacAddress mac = Firmware->GetHeader().MacAddr; MacAddress mac = FirmwareData.GetHeader().MacAddr;
Log(LogLevel::Info, "MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); Log(LogLevel::Info, "MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
// verify shit // verify shit
u32 mask = Firmware->Mask(); u32 mask = FirmwareData.Mask();
Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware->Buffer()[0x2C], 0x2A)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&FirmwareData.Buffer()[0x2C], 0x2A)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&mask, 0xFE, 0x7FAFE&mask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&mask, 0xFE, 0x7FAFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&mask, 0xFE, 0x7FBFE&mask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&mask, 0xFE, 0x7FBFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&mask, 0xFE, 0x7FCFE&mask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&mask, 0xFE, 0x7FCFE&mask)?"GOOD":"BAD");
@ -136,8 +127,8 @@ void FirmwareMem::DoSavestate(Savestate* file)
void FirmwareMem::SetupDirectBoot() void FirmwareMem::SetupDirectBoot()
{ {
const auto& header = Firmware->GetHeader(); const auto& header = FirmwareData.GetHeader();
const auto& userdata = Firmware->GetEffectiveUserData(); const auto& userdata = FirmwareData.GetEffectiveUserData();
if (NDS.ConsoleType == 1) if (NDS.ConsoleType == 1)
{ {
// The ARMWrite methods are virtual, they'll delegate to DSi if necessary // The ARMWrite methods are virtual, they'll delegate to DSi if necessary
@ -163,58 +154,9 @@ void FirmwareMem::SetupDirectBoot()
} }
} }
const class Firmware* FirmwareMem::GetFirmware()
{
return Firmware.get();
}
bool FirmwareMem::IsLoadedFirmwareBuiltIn() bool FirmwareMem::IsLoadedFirmwareBuiltIn()
{ {
return Firmware->GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER; return FirmwareData.GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER;
}
bool FirmwareMem::InstallFirmware(class Firmware&& firmware)
{
if (!firmware.Buffer())
{
Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n");
return false;
}
Firmware = std::make_unique<class Firmware>(std::move(firmware));
FirmwareIdentifier id = Firmware->GetHeader().Identifier;
Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]);
return true;
}
bool FirmwareMem::InstallFirmware(std::unique_ptr<class Firmware>&& firmware)
{
if (!firmware)
{
Log(LogLevel::Error, "SPI firmware: firmware is null!\n");
return false;
}
if (!firmware->Buffer())
{
Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n");
return false;
}
Firmware = std::move(firmware);
FirmwareIdentifier id = Firmware->GetHeader().Identifier;
Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]);
return true;
}
void FirmwareMem::RemoveFirmware()
{
Firmware.reset();
Log(LogLevel::Debug, "Removed installed firmware (if any)\n");
} }
void FirmwareMem::Write(u8 val) void FirmwareMem::Write(u8 val)
@ -256,7 +198,7 @@ void FirmwareMem::Write(u8 val)
} }
else else
{ {
Data = Firmware->Buffer()[Addr & Firmware->Mask()]; Data = FirmwareData.Buffer()[Addr & FirmwareData.Mask()];
Addr++; Addr++;
} }
@ -279,7 +221,7 @@ void FirmwareMem::Write(u8 val)
} }
else else
{ {
Firmware->Buffer()[Addr & Firmware->Mask()] = val; FirmwareData.Buffer()[Addr & FirmwareData.Mask()] = val;
Data = val; Data = val;
Addr++; Addr++;
} }
@ -314,11 +256,11 @@ void FirmwareMem::Release()
{ // If the SPI firmware chip just finished a write... { // If the SPI firmware chip just finished a write...
// We only notify the frontend of changes to the Wi-fi/userdata settings region // We only notify the frontend of changes to the Wi-fi/userdata settings region
// (although it might still decide to flush the whole thing) // (although it might still decide to flush the whole thing)
u32 wifioffset = Firmware->GetWifiAccessPointOffset(); u32 wifioffset = FirmwareData.GetWifiAccessPointOffset();
// Request that the start of the Wi-fi/userdata settings region // Request that the start of the Wi-fi/userdata settings region
// through the end of the firmware blob be flushed to disk // through the end of the firmware blob be flushed to disk
Platform::WriteFirmware(*Firmware, wifioffset, Firmware->Length() - wifioffset); Platform::WriteFirmware(FirmwareData, wifioffset, FirmwareData.Length() - wifioffset);
} }
SPIDevice::Release(); SPIDevice::Release();
@ -530,11 +472,11 @@ void TSC::Write(u8 val)
SPIHost::SPIHost(melonDS::NDS& nds) : NDS(nds) SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds)
{ {
NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone)); NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone));
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS); Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware));
Devices[SPIDevice_PowerMan] = new PowerMan(NDS); Devices[SPIDevice_PowerMan] = new PowerMan(NDS);
if (NDS.ConsoleType == 1) if (NDS.ConsoleType == 1)

View File

@ -66,24 +66,23 @@ protected:
class FirmwareMem : public SPIDevice class FirmwareMem : public SPIDevice
{ {
public: public:
FirmwareMem(melonDS::NDS& nds); FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware);
~FirmwareMem() override; ~FirmwareMem() override;
void Reset() override; void Reset() override;
void DoSavestate(Savestate* file) override; void DoSavestate(Savestate* file) override;
void SetupDirectBoot(); void SetupDirectBoot();
const class Firmware* GetFirmware(); Firmware& GetFirmware() noexcept { return FirmwareData; }
[[nodiscard]] const Firmware& GetFirmware() const noexcept { return FirmwareData; }
void SetFirmware(Firmware&& firmware) { FirmwareData = std::move(firmware); }
bool IsLoadedFirmwareBuiltIn(); bool IsLoadedFirmwareBuiltIn();
bool InstallFirmware(class Firmware&& firmware);
bool InstallFirmware(std::unique_ptr<class Firmware>&& firmware);
void RemoveFirmware();
void Write(u8 val) override; void Write(u8 val) override;
void Release() override; void Release() override;
private: private:
std::unique_ptr<class Firmware> Firmware; Firmware FirmwareData;
u8 CurCmd; u8 CurCmd;
@ -141,16 +140,19 @@ protected:
class SPIHost class SPIHost
{ {
public: public:
SPIHost(melonDS::NDS& nds); SPIHost(melonDS::NDS& nds, Firmware&& firmware);
~SPIHost(); ~SPIHost();
void Reset(); void Reset();
void DoSavestate(Savestate* file); void DoSavestate(Savestate* file);
FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; }
const FirmwareMem* GetFirmwareMem() const { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; }
PowerMan* GetPowerMan() { return (PowerMan*)Devices[SPIDevice_PowerMan]; } PowerMan* GetPowerMan() { return (PowerMan*)Devices[SPIDevice_PowerMan]; }
TSC* GetTSC() { return (TSC*)Devices[SPIDevice_TSC]; } TSC* GetTSC() { return (TSC*)Devices[SPIDevice_TSC]; }
const Firmware* GetFirmware() { return GetFirmwareMem()->GetFirmware(); } const Firmware& GetFirmware() const { return GetFirmwareMem()->GetFirmware(); }
Firmware& GetFirmware() { return GetFirmwareMem()->GetFirmware(); }
void SetFirmware(Firmware&& firmware) { GetFirmwareMem()->SetFirmware(std::move(firmware)); }
u16 ReadCnt() { return Cnt; } u16 ReadCnt() { return Cnt; }
void WriteCnt(u16 val); void WriteCnt(u16 val);

66
src/Utils.cpp Normal file
View File

@ -0,0 +1,66 @@
/*
Copyright 2016-2023 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include "Utils.h"
#include <string.h>
namespace melonDS
{
std::pair<std::unique_ptr<u8[]>, u32> PadToPowerOf2(std::unique_ptr<u8[]>&& data, u32 len) noexcept
{
if (data == nullptr || len == 0)
return {nullptr, 0};
if ((len & (len - 1)) == 0)
return {std::move(data), len};
u32 newlen = 1;
while (newlen < len)
newlen <<= 1;
auto newdata = std::make_unique<u8[]>(newlen);
memcpy(newdata.get(), data.get(), len);
data = nullptr;
return {std::move(newdata), newlen};
}
std::pair<std::unique_ptr<u8[]>, u32> PadToPowerOf2(const u8* data, u32 len) noexcept
{
if (len == 0)
return {nullptr, 0};
u32 newlen = 1;
while (newlen < len)
newlen <<= 1;
auto newdata = std::make_unique<u8[]>(newlen);
memcpy(newdata.get(), data, len);
return {std::move(newdata), newlen};
}
std::unique_ptr<u8[]> CopyToUnique(const u8* data, u32 len) noexcept
{
if (data == nullptr || len == 0)
return nullptr;
auto newdata = std::make_unique<u8[]>(len);
memcpy(newdata.get(), data, len);
return newdata;
}
}

43
src/Utils.h Normal file
View File

@ -0,0 +1,43 @@
/*
Copyright 2016-2023 melonDS team
This file is part of melonDS.
melonDS is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
melonDS is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#ifndef MELONDS_UTILS_H
#define MELONDS_UTILS_H
#include <memory>
#include "types.h"
#include <utility>
namespace melonDS
{
/// Ensures that the given array is a power of 2 in length.
///
/// @return If \c len is a power of 2, returns \c data and \c len unchanged
/// without copying anything.
/// If \c data is \c nullptr, returns <tt>{nullptr, 0}</tt>.
/// Otherwise, return a copy of \c data with zero-padding to the next power of 2.
/// @post \c data is \c nullptr, even if it doesn't need to be copied.
std::pair<std::unique_ptr<u8[]>, u32> PadToPowerOf2(std::unique_ptr<u8[]>&& data, u32 len) noexcept;
std::pair<std::unique_ptr<u8[]>, u32> PadToPowerOf2(const u8* data, u32 len) noexcept;
std::unique_ptr<u8[]> CopyToUnique(const u8* data, u32 len) noexcept;
}
#endif // MELONDS_UTILS_H

View File

@ -158,12 +158,12 @@ void Wifi::Reset()
} }
#undef BBREG_FIXED #undef BBREG_FIXED
const Firmware* fw = NDS.SPI.GetFirmware(); const Firmware& fw = NDS.SPI.GetFirmware();
RFVersion = fw->GetHeader().RFChipType; RFVersion = fw.GetHeader().RFChipType;
memset(RFRegs, 0, 4*0x40); memset(RFRegs, 0, 4*0x40);
Firmware::FirmwareConsoleType console = fw->GetHeader().ConsoleType; Firmware::FirmwareConsoleType console = fw.GetHeader().ConsoleType;
if (console == Firmware::FirmwareConsoleType::DS) if (console == Firmware::FirmwareConsoleType::DS)
IOPORT(0x000) = 0x1440; IOPORT(0x000) = 0x1440;
else if (console == Firmware::FirmwareConsoleType::DSLite) else if (console == Firmware::FirmwareConsoleType::DSLite)

View File

@ -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 *a = archive_read_new();
struct archive_entry *entry; struct archive_entry *entry;
int r; int r;
if (!filedata) return -1;
archive_read_support_format_all(a); archive_read_support_format_all(a);
archive_read_support_filter_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); size_t bytesToRead = archive_entry_size(entry);
if (filesize) *filesize = bytesToRead; if (filesize) *filesize = bytesToRead;
*filedata = new u8[bytesToRead]; filedata = std::make_unique<u8[]>(bytesToRead);
ssize_t bytesRead = archive_read_data(a, *filedata, bytesToRead); ssize_t bytesRead = archive_read_data(a, filedata.get(), bytesToRead);
archive_read_close(a); archive_read_close(a);
archive_read_free(a); archive_read_free(a);

View File

@ -37,7 +37,7 @@ namespace Archive
using namespace melonDS; using namespace melonDS;
QVector<QString> ListArchive(QString path); 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); //QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer);
//u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata); //u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata);

View File

@ -204,10 +204,6 @@ int GetConfigInt(ConfigEntry entry)
case JIT_MaxBlockSize: return Config::JIT_MaxBlockSize; case JIT_MaxBlockSize: return Config::JIT_MaxBlockSize;
#endif #endif
case DLDI_ImageSize: return imgsizes[Config::DLDISize];
case DSiSD_ImageSize: return imgsizes[Config::DSiSDSize];
case AudioBitDepth: return Config::AudioBitDepth; case AudioBitDepth: return Config::AudioBitDepth;
#ifdef GDBSTUB_ENABLED #ifdef GDBSTUB_ENABLED
@ -232,14 +228,6 @@ bool GetConfigBool(ConfigEntry entry)
case ExternalBIOSEnable: return Config::ExternalBIOSEnable != 0; 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; case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0;
#ifdef GDBSTUB_ENABLED #ifdef GDBSTUB_ENABLED
@ -252,22 +240,6 @@ bool GetConfigBool(ConfigEntry entry)
return false; 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) bool GetConfigArray(ConfigEntry entry, void* data)
{ {
switch (entry) switch (entry)

View File

@ -68,8 +68,8 @@ std::string BaseGBAROMDir = "";
std::string BaseGBAROMName = ""; std::string BaseGBAROMName = "";
std::string BaseGBAAssetName = ""; std::string BaseGBAAssetName = "";
SaveManager* NDSSave = nullptr; std::unique_ptr<SaveManager> NDSSave = nullptr;
SaveManager* GBASave = nullptr; std::unique_ptr<SaveManager> GBASave = nullptr;
std::unique_ptr<SaveManager> FirmwareSave = nullptr; std::unique_ptr<SaveManager> FirmwareSave = nullptr;
std::unique_ptr<Savestate> BackupState = nullptr; std::unique_ptr<Savestate> BackupState = nullptr;
@ -303,6 +303,28 @@ QString VerifySetup()
return ""; 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) std::string GetSavestateName(int slot)
{ {
@ -482,6 +504,11 @@ void LoadCheats(NDS& nds)
std::optional<std::array<u8, ARM9BIOSSize>> LoadARM9BIOS() noexcept 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)) if (FileHandle* f = OpenLocalFile(Config::BIOS9Path, Read))
{ {
std::array<u8, ARM9BIOSSize> bios {}; 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 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)) if (FileHandle* f = OpenLocalFile(Config::BIOS7Path, Read))
{ {
std::array<u8, ARM7BIOSSize> bios {}; std::array<u8, ARM7BIOSSize> bios {};
@ -518,6 +550,16 @@ std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM9BIOS() noexcept
std::array<u8, DSiBIOSSize> bios {}; std::array<u8, DSiBIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f); FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(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()); Log(Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str());
return bios; return bios;
} }
@ -533,6 +575,16 @@ std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM7BIOS() noexcept
std::array<u8, DSiBIOSSize> bios {}; std::array<u8, DSiBIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f); FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(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()); Log(Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str());
return bios; return bios;
} }
@ -589,6 +641,16 @@ Firmware GenerateFirmware(int type) noexcept
std::optional<Firmware> LoadFirmware(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; const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath;
Log(Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str()); 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; return std::nullopt;
} }
CustomizeFirmware(firmware); if (Config::FirmwareOverrideSettings)
{
CustomizeFirmware(firmware);
}
return firmware; return firmware;
} }
@ -694,7 +759,20 @@ std::optional<DSi_NAND::NANDImage> LoadNAND(const std::array<u8, DSiBIOSSize>& a
return nandImage; 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 std::optional<FATStorage> LoadDSiSDCard() noexcept
{ {
if (!Config::DSiSDEnable) if (!Config::DSiSDEnable)
@ -704,97 +782,34 @@ std::optional<FATStorage> LoadDSiSDCard() noexcept
Config::DSiSDPath, Config::DSiSDPath,
imgsizes[Config::DSiSDSize], imgsizes[Config::DSiSDSize],
Config::DSiSDReadOnly, 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 (!Config::DLDIEnable)
{ return std::nullopt;
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()); return FATStorageArgs{
Platform::CloseFile(f); Config::DLDISDPath,
} imgsizes[Config::DLDISize],
else Config::DLDIReadOnly,
{ Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt
Log(LogLevel::Warn, "ARM9 BIOS not found\n"); };
}
for (int i = 0; i < 16; i++) std::optional<FATStorage> LoadDLDISDCard() noexcept
((u32*)nds.ARM9BIOS)[i] = 0xE7FFDEFF; {
} if (!Config::DLDIEnable)
return std::nullopt;
if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS7Path, FileMode::Read)) return FATStorage(
{ Config::DLDISDPath,
FileRead(nds.ARM7BIOS, sizeof(NDS::ARM7BIOS), 1, f); imgsizes[Config::DLDISize],
Config::DLDIReadOnly,
Log(LogLevel::Info, "ARM7 BIOS loaded from\n", Config::BIOS7Path.c_str()); Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt
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");
for (int i = 0; i < 16; i++)
((u32*)dsi.ARM7iBIOS)[i] = 0xE7FFDEFF;
}
if (!Config::DSiFullBIOSBoot)
{
// herp
*(u32*)&dsi.ARM9iBIOS[0] = 0xEAFFFFFE;
*(u32*)&dsi.ARM7iBIOS[0] = 0xEAFFFFFE;
// TODO!!!!
// hax the upper 32K out of the goddamn DSi
// done that :) -pcy
}
}
} }
void EnableCheats(NDS& nds, bool enable) void EnableCheats(NDS& nds, bool enable)
@ -835,16 +850,10 @@ void SetDateTime(NDS& nds)
void Reset(EmuThread* thread) void Reset(EmuThread* thread)
{ {
thread->RecreateConsole(); thread->UpdateConsole(Keep {}, Keep {});
if (Config::ConsoleType == 1) EjectGBACart(*thread->NDS); 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(); thread->NDS->Reset();
SetBatteryLevels(*thread->NDS); SetBatteryLevels(*thread->NDS);
SetDateTime(*thread->NDS); SetDateTime(*thread->NDS);
@ -867,6 +876,7 @@ void Reset(EmuThread* thread)
GBASave->SetPath(newsave, false); GBASave->SetPath(newsave, false);
} }
InitFirmwareSaveManager(thread);
if (FirmwareSave) if (FirmwareSave)
{ {
std::string oldsave = FirmwareSave->GetPath(); std::string oldsave = FirmwareSave->GetPath();
@ -899,36 +909,25 @@ void Reset(EmuThread* thread)
} }
bool LoadBIOS(EmuThread* thread) bool BootToMenu(EmuThread* thread)
{ {
thread->RecreateConsole(); // Keep whatever cart is in the console, if any.
if (!thread->UpdateConsole(Keep {}, Keep {}))
LoadBIOSFiles(*thread->NDS); // Try to update the console, but keep the existing cart. If that fails...
if (!InstallFirmware(*thread->NDS))
return false;
if (Config::ConsoleType == 1 && !InstallNAND(static_cast<DSi&>(*thread->NDS)))
return false; return false;
// BIOS and firmware files are loaded, patched, and installed in UpdateConsole
if (thread->NDS->NeedsDirectBoot()) if (thread->NDS->NeedsDirectBoot())
return false; return false;
/*if (NDSSave) delete NDSSave; InitFirmwareSaveManager(thread);
NDSSave = nullptr;
CartType = -1;
BaseROMDir = "";
BaseROMName = "";
BaseAssetName = "";*/
thread->NDS->Reset(); thread->NDS->Reset();
SetBatteryLevels(*thread->NDS); SetBatteryLevels(*thread->NDS);
SetDateTime(*thread->NDS); SetDateTime(*thread->NDS);
return true; 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); u64 realSize = ZSTD_getFrameContentSize(inContent, inSize);
const u32 maxSize = 0x40000000; const u32 maxSize = 0x40000000;
@ -940,16 +939,15 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
if (realSize != ZSTD_CONTENTSIZE_UNKNOWN) if (realSize != ZSTD_CONTENTSIZE_UNKNOWN)
{ {
u8* realContent = new u8[realSize]; outContent = make_unique<u8[]>(realSize);
u64 decompressed = ZSTD_decompress(realContent, realSize, inContent, inSize); u64 decompressed = ZSTD_decompress(outContent.get(), realSize, inContent, inSize);
if (ZSTD_isError(decompressed)) if (ZSTD_isError(decompressed))
{ {
delete[] realContent; outContent = nullptr;
return 0; return 0;
} }
*outContent = realContent;
return realSize; return realSize;
} }
else else
@ -1005,8 +1003,8 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
} while (inBuf.pos < inBuf.size); } while (inBuf.pos < inBuf.size);
ZSTD_freeDStream(dStream); ZSTD_freeDStream(dStream);
*outContent = new u8[outBuf.pos]; outContent = make_unique<u8[]>(outBuf.pos);
memcpy(*outContent, outBuf.dst, outBuf.pos); memcpy(outContent.get(), outBuf.dst, outBuf.pos);
ZSTD_freeDStream(dStream); ZSTD_freeDStream(dStream);
free(outBuf.dst); 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() pair<unique_ptr<Firmware>, string> GenerateDefaultFirmware()
{ {
// Construct the default firmware... // 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. // 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. // 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(); settingspath = wfcsettingspath + Platform::InstanceFileSuffix();
FileHandle* f = Platform::OpenLocalFile(settingspath, FileMode::Read); FileHandle* f = Platform::OpenLocalFile(settingspath, FileMode::Read);
if (!f) if (!f)
@ -1201,155 +1163,12 @@ void CustomizeFirmware(Firmware& firmware) noexcept
firmware.UpdateChecksums(); firmware.UpdateChecksums();
} }
static Platform::FileHandle* OpenNANDFile() noexcept // 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
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)
{ {
if (filepath.empty()) return false; if (filepath.empty()) return false;
u8* filedata = nullptr; if (int num = filepath.count(); num == 1)
u32 filelen;
std::string basepath;
std::string romname;
int num = filepath.count();
if (num == 1)
{ {
// regular file // regular file
@ -1361,38 +1180,35 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
if (len > 0x40000000) if (len > 0x40000000)
{ {
Platform::CloseFile(f); Platform::CloseFile(f);
delete[] filedata;
return false; return false;
} }
Platform::FileRewind(f); Platform::FileRewind(f);
filedata = new u8[len]; filedata = make_unique<u8[]>(len);
size_t nread = Platform::FileRead(filedata, (size_t)len, 1, f); size_t nread = Platform::FileRead(filedata.get(), (size_t)len, 1, f);
Platform::CloseFile(f);
if (nread != 1) if (nread != 1)
{ {
Platform::CloseFile(f); filedata = nullptr;
delete[] filedata;
return false; return false;
} }
Platform::CloseFile(f);
filelen = (u32)len; filelen = (u32)len;
if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst") if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst")
{ {
u8* outContent = nullptr; filelen = DecompressROM(filedata.get(), len, filedata);
u32 decompressed = DecompressROM(filedata, len, &outContent);
if (decompressed > 0) if (filelen > 0)
{ {
delete[] filedata;
filedata = outContent;
filelen = decompressed;
filename = filename.substr(0, filename.length() - 4); filename = filename.substr(0, filename.length() - 4);
} }
else else
{ {
delete[] filedata; filedata = nullptr;
filelen = 0;
basepath = "";
romname = "";
return false; return false;
} }
} }
@ -1400,19 +1216,21 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
int pos = LastSep(filename); int pos = LastSep(filename);
if(pos != -1) if(pos != -1)
basepath = filename.substr(0, pos); basepath = filename.substr(0, pos);
romname = filename.substr(pos+1); romname = filename.substr(pos+1);
return true;
} }
#ifdef ARCHIVE_SUPPORT_ENABLED #ifdef ARCHIVE_SUPPORT_ENABLED
else if (num == 2) else if (num == 2)
{ {
// file inside archive // 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 (lenread < 0) return false;
if (!filedata) return false; if (!filedata) return false;
if (lenread != filelen) if (lenread != filelen)
{ {
delete[] filedata; filedata = nullptr;
return false; return false;
} }
@ -1421,38 +1239,31 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
std::string std_romname = filepath.at(1).toStdString(); std::string std_romname = filepath.at(1).toStdString();
romname = std_romname.substr(LastSep(std_romname)+1); romname = std_romname.substr(LastSep(std_romname)+1);
return true;
} }
#endif #endif
else else
return false; 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; NDSSave = nullptr;
BaseROMDir = basepath; BaseROMDir = basepath;
BaseROMName = romname; BaseROMName = romname;
BaseAssetName = romname.substr(0, romname.rfind('.')); 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; u32 savelen = 0;
u8* savedata = nullptr; std::unique_ptr<u8[]> savedata = nullptr;
std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav"); std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav");
std::string origsav = savname; std::string origsav = savname;
@ -1465,36 +1276,56 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
savelen = (u32)Platform::FileLength(sav); savelen = (u32)Platform::FileLength(sav);
FileRewind(sav); FileRewind(sav);
savedata = new u8[savelen]; savedata = std::make_unique<u8[]>(savelen);
FileRead(savedata, savelen, 1, sav); FileRead(savedata.get(), savelen, 1, sav);
CloseFile(sav); CloseFile(sav);
} }
bool res = emuthread->NDS->LoadCart(filedata, filelen, savedata, savelen); NDSCart::NDSCartArgs cartargs {
if (res && reset) // 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 (Config::DirectBoot || emuthread->NDS->NeedsDirectBoot())
{ { // If direct boot is enabled or forced...
emuthread->NDS->SetupDirectBoot(romname); emuthread->NDS->SetupDirectBoot(romname);
} }
}
if (res) SetBatteryLevels(*emuthread->NDS);
SetDateTime(*emuthread->NDS);
}
else
{ {
CartType = 0; assert(emuthread->NDS != nullptr);
NDSSave = new SaveManager(savname); emuthread->NDS->SetNDSCart(std::move(cart));
LoadCheats(*emuthread->NDS);
} }
if (savedata) delete[] savedata; CartType = 0;
delete[] filedata; NDSSave = std::make_unique<SaveManager>(savname);
return res; LoadCheats(*emuthread->NDS);
return true;
} }
void EjectCart(NDS& nds) void EjectCart(NDS& nds)
{ {
if (NDSSave) delete NDSSave;
NDSSave = nullptr; NDSSave = nullptr;
UnloadCheats(nds); UnloadCheats(nds);
@ -1529,92 +1360,16 @@ QString CartLabel()
bool LoadGBAROM(NDS& nds, QStringList filepath) bool LoadGBAROM(NDS& nds, QStringList filepath)
{ {
if (Config::ConsoleType == 1) return false; if (nds.ConsoleType == 1) return false; // DSi doesn't have a GBA slot
if (filepath.empty()) return false;
u8* filedata; unique_ptr<u8[]> filedata = nullptr;
u32 filelen; u32 filelen;
std::string basepath; std::string basepath;
std::string romname; std::string romname;
int num = filepath.count(); if (!LoadROMData(filepath, filedata, filelen, basepath, romname))
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
return false; return false;
if (GBASave) delete GBASave;
GBASave = nullptr; GBASave = nullptr;
BaseGBAROMDir = basepath; BaseGBAROMDir = basepath;
@ -1622,7 +1377,7 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
BaseGBAAssetName = romname.substr(0, romname.rfind('.')); BaseGBAAssetName = romname.substr(0, romname.rfind('.'));
u32 savelen = 0; u32 savelen = 0;
u8* savedata = nullptr; std::unique_ptr<u8[]> savedata = nullptr;
std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav"); std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav");
std::string origsav = savname; std::string origsav = savname;
@ -1634,30 +1389,29 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
{ {
savelen = (u32)FileLength(sav); savelen = (u32)FileLength(sav);
FileRewind(sav); if (savelen > 0)
savedata = new u8[savelen]; {
FileRead(savedata, savelen, 1, sav); FileRewind(sav);
savedata = std::make_unique<u8[]>(savelen);
FileRead(savedata.get(), savelen, 1, sav);
}
CloseFile(sav); CloseFile(sav);
} }
bool res = nds.LoadGBACart(filedata, filelen, savedata, savelen); auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen);
if (!cart)
return false;
if (res) nds.SetGBACart(std::move(cart));
{ GBACartType = 0;
GBACartType = 0; GBASave = std::make_unique<SaveManager>(savname);
GBASave = new SaveManager(savname); return true;
}
if (savedata) delete[] savedata;
delete[] filedata;
return res;
} }
void LoadGBAAddon(NDS& nds, int type) void LoadGBAAddon(NDS& nds, int type)
{ {
if (Config::ConsoleType == 1) return; if (Config::ConsoleType == 1) return;
if (GBASave) delete GBASave;
GBASave = nullptr; GBASave = nullptr;
nds.LoadGBAAddon(type); nds.LoadGBAAddon(type);
@ -1670,7 +1424,6 @@ void LoadGBAAddon(NDS& nds, int type)
void EjectGBACart(NDS& nds) void EjectGBACart(NDS& nds)
{ {
if (GBASave) delete GBASave;
GBASave = nullptr; GBASave = nullptr;
nds.EjectGBACart(); nds.EjectGBACart();

View File

@ -35,34 +35,43 @@ namespace melonDS
class NDS; class NDS;
class DSi; class DSi;
class FATStorage; class FATStorage;
class FATStorageArgs;
} }
class EmuThread; class EmuThread;
namespace ROMManager namespace ROMManager
{ {
using namespace melonDS; using namespace melonDS;
extern SaveManager* NDSSave; extern std::unique_ptr<SaveManager> NDSSave;
extern SaveManager* GBASave; extern std::unique_ptr<SaveManager> GBASave;
extern std::unique_ptr<SaveManager> FirmwareSave; extern std::unique_ptr<SaveManager> FirmwareSave;
QString VerifySetup(); QString VerifySetup();
void Reset(EmuThread* thread); 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(); 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, ARM9BIOSSize>> LoadARM9BIOS() noexcept;
std::optional<std::array<u8, ARM7BIOSSize>> LoadARM7BIOS() noexcept; std::optional<std::array<u8, ARM7BIOSSize>> LoadARM7BIOS() noexcept;
std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM9BIOS() noexcept; std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM9BIOS() noexcept;
std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM7BIOS() noexcept; std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM7BIOS() noexcept;
std::optional<FATStorageArgs> GetDSiSDCardArgs() noexcept;
std::optional<FATStorage> LoadDSiSDCard() noexcept; std::optional<FATStorage> LoadDSiSDCard() noexcept;
std::optional<FATStorageArgs> GetDLDISDCardArgs() noexcept;
std::optional<FATStorage> LoadDLDISDCard() noexcept;
void CustomizeFirmware(Firmware& firmware) noexcept; void CustomizeFirmware(Firmware& firmware) noexcept;
Firmware GenerateFirmware(int type) noexcept; Firmware GenerateFirmware(int type) noexcept;
/// Loads and customizes a firmware image based on the values in Config /// Loads and customizes a firmware image based on the values in Config
std::optional<Firmware> LoadFirmware(int type) noexcept; std::optional<Firmware> LoadFirmware(int type) noexcept;
/// Loads and customizes a NAND image based on the values in Config /// 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; 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); bool LoadROM(EmuThread*, QStringList filepath, bool reset);
void EjectCart(NDS& nds); void EjectCart(NDS& nds);
bool CartInserted(); bool CartInserted();

View File

@ -81,6 +81,7 @@
#include "FrontendUtil.h" #include "FrontendUtil.h"
#include "OSD.h" #include "OSD.h"
#include "Args.h"
#include "NDS.h" #include "NDS.h"
#include "NDSCart.h" #include "NDSCart.h"
#include "GBACart.h" #include "GBACart.h"
@ -204,29 +205,164 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
static_cast<ScreenPanelGL*>(mainWindow->panel)->transferLayout(this); 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) 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::Current = nullptr;
NDS = CreateConsole(); NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart));
// TODO: Insert ROMs
NDS::Current = NDS.get(); 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) void EmuThread::updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix)
{ {
@ -343,7 +479,8 @@ void EmuThread::run()
u32 mainScreenPos[3]; u32 mainScreenPos[3];
Platform::FileHandle* file; Platform::FileHandle* file;
RecreateConsole(); UpdateConsole(nullptr, nullptr);
// No carts are inserted when melonDS first boots
mainScreenPos[0] = 0; mainScreenPos[0] = 0;
mainScreenPos[1] = 0; mainScreenPos[1] = 0;
@ -2507,7 +2644,7 @@ void MainWindow::onBootFirmware()
return; return;
} }
if (!ROMManager::LoadBIOS(emuThread)) if (!ROMManager::BootToMenu(emuThread))
{ {
// TODO: better error reporting? // TODO: better error reporting?
QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); QMessageBox::critical(this, "melonDS", "This firmware is not bootable.");
@ -2750,13 +2887,12 @@ void MainWindow::onImportSavefile()
u32 len = FileLength(f); u32 len = FileLength(f);
u8* data = new u8[len]; std::unique_ptr<u8[]> data = std::make_unique<u8[]>(len);
Platform::FileRewind(f); Platform::FileRewind(f);
Platform::FileRead(data, len, 1, f); Platform::FileRead(data.get(), len, 1, f);
assert(emuThread->NDS != nullptr); assert(emuThread->NDS != nullptr);
emuThread->NDS->LoadSave(data, len); emuThread->NDS->SetNDSSave(data.get(), len);
delete[] data;
CloseFile(f); CloseFile(f);
emuThread->emuUnpause(); emuThread->emuUnpause();

View File

@ -34,12 +34,18 @@
#include <QCloseEvent> #include <QCloseEvent>
#include <atomic> #include <atomic>
#include <variant>
#include <optional> #include <optional>
#include "FrontendUtil.h" #include "FrontendUtil.h"
#include "duckstation/gl/context.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 namespace melonDS
{ {
class NDS; class NDS;
@ -72,7 +78,13 @@ public:
QMutex FrontBufferLock; QMutex FrontBufferLock;
void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); 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 std::unique_ptr<melonDS::NDS> NDS; // TODO: Proper encapsulation and synchronization
signals: signals:
void windowUpdate(); void windowUpdate();
@ -96,7 +108,10 @@ signals:
void syncVolumeLevel(); void syncVolumeLevel();
private: 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 drawScreenGL();
void initOpenGL(); void initOpenGL();
void deinitOpenGL(); void deinitOpenGL();