Refactor the core's handling of firmware and BIOS images to rely less on the file system (#1826)

* Introduce firmware-related structs

* Fix some indents

* Move the generated firmware identifier to a constant

* Document the WifiAccessPoint constructors

* Add some constants

* Remove a stray comment

* Implement Firmware::UserData

* Add Firmware::Mask

* Document Firmware::Buffer

* Add a Firmware constructor that uses a FileHandle

* Set the default username in UserData

* Update the UserData checksum

* Forgot to include Platform.h

* Remove some redundant assignments in the default Firmware constructor

* const-ify CRC16

* Replace the plain Firmware buffer with a Firmware object

- Remove some functions that were reimplemented in the Firmware constructors

* Fix some crashes due to undefined behavior

* Fix the user data initialization

- Initialize both user data objects to default
- Set both user data objects to the same touch screen calibration

* Follow the DS logic in determining which user data section is current

* Remove an unneeded include

* Remove another unneeded include

* Initialize FirmwareMask in Firmware::Firmware

* Use the DEFAULT_SSID constant

* Add SPI_Firmware::InstallFirmware and SPI_Firmware::RemoveFirmware

* Move a logging call to after the file is written

* Add a SaveManager for the firmware

* Touch up the SPI_Firmware::Firmware declaration

* Move all firmware loading and customization to the frontend

* Call Platform::WriteFirmware when it's time to write the firmware back to disk

* Fix some missing stuff

* Remove the Get* functions from SPI_Firmware in favor of GetFirmware()

* Implement SPI_Firmware::DeInit in terms of RemoveFirmware

* Add Firmware::UpdateChecksums

* Fix an incorrect length

* Update all checksums in the firmware after setting touch screen calibration data

* Use the Firmware object's Position methods

* Remove register fields from the Firmware object

* Install the firmware before seeing if direct boot is necessary

* Install the firmware before calling NDS::Reset in LoadROM

* Slight cleanup in ROMManager

* Fix the default access point name

* Shorten the various getters in Firmware

* Add qualifiers for certain uses of firmware types

- GCC can get picky if -fpermissive isn't defined

* Add an InstallFirmware overload that takes a unique_ptr

* Log when firmware is added or removed

* Don't clear the firmware in SPI_Firmware::Init

- The original code didn't, it just set the pointer to nullptr

* Fix a typo

* Write back the full firmware if it's not generated

* Move the FreeBIOS to an external file

* Load wfcsettings.bin into the correct part of the generated firmware blob

* Load BIOS files in the frontend, not in the core

* Fix logging the firmware ID

* Add some utility functions

* Mark Firmware's constructors as explicit

* Remove obsolete ConfigEntry values

* Include <locale> explicitly in ROMManager

* Fix up some includes

* Add Firmware::IsBootable()

* Add a newline to a log entry

- Whoops

* Log the number of bytes written out in SaveManager

* Mark FirmwareHeader's constructor as explicit

* Clean up GenerateDefaultFirmware and LoadFirmwareFromFile

- Now they return a pair instead of two by-ref values

* Refactor SaveManager a little bit

- Manage its buffers as unique_ptrs to mitigate leaks
- Reallocate the internal buffer if SetPath is asked to reload the file (and the new length is different)

* Remove some stray parens

* Fix some firmware-related bugs I introduced

- Firmware settings are now properly saved to disk (beforehand I misunderstood when the firmware blob was written)
- Firmware is no longer overwritten by contents of wfcsettings.bin

* Slight cleanup
This commit is contained in:
Jesse Talavera-Greenberg 2023-09-18 15:09:11 -04:00 committed by GitHub
parent db963aa002
commit 5bfe51e670
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 3259 additions and 2250 deletions

View File

@ -40,9 +40,11 @@ add_library(core STATIC
ROMList.h ROMList.h
ROMList.cpp ROMList.cpp
FreeBIOS.h FreeBIOS.h
FreeBIOS.cpp
RTC.cpp RTC.cpp
Savestate.cpp Savestate.cpp
SPI.cpp SPI.cpp
SPI_Firmware.cpp
SPU.cpp SPU.cpp
types.h types.h
version.h version.h

View File

@ -548,12 +548,12 @@ void SetupDirectBoot()
DSi_NAND::DeInit(); DSi_NAND::DeInit();
} }
u8 nwifiver = SPI_Firmware::GetNWifiVersion(); SPI_Firmware::WifiBoard nwifiver = SPI_Firmware::GetFirmware()->Header().WifiBoard;
ARM9Write8(0x020005E0, nwifiver); ARM9Write8(0x020005E0, static_cast<u8>(nwifiver));
// TODO: these should be taken from the wifi firmware in NAND // TODO: these should be taken from the wifi firmware in NAND
// but, hey, this works too. // but, hey, this works too.
if (nwifiver == 1) if (nwifiver == SPI_Firmware::WifiBoard::W015)
{ {
ARM9Write16(0x020005E2, 0xB57E); ARM9Write16(0x020005E2, 0xB57E);
ARM9Write32(0x020005E4, 0x00500400); ARM9Write32(0x020005E4, 0x00500400);
@ -726,64 +726,6 @@ void SoftReset()
GPU::DispStat[1] |= (1<<6); GPU::DispStat[1] |= (1<<6);
} }
bool LoadBIOS()
{
Platform::FileHandle* f;
u32 i;
memset(ARM9iBIOS, 0, 0x10000);
memset(ARM7iBIOS, 0, 0x10000);
f = Platform::OpenLocalFile(Platform::GetConfigString(Platform::DSi_BIOS9Path), FileMode::Read);
if (!f)
{
Log(LogLevel::Warn, "ARM9i BIOS not found\n");
for (i = 0; i < 16; i++)
((u32*)ARM9iBIOS)[i] = 0xE7FFDEFF;
}
else
{
FileRewind(f);
FileRead(ARM9iBIOS, 0x10000, 1, f);
Log(LogLevel::Info, "ARM9i BIOS loaded\n");
Platform::CloseFile(f);
}
f = Platform::OpenLocalFile(Platform::GetConfigString(Platform::DSi_BIOS7Path), FileMode::Read);
if (!f)
{
Log(LogLevel::Warn, "ARM7i BIOS not found\n");
for (i = 0; i < 16; i++)
((u32*)ARM7iBIOS)[i] = 0xE7FFDEFF;
}
else
{
// TODO: check if the first 32 bytes are crapoed
FileRewind(f);
FileRead(ARM7iBIOS, 0x10000, 1, f);
Log(LogLevel::Info, "ARM7i BIOS loaded\n");
CloseFile(f);
}
if (!Platform::GetConfigBool(Platform::DSi_FullBIOSBoot))
{
// herp
*(u32*)&ARM9iBIOS[0] = 0xEAFFFFFE;
*(u32*)&ARM7iBIOS[0] = 0xEAFFFFFE;
// TODO!!!!
// hax the upper 32K out of the goddamn DSi
// done that :) -pcy
}
return true;
}
bool LoadNAND() bool LoadNAND()
{ {
Log(LogLevel::Info, "Loading DSi NAND\n"); Log(LogLevel::Info, "Loading DSi NAND\n");

View File

@ -65,7 +65,6 @@ void SetCartInserted(bool inserted);
void SetupDirectBoot(); void SetupDirectBoot();
void SoftReset(); void SoftReset();
bool LoadBIOS();
bool LoadNAND(); bool LoadNAND();
void RunNDMAs(u32 cpu); void RunNDMAs(u32 cpu);

View File

@ -147,6 +147,7 @@ DSi_NWifi::~DSi_NWifi()
void DSi_NWifi::Reset() void DSi_NWifi::Reset()
{ {
using namespace SPI_Firmware;
TransferCmd = 0xFFFFFFFF; TransferCmd = 0xFFFFFFFF;
RemSize = 0; RemSize = 0;
@ -163,26 +164,26 @@ void DSi_NWifi::Reset()
for (int i = 0; i < 9; i++) for (int i = 0; i < 9; i++)
Mailbox[i].Clear(); Mailbox[i].Clear();
u8* mac = SPI_Firmware::GetWifiMAC(); MacAddress mac = GetFirmware()->Header().MacAddress;
Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
u8 type = SPI_Firmware::GetNWifiVersion(); WifiBoard type = GetFirmware()->Header().WifiBoard;
switch (type) switch (type)
{ {
case 1: // AR6002 case WifiBoard::W015: // AR6002
ROMID = 0x20000188; ROMID = 0x20000188;
ChipID = 0x02000001; ChipID = 0x02000001;
HostIntAddr = 0x00500400; HostIntAddr = 0x00500400;
break; break;
case 2: // AR6013 case WifiBoard::W024: // AR6013
ROMID = 0x23000024; ROMID = 0x23000024;
ChipID = 0x0D000000; ChipID = 0x0D000000;
HostIntAddr = 0x00520000; HostIntAddr = 0x00520000;
break; break;
case 3: // AR6014 (3DS) case WifiBoard::W028: // AR6014 (3DS)
ROMID = 0x2300006F; ROMID = 0x2300006F;
ChipID = 0x0D000001; ChipID = 0x0D000001;
HostIntAddr = 0x00520000; HostIntAddr = 0x00520000;
@ -190,7 +191,7 @@ void DSi_NWifi::Reset()
break; break;
default: default:
Log(LogLevel::Warn, "NWifi: unknown hardware type, assuming AR6002\n"); Log(LogLevel::Warn, "NWifi: unknown hardware type 0x%x, assuming AR6002\n", static_cast<u8>(type));
ROMID = 0x20000188; ROMID = 0x20000188;
ChipID = 0x02000001; ChipID = 0x02000001;
HostIntAddr = 0x00500400; HostIntAddr = 0x00500400;
@ -201,7 +202,7 @@ void DSi_NWifi::Reset()
*(u32*)&EEPROM[0x000] = 0x300; *(u32*)&EEPROM[0x000] = 0x300;
*(u16*)&EEPROM[0x008] = 0x8348; // TODO: determine properly (country code) *(u16*)&EEPROM[0x008] = 0x8348; // TODO: determine properly (country code)
memcpy(&EEPROM[0x00A], mac, 6); memcpy(&EEPROM[0x00A], mac.data(), mac.size());
*(u32*)&EEPROM[0x010] = 0x60000000; *(u32*)&EEPROM[0x010] = 0x60000000;
memset(&EEPROM[0x03C], 0xFF, 0x70); memset(&EEPROM[0x03C], 0xFF, 0x70);
@ -894,8 +895,9 @@ void DSi_NWifi::HTC_Command()
case 0x0004: // setup complete case 0x0004: // setup complete
{ {
SPI_Firmware::MacAddress mac = SPI_Firmware::GetFirmware()->Header().MacAddress;
u8 ready_evt[12]; u8 ready_evt[12];
memcpy(&ready_evt[0], SPI_Firmware::GetWifiMAC(), 6); memcpy(&ready_evt[0], &mac, mac.size());
ready_evt[6] = 0x02; ready_evt[6] = 0x02;
ready_evt[7] = 0; ready_evt[7] = 0;
*(u32*)&ready_evt[8] = 0x2300006C; *(u32*)&ready_evt[8] = 0x2300006C;

1741
src/FreeBIOS.cpp Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -375,7 +375,7 @@ bool NeedsDirectBoot()
return true; return true;
// DSi/3DS firmwares aren't bootable // DSi/3DS firmwares aren't bootable
if (SPI_Firmware::GetFirmwareLength() == 0x20000) if (!SPI_Firmware::GetFirmware()->IsBootable())
return true; return true;
return false; return false;
@ -524,50 +524,7 @@ void Reset()
RunningGame = false; RunningGame = false;
LastSysClockCycles = 0; LastSysClockCycles = 0;
// DS BIOSes are always loaded, even in DSi mode // BIOS files are now loaded by the frontend
// we need them for DS-compatible mode
if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
{
f = Platform::OpenLocalFile(Platform::GetConfigString(Platform::BIOS9Path), FileMode::Read);
if (!f)
{
Log(LogLevel::Warn, "ARM9 BIOS not found\n");
for (i = 0; i < 16; i++)
((u32*)ARM9BIOS)[i] = 0xE7FFDEFF;
}
else
{
FileRewind(f);
FileRead(ARM9BIOS, 0x1000, 1, f);
Log(LogLevel::Info, "ARM9 BIOS loaded\n");
Platform::CloseFile(f);
}
f = Platform::OpenLocalFile(Platform::GetConfigString(Platform::BIOS7Path), FileMode::Read);
if (!f)
{
Log(LogLevel::Warn, "ARM7 BIOS not found\n");
for (i = 0; i < 16; i++)
((u32*)ARM7BIOS)[i] = 0xE7FFDEFF;
}
else
{
FileRewind(f);
FileRead(ARM7BIOS, 0x4000, 1, f);
Log(LogLevel::Info, "ARM7 BIOS loaded\n");
Platform::CloseFile(f);
}
}
else
{
memcpy(ARM9BIOS, bios_arm9_bin, sizeof(bios_arm9_bin));
memcpy(ARM7BIOS, bios_arm7_bin, sizeof(bios_arm7_bin));
}
#ifdef JIT_ENABLED #ifdef JIT_ENABLED
ARMJIT::Reset(); ARMJIT::Reset();
@ -575,7 +532,7 @@ void Reset()
if (ConsoleType == 1) if (ConsoleType == 1)
{ {
DSi::LoadBIOS(); // BIOS files are now loaded by the frontend
ARM9ClockShift = 2; ARM9ClockShift = 2;
MainRAMMask = 0xFFFFFF; MainRAMMask = 0xFFFFFF;
@ -1046,6 +1003,15 @@ void LoadBIOS()
Reset(); Reset();
} }
bool IsLoadedARM9BIOSBuiltIn()
{
return memcmp(NDS::ARM9BIOS, bios_arm9_bin, sizeof(NDS::ARM9BIOS)) == 0;
}
bool IsLoadedARM7BIOSBuiltIn()
{
return memcmp(NDS::ARM7BIOS, bios_arm7_bin, sizeof(NDS::ARM7BIOS)) == 0;
}
u64 NextTarget() u64 NextTarget()
{ {

View File

@ -240,6 +240,8 @@ void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth,
void SetConsoleType(int type); void SetConsoleType(int type);
void LoadBIOS(); void LoadBIOS();
bool IsLoadedARM9BIOSBuiltIn();
bool IsLoadedARM7BIOSBuiltIn();
bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen);
void LoadSave(const u8* savedata, u32 savelen); void LoadSave(const u8* savedata, u32 savelen);

View File

@ -24,6 +24,11 @@
#include <functional> #include <functional>
#include <string> #include <string>
namespace SPI_Firmware
{
class Firmware;
}
namespace Platform namespace Platform
{ {
@ -102,13 +107,6 @@ enum ConfigEntry
ExternalBIOSEnable, ExternalBIOSEnable,
BIOS9Path,
BIOS7Path,
FirmwarePath,
DSi_BIOS9Path,
DSi_BIOS7Path,
DSi_FirmwarePath,
DSi_NANDPath, DSi_NANDPath,
DLDI_Enable, DLDI_Enable,
@ -125,7 +123,7 @@ enum ConfigEntry
DSiSD_FolderSync, DSiSD_FolderSync,
DSiSD_FolderPath, DSiSD_FolderPath,
Firm_OverrideSettings, Firm_OverrideSettings [[deprecated("Individual fields can now be overridden")]],
Firm_Username, Firm_Username,
Firm_Language, Firm_Language,
Firm_BirthdayMonth, Firm_BirthdayMonth,
@ -333,6 +331,13 @@ void Sleep(u64 usecs);
void WriteNDSSave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen); void WriteNDSSave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen);
void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen); void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen);
/// Called when the firmware needs to be written back to storage,
/// after one of the supported write commands finishes execution.
/// @param firmware The firmware that was just written.
/// @param writeoffset The offset of the first byte that was written to firmware.
/// @param writelen The number of bytes that were written to firmware.
void WriteFirmware(const SPI_Firmware::Firmware& firmware, u32 writeoffset, u32 writelen);
// local multiplayer comm interface // local multiplayer comm interface
// packet type: DS-style TX header (12 bytes) + original 802.11 frame // packet type: DS-style TX header (12 bytes) + original 802.11 frame

View File

@ -19,10 +19,8 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <string> #include <memory>
#include <algorithm> #include <utility>
#include <codecvt>
#include <locale>
#include "NDS.h" #include "NDS.h"
#include "DSi.h" #include "DSi.h"
#include "SPI.h" #include "SPI.h"
@ -34,12 +32,7 @@ using namespace Platform;
namespace SPI_Firmware namespace SPI_Firmware
{ {
std::string FirmwarePath; std::unique_ptr<Firmware> Firmware;
u8* Firmware;
u32 FirmwareLength;
u32 FirmwareMask;
u32 UserSettings;
u32 Hold; u32 Hold;
u8 CurCmd; u8 CurCmd;
@ -49,10 +42,9 @@ u8 Data;
u8 StatusReg; u8 StatusReg;
u32 Addr; u32 Addr;
u16 CRC16(const u8* data, u32 len, u32 start)
u16 CRC16(u8* data, u32 len, u32 start)
{ {
u16 blarg[8] = {0xC0C1, 0xC181, 0xC301, 0xC601, 0xCC01, 0xD801, 0xF001, 0xA001}; constexpr u16 blarg[8] = {0xC0C1, 0xC181, 0xC301, 0xC601, 0xCC01, 0xD801, 0xF001, 0xA001};
for (u32 i = 0; i < len; i++) for (u32 i = 0; i < len; i++)
{ {
@ -75,23 +67,20 @@ u16 CRC16(u8* data, u32 len, u32 start)
bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
{ {
u16 crc_stored = *(u16*)&Firmware[crcoffset]; u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset];
u16 crc_calced = CRC16(&Firmware[offset], len, start); u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start);
return (crc_stored == crc_calced); return (crc_stored == crc_calced);
} }
bool Init() bool Init()
{ {
FirmwarePath = "";
Firmware = nullptr;
return true; return true;
} }
void DeInit() void DeInit()
{ {
if (Firmware) delete[] Firmware; RemoveFirmware();
Firmware = nullptr;
} }
u32 FixFirmwareLength(u32 originalLength) u32 FixFirmwareLength(u32 originalLength)
@ -117,335 +106,43 @@ u32 FixFirmwareLength(u32 originalLength)
return originalLength; return originalLength;
} }
void LoadDefaultFirmware()
{
Log(LogLevel::Debug, "Using default firmware image...\n");
FirmwareLength = 0x20000;
Firmware = new u8[FirmwareLength];
memset(Firmware, 0xFF, FirmwareLength);
FirmwareMask = FirmwareLength - 1;
memset(Firmware, 0, 0x1D);
if (NDS::ConsoleType == 1)
{
Firmware[0x1D] = 0x57; // DSi
Firmware[0x2F] = 0x0F;
Firmware[0x1FD] = 0x01;
Firmware[0x1FE] = 0x20;
Firmware[0x2FF] = 0x80; // boot0: use NAND as stage2 medium
// these need to be zero (part of the stage2 firmware signature!)
memset(&Firmware[0x22], 0, 8);
}
else
{
Firmware[0x1D] = 0x20; // DS Lite (TODO: make configurable?)
Firmware[0x2F] = 0x06;
}
// wifi calibration
const u8 defaultmac[6] = {0x00, 0x09, 0xBF, 0x11, 0x22, 0x33};
const u8 bbinit[0x69] =
{
0x03, 0x17, 0x40, 0x00, 0x1B, 0x6C, 0x48, 0x80, 0x38, 0x00, 0x35, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0xBB, 0x01, 0x24, 0x7F,
0x5A, 0x01, 0x3F, 0x01, 0x3F, 0x36, 0x1D, 0x00, 0x78, 0x35, 0x55, 0x12, 0x34, 0x1C, 0x00, 0x01,
0x0E, 0x38, 0x03, 0x70, 0xC5, 0x2A, 0x0A, 0x08, 0x04, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFE,
0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xF8, 0xF8, 0xF6, 0x00, 0x12, 0x14,
0x12, 0x41, 0x23, 0x03, 0x04, 0x70, 0x35, 0x0E, 0x2C, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x12, 0x28, 0x1C
};
const u8 rfinit[0x29] =
{
0x31, 0x4C, 0x4F, 0x21, 0x00, 0x10, 0xB0, 0x08, 0xFA, 0x15, 0x26, 0xE6, 0xC1, 0x01, 0x0E, 0x50,
0x05, 0x00, 0x6D, 0x12, 0x00, 0x00, 0x01, 0xFF, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x06,
0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00
};
const u8 chandata[0x3C] =
{
0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x16,
0x26, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1E, 0x1F, 0x18,
0x01, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D,
0x02, 0x6C, 0x71, 0x76, 0x5B, 0x40, 0x45, 0x4A, 0x2F, 0x34, 0x39, 0x3E, 0x03, 0x08, 0x14
};
*(u16*)&Firmware[0x2C] = 0x138;
Firmware[0x2E] = 0;
*(u32*)&Firmware[0x30] = 0xFFFFFFFF;
*(u16*)&Firmware[0x34] = 0x00FF;
memcpy(&Firmware[0x36], defaultmac, 6);
*(u16*)&Firmware[0x3C] = 0x3FFE;
*(u16*)&Firmware[0x3E] = 0xFFFF;
Firmware[0x40] = 0x03;
Firmware[0x41] = 0x94;
Firmware[0x42] = 0x29;
Firmware[0x43] = 0x02;
*(u16*)&Firmware[0x44] = 0x0002;
*(u16*)&Firmware[0x46] = 0x0017;
*(u16*)&Firmware[0x48] = 0x0026;
*(u16*)&Firmware[0x4A] = 0x1818;
*(u16*)&Firmware[0x4C] = 0x0048;
*(u16*)&Firmware[0x4E] = 0x4840;
*(u16*)&Firmware[0x50] = 0x0058;
*(u16*)&Firmware[0x52] = 0x0042;
*(u16*)&Firmware[0x54] = 0x0146;
*(u16*)&Firmware[0x56] = 0x8064;
*(u16*)&Firmware[0x58] = 0xE6E6;
*(u16*)&Firmware[0x5A] = 0x2443;
*(u16*)&Firmware[0x5C] = 0x000E;
*(u16*)&Firmware[0x5E] = 0x0001;
*(u16*)&Firmware[0x60] = 0x0001;
*(u16*)&Firmware[0x62] = 0x0402;
memcpy(&Firmware[0x64], bbinit, 0x69);
Firmware[0xCD] = 0;
memcpy(&Firmware[0xCE], rfinit, 0x29);
Firmware[0xF7] = 0x02;
memcpy(&Firmware[0xF8], chandata, 0x3C);
*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
// user data
u32 userdata = 0x7FE00 & FirmwareMask;
*(u16*)&Firmware[0x20] = userdata >> 3;
memset(Firmware + userdata, 0, 0x74);
Firmware[userdata+0x00] = 5; // version
Firmware[userdata+0x03] = 1;
Firmware[userdata+0x04] = 1;
*(u16*)&Firmware[userdata+0x64] = 0x0031;
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
// wifi access points
// TODO: WFC ID??
std::string wfcsettings = Platform::GetConfigString(ConfigEntry::WifiSettingsPath);
FileHandle* f = Platform::OpenLocalFile(wfcsettings + Platform::InstanceFileSuffix(), FileMode::Read);
if (!f) f = Platform::OpenLocalFile(wfcsettings, FileMode::Read);
if (f)
{
u32 apdata = userdata - 0xA00;
FileRead(&Firmware[apdata], 0x900, 1, f);
CloseFile(f);
}
else
{
u32 apdata = userdata - 0x400;
memset(&Firmware[apdata], 0, 0x300);
strcpy((char*)&Firmware[apdata+0x40], "melonAP");
if (NDS::ConsoleType == 1) *(u16*)&Firmware[apdata+0xEA] = 1400;
Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
apdata += 0x100;
Firmware[apdata+0xE7] = 0xFF;
Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
apdata += 0x100;
Firmware[apdata+0xE7] = 0xFF;
Firmware[apdata+0xEF] = 0x01;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
if (NDS::ConsoleType == 1)
{
apdata = userdata - 0xA00;
Firmware[apdata+0xE7] = 0xFF;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
apdata += 0x200;
Firmware[apdata+0xE7] = 0xFF;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
apdata += 0x200;
Firmware[apdata+0xE7] = 0xFF;
*(u16*)&Firmware[apdata+0xFE] = CRC16(&Firmware[apdata], 0xFE, 0x0000);
}
}
}
void LoadFirmwareFromFile(FileHandle* f, bool makecopy)
{
FirmwareLength = FixFirmwareLength(FileLength(f));
Firmware = new u8[FirmwareLength];
FileRewind(f);
FileRead(Firmware, 1, FirmwareLength, f);
// take a backup
std::string fwBackupPath;
if (!makecopy) fwBackupPath = FirmwarePath + ".bak";
else fwBackupPath = FirmwarePath;
FileHandle* bf = Platform::OpenLocalFile(fwBackupPath, FileMode::Read);
if (!bf)
{
bf = Platform::OpenLocalFile(fwBackupPath, FileMode::Write);
if (bf)
{
FileWrite(Firmware, 1, FirmwareLength, bf);
CloseFile(bf);
}
else
{
Log(LogLevel::Error, "Could not write firmware backup!\n");
}
}
else
{
CloseFile(bf);
}
}
void LoadUserSettingsFromConfig()
{
// setting up username
std::string orig_username = Platform::GetConfigString(Platform::Firm_Username);
std::u16string username = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(orig_username);
size_t usernameLength = std::min(username.length(), (size_t) 10);
memcpy(Firmware + UserSettings + 0x06, username.data(), usernameLength * sizeof(char16_t));
Firmware[UserSettings+0x1A] = usernameLength;
// setting language
Firmware[UserSettings+0x64] = Platform::GetConfigInt(Platform::Firm_Language);
// setting up color
Firmware[UserSettings+0x02] = Platform::GetConfigInt(Platform::Firm_Color);
// setting up birthday
Firmware[UserSettings+0x03] = Platform::GetConfigInt(Platform::Firm_BirthdayMonth);
Firmware[UserSettings+0x04] = Platform::GetConfigInt(Platform::Firm_BirthdayDay);
// setup message
std::string orig_message = Platform::GetConfigString(Platform::Firm_Message);
std::u16string message = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(orig_message);
size_t messageLength = std::min(message.length(), (size_t) 26);
memcpy(Firmware + UserSettings + 0x1C, message.data(), messageLength * sizeof(char16_t));
Firmware[UserSettings+0x50] = messageLength;
}
void Reset() void Reset()
{ {
if (Firmware) delete[] Firmware; if (!Firmware)
Firmware = nullptr;
FirmwarePath = "";
bool firmoverride = false;
if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
{ {
if (NDS::ConsoleType == 1) Log(LogLevel::Warn, "SPI firmware: no firmware loaded! Using default\n");
FirmwarePath = Platform::GetConfigString(Platform::DSi_FirmwarePath); Firmware = std::make_unique<class Firmware>(NDS::ConsoleType);
else
FirmwarePath = Platform::GetConfigString(Platform::FirmwarePath);
Log(LogLevel::Debug, "SPI firmware: loading from file %s\n", FirmwarePath.c_str());
bool makecopy = false;
std::string origpath = FirmwarePath;
FirmwarePath += Platform::InstanceFileSuffix();
FileHandle* f = Platform::OpenLocalFile(FirmwarePath, FileMode::Read);
if (!f)
{
f = Platform::OpenLocalFile(origpath, FileMode::Read);
makecopy = true;
}
if (!f)
{
Log(LogLevel::Warn,"Firmware not found! Generating default firmware.\n");
FirmwarePath = "";
}
else
{
LoadFirmwareFromFile(f, makecopy);
CloseFile(f);
}
} }
if (FirmwarePath.empty())
{
LoadDefaultFirmware();
firmoverride = true;
}
else
{
firmoverride = Platform::GetConfigBool(Platform::Firm_OverrideSettings);
}
FirmwareMask = FirmwareLength - 1;
u32 userdata = 0x7FE00 & FirmwareMask;
if (*(u16*)&Firmware[userdata+0x170] == ((*(u16*)&Firmware[userdata+0x70] + 1) & 0x7F))
{
if (VerifyCRC16(0xFFFF, userdata+0x100, 0x70, userdata+0x172))
userdata += 0x100;
}
UserSettings = userdata;
if (firmoverride)
LoadUserSettingsFromConfig();
// fix touchscreen coords // fix touchscreen coords
*(u16*)&Firmware[userdata+0x58] = 0; for (UserData& u : Firmware->UserData())
*(u16*)&Firmware[userdata+0x5A] = 0; {
Firmware[userdata+0x5C] = 0; u.TouchCalibrationADC1[0] = 0;
Firmware[userdata+0x5D] = 0; u.TouchCalibrationADC1[1] = 0;
*(u16*)&Firmware[userdata+0x5E] = 255<<4; u.TouchCalibrationPixel1[0] = 0;
*(u16*)&Firmware[userdata+0x60] = 191<<4; u.TouchCalibrationPixel1[1] = 0;
Firmware[userdata+0x62] = 255; u.TouchCalibrationADC2[0] = 255<<4;
Firmware[userdata+0x63] = 191; u.TouchCalibrationADC2[1] = 191<<4;
u.TouchCalibrationPixel2[0] = 255;
u.TouchCalibrationPixel2[1] = 191;
}
Firmware->UpdateChecksums();
// disable autoboot // disable autoboot
//Firmware[userdata+0x64] &= 0xBF; //Firmware[userdata+0x64] &= 0xBF;
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF); MacAddress mac = Firmware->Header().MacAddress;
Log(LogLevel::Info, "MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
//if (firmoverride)
{
u8 mac[6];
bool rep = false;
memcpy(mac, &Firmware[0x36], 6);
if (firmoverride)
rep = Platform::GetConfigArray(Platform::Firm_MAC, mac);
int inst = Platform::InstanceID();
if (inst > 0)
{
rep = true;
mac[3] += inst;
mac[4] += inst*0x44;
mac[5] += inst*0x10;
}
if (rep)
{
mac[0] &= 0xFC; // ensure the MAC isn't a broadcast MAC
memcpy(&Firmware[0x36], mac, 6);
*(u16*)&Firmware[0x2A] = CRC16(&Firmware[0x2C], *(u16*)&Firmware[0x2C], 0x0000);
}
}
Log(LogLevel::Info, "MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
Firmware[0x36], Firmware[0x37], Firmware[0x38],
Firmware[0x39], Firmware[0x3A], Firmware[0x3B]);
// verify shit // verify shit
Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware[0x2C], 0x2A)?"GOOD":"BAD"); u32 mask = Firmware->Mask();
Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&FirmwareMask, 0xFE, 0x7FAFE&FirmwareMask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware->Buffer()[0x2C], 0x2A)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&FirmwareMask, 0xFE, 0x7FBFE&FirmwareMask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&mask, 0xFE, 0x7FAFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&FirmwareMask, 0xFE, 0x7FCFE&FirmwareMask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&mask, 0xFE, 0x7FBFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: USER0 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x7FE00&FirmwareMask, 0x70, 0x7FE72&FirmwareMask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&mask, 0xFE, 0x7FCFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: USER1 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x7FF00&FirmwareMask, 0x70, 0x7FF72&FirmwareMask)?"GOOD":"BAD"); Log(LogLevel::Debug, "FW: USER0 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x7FE00&mask, 0x70, 0x7FE72&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: USER1 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x7FF00&mask, 0x70, 0x7FF72&mask)?"GOOD":"BAD");
Hold = 0; Hold = 0;
CurCmd = 0; CurCmd = 0;
@ -471,36 +168,85 @@ void DoSavestate(Savestate* file)
void SetupDirectBoot(bool dsi) void SetupDirectBoot(bool dsi)
{ {
const FirmwareHeader& header = Firmware->Header();
const UserData& userdata = Firmware->EffectiveUserData();
if (dsi) if (dsi)
{ {
for (u32 i = 0; i < 6; i += 2) for (u32 i = 0; i < 6; i += 2)
DSi::ARM9Write16(0x02FFFCF4, *(u16*)&Firmware[0x36+i]); // MAC address DSi::ARM9Write16(0x02FFFCF4, *(u16*)&header.MacAddress[i]); // MAC address
// checkme // checkme
DSi::ARM9Write16(0x02FFFCFA, *(u16*)&Firmware[0x3C]); // enabled channels DSi::ARM9Write16(0x02FFFCFA, header.EnabledChannels); // enabled channels
for (u32 i = 0; i < 0x70; i += 4) for (u32 i = 0; i < 0x70; i += 4)
DSi::ARM9Write32(0x02FFFC80+i, *(u32*)&Firmware[UserSettings+i]); DSi::ARM9Write32(0x02FFFC80+i, *(u32*)&userdata.Bytes[i]);
} }
else else
{ {
NDS::ARM9Write32(0x027FF864, 0); NDS::ARM9Write32(0x027FF864, 0);
NDS::ARM9Write32(0x027FF868, *(u16*)&Firmware[0x20] << 3); // user settings offset NDS::ARM9Write32(0x027FF868, header.UserSettingsOffset << 3); // user settings offset
NDS::ARM9Write16(0x027FF874, *(u16*)&Firmware[0x26]); // CRC16 for data/gfx NDS::ARM9Write16(0x027FF874, header.DataGfxChecksum); // CRC16 for data/gfx
NDS::ARM9Write16(0x027FF876, *(u16*)&Firmware[0x04]); // CRC16 for GUI/wifi code NDS::ARM9Write16(0x027FF876, header.GUIWifiCodeChecksum); // CRC16 for GUI/wifi code
for (u32 i = 0; i < 0x70; i += 4) for (u32 i = 0; i < 0x70; i += 4)
NDS::ARM9Write32(0x027FFC80+i, *(u32*)&Firmware[UserSettings+i]); NDS::ARM9Write32(0x027FFC80+i, *(u32*)&userdata.Bytes[i]);
} }
} }
u32 GetFirmwareLength() { return FirmwareLength; } const class Firmware* GetFirmware()
u8 GetConsoleType() { return Firmware[0x1D]; } {
u8 GetWifiVersion() { return Firmware[0x2F]; } return Firmware.get();
u8 GetNWifiVersion() { return Firmware[0x1FD]; } // for DSi; will return 0xFF on a DS }
u8 GetRFVersion() { return Firmware[0x40]; }
u8* GetWifiMAC() { return &Firmware[0x36]; } bool IsLoadedFirmwareBuiltIn()
{
return Firmware->Header().Identifier == GENERATED_FIRMWARE_IDENTIFIER;
}
bool 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->Header().Identifier;
Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]);
return true;
}
bool 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->Header().Identifier;
Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]);
return true;
}
void RemoveFirmware()
{
Firmware.reset();
Log(LogLevel::Debug, "Removed installed firmware (if any)\n");
}
u8 Read() u8 Read()
{ {
@ -539,7 +285,7 @@ void Write(u8 val, u32 hold)
} }
else else
{ {
Data = Firmware[Addr & FirmwareMask]; Data = Firmware->Buffer()[Addr & Firmware->Mask()];
Addr++; Addr++;
} }
@ -565,14 +311,14 @@ void Write(u8 val, u32 hold)
{ {
// TODO: what happens if you write too many bytes? (max 256, they say) // TODO: what happens if you write too many bytes? (max 256, they say)
if (DataPos < 4) if (DataPos < 4)
{ { // If we're in the middle of writing the address...
Addr <<= 8; Addr <<= 8;
Addr |= val; Addr |= val;
Data = 0; Data = 0;
} }
else else
{ {
Firmware[Addr & FirmwareMask] = val; Firmware->Buffer()[Addr & Firmware->Mask()] = val;
Data = val; Data = val;
Addr++; Addr++;
} }
@ -601,31 +347,14 @@ void Write(u8 val, u32 hold)
} }
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A)) if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
{ { // If the SPI firmware chip just finished a write...
if (!FirmwarePath.empty()) // We only notify the frontend of changes to the Wi-fi/userdata settings region
{ // (although it might still decide to flush the whole thing)
FileHandle* f = Platform::OpenLocalFile(FirmwarePath, FileMode::ReadWriteExisting); u32 wifioffset = Firmware->WifiAccessPointOffset();
if (f)
{
u32 cutoff = ((NDS::ConsoleType==1) ? 0x7F400 : 0x7FA00) & FirmwareMask;
FileSeek(f, cutoff, FileSeekOrigin::Start);
FileWrite(&Firmware[cutoff], FirmwareLength-cutoff, 1, f);
CloseFile(f);
}
}
else
{
std::string wfcfile = Platform::GetConfigString(ConfigEntry::WifiSettingsPath);
if (Platform::InstanceID() > 0) wfcfile += Platform::InstanceFileSuffix();
FileHandle* f = Platform::OpenLocalFile(wfcfile, FileMode::Write); // Request that the start of the Wi-fi/userdata settings region
if (f) // through the end of the firmware blob be flushed to disk
{ Platform::WriteFirmware(*Firmware, wifioffset, Firmware->Length() - wifioffset);
u32 cutoff = 0x7F400 & FirmwareMask;
FileWrite(&Firmware[cutoff], 0x900, 1, f);
CloseFile(f);
}
}
} }
} }

View File

@ -19,22 +19,32 @@
#ifndef SPI_H #ifndef SPI_H
#define SPI_H #define SPI_H
#include <algorithm>
#include <array>
#include <memory>
#include <string_view>
#include <string.h>
#include "Savestate.h" #include "Savestate.h"
#include "SPI_Firmware.h"
namespace SPI_Firmware namespace SPI_Firmware
{ {
u16 CRC16(const u8* data, u32 len, u32 start);
void SetupDirectBoot(bool dsi); void SetupDirectBoot(bool dsi);
u32 FixFirmwareLength(u32 originalLength); u32 FixFirmwareLength(u32 originalLength);
u32 GetFirmwareLength(); /// @return A pointer to the installed firmware blob if one exists, otherwise \c nullptr.
u8 GetConsoleType(); /// @warning The pointer refers to memory that melonDS owns. Do not deallocate it yourself.
u8 GetWifiVersion(); /// @see InstallFirmware
u8 GetNWifiVersion(); const Firmware* GetFirmware();
u8 GetRFVersion();
u8* GetWifiMAC();
bool IsLoadedFirmwareBuiltIn();
bool InstallFirmware(Firmware&& firmware);
bool InstallFirmware(std::unique_ptr<Firmware>&& firmware);
void RemoveFirmware();
} }
namespace SPI_Powerman namespace SPI_Powerman

376
src/SPI_Firmware.cpp Normal file
View File

@ -0,0 +1,376 @@
/*
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 "SPI_Firmware.h"
#include "SPI.h"
#include <string.h>
constexpr u8 BBINIT[0x69]
{
0x03, 0x17, 0x40, 0x00, 0x1B, 0x6C, 0x48, 0x80, 0x38, 0x00, 0x35, 0x07, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xB0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC7, 0xBB, 0x01, 0x24, 0x7F,
0x5A, 0x01, 0x3F, 0x01, 0x3F, 0x36, 0x1D, 0x00, 0x78, 0x35, 0x55, 0x12, 0x34, 0x1C, 0x00, 0x01,
0x0E, 0x38, 0x03, 0x70, 0xC5, 0x2A, 0x0A, 0x08, 0x04, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFE,
0xFE, 0xFE, 0xFE, 0xFC, 0xFC, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xF8, 0xF8, 0xF6, 0x00, 0x12, 0x14,
0x12, 0x41, 0x23, 0x03, 0x04, 0x70, 0x35, 0x0E, 0x2C, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x12, 0x28, 0x1C
};
constexpr u8 RFINIT[0x29]
{
0x31, 0x4C, 0x4F, 0x21, 0x00, 0x10, 0xB0, 0x08, 0xFA, 0x15, 0x26, 0xE6, 0xC1, 0x01, 0x0E, 0x50,
0x05, 0x00, 0x6D, 0x12, 0x00, 0x00, 0x01, 0xFF, 0x0E, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x06,
0x06, 0x00, 0x00, 0x00, 0x18, 0x00, 0x02, 0x00, 0x00
};
constexpr u8 CHANDATA[0x3C]
{
0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x0E, 0x16,
0x26, 0x1C, 0x1C, 0x1C, 0x1D, 0x1D, 0x1D, 0x1E, 0x1E, 0x1E, 0x1E, 0x1F, 0x1E, 0x1F, 0x18,
0x01, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D,
0x02, 0x6C, 0x71, 0x76, 0x5B, 0x40, 0x45, 0x4A, 0x2F, 0x34, 0x39, 0x3E, 0x03, 0x08, 0x14
};
constexpr u8 DEFAULT_UNUSED3[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
SPI_Firmware::WifiAccessPoint::WifiAccessPoint()
{
memset(Bytes, 0, sizeof(Bytes));
Status = AccessPointStatus::NotConfigured;
ConnectionConfigured = 0x01;
UpdateChecksum();
}
SPI_Firmware::WifiAccessPoint::WifiAccessPoint(int consoletype)
{
memset(Bytes, 0, sizeof(Bytes));
strncpy(SSID, DEFAULT_SSID, sizeof(SSID));
if (consoletype == 1) Mtu = 1400;
Status = AccessPointStatus::Normal;
ConnectionConfigured = 0x01;
UpdateChecksum();
}
void SPI_Firmware::WifiAccessPoint::UpdateChecksum()
{
Checksum = CRC16(Bytes, 0xFE, 0x0000);
}
SPI_Firmware::ExtendedWifiAccessPoint::ExtendedWifiAccessPoint()
{
Data.Base = WifiAccessPoint();
UpdateChecksum();
}
void SPI_Firmware::ExtendedWifiAccessPoint::UpdateChecksum()
{
Data.Base.UpdateChecksum();
Data.ExtendedChecksum = CRC16(&Bytes[0x100], 0xFE, 0x0000);
}
SPI_Firmware::FirmwareHeader::FirmwareHeader(int consoletype)
{
if (consoletype == 1)
{
ConsoleType = FirmwareConsoleType::DSi;
WifiVersion = WifiVersion::W015;
WifiBoard = WifiBoard::W015;
WifiFlash = 0x20;
// these need to be zero (part of the stage2 firmware signature!)
memset(&Bytes[0x22], 0, 8);
}
else
{
ConsoleType = FirmwareConsoleType::DSLite; // TODO: make configurable?
WifiVersion = WifiVersion::W006;
}
Identifier = GENERATED_FIRMWARE_IDENTIFIER;
// wifi calibration
WifiConfigLength = 0x138;
Unused1 = 0;
memcpy(&Unused3, DEFAULT_UNUSED3, sizeof(DEFAULT_UNUSED3));
MacAddress = DEFAULT_MAC;
EnabledChannels = 0x3FFE;
memset(&Unknown2, 0xFF, sizeof(Unknown2));
RFChipType = RFChipType::Type3;
RFBitsPerEntry = 0x94;
RFEntries = 0x29;
Unknown3 = 0x02;
*(u16*)&Bytes[0x44] = 0x0002;
*(u16*)&Bytes[0x46] = 0x0017;
*(u16*)&Bytes[0x48] = 0x0026;
*(u16*)&Bytes[0x4A] = 0x1818;
*(u16*)&Bytes[0x4C] = 0x0048;
*(u16*)&Bytes[0x4E] = 0x4840;
*(u16*)&Bytes[0x50] = 0x0058;
*(u16*)&Bytes[0x52] = 0x0042;
*(u16*)&Bytes[0x54] = 0x0146;
*(u16*)&Bytes[0x56] = 0x8064;
*(u16*)&Bytes[0x58] = 0xE6E6;
*(u16*)&Bytes[0x5A] = 0x2443;
*(u16*)&Bytes[0x5C] = 0x000E;
*(u16*)&Bytes[0x5E] = 0x0001;
*(u16*)&Bytes[0x60] = 0x0001;
*(u16*)&Bytes[0x62] = 0x0402;
memcpy(&InitialBBValues, BBINIT, sizeof(BBINIT));
Unused4 = 0;
memcpy(&Type3Config.InitialRFValues, RFINIT, sizeof(RFINIT));
Type3Config.BBIndicesPerChannel = 0x02;
memcpy(&Bytes[0xF8], CHANDATA, sizeof(CHANDATA));
memset(&Type3Config.Unused0, 0xFF, sizeof(Type3Config.Unused0));
UpdateChecksum();
}
void SPI_Firmware::FirmwareHeader::UpdateChecksum()
{
WifiConfigChecksum = SPI_Firmware::CRC16(&Bytes[0x2C], WifiConfigLength, 0x0000);
}
SPI_Firmware::UserData::UserData()
{
memset(Bytes, 0, 0x74);
Version = 5;
BirthdayMonth = 1;
BirthdayDay = 1;
Settings = Language::English | BacklightLevel::Max; // NOLINT(*-suspicious-enum-usage)
memcpy(Nickname, DEFAULT_USERNAME.data(), DEFAULT_USERNAME.size() * sizeof(std::u16string_view::value_type));
NameLength = DEFAULT_USERNAME.size();
Checksum = CRC16(Bytes, 0x70, 0xFFFF);
}
void SPI_Firmware::UserData::UpdateChecksum()
{
Checksum = CRC16(Bytes, 0x70, 0xFFFF);
if (ExtendedSettings.Unknown0 == 0x01)
{
ExtendedSettings.Checksum = CRC16(&Bytes[0x74], 0x8A, 0xFFFF);
}
}
SPI_Firmware::Firmware::Firmware(int consoletype)
{
FirmwareBufferLength = DEFAULT_FIRMWARE_LENGTH;
FirmwareBuffer = new u8[FirmwareBufferLength];
memset(FirmwareBuffer, 0xFF, FirmwareBufferLength);
FirmwareMask = FirmwareBufferLength - 1;
memset(FirmwareBuffer, 0, 0x1D);
FirmwareHeader& header = *reinterpret_cast<FirmwareHeader*>(FirmwareBuffer);
header = FirmwareHeader(consoletype);
FirmwareBuffer[0x2FF] = 0x80; // boot0: use NAND as stage2 medium
// user data
header.UserSettingsOffset = (0x7FE00 & FirmwareMask) >> 3;
std::array<union UserData, 2>& settings = *reinterpret_cast<std::array<union UserData, 2>*>(UserDataPosition());
settings = {
SPI_Firmware::UserData(),
SPI_Firmware::UserData(),
};
// wifi access points
// TODO: WFC ID??
std::array<WifiAccessPoint, 3>& accesspoints = *reinterpret_cast<std::array<WifiAccessPoint, 3>*>(WifiAccessPointPosition());
accesspoints = {
WifiAccessPoint(consoletype),
WifiAccessPoint(),
WifiAccessPoint(),
};
if (consoletype == 1)
{
std::array<ExtendedWifiAccessPoint, 3>& extendedaccesspoints = *reinterpret_cast<std::array<ExtendedWifiAccessPoint, 3>*>(ExtendedAccessPointPosition());
extendedaccesspoints = {
ExtendedWifiAccessPoint(),
ExtendedWifiAccessPoint(),
ExtendedWifiAccessPoint(),
};
}
}
SPI_Firmware::Firmware::Firmware(Platform::FileHandle* file) : FirmwareBuffer(nullptr), FirmwareBufferLength(0), FirmwareMask(0)
{
if (file)
{
u64 length = Platform::FileLength(file);
if (length > 0)
{
FirmwareBufferLength = FixFirmwareLength(length);
FirmwareBuffer = new u8[FirmwareBufferLength];
FirmwareMask = FirmwareBufferLength - 1;
memset(FirmwareBuffer, 0, FirmwareBufferLength);
Platform::FileRewind(file);
if (!Platform::FileRead(FirmwareBuffer, length, 1, file))
{
delete[] FirmwareBuffer;
FirmwareBuffer = nullptr;
FirmwareBufferLength = 0;
FirmwareMask = 0;
}
}
Platform::FileRewind(file);
}
}
SPI_Firmware::Firmware::Firmware(const u8* data, u32 length) : FirmwareBuffer(nullptr), FirmwareBufferLength(FixFirmwareLength(length))
{
if (data)
{
FirmwareBuffer = new u8[FirmwareBufferLength];
memcpy(FirmwareBuffer, data, FirmwareBufferLength);
FirmwareMask = FirmwareBufferLength - 1;
}
}
SPI_Firmware::Firmware::Firmware(const Firmware& other) : FirmwareBuffer(nullptr), FirmwareBufferLength(other.FirmwareBufferLength)
{
FirmwareBuffer = new u8[FirmwareBufferLength];
memcpy(FirmwareBuffer, other.FirmwareBuffer, FirmwareBufferLength);
FirmwareMask = other.FirmwareMask;
}
SPI_Firmware::Firmware::Firmware(Firmware&& other) noexcept
{
FirmwareBuffer = other.FirmwareBuffer;
FirmwareBufferLength = other.FirmwareBufferLength;
FirmwareMask = other.FirmwareMask;
other.FirmwareBuffer = nullptr;
other.FirmwareBufferLength = 0;
other.FirmwareMask = 0;
}
SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(const Firmware& other)
{
if (this != &other)
{
delete[] FirmwareBuffer;
FirmwareBufferLength = other.FirmwareBufferLength;
FirmwareBuffer = new u8[other.FirmwareBufferLength];
memcpy(FirmwareBuffer, other.FirmwareBuffer, other.FirmwareBufferLength);
FirmwareMask = other.FirmwareMask;
}
return *this;
}
SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(Firmware&& other) noexcept
{
if (this != &other)
{
delete[] FirmwareBuffer;
FirmwareBuffer = other.FirmwareBuffer;
FirmwareBufferLength = other.FirmwareBufferLength;
FirmwareMask = other.FirmwareMask;
other.FirmwareBuffer = nullptr;
other.FirmwareBufferLength = 0;
other.FirmwareMask = 0;
}
return *this;
}
SPI_Firmware::Firmware::~Firmware()
{
delete[] FirmwareBuffer;
}
bool SPI_Firmware::Firmware::IsBootable() const
{
return
FirmwareBufferLength != DEFAULT_FIRMWARE_LENGTH &&
Header().Identifier != GENERATED_FIRMWARE_IDENTIFIER
;
}
const SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() const {
const std::array<union UserData, 2>& userdata = UserData();
bool userdata0ChecksumOk = userdata[0].ChecksumValid();
bool userdata1ChecksumOk = userdata[1].ChecksumValid();
if (userdata0ChecksumOk && userdata1ChecksumOk)
{
return userdata[0].UpdateCounter > userdata[1].UpdateCounter ? userdata[0] : userdata[1];
}
else if (userdata0ChecksumOk)
{
return userdata[0];
}
else if (userdata1ChecksumOk)
{
return userdata[1];
}
else
{
return userdata[0];
}
}
SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() {
std::array<union UserData, 2>& userdata = UserData();
bool userdata0ChecksumOk = userdata[0].ChecksumValid();
bool userdata1ChecksumOk = userdata[1].ChecksumValid();
if (userdata0ChecksumOk && userdata1ChecksumOk)
{
return userdata[0].UpdateCounter > userdata[1].UpdateCounter ? userdata[0] : userdata[1];
}
else if (userdata0ChecksumOk)
{
return userdata[0];
}
else if (userdata1ChecksumOk)
{
return userdata[1];
}
else
{
return userdata[0];
}
}
void SPI_Firmware::Firmware::UpdateChecksums()
{
Header().UpdateChecksum();
for (SPI_Firmware::WifiAccessPoint& ap : AccessPoints())
{
ap.UpdateChecksum();
}
for (SPI_Firmware::ExtendedWifiAccessPoint& eap : ExtendedAccessPoints())
{
eap.UpdateChecksum();
}
for (SPI_Firmware::UserData& u : UserData())
{
u.UpdateChecksum();
}
}

563
src/SPI_Firmware.h Normal file
View File

@ -0,0 +1,563 @@
/*
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_SPI_FIRMWARE_H
#define MELONDS_SPI_FIRMWARE_H
#include <array>
#include <string_view>
#include "types.h"
#include "Platform.h"
namespace SPI_Firmware
{
u16 CRC16(const u8* data, u32 len, u32 start);
using MacAddress = std::array<u8, 6>;
using IpAddress = std::array<u8, 4>;
constexpr unsigned DEFAULT_FIRMWARE_LENGTH = 0x20000;
constexpr MacAddress DEFAULT_MAC = {0x00, 0x09, 0xBF, 0x11, 0x22, 0x33};
constexpr unsigned MAX_SSID_LENGTH = 32;
constexpr std::u16string_view DEFAULT_USERNAME = u"melonDS";
constexpr const char* const DEFAULT_SSID = "melonAP";
/**
* The position of the first extended Wi-fi settings block in the DSi firmware,
* relative to the position of the first user settings block.
* The generated firmware also uses this offset.
*/
constexpr int EXTENDED_WIFI_SETTINGS_OFFSET = -0xA00;
enum class WepMode : u8
{
None = 0,
Hex5 = 1,
Hex13 = 2,
Hex16 = 3,
Ascii5 = 5,
Ascii13 = 6,
Ascii16 = 7,
};
enum class WpaMode : u8
{
Normal = 0,
WPA_WPA2 = 0x10,
WPS_WPA = 0x13,
Unused = 0xff,
};
enum class WpaSecurity : u8
{
None = 0,
WPA_TKIP = 4,
WPA2_TKIP = 5,
WPA_AES = 6,
WPA2_AES = 7,
};
enum class AccessPointStatus : u8
{
Normal = 0,
Aoss = 1,
NotConfigured = 0xff
};
/**
* @see https://problemkaputt.de/gbatek.htm#dsfirmwarewifiinternetaccesspoints
*/
union WifiAccessPoint
{
/**
* Constructs an unconfigured access point.
*/
WifiAccessPoint();
/**
* Constructs an access point configured with melonDS's defaults.
*/
explicit WifiAccessPoint(int consoletype);
void UpdateChecksum();
u8 Bytes[256];
struct
{
char ProxyUsername[32];
char ProxyPassword[32];
char SSID[32];
char SSIDWEP64[32];
u8 WEPKey1[16];
u8 WEPKey2[16];
u8 WEPKey3[16];
u8 WEPKey4[16];
IpAddress Address;
IpAddress Gateway;
IpAddress PrimaryDns;
IpAddress SecondaryDns;
u8 SubnetMask;
u8 Unknown0[21];
enum WepMode WepMode;
AccessPointStatus Status;
u8 SSIDLength;
u8 Unknown1;
u16 Mtu;
u8 Unknown2[3];
u8 ConnectionConfigured;
u8 NintendoWFCID[6];
u8 Unknown3[8];
u16 Checksum;
};
};
static_assert(sizeof(WifiAccessPoint) == 256, "WifiAccessPoint should be 256 bytes");
union ExtendedWifiAccessPoint
{
ExtendedWifiAccessPoint();
void UpdateChecksum();
u8 Bytes[512];
struct
{
WifiAccessPoint Base;
// DSi-specific entries now
u8 PrecomputedPSK[32];
char WPAPassword[64];
char Unused0[33];
WpaSecurity Security;
bool ProxyEnabled;
bool ProxyAuthentication;
char ProxyName[48];
u8 Unused1[52];
u16 ProxyPort;
u8 Unused2[20];
u16 ExtendedChecksum;
} Data;
};
static_assert(sizeof(ExtendedWifiAccessPoint) == 512, "WifiAccessPoint should be 512 bytes");
enum class FirmwareConsoleType : u8
{
DS = 0xFF,
DSLite = 0x20,
DSi = 0x57,
iQueDS = 0x43,
iQueDSLite = 0x63,
};
enum class WifiVersion : u8
{
V1_4 = 0,
V5 = 3,
V6_7 = 5,
W006 = 6,
W015 = 15,
W024 = 24,
N3DS = 34,
};
enum RFChipType : u8
{
Type2 = 0x2,
Type3 = 0x3,
};
enum class WifiBoard : u8
{
W015 = 0x1,
W024 = 0x2,
W028 = 0x3,
Unused = 0xff,
};
enum Language : u8
{
Japanese = 0,
English = 1,
French = 2,
German = 3,
Italian = 4,
Spanish = 5,
Chinese = 6,
Reserved = 7,
};
enum GBAScreen : u8
{
Upper = 0,
Lower = (1 << 3),
};
enum BacklightLevel : u8
{
Low = 0,
Medium = 1 << 4,
High = 2 << 4,
Max = 3 << 4
};
enum BootMenu : u8
{
Manual = 0,
Autostart = 1 << 6,
};
using FirmwareIdentifier = std::array<u8, 4>;
using MacAddress = std::array<u8, 6>;
constexpr FirmwareIdentifier GENERATED_FIRMWARE_IDENTIFIER = {'M', 'E', 'L', 'N'};
/**
* @note GBATek says the header is actually 511 bytes;
* this header struct is 512 bytes due to padding,
* but the last byte is just the first byte of the firmware's code.
* It doesn't affect the offset of any of the fields,
* so leaving that last byte in there is harmless.
* @see https://problemkaputt.de/gbatek.htm#dsfirmwareheader
* @see https://problemkaputt.de/gbatek.htm#dsfirmwarewificalibrationdata
*/
union FirmwareHeader
{
explicit FirmwareHeader(int consoletype);
void UpdateChecksum();
u8 Bytes[512];
struct
{
u16 ARM9GUICodeOffset;
u16 ARM7WifiCodeOffset;
u16 GUIWifiCodeChecksum;
u16 BootCodeChecksum;
FirmwareIdentifier Identifier;
u16 ARM9BootCodeROMAddress;
u16 ARM9BootCodeRAMAddress;
u16 ARM7BootCodeRAMAddress;
u16 ARM7BootCodeROMAddress;
u16 ShiftAmounts;
u16 DataGfxRomAddress;
u8 BuildMinute;
u8 BuildHour;
u8 BuildDay;
u8 BuildMonth;
u8 BuildYear;
FirmwareConsoleType ConsoleType;
u8 Unused0[2];
u16 UserSettingsOffset;
u8 Unknown0[2];
u8 Unknown1[2];
u16 DataGfxChecksum;
u8 Unused2[2];
// Begin wi-fi settings
u16 WifiConfigChecksum;
u16 WifiConfigLength;
u8 Unused1;
enum WifiVersion WifiVersion;
u8 Unused3[6];
SPI_Firmware::MacAddress MacAddress;
u16 EnabledChannels;
u8 Unknown2[2];
enum RFChipType RFChipType;
u8 RFBitsPerEntry;
u8 RFEntries;
u8 Unknown3;
u8 InitialValues[32];
u8 InitialBBValues[105];
u8 Unused4;
union
{
struct
{
u8 InitialRFValues[36];
u8 InitialRF56Values[84];
u8 InitialBB1EValues[14];
u8 InitialRf9Values[14];
} Type2Config;
struct
{
u8 InitialRFValues[41];
u8 BBIndicesPerChannel;
u8 BBIndex1;
u8 BBData1[14];
u8 BBIndex2;
u8 BBData2[14];
u8 RFIndex1;
u8 RFData1[14];
u8 RFIndex2;
u8 RFData2[14];
u8 Unused0[46];
} Type3Config;
};
u8 Unknown4;
u8 Unused5;
u8 Unused6[153];
enum WifiBoard WifiBoard;
u8 WifiFlash;
u8 Unused7;
};
};
static_assert(sizeof(FirmwareHeader) == 512, "FirmwareHeader should be 512 bytes");
struct ExtendedUserSettings
{
char ID[8];
u16 Checksum;
u16 ChecksumLength;
u8 Version;
u8 UpdateCount;
u8 BootMenuFlags;
u8 GBABorder;
u16 TemperatureCalibration0;
u16 TemperatureCalibration1;
u16 TemperatureCalibrationDegrees;
u8 TemperatureFlags;
u8 BacklightIntensity;
u32 DateCenturyOffset;
u8 DateMonthRecovery;
u8 DateDayRecovery;
u8 DateYearRecovery;
u8 DateTimeFlags;
char DateSeparator;
char TimeSeparator;
char DecimalSeparator;
char ThousandsSeparator;
u8 DaylightSavingsTimeNth;
u8 DaylightSavingsTimeDay;
u8 DaylightSavingsTimeOfMonth;
u8 DaylightSavingsTimeFlags;
};
static_assert(sizeof(ExtendedUserSettings) == 0x28, "ExtendedUserSettings should be 40 bytes");
union UserData
{
UserData();
void UpdateChecksum();
[[nodiscard]] bool ChecksumValid() const
{
bool baseChecksumOk = Checksum == CRC16(Bytes, 0x70, 0xFFFF);
bool extendedChecksumOk = Bytes[0x74] != 1 || ExtendedSettings.Checksum == CRC16(Bytes + 0x74, 0x8A, 0xFFFF);
// For our purposes, the extended checksum is OK if we're not using extended data
return baseChecksumOk && extendedChecksumOk;
}
u8 Bytes[256];
struct
{
u16 Version;
u8 FavoriteColor;
u8 BirthdayMonth;
u8 BirthdayDay;
u8 Unused0;
char16_t Nickname[10];
u16 NameLength;
char16_t Message[26];
u16 MessageLength;
u8 AlarmHour;
u8 AlarmMinute;
u8 Unknown0[2];
u8 AlarmFlags;
u8 Unused1;
u16 TouchCalibrationADC1[2];
u8 TouchCalibrationPixel1[2];
u16 TouchCalibrationADC2[2];
u8 TouchCalibrationPixel2[2];
u16 Settings;
u8 Year;
u8 Unknown1;
u32 RTCOffset;
u8 Unused2[4];
u16 UpdateCounter;
u16 Checksum;
union
{
u8 Unused3[0x8C];
struct
{
u8 Unknown0;
Language ExtendedLanguage; // padded
u16 SupportedLanguageMask;
u8 Unused0[0x86];
u16 Checksum;
} ExtendedSettings;
};
};
};
static_assert(sizeof(UserData) == 256, "UserData should be 256 bytes");
class Firmware
{
public:
/**
* Constructs a default firmware blob
* filled with data necessary for booting and configuring NDS games.
* The Wi-fi section has one access point configured with melonDS's defaults.
* Will not contain executable code.
* @param consoletype Console type to use. 1 for DSi, 0 for NDS.
*/
explicit Firmware(int consoletype);
/**
* Loads a firmware blob from the given file.
* Will rewind the file's stream offset to its initial position when finished.
*/
explicit Firmware(Platform::FileHandle* file);
/**
* Constructs a firmware blob from a copy of the given data.
* @param data Buffer containing the firmware data.
* @param length Length of the buffer in bytes.
* If too short, the firmware will be padded with zeroes.
* If too long, the extra data will be ignored.
*/
Firmware(const u8* data, u32 length);
Firmware(const Firmware& other);
Firmware(Firmware&& other) noexcept;
Firmware& operator=(const Firmware& other);
Firmware& operator=(Firmware&& other) noexcept;
~Firmware();
[[nodiscard]] FirmwareHeader& Header() { return *reinterpret_cast<FirmwareHeader*>(FirmwareBuffer); }
[[nodiscard]] const FirmwareHeader& Header() const { return *reinterpret_cast<const FirmwareHeader*>(FirmwareBuffer); }
/// @return The offset of the first basic Wi-fi settings block in the firmware
/// (not the extended Wi-fi settings block used by the DSi).
/// @see WifiAccessPointPosition
[[nodiscard]] u32 WifiAccessPointOffset() const { return UserDataOffset() - 0x400; }
/// @return The address of the first basic Wi-fi settings block in the firmware.
[[nodiscard]] u8* WifiAccessPointPosition() { return FirmwareBuffer + WifiAccessPointOffset(); }
[[nodiscard]] const u8* WifiAccessPointPosition() const { return FirmwareBuffer + WifiAccessPointOffset(); }
[[nodiscard]] const std::array<WifiAccessPoint, 3>& AccessPoints() const
{
// An std::array is a wrapper around a C array, so this cast is fine.
return *reinterpret_cast<const std::array<WifiAccessPoint, 3>*>(WifiAccessPointPosition());
}
[[nodiscard]] std::array<WifiAccessPoint, 3>& AccessPoints()
{
// An std::array is a wrapper around a C array, so this cast is fine.
return *reinterpret_cast<std::array<WifiAccessPoint, 3>*>(WifiAccessPointPosition());
}
/// @returns \c true if this firmware image contains bootable code.
/// @note Non-bootable firmware can still be valid;
/// DSi firmware is non-bootable for instance.
/// If this firmware is not bootable, then melonDS should use direct-boot mode.
[[nodiscard]] bool IsBootable() const;
/// @return The address of the first extended Wi-fi settings block in the firmware.
/// @warning Only meaningful if this is DSi firmware.
[[nodiscard]] u32 ExtendedAccessPointOffset() const { return UserDataOffset() + EXTENDED_WIFI_SETTINGS_OFFSET; }
[[nodiscard]] u8* ExtendedAccessPointPosition() { return FirmwareBuffer + ExtendedAccessPointOffset(); }
[[nodiscard]] const u8* ExtendedAccessPointPosition() const { return FirmwareBuffer + ExtendedAccessPointOffset(); }
[[nodiscard]] const std::array<ExtendedWifiAccessPoint, 3>& ExtendedAccessPoints() const
{
// An std::array is a wrapper around a C array, so this cast is fine.
return *reinterpret_cast<const std::array<ExtendedWifiAccessPoint, 3>*>(ExtendedAccessPointPosition());
}
[[nodiscard]] std::array<ExtendedWifiAccessPoint, 3>& ExtendedAccessPoints()
{
// An std::array is a wrapper around a C array, so this cast is fine.
return *reinterpret_cast<std::array<ExtendedWifiAccessPoint, 3>*>(ExtendedAccessPointPosition());
}
/// @return The pointer to the firmware buffer,
/// or \c nullptr if this object is invalid
/// (e.g. it was moved from, or its constructor failed).
[[nodiscard]] u8* Buffer() { return FirmwareBuffer; }
[[nodiscard]] const u8* Buffer() const { return FirmwareBuffer; }
[[nodiscard]] u32 Length() const { return FirmwareBufferLength; }
[[nodiscard]] u32 Mask() const { return FirmwareMask; }
/// @return The offset of the first user data section in the firmware.
/// @see UserDataPosition
[[nodiscard]] u32 UserDataOffset() const { return Header().UserSettingsOffset << 3; }
/// @return The address of the first user data section in the firmware.
/// @see UserDataOffset
[[nodiscard]] u8* UserDataPosition() { return FirmwareBuffer + UserDataOffset(); }
[[nodiscard]] const u8* UserDataPosition() const { return FirmwareBuffer + UserDataOffset(); }
/// @return Reference to the two user data sections.
/// @note Either \c UserData object could be the "effective" one,
/// so prefer using \c EffectiveUserData() if you're not modifying both.
[[nodiscard]] const std::array<union UserData, 2>& UserData() const
{
// An std::array is a wrapper around a C array, so this cast is fine.
return *reinterpret_cast<const std::array<union UserData, 2>*>(UserDataPosition());
};
/**
* @return Reference to the two user data sections.
* @note Either \c UserData object could be the "effective" one,
* so prefer using \c EffectiveUserData() if you're not modifying both.
* @warning Remember to call UserData::UpdateChecksum() after modifying any of its fields.
*/
[[nodiscard]] std::array<union UserData, 2>& UserData()
{
// An std::array is a wrapper around a C array, so this cast is fine.
return *reinterpret_cast<std::array<union UserData, 2>*>(UserDataPosition());
}
/**
* @return Reference to whichever of the two user data sections
* will be used by the firmware.
* Specifically, the firmware will use whichever one has the valid checksum
* (or the newer one if they're both valid).
*/
[[nodiscard]] const union UserData& EffectiveUserData() const;
/**
* @return Reference to whichever of the two user data sections
* has the highest update counter.
*/
[[nodiscard]] union UserData& EffectiveUserData();
/// Updates the checksums of all used sections of the firmware.
void UpdateChecksums();
private:
u8* FirmwareBuffer;
u32 FirmwareBufferLength;
u32 FirmwareMask;
};
}
#endif //MELONDS_SPI_FIRMWARE_H

View File

@ -176,6 +176,7 @@ void DeInit()
void Reset() void Reset()
{ {
using namespace SPI_Firmware;
memset(RAM, 0, 0x2000); memset(RAM, 0, 0x2000);
memset(IO, 0, 0x1000); memset(IO, 0, 0x1000);
@ -215,15 +216,15 @@ void Reset()
} }
#undef BBREG_FIXED #undef BBREG_FIXED
RFVersion = SPI_Firmware::GetRFVersion(); RFVersion = GetFirmware()->Header().RFChipType;
memset(RFRegs, 0, 4*0x40); memset(RFRegs, 0, 4*0x40);
u8 console = SPI_Firmware::GetConsoleType(); FirmwareConsoleType console = GetFirmware()->Header().ConsoleType;
if (console == 0xFF) if (console == FirmwareConsoleType::DS)
IOPORT(0x000) = 0x1440; IOPORT(0x000) = 0x1440;
else if (console == 0x20) else if (console == FirmwareConsoleType::DSLite)
IOPORT(0x000) = 0xC340; IOPORT(0x000) = 0xC340;
else if (NDS::ConsoleType == 1 && console == 0x57) else if (NDS::ConsoleType == 1 && console == FirmwareConsoleType::DSi)
IOPORT(0x000) = 0xC340; // DSi has the modern DS-wifi variant IOPORT(0x000) = 0xC340; // DSi has the modern DS-wifi variant
else else
{ {

View File

@ -39,6 +39,7 @@
#include "LAN_PCap.h" #include "LAN_PCap.h"
#include "LocalMP.h" #include "LocalMP.h"
#include "OSD.h" #include "OSD.h"
#include "SPI_Firmware.h"
#ifdef __WIN32__ #ifdef __WIN32__
#define fseek _fseeki64 #define fseek _fseeki64
@ -249,13 +250,6 @@ std::string GetConfigString(ConfigEntry entry)
{ {
switch (entry) switch (entry)
{ {
case BIOS9Path: return Config::BIOS9Path;
case BIOS7Path: return Config::BIOS7Path;
case FirmwarePath: return Config::FirmwarePath;
case DSi_BIOS9Path: return Config::DSiBIOS9Path;
case DSi_BIOS7Path: return Config::DSiBIOS7Path;
case DSi_FirmwarePath: return Config::DSiFirmwarePath;
case DSi_NANDPath: return Config::DSiNANDPath; case DSi_NANDPath: return Config::DSiNANDPath;
case DLDI_ImagePath: return Config::DLDISDPath; case DLDI_ImagePath: return Config::DLDISDPath;
@ -584,7 +578,36 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen
ROMManager::GBASave->RequestFlush(savedata, savelen, writeoffset, writelen); ROMManager::GBASave->RequestFlush(savedata, savelen, writeoffset, writelen);
} }
void WriteFirmware(const SPI_Firmware::Firmware& firmware, u32 writeoffset, u32 writelen)
{
if (!ROMManager::FirmwareSave)
return;
if (firmware.Header().Identifier != SPI_Firmware::GENERATED_FIRMWARE_IDENTIFIER)
{ // If this is not the default built-in firmware...
// ...then write the whole thing back.
ROMManager::FirmwareSave->RequestFlush(firmware.Buffer(), firmware.Length(), writeoffset, writelen);
}
else
{
u32 eapstart = firmware.ExtendedAccessPointOffset();
u32 eapend = eapstart + sizeof(firmware.ExtendedAccessPoints());
u32 apstart = firmware.WifiAccessPointOffset();
u32 apend = apstart + sizeof(firmware.AccessPoints());
// assert that the extended access points come just before the regular ones
assert(eapend == apstart);
if (eapstart <= writeoffset && writeoffset < apend)
{ // If we're writing to the access points...
const u8* buffer = firmware.ExtendedAccessPointPosition();
u32 length = sizeof(firmware.ExtendedAccessPoints()) + sizeof(firmware.AccessPoints());
ROMManager::FirmwareSave->RequestFlush(buffer, length, writeoffset - eapstart, writelen);
}
}
}
bool MP_Init() bool MP_Init()
{ {

View File

@ -16,9 +16,14 @@
with melonDS. If not, see http://www.gnu.org/licenses/. with melonDS. If not, see http://www.gnu.org/licenses/.
*/ */
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <codecvt>
#include <locale>
#include <memory>
#include <tuple>
#include <string> #include <string>
#include <utility> #include <utility>
#include <fstream> #include <fstream>
@ -35,7 +40,13 @@
#include "DSi.h" #include "DSi.h"
#include "SPI.h" #include "SPI.h"
#include "DSi_I2C.h" #include "DSi_I2C.h"
#include "FreeBIOS.h"
using std::make_unique;
using std::pair;
using std::string;
using std::tie;
using std::unique_ptr;
using namespace Platform; using namespace Platform;
namespace ROMManager namespace ROMManager
@ -53,6 +64,7 @@ std::string BaseGBAAssetName = "";
SaveManager* NDSSave = nullptr; SaveManager* NDSSave = nullptr;
SaveManager* GBASave = nullptr; SaveManager* GBASave = nullptr;
std::unique_ptr<SaveManager> FirmwareSave = nullptr;
std::unique_ptr<Savestate> BackupState = nullptr; std::unique_ptr<Savestate> BackupState = nullptr;
bool SavestateLoaded = false; bool SavestateLoaded = false;
@ -462,6 +474,94 @@ void LoadCheats()
AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr);
} }
void LoadBIOSFiles()
{
if (Config::ExternalBIOSEnable)
{
if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS9Path, FileMode::Read))
{
FileRewind(f);
FileRead(NDS::ARM9BIOS, sizeof(NDS::ARM9BIOS), 1, f);
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");
for (int i = 0; i < 16; i++)
((u32*)NDS::ARM9BIOS)[i] = 0xE7FFDEFF;
}
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)
{
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
}
}
}
void EnableCheats(bool enable) void EnableCheats(bool enable)
{ {
CheatsOn = enable; CheatsOn = enable;
@ -492,6 +592,7 @@ void Reset()
{ {
NDS::SetConsoleType(Config::ConsoleType); NDS::SetConsoleType(Config::ConsoleType);
if (Config::ConsoleType == 1) EjectGBACart(); if (Config::ConsoleType == 1) EjectGBACart();
LoadBIOSFiles();
NDS::Reset(); NDS::Reset();
SetBatteryLevels(); SetBatteryLevels();
@ -513,6 +614,28 @@ void Reset()
GBASave->SetPath(newsave, false); GBASave->SetPath(newsave, false);
} }
if (FirmwareSave)
{
std::string oldsave = FirmwareSave->GetPath();
string newsave;
if (Config::ExternalBIOSEnable)
{
if (Config::ConsoleType == 1)
newsave = Config::DSiFirmwarePath + Platform::InstanceFileSuffix();
else
newsave = Config::FirmwarePath + Platform::InstanceFileSuffix();
}
else
{
newsave = Config::WifiSettingsPath + Platform::InstanceFileSuffix();
}
if (oldsave != newsave)
{ // If the player toggled the ConsoleType or ExternalBIOSEnable...
FirmwareSave->SetPath(newsave, true);
}
}
if (!BaseROMName.empty()) if (!BaseROMName.empty())
{ {
if (Config::DirectBoot || NDS::NeedsDirectBoot()) if (Config::DirectBoot || NDS::NeedsDirectBoot())
@ -527,6 +650,11 @@ bool LoadBIOS()
{ {
NDS::SetConsoleType(Config::ConsoleType); NDS::SetConsoleType(Config::ConsoleType);
LoadBIOSFiles();
if (!InstallFirmware())
return false;
if (NDS::NeedsDirectBoot()) if (NDS::NeedsDirectBoot())
return false; return false;
@ -638,6 +766,222 @@ 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<SPI_Firmware::Firmware>, string> LoadFirmwareFromFile()
{
string loadedpath;
unique_ptr<SPI_Firmware::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<SPI_Firmware::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<SPI_Firmware::Firmware>, string> GenerateDefaultFirmware()
{
using namespace SPI_Firmware;
// Construct the default firmware...
string settingspath;
std::unique_ptr<Firmware> firmware = std::make_unique<Firmware>(Config::ConsoleType);
assert(firmware->Buffer() != nullptr);
// 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);
settingspath = wfcsettingspath + Platform::InstanceFileSuffix();
FileHandle* f = Platform::OpenLocalFile(settingspath, FileMode::Read);
if (!f)
{
settingspath = wfcsettingspath;
f = Platform::OpenLocalFile(settingspath, FileMode::Read);
}
// If using generated firmware, we keep the wi-fi settings on the host disk separately.
// Wi-fi access point data includes Nintendo WFC settings,
// and if we didn't keep them then the player would have to reset them in each session.
if (f)
{ // If we have Wi-fi settings to load...
constexpr unsigned TOTAL_WFC_SETTINGS_SIZE = 3 * (sizeof(WifiAccessPoint) + sizeof(ExtendedWifiAccessPoint));
// The access point and extended access point segments might
// be in different locations depending on the firmware revision,
// but our generated firmware always keeps them next to each other.
// (Extended access points first, then regular ones.)
if (!FileRead(firmware->ExtendedAccessPointPosition(), TOTAL_WFC_SETTINGS_SIZE, 1, f))
{ // If we couldn't read the Wi-fi settings from this file...
Platform::Log(Platform::LogLevel::Warn, "Failed to read Wi-fi settings from \"%s\"; using defaults instead\n", wfcsettingspath.c_str());
firmware->AccessPoints() = {
WifiAccessPoint(Config::ConsoleType),
WifiAccessPoint(),
WifiAccessPoint(),
};
firmware->ExtendedAccessPoints() = {
ExtendedWifiAccessPoint(),
ExtendedWifiAccessPoint(),
ExtendedWifiAccessPoint(),
};
}
firmware->UpdateChecksums();
CloseFile(f);
}
// If we don't have Wi-fi settings to load,
// then the defaults will have already been populated by the constructor.
return std::make_pair(std::move(firmware), std::move(wfcsettingspath));
}
void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware)
{
using namespace SPI_Firmware;
UserData& currentData = firmware.EffectiveUserData();
// setting up username
std::string orig_username = Platform::GetConfigString(Platform::Firm_Username);
if (!orig_username.empty())
{ // If the frontend defines a username, take it. If not, leave the existing one.
std::u16string username = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(orig_username);
size_t usernameLength = std::min(username.length(), (size_t) 10);
currentData.NameLength = usernameLength;
memcpy(currentData.Nickname, username.data(), usernameLength * sizeof(char16_t));
}
auto language = static_cast<Language>(Platform::GetConfigInt(Platform::Firm_Language));
if (language != Language::Reserved)
{ // If the frontend specifies a language (rather than using the existing value)...
currentData.Settings &= ~Language::Reserved; // ..clear the existing language...
currentData.Settings |= language; // ...and set the new one.
}
// setting up color
u8 favoritecolor = Platform::GetConfigInt(Platform::Firm_Color);
if (favoritecolor != 0xFF)
{
currentData.FavoriteColor = favoritecolor;
}
u8 birthmonth = Platform::GetConfigInt(Platform::Firm_BirthdayMonth);
if (birthmonth != 0)
{ // If the frontend specifies a birth month (rather than using the existing value)...
currentData.BirthdayMonth = birthmonth;
}
u8 birthday = Platform::GetConfigInt(Platform::Firm_BirthdayDay);
if (birthday != 0)
{ // If the frontend specifies a birthday (rather than using the existing value)...
currentData.BirthdayDay = birthday;
}
// setup message
std::string orig_message = Platform::GetConfigString(Platform::Firm_Message);
if (!orig_message.empty())
{
std::u16string message = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.from_bytes(orig_message);
size_t messageLength = std::min(message.length(), (size_t) 26);
currentData.MessageLength = messageLength;
memcpy(currentData.Message, message.data(), messageLength * sizeof(char16_t));
}
MacAddress mac;
bool rep = false;
auto& header = firmware.Header();
memcpy(&mac, header.MacAddress.data(), sizeof(MacAddress));
MacAddress configuredMac;
rep = Platform::GetConfigArray(Platform::Firm_MAC, &configuredMac);
rep &= (configuredMac != MacAddress());
if (rep)
{
mac = configuredMac;
}
int inst = Platform::InstanceID();
if (inst > 0)
{
rep = true;
mac[3] += inst;
mac[4] += inst*0x44;
mac[5] += inst*0x10;
}
if (rep)
{
mac[0] &= 0xFC; // ensure the MAC isn't a broadcast MAC
header.MacAddress = mac;
header.UpdateChecksum();
}
firmware.UpdateChecksums();
}
bool InstallFirmware()
{
using namespace SPI_Firmware;
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)
{
LoadUserSettingsFromConfig(*firmware);
}
FirmwareSave = std::make_unique<SaveManager>(firmwarepath);
return InstallFirmware(std::move(firmware));
}
bool LoadROM(QStringList filepath, bool reset) bool LoadROM(QStringList filepath, bool reset)
{ {
if (filepath.empty()) return false; if (filepath.empty()) return false;
@ -733,10 +1077,16 @@ bool LoadROM(QStringList filepath, bool reset)
BaseROMName = romname; BaseROMName = romname;
BaseAssetName = romname.substr(0, romname.rfind('.')); BaseAssetName = romname.substr(0, romname.rfind('.'));
if (!InstallFirmware())
{
return false;
}
if (reset) if (reset)
{ {
NDS::SetConsoleType(Config::ConsoleType); NDS::SetConsoleType(Config::ConsoleType);
NDS::EjectCart(); NDS::EjectCart();
LoadBIOSFiles();
NDS::Reset(); NDS::Reset();
SetBatteryLevels(); SetBatteryLevels();
} }

View File

@ -24,6 +24,7 @@
#include "AREngine.h" #include "AREngine.h"
#include <string> #include <string>
#include <memory>
#include <vector> #include <vector>
namespace ROMManager namespace ROMManager
@ -31,12 +32,14 @@ namespace ROMManager
extern SaveManager* NDSSave; extern SaveManager* NDSSave;
extern SaveManager* GBASave; extern SaveManager* GBASave;
extern std::unique_ptr<SaveManager> FirmwareSave;
QString VerifySetup(); QString VerifySetup();
void Reset(); void Reset();
bool LoadBIOS(); bool LoadBIOS();
void ClearBackupState(); void ClearBackupState();
bool InstallFirmware();
bool LoadROM(QStringList filepath, bool reset); bool LoadROM(QStringList filepath, bool reset);
void EjectCart(); void EjectCart();
bool CartInserted(); bool CartInserted();

View File

@ -58,11 +58,11 @@ SaveManager::~SaveManager()
FlushSecondaryBuffer(); FlushSecondaryBuffer();
} }
if (SecondaryBuffer) delete[] SecondaryBuffer; SecondaryBuffer = nullptr;
delete SecondaryBufferLock; delete SecondaryBufferLock;
if (Buffer) delete[] Buffer; Buffer = nullptr;
} }
std::string SaveManager::GetPath() std::string SaveManager::GetPath()
@ -75,11 +75,17 @@ void SaveManager::SetPath(const std::string& path, bool reload)
Path = path; Path = path;
if (reload) if (reload)
{ { // If we should load whatever file is at the new path...
FileHandle* f = Platform::OpenFile(Path, FileMode::Read);
if (f) if (FileHandle* f = Platform::OpenFile(Path, FileMode::Read))
{ {
FileRead(Buffer, 1, Length, f); if (u32 length = Platform::FileLength(f); length != Length)
{ // If the new file is a different size, we need to re-allocate the buffer.
Length = length;
Buffer = std::make_unique<u8[]>(Length);
}
FileRead(Buffer.get(), 1, Length, f);
CloseFile(f); CloseFile(f);
} }
} }
@ -91,12 +97,10 @@ void SaveManager::RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset,
{ {
if (Length != savelen) if (Length != savelen)
{ {
if (Buffer) delete[] Buffer;
Length = savelen; Length = savelen;
Buffer = new u8[Length]; Buffer = std::make_unique<u8[]>(Length);
memcpy(Buffer, savedata, Length); memcpy(Buffer.get(), savedata, Length);
} }
else else
{ {
@ -127,13 +131,11 @@ void SaveManager::CheckFlush()
if (SecondaryBufferLength != Length) if (SecondaryBufferLength != Length)
{ {
if (SecondaryBuffer) delete[] SecondaryBuffer;
SecondaryBufferLength = Length; SecondaryBufferLength = Length;
SecondaryBuffer = new u8[SecondaryBufferLength]; SecondaryBuffer = std::make_unique<u8[]>(SecondaryBufferLength);
} }
memcpy(SecondaryBuffer, Buffer, Length); memcpy(SecondaryBuffer.get(), Buffer.get(), Length);
FlushRequested = false; FlushRequested = false;
FlushVersion++; FlushVersion++;
@ -172,15 +174,15 @@ void SaveManager::FlushSecondaryBuffer(u8* dst, u32 dstLength)
SecondaryBufferLock->lock(); SecondaryBufferLock->lock();
if (dst) if (dst)
{ {
memcpy(dst, SecondaryBuffer, SecondaryBufferLength); memcpy(dst, SecondaryBuffer.get(), SecondaryBufferLength);
} }
else else
{ {
FileHandle* f = Platform::OpenFile(Path, FileMode::Write); FileHandle* f = Platform::OpenFile(Path, FileMode::Write);
if (f) if (f)
{ {
Log(LogLevel::Info, "SaveManager: Written\n"); FileWrite(SecondaryBuffer.get(), SecondaryBufferLength, 1, f);
FileWrite(SecondaryBuffer, SecondaryBufferLength, 1, f); Log(LogLevel::Info, "SaveManager: Wrote %u bytes to %s\n", SecondaryBufferLength, Path.c_str());
CloseFile(f); CloseFile(f);
} }
} }

View File

@ -23,6 +23,7 @@
#include <unistd.h> #include <unistd.h>
#include <time.h> #include <time.h>
#include <atomic> #include <atomic>
#include <memory>
#include <QThread> #include <QThread>
#include <QMutex> #include <QMutex>
@ -51,12 +52,12 @@ private:
std::atomic_bool Running; std::atomic_bool Running;
u8* Buffer; std::unique_ptr<u8[]> Buffer;
u32 Length; u32 Length;
bool FlushRequested; bool FlushRequested;
QMutex* SecondaryBufferLock; QMutex* SecondaryBufferLock;
u8* SecondaryBuffer; std::unique_ptr<u8[]> SecondaryBuffer;
u32 SecondaryBufferLength; u32 SecondaryBufferLength;
time_t TimeAtLastFlushRequest; time_t TimeAtLastFlushRequest;

View File

@ -513,6 +513,9 @@ void EmuThread::run()
if (ROMManager::GBASave) if (ROMManager::GBASave)
ROMManager::GBASave->CheckFlush(); ROMManager::GBASave->CheckFlush();
if (ROMManager::FirmwareSave)
ROMManager::FirmwareSave->CheckFlush();
if (!oglContext) if (!oglContext)
{ {
FrontBufferLock.lock(); FrontBufferLock.lock();