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