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
32 changed files with 2511 additions and 2362 deletions

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)
{
u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset];
u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start);
u16 crc_stored = *(u16*)&FirmwareData.Buffer()[crcoffset];
u16 crc_calced = CRC16(&FirmwareData.Buffer()[offset], len, start);
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()
{
RemoveFirmware();
}
FirmwareMem::~FirmwareMem() = default;
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
for (auto& u : Firmware->GetUserData())
for (auto& u : FirmwareData.GetUserData())
{
u.TouchCalibrationADC1[0] = 0;
u.TouchCalibrationADC1[1] = 0;
@ -95,17 +86,17 @@ void FirmwareMem::Reset()
u.TouchCalibrationPixel2[1] = 191;
}
Firmware->UpdateChecksums();
FirmwareData.UpdateChecksums();
// disable autoboot
//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]);
// verify shit
u32 mask = Firmware->Mask();
Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware->Buffer()[0x2C], 0x2A)?"GOOD":"BAD");
u32 mask = FirmwareData.Mask();
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: 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");
@ -136,8 +127,8 @@ void FirmwareMem::DoSavestate(Savestate* file)
void FirmwareMem::SetupDirectBoot()
{
const auto& header = Firmware->GetHeader();
const auto& userdata = Firmware->GetEffectiveUserData();
const auto& header = FirmwareData.GetHeader();
const auto& userdata = FirmwareData.GetEffectiveUserData();
if (NDS.ConsoleType == 1)
{
// 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()
{
return Firmware->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");
return FirmwareData.GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER;
}
void FirmwareMem::Write(u8 val)
@ -256,7 +198,7 @@ void FirmwareMem::Write(u8 val)
}
else
{
Data = Firmware->Buffer()[Addr & Firmware->Mask()];
Data = FirmwareData.Buffer()[Addr & FirmwareData.Mask()];
Addr++;
}
@ -279,7 +221,7 @@ void FirmwareMem::Write(u8 val)
}
else
{
Firmware->Buffer()[Addr & Firmware->Mask()] = val;
FirmwareData.Buffer()[Addr & FirmwareData.Mask()] = val;
Data = val;
Addr++;
}
@ -314,11 +256,11 @@ void FirmwareMem::Release()
{ // If the SPI firmware chip just finished a write...
// We only notify the frontend of changes to the Wi-fi/userdata settings region
// (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
// 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();
@ -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));
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS);
Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware));
Devices[SPIDevice_PowerMan] = new PowerMan(NDS);
if (NDS.ConsoleType == 1)