aboutsummaryrefslogtreecommitdiff
path: root/src/NDSCart.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/NDSCart.h')
-rw-r--r--src/NDSCart.h230
1 files changed, 130 insertions, 100 deletions
diff --git a/src/NDSCart.h b/src/NDSCart.h
index 24f9f9e..43bf1fc 100644
--- a/src/NDSCart.h
+++ b/src/NDSCart.h
@@ -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; }
-
- virtual void Reset() override;
+ void Reset() override;
- virtual void DoSavestate(Savestate* file) override;
+ void DoSavestate(Savestate* file) override;
- virtual void SetupSave(u32 type) override;
- virtual void LoadSave(const u8* savedata, u32 savelen) override;
+ void SetSaveMemory(const u8* savedata, u32 savelen) override;
- virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override;
+ int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override;
- virtual u8 SPIWrite(u8 val, u32 pos, bool last) override;
+ 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�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;
-
- /// 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 SetCart(std::unique_ptr<CartCommon>&& cart) noexcept;
+ [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); }
+ [[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); }
+
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