mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-23 06:10:03 -06:00
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:
379
src/NDSCart.cpp
379
src/NDSCart.cpp
@ -20,12 +20,12 @@
|
||||
#include "NDS.h"
|
||||
#include "DSi.h"
|
||||
#include "NDSCart.h"
|
||||
#include "ARM.h"
|
||||
#include "CRC32.h"
|
||||
#include "Platform.h"
|
||||
#include "ROMList.h"
|
||||
#include "melonDLDI.h"
|
||||
#include "xxhash/xxhash.h"
|
||||
#include "FATStorage.h"
|
||||
#include "Utils.h"
|
||||
|
||||
namespace melonDS
|
||||
{
|
||||
@ -43,7 +43,7 @@ enum
|
||||
|
||||
// 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);
|
||||
}
|
||||
@ -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;
|
||||
DSiBase = Header.DSiRegionStart << 19;
|
||||
}
|
||||
|
||||
CartCommon::~CartCommon()
|
||||
{
|
||||
delete[] ROM;
|
||||
}
|
||||
CartCommon::~CartCommon() = default;
|
||||
|
||||
u32 CartCommon::Checksum() const
|
||||
{
|
||||
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.ARM7ROMOffset], header.ARM7Size, crc);
|
||||
@ -230,14 +232,6 @@ void CartCommon::DoSavestate(Savestate* file)
|
||||
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)
|
||||
{
|
||||
if (CmdEncMode == 0)
|
||||
@ -267,7 +261,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da
|
||||
|
||||
case 0x3C:
|
||||
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;
|
||||
return 0;
|
||||
|
||||
@ -276,7 +270,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da
|
||||
{
|
||||
auto& dsi = static_cast<DSi&>(nds);
|
||||
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;
|
||||
}
|
||||
return 0;
|
||||
@ -360,23 +354,13 @@ u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last)
|
||||
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)
|
||||
{
|
||||
if (addr >= ROMLength) return;
|
||||
if ((addr+len) > ROMLength)
|
||||
len = ROMLength - addr;
|
||||
|
||||
memcpy(data+offset, ROM+addr, len);
|
||||
memcpy(data+offset, ROM.get()+addr, len);
|
||||
}
|
||||
|
||||
const NDSBanner* CartCommon::Banner() const
|
||||
@ -385,22 +369,64 @@ const NDSBanner* CartCommon::Banner() const
|
||||
size_t bannersize = header.IsDSi() ? 0x23C0 : 0xA40;
|
||||
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;
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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, "oh well. loading it anyway. adsfgdsf\n");
|
||||
|
||||
if (oldlen) delete[] SRAM;
|
||||
SRAM = nullptr;
|
||||
if (SRAMLength) SRAM = new u8[SRAMLength];
|
||||
SRAM = SRAMLength ? std::make_unique<u8[]>(SRAMLength) : nullptr;
|
||||
}
|
||||
if (SRAMLength)
|
||||
{
|
||||
file->VarArray(SRAM, SRAMLength);
|
||||
file->VarArray(SRAM.get(), SRAMLength);
|
||||
}
|
||||
|
||||
// SPI status shito
|
||||
@ -441,53 +465,15 @@ void CartRetail::DoSavestate(Savestate* file)
|
||||
file->Var8(&SRAMStatus);
|
||||
|
||||
if ((!file->Saving) && SRAM)
|
||||
Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength);
|
||||
Platform::WriteNDSSave(SRAM.get(), SRAMLength, 0, SRAMLength);
|
||||
}
|
||||
|
||||
void CartRetail::SetupSave(u32 type)
|
||||
{
|
||||
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)
|
||||
void CartRetail::SetSaveMemory(const u8* savedata, u32 savelen)
|
||||
{
|
||||
if (!SRAM) return;
|
||||
|
||||
u32 len = std::min(savelen, SRAMLength);
|
||||
memcpy(SRAM, savedata, len);
|
||||
memcpy(SRAM.get(), savedata, 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)
|
||||
{
|
||||
addr &= (ROMLength-1);
|
||||
@ -578,7 +554,7 @@ void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
|
||||
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)
|
||||
@ -613,7 +589,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last)
|
||||
if (last)
|
||||
{
|
||||
SRAMStatus &= ~(1<<1);
|
||||
Platform::WriteNDSSave(SRAM, SRAMLength,
|
||||
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
|
||||
(SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr);
|
||||
}
|
||||
return 0;
|
||||
@ -677,7 +653,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last)
|
||||
if (last)
|
||||
{
|
||||
SRAMStatus &= ~(1<<1);
|
||||
Platform::WriteNDSSave(SRAM, SRAMLength,
|
||||
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
|
||||
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
||||
}
|
||||
return 0;
|
||||
@ -734,7 +710,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
|
||||
if (last)
|
||||
{
|
||||
SRAMStatus &= ~(1<<1);
|
||||
Platform::WriteNDSSave(SRAM, SRAMLength,
|
||||
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
|
||||
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
||||
}
|
||||
return 0;
|
||||
@ -771,7 +747,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
|
||||
if (last)
|
||||
{
|
||||
SRAMStatus &= ~(1<<1);
|
||||
Platform::WriteNDSSave(SRAM, SRAMLength,
|
||||
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
|
||||
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
||||
}
|
||||
return 0;
|
||||
@ -817,7 +793,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
|
||||
if (last)
|
||||
{
|
||||
SRAMStatus &= ~(1<<1);
|
||||
Platform::WriteNDSSave(SRAM, SRAMLength,
|
||||
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
|
||||
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
||||
}
|
||||
return 0;
|
||||
@ -840,7 +816,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
|
||||
if (last)
|
||||
{
|
||||
SRAMStatus &= ~(1<<1);
|
||||
Platform::WriteNDSSave(SRAM, SRAMLength,
|
||||
Platform::WriteNDSSave(SRAM.get(), SRAMLength,
|
||||
SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr);
|
||||
}
|
||||
return 0;
|
||||
@ -852,15 +828,19 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams)
|
||||
CartRetailNAND::CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr<u8[]>&& sram, u32 sramlen) :
|
||||
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()
|
||||
{
|
||||
CartRetail::Reset();
|
||||
@ -889,9 +869,9 @@ void CartRetailNAND::DoSavestate(Savestate* file)
|
||||
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();
|
||||
}
|
||||
|
||||
@ -924,7 +904,7 @@ int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8
|
||||
if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000))
|
||||
{
|
||||
memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800);
|
||||
Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800);
|
||||
Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMAddr - SRAMBase, 0x800);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
CartRetail::Reset();
|
||||
@ -1125,25 +1118,18 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last)
|
||||
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");
|
||||
}
|
||||
|
||||
CartRetailBT::~CartRetailBT()
|
||||
{
|
||||
}
|
||||
|
||||
void CartRetailBT::Reset()
|
||||
{
|
||||
CartRetail::Reset();
|
||||
}
|
||||
|
||||
void CartRetailBT::DoSavestate(Savestate* file)
|
||||
{
|
||||
CartRetail::DoSavestate(file);
|
||||
}
|
||||
CartRetailBT::~CartRetailBT() = default;
|
||||
|
||||
u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
|
||||
{
|
||||
@ -1159,50 +1145,30 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartCommon(rom, len, chipid, false, romparams)
|
||||
CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional<FATStorage>&& sdcard) :
|
||||
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)
|
||||
{
|
||||
SD->Close();
|
||||
delete SD;
|
||||
}
|
||||
sdcard = std::nullopt;
|
||||
// std::move on optionals usually results in an optional with a moved-from object
|
||||
}
|
||||
|
||||
CartHomebrew::~CartHomebrew() = default;
|
||||
// The SD card is destroyed by the optional's destructor
|
||||
|
||||
void CartHomebrew::Reset()
|
||||
{
|
||||
CartCommon::Reset();
|
||||
|
||||
ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly);
|
||||
|
||||
if (SD)
|
||||
{
|
||||
SD->Close();
|
||||
delete SD;
|
||||
ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly());
|
||||
}
|
||||
|
||||
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)
|
||||
@ -1213,7 +1179,7 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds)
|
||||
{
|
||||
// add the ROM to the SD volume
|
||||
|
||||
if (!SD->InjectFile(romname, ROM, ROMLength))
|
||||
if (!SD->InjectFile(romname, ROM.get(), ROMLength))
|
||||
return;
|
||||
|
||||
// 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)
|
||||
{
|
||||
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:
|
||||
{
|
||||
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;
|
||||
|
||||
@ -1439,17 +1400,20 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset)
|
||||
|
||||
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_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer));
|
||||
NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone));
|
||||
// All fields are default-constructed because they're listed as such in the class declaration
|
||||
|
||||
if (rom)
|
||||
SetCart(std::move(rom));
|
||||
}
|
||||
|
||||
NDSCartSlot::~NDSCartSlot() noexcept
|
||||
@ -1569,16 +1533,13 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept
|
||||
|
||||
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_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)
|
||||
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))
|
||||
{
|
||||
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)
|
||||
{
|
||||
@ -1607,28 +1573,10 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 cartromsize = 0x200;
|
||||
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);
|
||||
auto [cartrom, cartromsize] = PadToPowerOf2(std::move(romdata), romlen);
|
||||
|
||||
NDSHeader header {};
|
||||
memcpy(&header, cartrom, sizeof(header));
|
||||
memcpy(&header, cartrom.get(), sizeof(header));
|
||||
|
||||
bool dsi = header.IsDSi();
|
||||
bool badDSiDump = false;
|
||||
@ -1694,30 +1642,24 @@ std::unique_ptr<CartCommon> ParseROM(const u8* romdata, u32 romlen)
|
||||
}
|
||||
|
||||
std::unique_ptr<CartCommon> cart;
|
||||
auto [sram, sramlen] = args ? std::move(*args->SRAM) : std::make_pair(nullptr, 0);
|
||||
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)
|
||||
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)
|
||||
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
|
||||
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
|
||||
cart = std::make_unique<CartRetail>(cartrom, cartromsize, cartid, badDSiDump, romparams);
|
||||
|
||||
if (romparams.SaveMemType > 0)
|
||||
cart->SetupSave(romparams.SaveMemType);
|
||||
cart = std::make_unique<CartRetail>(std::move(cartrom), cartromsize, cartid, badDSiDump, romparams, std::move(sram), sramlen);
|
||||
|
||||
args = std::nullopt;
|
||||
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)
|
||||
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.
|
||||
Cart = std::move(cart);
|
||||
|
||||
if (!Cart)
|
||||
// If we're ejecting an existing cart without inserting a new one...
|
||||
return;
|
||||
|
||||
Cart->Reset();
|
||||
|
||||
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);
|
||||
|
||||
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)
|
||||
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]);
|
||||
|
||||
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 ID: %08X\n", Cart->ID());
|
||||
Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NDSCartSlot::LoadROM(const u8* romdata, u32 romlen) noexcept
|
||||
{
|
||||
std::unique_ptr<CartCommon> cart = ParseROM(romdata, romlen);
|
||||
|
||||
return InsertROM(std::move(cart));
|
||||
}
|
||||
|
||||
void NDSCartSlot::LoadSave(const u8* savedata, u32 savelen) noexcept
|
||||
void NDSCartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept
|
||||
{
|
||||
if (Cart)
|
||||
Cart->LoadSave(savedata, savelen);
|
||||
Cart->SetSaveMemory(savedata, savelen);
|
||||
}
|
||||
|
||||
void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept
|
||||
@ -1780,15 +1717,15 @@ void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept
|
||||
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
|
||||
NDS.SetIRQ(0, 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?
|
||||
}
|
||||
|
Reference in New Issue
Block a user