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
SPU.cpp
types.h
Utils.cpp
Utils.h
version.h
Wifi.cpp
WifiAP.cpp

View File

@ -19,6 +19,7 @@
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "Args.h"
#include "NDS.h"
#include "DSi.h"
#include "ARM.h"
@ -68,8 +69,8 @@ const u32 NDMAModes[] =
0xFF, // wifi / GBA cart slot (TODO)
};
DSi::DSi() noexcept :
NDS(1),
DSi::DSi(DSiArgs&& args) noexcept :
NDS(std::move(args), 1),
NDMAs {
DSi_NDMA(0, 0, *this),
DSi_NDMA(0, 1, *this),
@ -80,9 +81,11 @@ DSi::DSi() noexcept :
DSi_NDMA(1, 2, *this),
DSi_NDMA(1, 3, *this),
},
ARM7iBIOS(args.ARM7iBIOS),
ARM9iBIOS(args.ARM9iBIOS),
DSP(*this),
SDMMC(*this, 0),
SDIO(*this, 1),
SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)),
SDIO(*this),
I2C(*this),
CamModule(*this),
AES(*this)
@ -118,9 +121,6 @@ void DSi::Reset()
CamModule.Reset();
DSP.Reset();
SDMMC.CloseHandles();
SDIO.CloseHandles();
LoadNAND();
SDMMC.Reset();
@ -162,24 +162,22 @@ void DSi::Stop(Platform::StopReason reason)
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))
{
SetCartInserted(true);
return true;
}
return false;
NDS::SetNDSCart(std::move(cart));
SetCartInserted(NDSCartSlot.GetCart() != nullptr);
}
void DSi::EjectCart()
std::unique_ptr<NDSCart::CartCommon> DSi::EjectCart()
{
NDS::EjectCart();
auto oldcart = NDS::EjectCart();
SetCartInserted(false);
return oldcart;
}
void DSi::CamInputFrame(int cam, u32* data, int width, int height, bool rgb)
{
switch (cam)
@ -509,9 +507,9 @@ void DSi::SetupDirectBoot()
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 (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*NANDImage))
if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*image))
{
DSi_NAND::DSiFirmwareSystemSettings 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));
// TODO: these should be taken from the wifi firmware in NAND
@ -674,9 +672,6 @@ void DSi::SoftReset()
// the DSP most likely gets reset
DSP.Reset();
SDMMC.CloseHandles();
SDIO.CloseHandles();
LoadNAND();
SDMMC.Reset();
@ -709,21 +704,22 @@ void DSi::SoftReset()
bool DSi::LoadNAND()
{
if (!NANDImage)
DSi_NAND::NANDImage* image = SDMMC.GetNAND();
if (!(image && *image))
{
Log(LogLevel::Error, "No NAND image loaded\n");
return false;
}
Log(LogLevel::Info, "Loading DSi NAND\n");
DSi_NAND::NANDMount nandmount(*NANDImage);
DSi_NAND::NANDMount nandmount(*SDMMC.GetNAND());
if (!nandmount)
{
Log(LogLevel::Error, "Failed to load DSi NAND\n");
return false;
}
FileHandle* nand = NANDImage->GetFile();
FileHandle* nand = image->GetFile();
// Make sure NWRAM is accessible.
// 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, "Console ID: %" PRIx64 "\n", NANDImage->GetConsoleID());
Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", image->GetConsoleID());
if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
{
@ -1728,12 +1724,12 @@ bool DSi::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
return false;
}
region->Mem = ARM9BIOS;
region->Mem = &ARM9BIOS[0];
region->Mask = 0xFFF;
}
else
{
region->Mem = ARM9iBIOS;
region->Mem = &ARM9iBIOS[0];
region->Mask = 0xFFFF;
}
return true;
@ -2678,14 +2674,14 @@ u8 DSi::ARM7IORead8(u32 addr)
case 0x04004500: return I2C.ReadData();
case 0x04004501: return I2C.ReadCnt();
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF;
case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF;
case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFF;
case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 24) & 0xFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFF;
case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 40) & 0xFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 48) & 0xFF;
case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56;
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 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 (SDMMC.GetNAND()->GetConsoleID() >> 16) & 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 (SDMMC.GetNAND()->GetConsoleID() >> 32) & 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 (SDMMC.GetNAND()->GetConsoleID() >> 48) & 0xFF;
case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 56;
case 0x04004D08: return 0;
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(0x04004060, MBK[1][8])
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFF;
case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFFFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48;
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 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 (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFFFF;
case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 48;
case 0x04004D08: return 0;
case 0x4004700: return DSP.ReadSNDExCnt();
@ -2806,8 +2802,8 @@ u32 DSi::ARM7IORead32(u32 addr)
case 0x04004400: return AES.ReadCnt();
case 0x0400440C: return AES.ReadOutputFIFO();
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32;
case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFFFFFF;
case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 32;
case 0x04004D08: return 0;
case 0x4004700:

View File

@ -33,6 +33,7 @@ class DSi_I2CHost;
class DSi_CamModule;
class DSi_AES;
class DSi_DSP;
class DSiArgs;
namespace DSi_NAND
{
@ -48,9 +49,8 @@ public:
u16 SCFG_Clock9;
u32 SCFG_EXT[2];
u8 ARM9iBIOS[0x10000];
u8 ARM7iBIOS[0x10000];
std::unique_ptr<DSi_NAND::NANDImage> NANDImage;
std::array<u8, DSiBIOSSize> ARM9iBIOS;
std::array<u8, DSiBIOSSize> ARM7iBIOS;
DSi_SDHost SDMMC;
DSi_SDHost SDIO;
@ -130,19 +130,29 @@ public:
void ARM7IOWrite32(u32 addr, u32 val) override;
public:
DSi() noexcept;
DSi(DSiArgs&& args) noexcept;
~DSi() noexcept override;
DSi(const DSi&) = delete;
DSi& operator=(const DSi&) = delete;
DSi(DSi&&) = delete;
DSi& operator=(DSi&&) = delete;
bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) override;
void EjectCart() override;
void SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart) override;
std::unique_ptr<NDSCart::CartCommon> EjectCart() override;
bool NeedsDirectBoot() override
{
// for now, DSi mode requires original BIOS/NAND
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;
bool DMAsInMode(u32 cpu, u32 mode) override;
bool DMAsRunning(u32 cpu) override;

View File

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

View File

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

View File

@ -165,13 +165,13 @@ void DSi_NWifi::Reset()
for (int i = 0; i < 9; i++)
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",
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)
{
case Firmware::WifiBoard::W015: // AR6002

View File

@ -18,6 +18,7 @@
#include <stdio.h>
#include <string.h>
#include "Args.h"
#include "DSi.h"
#include "DSi_SD.h"
#include "DSi_NAND.h"
@ -26,6 +27,10 @@
namespace melonDS
{
using std::holds_alternative;
using std::unique_ptr;
using std::get_if;
using std::get;
using namespace Platform;
// 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(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer,
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));
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;
}
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,
Transfer_TX);
DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer,
Transfer_RX);
}
void DSi_SDHost::CloseHandles()
{
if (Ports[0]) delete Ports[0];
if (Ports[1]) delete Ports[1];
Ports[0] = nullptr;
Ports[1] = nullptr;
// unique_ptr's destructor will clean up the ports
}
void DSi_SDHost::Reset()
@ -129,48 +136,70 @@ void DSi_SDHost::Reset()
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;
DSi_MMCStorage* mmc;
if (Platform::GetConfigBool(Platform::DSiSD_Enable))
if (!Ports[0])
{
std::string folderpath;
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);
Ports[0] = std::make_unique<DSi_MMCStorage>(DSi, this, std::move(*sdcard));
}
else
sd = nullptr;
mmc = new DSi_MMCStorage(this, *DSi.NANDImage);
mmc->SetCID(DSi.NANDImage->GetEMMCID().data());
Ports[0] = sd;
Ports[1] = mmc;
{
static_cast<DSi_MMCStorage*>(Ports[0].get())->SetSDCard(std::move(*sdcard));
}
}
else
{
DSi_NWifi* nwifi = new DSi_NWifi(DSi, this);
Ports[0] = nwifi;
Ports[0] = nullptr;
}
if (Ports[0]) Ports[0]->Reset();
if (Ports[1]) Ports[1]->Reset();
sdcard = std::nullopt;
// 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)
@ -261,7 +290,7 @@ void DSi_SDHost::SetCardIRQ()
if (!(CardIRQCtl & (1<<0))) return;
u16 oldflags = CardIRQStatus & ~CardIRQMask;
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev->IRQ) 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)
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (BlockCountInternal == 0)
{
@ -419,7 +448,7 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len)
void DSi_SDHost::CheckRX()
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
CheckSwapFIFO();
@ -459,7 +488,7 @@ void DSi_SDHost::CheckTX()
return;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev) dev->ContinueTransfer();
}
@ -550,7 +579,6 @@ u16 DSi_SDHost::ReadFIFO16()
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u16 ret = DataFIFO[f].Read();
if (DataFIFO[f].IsEmpty())
@ -571,7 +599,6 @@ u32 DSi_SDHost::ReadFIFO32()
return 0;
}
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 ret = DataFIFO32.Read();
if (DataFIFO32.IsEmpty())
@ -593,7 +620,7 @@ void DSi_SDHost::Write(u32 addr, u16 val)
Command = val;
u8 cmd = Command & 0x3F;
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
DSi_SDDevice* dev = Ports[PortSelect & 0x1].get();
if (dev)
{
// CHECKME
@ -707,7 +734,6 @@ void DSi_SDHost::Write(u32 addr, u16 val)
void DSi_SDHost::WriteFIFO16(u16 val)
{
DSi_SDDevice* dev = Ports[PortSelect & 0x1];
u32 f = CurFIFO;
if (DataFIFO[f].IsFull())
{
@ -780,34 +806,23 @@ void DSi_SDHost::CheckSwapFIFO()
#define MMC_DESC (Internal?"NAND":"SDcard")
DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand)
: DSi_SDDevice(host), Internal(true), NAND(&nand), SD(nullptr)
DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept
: DSi_SDDevice(host), DSi(dsi), Storage(std::move(nand))
{
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_SDDevice(host)
DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept
: DSi_SDDevice(host), DSi(dsi), Storage(std::move(sdcard))
{
Internal = internal;
NAND = nullptr;
SD = new FATStorage(filename, size, readonly, sourcedir);
SD->Open();
ReadOnly = readonly;
ReadOnly = get<FATStorage>(Storage).IsReadOnly();
SetCID(DSiSDCardCID);
}
DSi_MMCStorage::~DSi_MMCStorage()
{
if (SD)
{
SD->Close();
delete SD;
}
// Do not close the NANDImage, it's not owned by this object
}
// The FATStorage or NANDImage is owned by this object;
// std::variant's destructor will clean it up.
DSi_MMCStorage::~DSi_MMCStorage() = default;
void DSi_MMCStorage::Reset()
{
@ -836,7 +851,7 @@ void DSi_MMCStorage::Reset()
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(CSD, 16);
@ -871,7 +886,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 1: // SEND_OP_COND
// CHECKME!!
// also TODO: it's different for the SD card
if (Internal)
if (std::holds_alternative<DSi_NAND::NANDImage>(Storage))
{
param &= ~(1<<30);
OCR &= 0xBF000000;
@ -895,7 +910,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
return;
case 3: // get/set RCA
if (Internal)
if (holds_alternative<DSi_NAND::NANDImage>(Storage))
{
RCA = param >> 16;
Host->SendResponse(CSR|0x10000, true); // huh??
@ -930,7 +945,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param)
case 12: // stop operation
SetState(0x04);
if (NAND) FileFlush(NAND->GetFile());
if (auto* nand = get_if<DSi_NAND::NANDImage>(&Storage))
FileFlush(nand->GetFile());
RWCommand = 0;
Host->SendResponse(CSR, true);
return;
@ -1011,7 +1027,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param)
// DSi boot2 sets this to 0x40100000 (hardcoded)
// then has two codepaths depending on whether bit30 did get set
// 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 |= (param & 0x40FFFFFF);
Host->SendResponse(OCR, true);
@ -1057,14 +1073,14 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr)
len = Host->GetTransferrableLen(len);
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);
FileRead(&data[addr & 0x1FF], 1, len, NAND->GetFile());
FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
FileRead(&data[addr & 0x1FF], 1, len, nand->GetFile());
}
return Host->DataRX(&data[addr & 0x1FF], len);
@ -1078,23 +1094,23 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr)
u8 data[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 (!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);
FileWrite(&data[addr & 0x1FF], 1, len, NAND->GetFile());
FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start);
FileWrite(&data[addr & 0x1FF], 1, len, nand->GetFile());
}
}
}

View File

@ -20,28 +20,30 @@
#define DSI_SD_H
#include <cstring>
#include <variant>
#include "FIFO.h"
#include "FATStorage.h"
#include "DSi_NAND.h"
#include "Savestate.h"
namespace melonDS
{
namespace DSi_NAND
{
class NANDImage;
}
class DSi_SDDevice;
class DSi;
using Nothing = std::monostate;
using DSiStorage = std::variant<std::monostate, FATStorage, DSi_NAND::NANDImage>;
class DSi_SDHost
{
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();
void CloseHandles();
void Reset();
void DoSavestate(Savestate* file);
@ -59,6 +61,15 @@ public:
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);
void Write(u32 addr, u16 val);
u16 ReadFIFO16();
@ -96,7 +107,7 @@ private:
u32 Param;
u16 ResponseBuffer[8];
DSi_SDDevice* Ports[2];
std::array<std::unique_ptr<DSi_SDDevice>, 2> Ports {};
u32 CurFIFO; // FIFO accessible for read/write
FIFO<u16, 0x100> DataFIFO[2];
@ -134,25 +145,53 @@ protected:
class DSi_MMCStorage : public DSi_SDDevice
{
public:
DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand);
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, DSi_NAND::NANDImage&& nand) noexcept;
DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept;
~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 SendCMD(u8 cmd, u32 param);
void SendCMD(u8 cmd, u32 param) override;
void SendACMD(u8 cmd, u32 param);
void ContinueTransfer();
void ContinueTransfer() override;
private:
bool Internal;
DSi_NAND::NANDImage* NAND;
FATStorage* SD;
static constexpr u8 DSiSDCardCID[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00};
melonDS::DSi& DSi;
DSiStorage Storage;
u8 CID[16];
u8 CSD[16];

View File

@ -29,39 +29,79 @@ namespace melonDS
{
namespace fs = std::filesystem;
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);
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;
}
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()
{
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);
File = nullptr;
}
bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len)
{
if (!File) return false;
@ -930,19 +970,15 @@ u64 FATStorage::GetDirectorySize(fs::path sourcedir)
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;
FileSize = size;
SourceDir = sourcedir;
bool hasdir = !sourcedir.empty();
if (hasdir)
bool hasdir = sourcedir && !sourcedir->empty();
if (sourcedir)
{
if (!fs::is_directory(fs::u8path(sourcedir)))
if (!fs::is_directory(fs::u8path(*sourcedir)))
{
hasdir = false;
SourceDir = "";
SourceDir = std::nullopt;
}
}
@ -1005,7 +1041,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
{
if (hasdir)
{
FileSize = GetDirectorySize(fs::u8path(sourcedir));
FileSize = GetDirectorySize(fs::u8path(*sourcedir));
FileSize += 0x8000000ULL; // 128MB leeway
// 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 (hasdir)
ImportDirectory(sourcedir);
ImportDirectory(*sourcedir);
}
f_unmount("0:");
@ -1068,9 +1104,9 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string&
bool FATStorage::Save()
{
if (SourceDir.empty())
{
return true;
if (!SourceDir)
{ // If we're not syncing the SD card image to a host directory...
return true; // Not an error.
}
FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting);
@ -1094,7 +1130,7 @@ bool FATStorage::Save()
return false;
}
ExportChanges(SourceDir);
ExportChanges(*SourceDir);
SaveIndex();

View File

@ -22,6 +22,7 @@
#include <stdio.h>
#include <string>
#include <map>
#include <optional>
#include <filesystem>
#include "Platform.h"
@ -30,24 +31,41 @@
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
{
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();
bool Open();
void Close();
bool InjectFile(const std::string& path, u8* data, u32 len);
u32 ReadSectors(u32 start, u32 num, u8* data);
u32 WriteSectors(u32 start, u32 num, u8* data);
[[nodiscard]] bool IsReadOnly() const noexcept { return ReadOnly; }
private:
std::string FilePath;
std::string IndexPath;
std::string SourceDir;
std::optional<std::string> SourceDir;
bool ReadOnly;
Platform::FileHandle* File;
@ -76,7 +94,7 @@ private:
bool ImportDirectory(const std::string& 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();
typedef struct

View File

@ -28,7 +28,7 @@
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,
0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea,
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
};
unsigned char bios_arm9_bin[] = {
std::array<u8, ARM9BIOSSize> bios_arm9_bin = {
0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 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,

View File

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

File diff suppressed because it is too large Load Diff

View File

@ -1,257 +1,289 @@
/*
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 GBACART_H
#define GBACART_H
#include <memory>
#include "types.h"
#include "Savestate.h"
namespace melonDS::GBACart
{
enum CartType
{
Default = 0x001,
Game = 0x101,
GameSolarSensor = 0x102,
RAMExpansion = 0x201,
};
// CartCommon -- base code shared by all cart types
class CartCommon
{
public:
CartCommon();
virtual ~CartCommon();
virtual u32 Type() const = 0;
virtual u32 Checksum() const { return 0; }
virtual void Reset();
virtual void DoSavestate(Savestate* file);
virtual void SetupSave(u32 type);
virtual void LoadSave(const u8* savedata, u32 savelen);
virtual int SetInput(int num, bool pressed);
virtual u16 ROMRead(u32 addr) const;
virtual void ROMWrite(u32 addr, u16 val);
virtual u8 SRAMRead(u32 addr);
virtual void SRAMWrite(u32 addr, u8 val);
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; }
[[nodiscard]] virtual u32 GetROMLength() const { return 0; }
virtual u8* GetSaveMemory() const;
virtual u32 GetSaveMemoryLength() const;
};
// CartGame -- regular retail game cart (ROM, SRAM)
class CartGame : public CartCommon
{
public:
CartGame(u8* rom, u32 len);
virtual ~CartGame() override;
virtual u32 Type() const override { return CartType::Game; }
virtual u32 Checksum() const override;
virtual void Reset() override;
virtual void DoSavestate(Savestate* file) override;
virtual void SetupSave(u32 type) override;
virtual void LoadSave(const u8* savedata, u32 savelen) override;
virtual u16 ROMRead(u32 addr) const override;
virtual void ROMWrite(u32 addr, u16 val) override;
virtual u8 SRAMRead(u32 addr) override;
virtual void SRAMWrite(u32 addr, u8 val) override;
[[nodiscard]] const u8* GetROM() const override { return ROM; }
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
virtual u8* GetSaveMemory() const override;
virtual u32 GetSaveMemoryLength() const override;
protected:
virtual void ProcessGPIO();
u8 SRAMRead_EEPROM(u32 addr);
void SRAMWrite_EEPROM(u32 addr, u8 val);
u8 SRAMRead_FLASH(u32 addr);
void SRAMWrite_FLASH(u32 addr, u8 val);
u8 SRAMRead_SRAM(u32 addr);
void SRAMWrite_SRAM(u32 addr, u8 val);
u8* ROM;
u32 ROMLength;
struct
{
u16 data;
u16 direction;
u16 control;
} GPIO;
enum SaveType
{
S_NULL,
S_EEPROM4K,
S_EEPROM64K,
S_SRAM256K,
S_FLASH512K,
S_FLASH1M
};
// from DeSmuME
struct
{
u8 state;
u8 cmd;
u8 device;
u8 manufacturer;
u8 bank;
} SRAMFlashState;
u8* SRAM;
u32 SRAMLength;
SaveType SRAMType;
};
// CartGameSolarSensor -- Boktai game cart
class CartGameSolarSensor : public CartGame
{
public:
CartGameSolarSensor(u8* rom, u32 len);
virtual ~CartGameSolarSensor() override;
virtual u32 Type() const override { return CartType::GameSolarSensor; }
virtual void Reset() override;
virtual void DoSavestate(Savestate* file) override;
virtual int SetInput(int num, bool pressed) override;
private:
virtual void ProcessGPIO() override;
static const int kLuxLevels[11];
bool LightEdge;
u8 LightCounter;
u8 LightSample;
u8 LightLevel;
};
// CartRAMExpansion -- RAM expansion cart (DS browser, ...)
class CartRAMExpansion : public CartCommon
{
public:
CartRAMExpansion();
~CartRAMExpansion() override;
virtual u32 Type() const override { return CartType::RAMExpansion; }
void Reset() override;
void DoSavestate(Savestate* file) override;
u16 ROMRead(u32 addr) const override;
void ROMWrite(u32 addr, u16 val) override;
private:
u8 RAM[0x800000];
u16 RAMEnable;
};
// possible inputs for GBA carts that might accept user input
enum
{
Input_SolarSensorDown = 0,
Input_SolarSensorUp,
};
class GBACartSlot
{
public:
GBACartSlot() noexcept = default;
~GBACartSlot() noexcept = default;
void Reset() noexcept;
void DoSavestate(Savestate* file) noexcept;
/// Applies the GBACartData to the emulator state and unloads an existing ROM if any.
/// Upon successful insertion, \c cart will be nullptr and the global GBACart state
/// (\c CartROM, CartInserted, etc.) will be updated.
bool InsertROM(std::unique_ptr<CartCommon>&& cart) noexcept;
bool LoadROM(const u8* romdata, u32 romlen) noexcept;
void LoadSave(const u8* savedata, u32 savelen) noexcept;
void LoadAddon(int type) noexcept;
void EjectCart() noexcept;
// TODO: make more flexible, support nonbinary inputs
int SetInput(int num, bool pressed) noexcept;
void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; }
u16 ROMRead(u32 addr) const noexcept;
void ROMWrite(u32 addr, u16 val) noexcept;
u8 SRAMRead(u32 addr) noexcept;
void SRAMWrite(u32 addr, u8 val) noexcept;
/// This function is intended to allow frontends to save and load SRAM
/// without using melonDS APIs.
/// Modifying the emulated SRAM for any other reason is strongly discouraged.
/// The returned pointer may be invalidated if the emulator is reset,
/// or when a new game is loaded.
/// 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; }
[[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
/// @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);
}
#endif // GBACART_H
/*
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 GBACART_H
#define GBACART_H
#include <memory>
#include "types.h"
#include "Savestate.h"
namespace melonDS::GBACart
{
enum CartType
{
Default = 0x001,
Game = 0x101,
GameSolarSensor = 0x102,
RAMExpansion = 0x201,
};
// CartCommon -- base code shared by all cart types
class CartCommon
{
public:
virtual ~CartCommon() = default;
[[nodiscard]] u32 Type() const { return CartType; }
virtual u32 Checksum() const { return 0; }
virtual void Reset();
virtual void DoSavestate(Savestate* file);
virtual int SetInput(int num, bool pressed);
virtual u16 ROMRead(u32 addr) const;
virtual void ROMWrite(u32 addr, u16 val);
virtual u8 SRAMRead(u32 addr);
virtual void SRAMWrite(u32 addr, u8 val);
[[nodiscard]] virtual const u8* GetROM() const { return nullptr; }
[[nodiscard]] virtual u32 GetROMLength() const { return 0; }
virtual u8* GetSaveMemory() const;
virtual u32 GetSaveMemoryLength() const;
virtual void SetSaveMemory(const u8* savedata, u32 savelen);
protected:
CartCommon(GBACart::CartType type);
friend class GBACartSlot;
private:
GBACart::CartType CartType;
};
// CartGame -- regular retail game cart (ROM, SRAM)
class CartGame : public CartCommon
{
public:
CartGame(const u8* rom, u32 len, const u8* sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game);
CartGame(std::unique_ptr<u8[]>&& rom, u32 len, std::unique_ptr<u8[]>&& sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game);
~CartGame() override;
u32 Checksum() const override;
void Reset() override;
void DoSavestate(Savestate* file) override;
u16 ROMRead(u32 addr) const override;
void ROMWrite(u32 addr, u16 val) override;
u8 SRAMRead(u32 addr) override;
void SRAMWrite(u32 addr, u8 val) override;
[[nodiscard]] const u8* GetROM() const override { return ROM.get(); }
[[nodiscard]] u32 GetROMLength() const override { return ROMLength; }
u8* GetSaveMemory() const override;
u32 GetSaveMemoryLength() const override;
void SetSaveMemory(const u8* savedata, u32 savelen) override;
protected:
virtual void ProcessGPIO();
u8 SRAMRead_EEPROM(u32 addr);
void SRAMWrite_EEPROM(u32 addr, u8 val);
u8 SRAMRead_FLASH(u32 addr);
void SRAMWrite_FLASH(u32 addr, u8 val);
u8 SRAMRead_SRAM(u32 addr);
void SRAMWrite_SRAM(u32 addr, u8 val);
std::unique_ptr<u8[]> ROM;
u32 ROMLength;
struct
{
u16 data;
u16 direction;
u16 control;
} GPIO {};
enum SaveType
{
S_NULL,
S_EEPROM4K,
S_EEPROM64K,
S_SRAM256K,
S_FLASH512K,
S_FLASH1M
};
// from DeSmuME
struct
{
u8 state;
u8 cmd;
u8 device;
u8 manufacturer;
u8 bank;
} SRAMFlashState {};
std::unique_ptr<u8[]> SRAM = nullptr;
u32 SRAMLength = 0;
SaveType SRAMType = S_NULL;
private:
void SetupSave(u32 type);
};
// CartGameSolarSensor -- Boktai game cart
class CartGameSolarSensor : public CartGame
{
public:
CartGameSolarSensor(const u8* rom, u32 len, const u8* sram, u32 sramlen);
CartGameSolarSensor(std::unique_ptr<u8[]>&& rom, u32 len, std::unique_ptr<u8[]>&& sram, u32 sramlen);
void Reset() override;
void DoSavestate(Savestate* file) override;
int SetInput(int num, bool pressed) override;
protected:
void ProcessGPIO() override;
private:
static const int kLuxLevels[11];
bool LightEdge = false;
u8 LightCounter = 0;
u8 LightSample = 0;
u8 LightLevel = 0;
};
// CartRAMExpansion -- RAM expansion cart (DS browser, ...)
class CartRAMExpansion : public CartCommon
{
public:
CartRAMExpansion();
~CartRAMExpansion() override;
void Reset() override;
void DoSavestate(Savestate* file) override;
u16 ROMRead(u32 addr) const override;
void ROMWrite(u32 addr, u16 val) override;
private:
u8 RAM[0x800000] {};
u16 RAMEnable = 0;
};
// possible inputs for GBA carts that might accept user input
enum
{
Input_SolarSensorDown = 0,
Input_SolarSensorUp,
};
class GBACartSlot
{
public:
GBACartSlot(std::unique_ptr<CartCommon>&& cart = nullptr) noexcept;
~GBACartSlot() noexcept = default;
void Reset() noexcept;
void DoSavestate(Savestate* file) noexcept;
/// Ejects the cart in the GBA slot (if any)
/// and inserts the given one.
///
/// To insert a cart that does not require ROM data
/// (such as the RAM expansion pack),
/// create it manually with std::make_unique and pass it here.
///
/// @param cart Movable \c unique_ptr to the GBA cart object.
/// May be \c nullptr, in which case the cart slot remains empty.
/// @post \c cart is \c nullptr and the underlying object
/// is moved into the cart slot.
void SetCart(std::unique_ptr<CartCommon>&& cart) noexcept;
[[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
[[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
void LoadAddon(int type) noexcept;
/// @return The cart that was in the cart slot if any,
/// or \c nullptr if the cart slot was empty.
std::unique_ptr<CartCommon> EjectCart() noexcept;
// TODO: make more flexible, support nonbinary inputs
int SetInput(int num, bool pressed) noexcept;
void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; }
u16 ROMRead(u32 addr) const noexcept;
void ROMWrite(u32 addr, u16 val) noexcept;
u8 SRAMRead(u32 addr) noexcept;
void SRAMWrite(u32 addr, u8 val) noexcept;
/// This function is intended to allow frontends to save and load SRAM
/// without using melonDS APIs.
/// Modifying the emulated SRAM for any other reason is strongly discouraged.
/// The returned pointer may be invalidated if the emulator is reset,
/// or when a new game is loaded.
/// 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; }
[[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; }
/// Sets the loaded cart's SRAM.
/// Does nothing if no cart is inserted
/// or the inserted cart doesn't support SRAM.
///
/// @param savedata Buffer containing the raw contents of the SRAM.
/// The contents of this buffer are copied into the cart slot,
/// 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;
/// @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 "Platform.h"
#include "FreeBIOS.h"
#include "Args.h"
#include "DSi.h"
#include "DSi_SPI_TSC.h"
@ -74,16 +75,31 @@ const s32 kIterationCycleMargin = 8;
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),
ARM7BIOS(args.ARM7BIOS),
ARM9BIOS(args.ARM9BIOS),
JIT(*this),
SPU(*this),
GPU(*this),
SPI(*this),
SPI(*this, std::move(args.Firmware)),
RTC(*this),
Wifi(*this),
NDSCartSlot(*this),
GBACartSlot(),
NDSCartSlot(*this, std::move(args.NDSROM)),
GBACartSlot(type == 1 ? nullptr : std::move(args.GBAROM)),
AREngine(*this),
ARM9(*this),
ARM7(*this),
@ -238,7 +254,7 @@ bool NDS::NeedsDirectBoot()
return true;
// DSi/3DS firmwares aren't bootable
if (!SPI.GetFirmware()->IsBootable())
if (!SPI.GetFirmware().IsBootable())
return true;
return false;
@ -710,42 +726,27 @@ bool NDS::DoSavestate(Savestate* file)
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))
return false;
if (savedata && savelen)
NDSCartSlot.LoadSave(savedata, savelen);
return true;
NDSCartSlot.SetCart(std::move(cart));
// The existing cart will always be ejected;
// if cart is null, then that's equivalent to ejecting a cart
// without inserting a new one.
}
void NDS::LoadSave(const u8* savedata, u32 savelen)
void NDS::SetNDSSave(const u8* savedata, u32 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)
@ -753,26 +754,11 @@ void NDS::LoadGBAAddon(int type)
GBACartSlot.LoadAddon(type);
}
void NDS::EjectGBACart()
{
GBACartSlot.EjectCart();
}
void NDS::LoadBIOS()
{
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 minEvent = UINT64_MAX;
@ -2252,7 +2238,7 @@ bool NDS::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region)
if ((addr & 0xFFFFF000) == 0xFFFF0000 && !write)
{
region->Mem = ARM9BIOS;
region->Mem = &ARM9BIOS[0];
region->Mask = 0xFFF;
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))
{
region->Mem = ARM7BIOS;
region->Mem = &ARM7BIOS[0];
region->Mask = 0x3FFF;
return true;
}

View File

@ -21,7 +21,7 @@
#include <memory>
#include <string>
#include <memory>
#include <optional>
#include <functional>
#include "Platform.h"
@ -37,6 +37,7 @@
#include "GPU.h"
#include "ARMJIT.h"
#include "DMA.h"
#include "FreeBIOS.h"
// when touching the main loop/timing code, pls test a lot of shit
// with this enabled, to make sure it doesn't desync
@ -44,7 +45,8 @@
namespace melonDS
{
struct NDSArgs;
class Firmware;
enum
{
Event_LCD = 0,
@ -255,8 +257,8 @@ public:
u8 ROMSeed0[2*8];
u8 ROMSeed1[2*8];
u8 ARM9BIOS[0x1000];
u8 ARM7BIOS[0x4000];
std::array<u8, ARM9BIOSSize> ARM9BIOS;
std::array<u8, ARM7BIOSSize> ARM7BIOS;
u16 ARM7BIOSProt;
u8* MainRAM;
@ -303,25 +305,51 @@ public:
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);
// 0=DS 1=DSi
void SetConsoleType(int type);
void LoadBIOS();
bool IsLoadedARM9BIOSBuiltIn();
bool IsLoadedARM7BIOSBuiltIn();
[[nodiscard]] bool IsLoadedARM9BIOSBuiltIn() const noexcept { return ARM9BIOS == bios_arm9_bin; }
[[nodiscard]] bool IsLoadedARM7BIOSBuiltIn() const noexcept { return ARM7BIOS == bios_arm7_bin; }
virtual bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen);
void LoadSave(const u8* savedata, u32 savelen);
virtual void EjectCart();
bool CartInserted();
[[nodiscard]] NDSCart::CartCommon* GetNDSCart() { return NDSCartSlot.GetCart(); }
[[nodiscard]] const NDSCart::CartCommon* GetNDSCart() const { return NDSCartSlot.GetCart(); }
virtual void SetNDSCart(std::unique_ptr<NDSCart::CartCommon>&& cart);
[[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();
void SetupDirectBoot(const std::string& romname);
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 EjectGBACart();
std::unique_ptr<GBACart::CartCommon> EjectGBACart() { return GBACartSlot.EjectCart(); }
u32 RunFrame();
@ -456,7 +484,8 @@ private:
template <bool EnableJIT>
u32 RunFrame();
public:
NDS() noexcept : NDS(0) {}
NDS(NDSArgs&& args) noexcept : NDS(std::move(args), 0) {}
NDS() noexcept;
virtual ~NDS() noexcept;
NDS(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.
[[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current;
protected:
explicit NDS(int type) noexcept;
explicit NDS(NDSArgs&& args, int type) noexcept;
virtual void DoSavestateExtra(Savestate* file) {}
};

View File

@ -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?
}

View File

@ -22,7 +22,7 @@
#include <array>
#include <string>
#include <memory>
#include <array>
#include <variant>
#include "types.h"
#include "Savestate.h"
@ -49,14 +49,32 @@ enum CartType
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
class CartCommon
{
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 u32 Type() const = 0;
[[nodiscard]] u32 Type() const { return CartType; };
[[nodiscard]] u32 Checksum() const;
virtual void Reset();
@ -64,16 +82,16 @@ public:
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 void ROMCommandFinish(u8* cmd, u8* data, u32 len);
virtual u8 SPIWrite(u8 val, u32 pos, bool last);
virtual u8* GetSaveMemory() const;
virtual u32 GetSaveMemoryLength() const;
virtual u8* GetSaveMemory() { return nullptr; }
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]] NDSHeader& GetHeader() { return Header; }
@ -82,48 +100,65 @@ public:
[[nodiscard]] const NDSBanner* Banner() const;
[[nodiscard]] const ROMListEntry& GetROMParams() const { return ROMParams; };
[[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; }
protected:
void ReadROM(u32 addr, u32 len, u8* data, u32 offset);
u8* ROM;
u32 ROMLength;
u32 ChipID;
bool IsDSi;
bool DSiMode;
u32 DSiBase;
std::unique_ptr<u8[]> ROM = nullptr;
u32 ROMLength = 0;
u32 ChipID = 0;
bool IsDSi = false;
bool DSiMode = false;
u32 DSiBase = 0;
u32 CmdEncMode;
u32 DataEncMode;
u32 CmdEncMode = 0;
u32 DataEncMode = 0;
// Kept separate from the ROM data so we can decrypt the modcrypt area
// without touching the overall ROM data
NDSHeader Header;
ROMListEntry ROMParams;
NDSHeader Header {};
ROMListEntry ROMParams {};
const melonDS::NDSCart::CartType CartType = Default;
};
// CartRetail -- regular retail cart (ROM, SPI SRAM)
class CartRetail : public CartCommon
{
public:
CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams);
virtual ~CartRetail() override;
CartRetail(
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;
virtual void LoadSave(const u8* savedata, u32 savelen) override;
int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) 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;
virtual u8* GetSaveMemory() const override;
virtual u32 GetSaveMemoryLength() const override;
u8* GetSaveMemory() override { return SRAM.get(); }
const u8* GetSaveMemory() const override { return SRAM.get(); }
u32 GetSaveMemoryLength() const override { return SRAMLength; }
protected:
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_FLASH(u8 val, u32 pos, bool last);
u8* SRAM;
u32 SRAMLength;
u32 SRAMType;
std::unique_ptr<u8[]> SRAM = nullptr;
u32 SRAMLength = 0;
u32 SRAMType = 0;
u8 SRAMCmd;
u32 SRAMAddr;
u32 SRAMFirstAddr;
u8 SRAMStatus;
u8 SRAMCmd = 0;
u32 SRAMAddr = 0;
u32 SRAMFirstAddr = 0;
u8 SRAMStatus = 0;
};
// CartRetailNAND -- retail cart with NAND SRAM (WarioWare DIY, Jam with the Band, ...)
class CartRetailNAND : public CartRetail
{
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;
virtual u32 Type() const override { return CartType::RetailNAND; }
void Reset() 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;
void ROMCommandFinish(u8* cmd, u8* data, u32 len) override;
@ -165,22 +199,21 @@ public:
private:
void BuildSRAMID();
u32 SRAMBase;
u32 SRAMWindow;
u32 SRAMBase = 0;
u32 SRAMWindow = 0;
u8 SRAMWriteBuffer[0x800];
u32 SRAMWritePos;
u8 SRAMWriteBuffer[0x800] {};
u32 SRAMWritePos = 0;
};
// CartRetailIR -- SPI IR device and SRAM
class CartRetailIR : public CartRetail
{
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;
virtual u32 Type() const override { return CartType::RetailIR; }
void Reset() override;
void DoSavestate(Savestate* file) override;
@ -188,23 +221,18 @@ public:
u8 SPIWrite(u8 val, u32 pos, bool last) override;
private:
u32 IRVersion;
u8 IRCmd;
u32 IRVersion = 0;
u8 IRCmd = 0;
};
// CartRetailBT - Pok<6F>mon Typing Adventure (SPI BT controller)
class CartRetailBT : public CartRetail
{
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;
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;
};
@ -212,32 +240,38 @@ public:
class CartHomebrew : public CartCommon
{
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;
virtual u32 Type() const override { return CartType::Homebrew; }
void Reset() 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;
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:
void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, 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);
FATStorage* SD;
bool ReadOnly;
std::optional<FATStorage> SD {};
};
class NDSCartSlot
{
public:
NDSCartSlot(melonDS::NDS& nds) noexcept;
explicit NDSCartSlot(melonDS::NDS& nds, std::unique_ptr<CartCommon>&& rom = nullptr) noexcept;
~NDSCartSlot() noexcept;
void Reset() noexcept;
void ResetCart() noexcept;
@ -252,25 +286,16 @@ public:
/// If the provided cart is not valid,
/// then the currently-loaded ROM will not be ejected.
///
/// @param cart Movable reference to the cart.
/// @returns \c true if the cart was successfully loaded,
/// \c false otherwise.
/// @param cart Movable reference to the cart,
/// or \c nullptr to eject the cart.
/// @post If the cart was successfully loaded,
/// then \c cart will be \c nullptr
/// and \c Cart will contain the object that \c cart previously pointed to.
/// 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;
/// 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.
[[nodiscard]] const u8* GetSaveMemory() const 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()
/// if a cart is loaded and supports SRAM, otherwise zero.
[[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;
void WriteROMData(u32 val) noexcept;
void WriteSPICnt(u16 val) noexcept;
@ -294,9 +323,6 @@ public:
[[nodiscard]] u8 ReadSPIData() const 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]; }
void SetROMCommand(u8 index, u8 val) noexcept { ROMCommand[index] = val; }
@ -306,27 +332,27 @@ public:
private:
friend class CartCommon;
melonDS::NDS& NDS;
u16 SPICnt {};
u32 ROMCnt {};
u16 SPICnt = 0;
u32 ROMCnt = 0;
std::array<u8, 8> ROMCommand {};
u8 SPIData;
u32 SPIDataPos;
bool SPIHold;
u8 SPIData = 0;
u32 SPIDataPos = 0;
bool SPIHold = false;
u32 ROMData;
u32 ROMData = 0;
std::array<u8, 0x4000> TransferData;
u32 TransferPos;
u32 TransferLen;
u32 TransferDir;
std::array<u8, 8> TransferCmd;
std::array<u8, 0x4000> TransferData {};
u32 TransferPos = 0;
u32 TransferLen = 0;
u32 TransferDir = 0;
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_Y;
u64 Key2_X = 0;
u64 Key2_Y = 0;
void Key1_Encrypt(u32* data) noexcept;
void Key1_Decrypt(u32* data) noexcept;
@ -346,9 +372,13 @@ private:
/// 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.
/// @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,
/// 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

View File

@ -106,24 +106,8 @@ enum ConfigEntry
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,
WifiSettingsPath,
AudioBitDepth,
DSi_FullBIOSBoot,
@ -139,7 +123,6 @@ enum ConfigEntry
int GetConfigInt(ConfigEntry entry);
bool GetConfigBool(ConfigEntry entry);
std::string GetConfigString(ConfigEntry entry);
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)
{
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)

View File

@ -66,24 +66,23 @@ protected:
class FirmwareMem : public SPIDevice
{
public:
FirmwareMem(melonDS::NDS& nds);
FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware);
~FirmwareMem() override;
void Reset() override;
void DoSavestate(Savestate* file) override;
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 InstallFirmware(class Firmware&& firmware);
bool InstallFirmware(std::unique_ptr<class Firmware>&& firmware);
void RemoveFirmware();
void Write(u8 val) override;
void Release() override;
private:
std::unique_ptr<class Firmware> Firmware;
Firmware FirmwareData;
u8 CurCmd;
@ -141,16 +140,19 @@ protected:
class SPIHost
{
public:
SPIHost(melonDS::NDS& nds);
SPIHost(melonDS::NDS& nds, Firmware&& firmware);
~SPIHost();
void Reset();
void DoSavestate(Savestate* file);
FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; }
const FirmwareMem* GetFirmwareMem() const { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; }
PowerMan* GetPowerMan() { return (PowerMan*)Devices[SPIDevice_PowerMan]; }
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; }
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
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);
Firmware::FirmwareConsoleType console = fw->GetHeader().ConsoleType;
Firmware::FirmwareConsoleType console = fw.GetHeader().ConsoleType;
if (console == Firmware::FirmwareConsoleType::DS)
IOPORT(0x000) = 0x1440;
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_entry *entry;
int r;
if (!filedata) return -1;
archive_read_support_format_all(a);
archive_read_support_filter_all(a);
@ -148,8 +147,8 @@ s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32*
size_t bytesToRead = archive_entry_size(entry);
if (filesize) *filesize = bytesToRead;
*filedata = new u8[bytesToRead];
ssize_t bytesRead = archive_read_data(a, *filedata, bytesToRead);
filedata = std::make_unique<u8[]>(bytesToRead);
ssize_t bytesRead = archive_read_data(a, filedata.get(), bytesToRead);
archive_read_close(a);
archive_read_free(a);

View File

@ -37,7 +37,7 @@ namespace Archive
using namespace melonDS;
QVector<QString> ListArchive(QString path);
s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize);
s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr<u8[]>& filedata, u32* filesize);
//QVector<QString> ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer);
//u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata);

View File

@ -204,10 +204,6 @@ int GetConfigInt(ConfigEntry entry)
case JIT_MaxBlockSize: return Config::JIT_MaxBlockSize;
#endif
case DLDI_ImageSize: return imgsizes[Config::DLDISize];
case DSiSD_ImageSize: return imgsizes[Config::DSiSDSize];
case AudioBitDepth: return Config::AudioBitDepth;
#ifdef GDBSTUB_ENABLED
@ -232,14 +228,6 @@ bool GetConfigBool(ConfigEntry entry)
case ExternalBIOSEnable: return Config::ExternalBIOSEnable != 0;
case DLDI_Enable: return Config::DLDIEnable != 0;
case DLDI_ReadOnly: return Config::DLDIReadOnly != 0;
case DLDI_FolderSync: return Config::DLDIFolderSync != 0;
case DSiSD_Enable: return Config::DSiSDEnable != 0;
case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0;
case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0;
case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0;
#ifdef GDBSTUB_ENABLED
@ -252,22 +240,6 @@ bool GetConfigBool(ConfigEntry entry)
return false;
}
std::string GetConfigString(ConfigEntry entry)
{
switch (entry)
{
case DLDI_ImagePath: return Config::DLDISDPath;
case DLDI_FolderPath: return Config::DLDIFolderPath;
case DSiSD_ImagePath: return Config::DSiSDPath;
case DSiSD_FolderPath: return Config::DSiSDFolderPath;
case WifiSettingsPath: return Config::WifiSettingsPath;
}
return "";
}
bool GetConfigArray(ConfigEntry entry, void* data)
{
switch (entry)

View File

@ -68,8 +68,8 @@ std::string BaseGBAROMDir = "";
std::string BaseGBAROMName = "";
std::string BaseGBAAssetName = "";
SaveManager* NDSSave = nullptr;
SaveManager* GBASave = nullptr;
std::unique_ptr<SaveManager> NDSSave = nullptr;
std::unique_ptr<SaveManager> GBASave = nullptr;
std::unique_ptr<SaveManager> FirmwareSave = nullptr;
std::unique_ptr<Savestate> BackupState = nullptr;
@ -303,6 +303,28 @@ QString VerifySetup()
return "";
}
std::string GetEffectiveFirmwareSavePath(EmuThread* thread)
{
if (!Config::ExternalBIOSEnable)
{
return Config::WifiSettingsPath;
}
if (thread->NDS->ConsoleType == 1)
{
return Config::DSiFirmwarePath;
}
else
{
return Config::FirmwarePath;
}
}
// Initializes the firmware save manager with the selected firmware image's path
// OR the path to the wi-fi settings.
void InitFirmwareSaveManager(EmuThread* thread) noexcept
{
FirmwareSave = std::make_unique<SaveManager>(GetEffectiveFirmwareSavePath(thread));
}
std::string GetSavestateName(int slot)
{
@ -482,6 +504,11 @@ void LoadCheats(NDS& nds)
std::optional<std::array<u8, ARM9BIOSSize>> LoadARM9BIOS() noexcept
{
if (!Config::ExternalBIOSEnable)
{
return Config::ConsoleType == 0 ? std::make_optional(bios_arm9_bin) : std::nullopt;
}
if (FileHandle* f = OpenLocalFile(Config::BIOS9Path, Read))
{
std::array<u8, ARM9BIOSSize> bios {};
@ -498,6 +525,11 @@ std::optional<std::array<u8, ARM9BIOSSize>> LoadARM9BIOS() noexcept
std::optional<std::array<u8, ARM7BIOSSize>> LoadARM7BIOS() noexcept
{
if (!Config::ExternalBIOSEnable)
{
return Config::ConsoleType == 0 ? std::make_optional(bios_arm7_bin) : std::nullopt;
}
if (FileHandle* f = OpenLocalFile(Config::BIOS7Path, Read))
{
std::array<u8, ARM7BIOSSize> bios {};
@ -518,6 +550,16 @@ std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM9BIOS() noexcept
std::array<u8, DSiBIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(f);
if (!Config::DSiFullBIOSBoot)
{
// herp
*(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector
// TODO!!!!
// hax the upper 32K out of the goddamn DSi
// done that :) -pcy
}
Log(Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str());
return bios;
}
@ -533,6 +575,16 @@ std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM7BIOS() noexcept
std::array<u8, DSiBIOSSize> bios {};
FileRead(bios.data(), sizeof(bios), 1, f);
CloseFile(f);
if (!Config::DSiFullBIOSBoot)
{
// herp
*(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector
// TODO!!!!
// hax the upper 32K out of the goddamn DSi
// done that :) -pcy
}
Log(Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str());
return bios;
}
@ -589,6 +641,16 @@ Firmware GenerateFirmware(int type) noexcept
std::optional<Firmware> LoadFirmware(int type) noexcept
{
if (!Config::ExternalBIOSEnable)
{ // If we're using built-in firmware...
if (type == 1)
{
Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n");
return std::nullopt;
}
return GenerateFirmware(type);
}
const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath;
Log(Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str());
@ -609,7 +671,10 @@ std::optional<Firmware> LoadFirmware(int type) noexcept
return std::nullopt;
}
CustomizeFirmware(firmware);
if (Config::FirmwareOverrideSettings)
{
CustomizeFirmware(firmware);
}
return firmware;
}
@ -694,7 +759,20 @@ std::optional<DSi_NAND::NANDImage> LoadNAND(const std::array<u8, DSiBIOSSize>& a
return nandImage;
}
constexpr int imgsizes[] = {0, 256, 512, 1024, 2048, 4096};
constexpr u64 imgsizes[] = {0, 256, 512, 1024, 2048, 4096};
std::optional<FATStorageArgs> GetDSiSDCardArgs() noexcept
{
if (!Config::DSiSDEnable)
return std::nullopt;
return FATStorageArgs {
Config::DSiSDPath,
imgsizes[Config::DSiSDSize],
Config::DSiSDReadOnly,
Config::DSiSDFolderSync ? std::make_optional(Config::DSiSDFolderPath) : std::nullopt
};
}
std::optional<FATStorage> LoadDSiSDCard() noexcept
{
if (!Config::DSiSDEnable)
@ -704,97 +782,34 @@ std::optional<FATStorage> LoadDSiSDCard() noexcept
Config::DSiSDPath,
imgsizes[Config::DSiSDSize],
Config::DSiSDReadOnly,
Config::DSiSDFolderSync ? Config::DSiSDFolderPath : ""
Config::DSiSDFolderSync ? std::make_optional(Config::DSiSDFolderPath) : std::nullopt
);
}
void LoadBIOSFiles(NDS& nds)
std::optional<FATStorageArgs> GetDLDISDCardArgs() noexcept
{
if (Config::ExternalBIOSEnable)
{
if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS9Path, FileMode::Read))
{
FileRewind(f);
FileRead(nds.ARM9BIOS, sizeof(NDS::ARM9BIOS), 1, f);
if (!Config::DLDIEnable)
return std::nullopt;
Log(LogLevel::Info, "ARM9 BIOS loaded from %s\n", Config::BIOS9Path.c_str());
Platform::CloseFile(f);
}
else
{
Log(LogLevel::Warn, "ARM9 BIOS not found\n");
return FATStorageArgs{
Config::DLDISDPath,
imgsizes[Config::DLDISize],
Config::DLDIReadOnly,
Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt
};
}
for (int i = 0; i < 16; i++)
((u32*)nds.ARM9BIOS)[i] = 0xE7FFDEFF;
}
std::optional<FATStorage> LoadDLDISDCard() noexcept
{
if (!Config::DLDIEnable)
return std::nullopt;
if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS7Path, FileMode::Read))
{
FileRead(nds.ARM7BIOS, sizeof(NDS::ARM7BIOS), 1, f);
Log(LogLevel::Info, "ARM7 BIOS loaded from\n", Config::BIOS7Path.c_str());
Platform::CloseFile(f);
}
else
{
Log(LogLevel::Warn, "ARM7 BIOS not found\n");
for (int i = 0; i < 16; i++)
((u32*)nds.ARM7BIOS)[i] = 0xE7FFDEFF;
}
}
else
{
Log(LogLevel::Info, "Using built-in ARM7 and ARM9 BIOSes\n");
memcpy(nds.ARM9BIOS, bios_arm9_bin, sizeof(bios_arm9_bin));
memcpy(nds.ARM7BIOS, bios_arm7_bin, sizeof(bios_arm7_bin));
}
if (Config::ConsoleType == 1)
{
DSi& dsi = static_cast<DSi&>(nds);
if (FileHandle* f = Platform::OpenLocalFile(Config::DSiBIOS9Path, FileMode::Read))
{
FileRead(dsi.ARM9iBIOS, sizeof(DSi::ARM9iBIOS), 1, f);
Log(LogLevel::Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str());
Platform::CloseFile(f);
}
else
{
Log(LogLevel::Warn, "ARM9i BIOS not found\n");
for (int i = 0; i < 16; i++)
((u32*)dsi.ARM9iBIOS)[i] = 0xE7FFDEFF;
}
if (FileHandle* f = Platform::OpenLocalFile(Config::DSiBIOS7Path, FileMode::Read))
{
// TODO: check if the first 32 bytes are crapoed
FileRead(dsi.ARM7iBIOS, sizeof(DSi::ARM7iBIOS), 1, f);
Log(LogLevel::Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str());
CloseFile(f);
}
else
{
Log(LogLevel::Warn, "ARM7i BIOS not found\n");
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
}
}
return FATStorage(
Config::DLDISDPath,
imgsizes[Config::DLDISize],
Config::DLDIReadOnly,
Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt
);
}
void EnableCheats(NDS& nds, bool enable)
@ -835,16 +850,10 @@ void SetDateTime(NDS& nds)
void Reset(EmuThread* thread)
{
thread->RecreateConsole();
thread->UpdateConsole(Keep {}, Keep {});
if (Config::ConsoleType == 1) EjectGBACart(*thread->NDS);
LoadBIOSFiles(*thread->NDS);
InstallFirmware(*thread->NDS);
if (Config::ConsoleType == 1)
{
InstallNAND(static_cast<DSi&>(*thread->NDS));
}
thread->NDS->Reset();
SetBatteryLevels(*thread->NDS);
SetDateTime(*thread->NDS);
@ -867,6 +876,7 @@ void Reset(EmuThread* thread)
GBASave->SetPath(newsave, false);
}
InitFirmwareSaveManager(thread);
if (FirmwareSave)
{
std::string oldsave = FirmwareSave->GetPath();
@ -899,36 +909,25 @@ void Reset(EmuThread* thread)
}
bool LoadBIOS(EmuThread* thread)
bool BootToMenu(EmuThread* thread)
{
thread->RecreateConsole();
LoadBIOSFiles(*thread->NDS);
if (!InstallFirmware(*thread->NDS))
return false;
if (Config::ConsoleType == 1 && !InstallNAND(static_cast<DSi&>(*thread->NDS)))
// Keep whatever cart is in the console, if any.
if (!thread->UpdateConsole(Keep {}, Keep {}))
// Try to update the console, but keep the existing cart. If that fails...
return false;
// BIOS and firmware files are loaded, patched, and installed in UpdateConsole
if (thread->NDS->NeedsDirectBoot())
return false;
/*if (NDSSave) delete NDSSave;
NDSSave = nullptr;
CartType = -1;
BaseROMDir = "";
BaseROMName = "";
BaseAssetName = "";*/
InitFirmwareSaveManager(thread);
thread->NDS->Reset();
SetBatteryLevels(*thread->NDS);
SetDateTime(*thread->NDS);
return true;
}
u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
u32 DecompressROM(const u8* inContent, const u32 inSize, unique_ptr<u8[]>& outContent)
{
u64 realSize = ZSTD_getFrameContentSize(inContent, inSize);
const u32 maxSize = 0x40000000;
@ -940,16 +939,15 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
if (realSize != ZSTD_CONTENTSIZE_UNKNOWN)
{
u8* realContent = new u8[realSize];
u64 decompressed = ZSTD_decompress(realContent, realSize, inContent, inSize);
outContent = make_unique<u8[]>(realSize);
u64 decompressed = ZSTD_decompress(outContent.get(), realSize, inContent, inSize);
if (ZSTD_isError(decompressed))
{
delete[] realContent;
outContent = nullptr;
return 0;
}
*outContent = realContent;
return realSize;
}
else
@ -1005,8 +1003,8 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent)
} while (inBuf.pos < inBuf.size);
ZSTD_freeDStream(dStream);
*outContent = new u8[outBuf.pos];
memcpy(*outContent, outBuf.dst, outBuf.pos);
outContent = make_unique<u8[]>(outBuf.pos);
memcpy(outContent.get(), outBuf.dst, outBuf.pos);
ZSTD_freeDStream(dStream);
free(outBuf.dst);
@ -1023,42 +1021,6 @@ void ClearBackupState()
}
}
// We want both the firmware object and the path that was used to load it,
// since we'll need to give it to the save manager later
pair<unique_ptr<Firmware>, string> LoadFirmwareFromFile()
{
string loadedpath;
unique_ptr<Firmware> firmware = nullptr;
string firmwarepath = Config::ConsoleType == 0 ? Config::FirmwarePath : Config::DSiFirmwarePath;
Log(LogLevel::Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str());
string firmwareinstancepath = firmwarepath + Platform::InstanceFileSuffix();
loadedpath = firmwareinstancepath;
FileHandle* f = Platform::OpenLocalFile(firmwareinstancepath, FileMode::Read);
if (!f)
{
loadedpath = firmwarepath;
f = Platform::OpenLocalFile(firmwarepath, FileMode::Read);
}
if (f)
{
firmware = make_unique<Firmware>(f);
if (!firmware->Buffer())
{
Log(LogLevel::Warn, "Couldn't read firmware file!\n");
firmware = nullptr;
loadedpath = "";
}
CloseFile(f);
}
return std::make_pair(std::move(firmware), loadedpath);
}
pair<unique_ptr<Firmware>, string> GenerateDefaultFirmware()
{
// Construct the default firmware...
@ -1068,7 +1030,7 @@ pair<unique_ptr<Firmware>, string> GenerateDefaultFirmware()
// Try to open the instanced Wi-fi settings, falling back to the regular Wi-fi settings if they don't exist.
// We don't need to save the whole firmware, just the part that may actually change.
std::string wfcsettingspath = Platform::GetConfigString(ConfigEntry::WifiSettingsPath);
std::string wfcsettingspath = Config::WifiSettingsPath;
settingspath = wfcsettingspath + Platform::InstanceFileSuffix();
FileHandle* f = Platform::OpenLocalFile(settingspath, FileMode::Read);
if (!f)
@ -1201,155 +1163,12 @@ void CustomizeFirmware(Firmware& firmware) noexcept
firmware.UpdateChecksums();
}
static Platform::FileHandle* OpenNANDFile() noexcept
{
std::string nandpath = Config::DSiNANDPath;
std::string instnand = nandpath + Platform::InstanceFileSuffix();
FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting);
if ((!nandfile) && (Platform::InstanceID() > 0))
{
FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read);
if (!orig)
{
Log(LogLevel::Error, "Failed to open DSi NAND from %s\n", nandpath.c_str());
return nullptr;
}
QFile::copy(QString::fromStdString(nandpath), QString::fromStdString(instnand));
nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting);
}
return nandfile;
}
bool InstallNAND(DSi& dsi)
{
Platform::FileHandle* nandfile = OpenNANDFile();
if (!nandfile)
return false;
DSi_NAND::NANDImage nandImage(nandfile, &dsi.ARM7iBIOS[0x8308]);
if (!nandImage)
{
Log(LogLevel::Error, "Failed to parse DSi NAND\n");
return false;
}
// scoped so that mount isn't alive when we move the NAND image to DSi::NANDImage
{
auto mount = DSi_NAND::NANDMount(nandImage);
if (!mount)
{
Log(LogLevel::Error, "Failed to mount DSi NAND\n");
return false;
}
DSi_NAND::DSiFirmwareSystemSettings settings {};
if (!mount.ReadUserData(settings))
{
Log(LogLevel::Error, "Failed to read DSi NAND user data\n");
return false;
}
// override user settings, if needed
if (Config::FirmwareOverrideSettings)
{
// we store relevant strings as UTF-8, so we need to convert them to UTF-16
auto converter = wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{};
// setting up username
std::u16string username = converter.from_bytes(Config::FirmwareUsername);
size_t usernameLength = std::min(username.length(), (size_t) 10);
memset(&settings.Nickname, 0, sizeof(settings.Nickname));
memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t));
// setting language
settings.Language = static_cast<Firmware::Language>(Config::FirmwareLanguage);
// setting up color
settings.FavoriteColor = Config::FirmwareFavouriteColour;
// setting up birthday
settings.BirthdayMonth = Config::FirmwareBirthdayMonth;
settings.BirthdayDay = Config::FirmwareBirthdayDay;
// setup message
std::u16string message = converter.from_bytes(Config::FirmwareMessage);
size_t messageLength = std::min(message.length(), (size_t) 26);
memset(&settings.Message, 0, sizeof(settings.Message));
memcpy(&settings.Message, message.data(), messageLength * sizeof(char16_t));
// TODO: make other items configurable?
}
// fix touchscreen coords
settings.TouchCalibrationADC1 = {0, 0};
settings.TouchCalibrationPixel1 = {0, 0};
settings.TouchCalibrationADC2 = {255 << 4, 191 << 4};
settings.TouchCalibrationPixel2 = {255, 191};
settings.UpdateHash();
if (!mount.ApplyUserData(settings))
{
Log(LogLevel::Error, "Failed to write patched DSi NAND user data\n");
return false;
}
}
dsi.NANDImage = std::make_unique<DSi_NAND::NANDImage>(std::move(nandImage));
return true;
}
bool InstallFirmware(NDS& nds)
{
FirmwareSave.reset();
unique_ptr<Firmware> firmware;
string firmwarepath;
bool generated = false;
if (Config::ExternalBIOSEnable)
{ // If we want to try loading a firmware dump...
tie(firmware, firmwarepath) = LoadFirmwareFromFile();
if (!firmware)
{ // Try to load the configured firmware dump. If that fails...
Log(LogLevel::Warn, "Firmware not found! Generating default firmware.\n");
}
}
if (!firmware)
{ // If we haven't yet loaded firmware (either because the load failed or we want to use the default...)
tie(firmware, firmwarepath) = GenerateDefaultFirmware();
}
if (!firmware)
return false;
if (Config::FirmwareOverrideSettings)
{
CustomizeFirmware(*firmware);
}
FirmwareSave = std::make_unique<SaveManager>(firmwarepath);
return nds.SPI.GetFirmwareMem()->InstallFirmware(std::move(firmware));
}
bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
// Loads ROM data without parsing it. Works for GBA and NDS ROMs.
bool LoadROMData(const QStringList& filepath, std::unique_ptr<u8[]>& filedata, u32& filelen, string& basepath, string& romname) noexcept
{
if (filepath.empty()) return false;
u8* filedata = nullptr;
u32 filelen;
std::string basepath;
std::string romname;
int num = filepath.count();
if (num == 1)
if (int num = filepath.count(); num == 1)
{
// regular file
@ -1361,38 +1180,35 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
if (len > 0x40000000)
{
Platform::CloseFile(f);
delete[] filedata;
return false;
}
Platform::FileRewind(f);
filedata = new u8[len];
size_t nread = Platform::FileRead(filedata, (size_t)len, 1, f);
filedata = make_unique<u8[]>(len);
size_t nread = Platform::FileRead(filedata.get(), (size_t)len, 1, f);
Platform::CloseFile(f);
if (nread != 1)
{
Platform::CloseFile(f);
delete[] filedata;
filedata = nullptr;
return false;
}
Platform::CloseFile(f);
filelen = (u32)len;
if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst")
{
u8* outContent = nullptr;
u32 decompressed = DecompressROM(filedata, len, &outContent);
filelen = DecompressROM(filedata.get(), len, filedata);
if (decompressed > 0)
if (filelen > 0)
{
delete[] filedata;
filedata = outContent;
filelen = decompressed;
filename = filename.substr(0, filename.length() - 4);
}
else
{
delete[] filedata;
filedata = nullptr;
filelen = 0;
basepath = "";
romname = "";
return false;
}
}
@ -1400,19 +1216,21 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
int pos = LastSep(filename);
if(pos != -1)
basepath = filename.substr(0, pos);
romname = filename.substr(pos+1);
return true;
}
#ifdef ARCHIVE_SUPPORT_ENABLED
else if (num == 2)
{
// file inside archive
s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), filedata, &filelen);
if (lenread < 0) return false;
if (!filedata) return false;
if (lenread != filelen)
{
delete[] filedata;
filedata = nullptr;
return false;
}
@ -1421,38 +1239,31 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
std::string std_romname = filepath.at(1).toStdString();
romname = std_romname.substr(LastSep(std_romname)+1);
return true;
}
#endif
else
return false;
}
bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
{
unique_ptr<u8[]> filedata = nullptr;
u32 filelen;
std::string basepath;
std::string romname;
if (!LoadROMData(filepath, filedata, filelen, basepath, romname))
return false;
if (NDSSave) delete NDSSave;
NDSSave = nullptr;
BaseROMDir = basepath;
BaseROMName = romname;
BaseAssetName = romname.substr(0, romname.rfind('.'));
emuthread->RecreateConsole();
if (!InstallFirmware(*emuthread->NDS))
{
return false;
}
if (reset)
{
emuthread->NDS->EjectCart();
LoadBIOSFiles(*emuthread->NDS);
if (Config::ConsoleType == 1)
InstallNAND(static_cast<DSi&>(*emuthread->NDS));
emuthread->NDS->Reset();
SetBatteryLevels(*emuthread->NDS);
SetDateTime(*emuthread->NDS);
}
u32 savelen = 0;
u8* savedata = nullptr;
std::unique_ptr<u8[]> savedata = nullptr;
std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav");
std::string origsav = savname;
@ -1465,36 +1276,56 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset)
savelen = (u32)Platform::FileLength(sav);
FileRewind(sav);
savedata = new u8[savelen];
FileRead(savedata, savelen, 1, sav);
savedata = std::make_unique<u8[]>(savelen);
FileRead(savedata.get(), savelen, 1, sav);
CloseFile(sav);
}
bool res = emuthread->NDS->LoadCart(filedata, filelen, savedata, savelen);
if (res && reset)
NDSCart::NDSCartArgs cartargs {
// Don't load the SD card itself yet, because we don't know if
// the ROM is homebrew or not.
// So this is the card we *would* load if the ROM were homebrew.
.SDCard = GetDLDISDCardArgs(),
.SRAM = std::make_pair(std::move(savedata), savelen),
};
auto cart = NDSCart::ParseROM(std::move(filedata), filelen, std::move(cartargs));
if (!cart)
// If we couldn't parse the ROM...
return false;
if (reset)
{
if (!emuthread->UpdateConsole(std::move(cart), Keep {}))
return false;
InitFirmwareSaveManager(emuthread);
emuthread->NDS->Reset();
if (Config::DirectBoot || emuthread->NDS->NeedsDirectBoot())
{
{ // If direct boot is enabled or forced...
emuthread->NDS->SetupDirectBoot(romname);
}
}
if (res)
SetBatteryLevels(*emuthread->NDS);
SetDateTime(*emuthread->NDS);
}
else
{
CartType = 0;
NDSSave = new SaveManager(savname);
LoadCheats(*emuthread->NDS);
assert(emuthread->NDS != nullptr);
emuthread->NDS->SetNDSCart(std::move(cart));
}
if (savedata) delete[] savedata;
delete[] filedata;
return res;
CartType = 0;
NDSSave = std::make_unique<SaveManager>(savname);
LoadCheats(*emuthread->NDS);
return true;
}
void EjectCart(NDS& nds)
{
if (NDSSave) delete NDSSave;
NDSSave = nullptr;
UnloadCheats(nds);
@ -1529,92 +1360,16 @@ QString CartLabel()
bool LoadGBAROM(NDS& nds, QStringList filepath)
{
if (Config::ConsoleType == 1) return false;
if (filepath.empty()) return false;
if (nds.ConsoleType == 1) return false; // DSi doesn't have a GBA slot
u8* filedata;
unique_ptr<u8[]> filedata = nullptr;
u32 filelen;
std::string basepath;
std::string romname;
int num = filepath.count();
if (num == 1)
{
// regular file
std::string filename = filepath.at(0).toStdString();
FileHandle* f = Platform::OpenFile(filename, FileMode::Read);
if (!f) return false;
long len = FileLength(f);
if (len > 0x40000000)
{
CloseFile(f);
return false;
}
FileRewind(f);
filedata = new u8[len];
size_t nread = FileRead(filedata, (size_t)len, 1, f);
if (nread != 1)
{
CloseFile(f);
delete[] filedata;
return false;
}
CloseFile(f);
filelen = (u32)len;
if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst")
{
u8* outContent = nullptr;
u32 decompressed = DecompressROM(filedata, len, &outContent);
if (decompressed > 0)
{
delete[] filedata;
filedata = outContent;
filelen = decompressed;
filename = filename.substr(0, filename.length() - 4);
}
else
{
delete[] filedata;
return false;
}
}
int pos = LastSep(filename);
basepath = filename.substr(0, pos);
romname = filename.substr(pos+1);
}
#ifdef ARCHIVE_SUPPORT_ENABLED
else if (num == 2)
{
// file inside archive
s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen);
if (lenread < 0) return false;
if (!filedata) return false;
if (lenread != filelen)
{
delete[] filedata;
return false;
}
std::string std_archivepath = filepath.at(0).toStdString();
basepath = std_archivepath.substr(0, LastSep(std_archivepath));
std::string std_romname = filepath.at(1).toStdString();
romname = std_romname.substr(LastSep(std_romname)+1);
}
#endif
else
if (!LoadROMData(filepath, filedata, filelen, basepath, romname))
return false;
if (GBASave) delete GBASave;
GBASave = nullptr;
BaseGBAROMDir = basepath;
@ -1622,7 +1377,7 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
BaseGBAAssetName = romname.substr(0, romname.rfind('.'));
u32 savelen = 0;
u8* savedata = nullptr;
std::unique_ptr<u8[]> savedata = nullptr;
std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav");
std::string origsav = savname;
@ -1634,30 +1389,29 @@ bool LoadGBAROM(NDS& nds, QStringList filepath)
{
savelen = (u32)FileLength(sav);
FileRewind(sav);
savedata = new u8[savelen];
FileRead(savedata, savelen, 1, sav);
if (savelen > 0)
{
FileRewind(sav);
savedata = std::make_unique<u8[]>(savelen);
FileRead(savedata.get(), savelen, 1, sav);
}
CloseFile(sav);
}
bool res = nds.LoadGBACart(filedata, filelen, savedata, savelen);
auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen);
if (!cart)
return false;
if (res)
{
GBACartType = 0;
GBASave = new SaveManager(savname);
}
if (savedata) delete[] savedata;
delete[] filedata;
return res;
nds.SetGBACart(std::move(cart));
GBACartType = 0;
GBASave = std::make_unique<SaveManager>(savname);
return true;
}
void LoadGBAAddon(NDS& nds, int type)
{
if (Config::ConsoleType == 1) return;
if (GBASave) delete GBASave;
GBASave = nullptr;
nds.LoadGBAAddon(type);
@ -1670,7 +1424,6 @@ void LoadGBAAddon(NDS& nds, int type)
void EjectGBACart(NDS& nds)
{
if (GBASave) delete GBASave;
GBASave = nullptr;
nds.EjectGBACart();

View File

@ -35,34 +35,43 @@ namespace melonDS
class NDS;
class DSi;
class FATStorage;
class FATStorageArgs;
}
class EmuThread;
namespace ROMManager
{
using namespace melonDS;
extern SaveManager* NDSSave;
extern SaveManager* GBASave;
extern std::unique_ptr<SaveManager> NDSSave;
extern std::unique_ptr<SaveManager> GBASave;
extern std::unique_ptr<SaveManager> FirmwareSave;
QString VerifySetup();
void Reset(EmuThread* thread);
bool LoadBIOS(EmuThread* thread);
/// Boots the emulated console into its system menu without starting a game.
bool BootToMenu(EmuThread* thread);
void ClearBackupState();
/// Returns the configured ARM9 BIOS loaded from disk,
/// the FreeBIOS if external BIOS is disabled and we're in NDS mode,
/// or nullopt if loading failed.
std::optional<std::array<u8, ARM9BIOSSize>> LoadARM9BIOS() noexcept;
std::optional<std::array<u8, ARM7BIOSSize>> LoadARM7BIOS() noexcept;
std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM9BIOS() noexcept;
std::optional<std::array<u8, DSiBIOSSize>> LoadDSiARM7BIOS() noexcept;
std::optional<FATStorageArgs> GetDSiSDCardArgs() noexcept;
std::optional<FATStorage> LoadDSiSDCard() noexcept;
std::optional<FATStorageArgs> GetDLDISDCardArgs() noexcept;
std::optional<FATStorage> LoadDLDISDCard() noexcept;
void CustomizeFirmware(Firmware& firmware) noexcept;
Firmware GenerateFirmware(int type) noexcept;
/// Loads and customizes a firmware image based on the values in Config
std::optional<Firmware> LoadFirmware(int type) noexcept;
/// Loads and customizes a NAND image based on the values in Config
std::optional<DSi_NAND::NANDImage> LoadNAND(const std::array<u8, DSiBIOSSize>& arm7ibios) noexcept;
bool InstallFirmware(NDS& nds);
bool InstallNAND(DSi& dsi);
/// Inserts a ROM into the emulated console.
bool LoadROM(EmuThread*, QStringList filepath, bool reset);
void EjectCart(NDS& nds);
bool CartInserted();

View File

@ -81,6 +81,7 @@
#include "FrontendUtil.h"
#include "OSD.h"
#include "Args.h"
#include "NDS.h"
#include "NDSCart.h"
#include "GBACart.h"
@ -204,29 +205,164 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent)
static_cast<ScreenPanelGL*>(mainWindow->panel)->transferLayout(this);
}
std::unique_ptr<NDS> EmuThread::CreateConsole()
std::unique_ptr<NDS> EmuThread::CreateConsole(
std::unique_ptr<melonDS::NDSCart::CartCommon>&& ndscart,
std::unique_ptr<melonDS::GBACart::CartCommon>&& gbacart
) noexcept
{
auto arm7bios = ROMManager::LoadARM7BIOS();
if (!arm7bios)
return nullptr;
auto arm9bios = ROMManager::LoadARM9BIOS();
if (!arm9bios)
return nullptr;
auto firmware = ROMManager::LoadFirmware(Config::ConsoleType);
if (!firmware)
return nullptr;
NDSArgs ndsargs {
std::move(ndscart),
std::move(gbacart),
*arm9bios,
*arm7bios,
std::move(*firmware),
};
if (Config::ConsoleType == 1)
{
return std::make_unique<melonDS::DSi>();
auto arm7ibios = ROMManager::LoadDSiARM7BIOS();
if (!arm7ibios)
return nullptr;
auto arm9ibios = ROMManager::LoadDSiARM9BIOS();
if (!arm9ibios)
return nullptr;
auto nand = ROMManager::LoadNAND(*arm7ibios);
if (!nand)
return nullptr;
auto sdcard = ROMManager::LoadDSiSDCard();
DSiArgs args {
std::move(ndsargs),
*arm9ibios,
*arm7ibios,
std::move(*nand),
std::move(sdcard),
};
args.GBAROM = nullptr;
return std::make_unique<melonDS::DSi>(std::move(args));
}
return std::make_unique<melonDS::NDS>();
return std::make_unique<melonDS::NDS>(std::move(ndsargs));
}
void EmuThread::RecreateConsole()
bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept
{
if (!NDS || NDS->ConsoleType != Config::ConsoleType)
// Let's get the cart we want to use;
// if we wnat to keep the cart, we'll eject it from the existing console first.
std::unique_ptr<NDSCart::CartCommon> nextndscart;
if (std::holds_alternative<Keep>(ndsargs))
{ // If we want to keep the existing cart (if any)...
nextndscart = NDS ? NDS->EjectCart() : nullptr;
ndsargs = {};
}
else if (const auto ptr = std::get_if<std::unique_ptr<NDSCart::CartCommon>>(&ndsargs))
{
NDS = nullptr; // To ensure the destructor is called before a new one is created
nextndscart = std::move(*ptr);
ndsargs = {};
}
if (nextndscart && nextndscart->Type() == NDSCart::Homebrew)
{
// Load DLDISDCard will return nullopt if the SD card is disabled;
// SetSDCard will accept nullopt, which means no SD card
auto& homebrew = static_cast<NDSCart::CartHomebrew&>(*nextndscart);
homebrew.SetSDCard(ROMManager::LoadDLDISDCard());
}
std::unique_ptr<GBACart::CartCommon> nextgbacart;
if (std::holds_alternative<Keep>(gbaargs))
{
nextgbacart = NDS ? NDS->EjectGBACart() : nullptr;
}
else if (const auto ptr = std::get_if<std::unique_ptr<GBACart::CartCommon>>(&gbaargs))
{
nextgbacart = std::move(*ptr);
gbaargs = {};
}
if (!NDS || NDS->ConsoleType != Config::ConsoleType)
{ // If we're switching between DS and DSi mode, or there's no console...
// To ensure the destructor is called before a new one is created,
// as the presence of global signal handlers still complicates things a bit
NDS = nullptr;
NDS::Current = nullptr;
NDS = CreateConsole();
// TODO: Insert ROMs
NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart));
NDS::Current = NDS.get();
}
}
return NDS != nullptr;
}
auto arm9bios = ROMManager::LoadARM9BIOS();
if (!arm9bios)
return false;
auto arm7bios = ROMManager::LoadARM7BIOS();
if (!arm7bios)
return false;
auto firmware = ROMManager::LoadFirmware(NDS->ConsoleType);
if (!firmware)
return false;
if (NDS->ConsoleType == 1)
{ // If the console we're updating is a DSi...
DSi& dsi = static_cast<DSi&>(*NDS);
auto arm9ibios = ROMManager::LoadDSiARM9BIOS();
if (!arm9ibios)
return false;
auto arm7ibios = ROMManager::LoadDSiARM7BIOS();
if (!arm7ibios)
return false;
auto nandimage = ROMManager::LoadNAND(*arm7ibios);
if (!nandimage)
return false;
auto dsisdcard = ROMManager::LoadDSiSDCard();
dsi.ARM7iBIOS = *arm7ibios;
dsi.ARM9iBIOS = *arm9ibios;
dsi.SetNAND(std::move(*nandimage));
dsi.SetSDCard(std::move(dsisdcard));
// We're moving the optional, not the card
// (inserting std::nullopt here is okay, it means no card)
dsi.EjectGBACart();
}
if (NDS->ConsoleType == 0)
{
NDS->SetGBACart(std::move(nextgbacart));
}
NDS->ARM7BIOS = *arm7bios;
NDS->ARM9BIOS = *arm9bios;
NDS->SetFirmware(std::move(*firmware));
NDS->SetNDSCart(std::move(nextndscart));
NDS::Current = NDS.get();
return true;
}
void EmuThread::updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix)
{
@ -343,7 +479,8 @@ void EmuThread::run()
u32 mainScreenPos[3];
Platform::FileHandle* file;
RecreateConsole();
UpdateConsole(nullptr, nullptr);
// No carts are inserted when melonDS first boots
mainScreenPos[0] = 0;
mainScreenPos[1] = 0;
@ -2507,7 +2644,7 @@ void MainWindow::onBootFirmware()
return;
}
if (!ROMManager::LoadBIOS(emuThread))
if (!ROMManager::BootToMenu(emuThread))
{
// TODO: better error reporting?
QMessageBox::critical(this, "melonDS", "This firmware is not bootable.");
@ -2750,13 +2887,12 @@ void MainWindow::onImportSavefile()
u32 len = FileLength(f);
u8* data = new u8[len];
std::unique_ptr<u8[]> data = std::make_unique<u8[]>(len);
Platform::FileRewind(f);
Platform::FileRead(data, len, 1, f);
Platform::FileRead(data.get(), len, 1, f);
assert(emuThread->NDS != nullptr);
emuThread->NDS->LoadSave(data, len);
delete[] data;
emuThread->NDS->SetNDSSave(data.get(), len);
CloseFile(f);
emuThread->emuUnpause();

View File

@ -34,12 +34,18 @@
#include <QCloseEvent>
#include <atomic>
#include <variant>
#include <optional>
#include "FrontendUtil.h"
#include "duckstation/gl/context.h"
#include "NDSCart.h"
#include "GBACart.h"
using Keep = std::monostate;
using UpdateConsoleNDSArgs = std::variant<Keep, std::unique_ptr<melonDS::NDSCart::CartCommon>>;
using UpdateConsoleGBAArgs = std::variant<Keep, std::unique_ptr<melonDS::GBACart::CartCommon>>;
namespace melonDS
{
class NDS;
@ -72,7 +78,13 @@ public:
QMutex FrontBufferLock;
void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix);
void RecreateConsole();
/// Applies the config in args.
/// Creates a new NDS console if needed,
/// modifies the existing one if possible.
/// @return \c true if the console was updated.
/// If this returns \c false, then the existing NDS console is not modified.
bool UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept;
std::unique_ptr<melonDS::NDS> NDS; // TODO: Proper encapsulation and synchronization
signals:
void windowUpdate();
@ -96,7 +108,10 @@ signals:
void syncVolumeLevel();
private:
std::unique_ptr<melonDS::NDS> CreateConsole();
std::unique_ptr<melonDS::NDS> CreateConsole(
std::unique_ptr<melonDS::NDSCart::CartCommon>&& ndscart,
std::unique_ptr<melonDS::GBACart::CartCommon>&& gbacart
) noexcept;
void drawScreenGL();
void initOpenGL();
void deinitOpenGL();