mirror of
https://github.com/melonDS-emu/melonDS.git
synced 2025-07-23 06:10:03 -06:00
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:

committed by
GitHub

parent
db963aa002
commit
5bfe51e670
563
src/SPI_Firmware.h
Normal file
563
src/SPI_Firmware.h
Normal 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
|
Reference in New Issue
Block a user