From b2fcff97c186cc9db263089acd4810ea7d58517d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 2 Oct 2023 11:54:17 -0400 Subject: [PATCH] Add some structs for files that DSi_NAND reads (#1842) * Add DSiFirmwareSystemSettings * Replace DSiFirmwareSystemSettings::TouchCalibration fields with std::arrays - So assignment can be done in one line * Make DSiFirmwareSystemSettings a union - So its bytes can be accessed * Add a comment * Use DSiFirmwareSystemSettings instead of raw byte offsets * Add definitions for DSiSerialData and DSiHardwareInfoN * Move DSiFirmwareSystemSettings's hash update logic into its own method --- src/DSi.cpp | 10 ++--- src/DSi_NAND.cpp | 57 ++++++++++++------------ src/DSi_NAND.h | 112 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 35 deletions(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index 64bc6d2d..cdec8199 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -530,20 +530,20 @@ void SetupDirectBoot() if (DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308])) { - u8 userdata[0x1B0]; + DSi_NAND::DSiFirmwareSystemSettings userdata {}; DSi_NAND::ReadUserData(userdata); for (u32 i = 0; i < 0x128; i+=4) - ARM9Write32(0x02000400+i, *(u32*)&userdata[0x88+i]); + ARM9Write32(0x02000400+i, *(u32*)&userdata.Bytes[0x88+i]); - u8 hwinfoS[0xA4]; - u8 hwinfoN[0x9C]; + DSi_NAND::DSiSerialData hwinfoS {}; + DSi_NAND::DSiHardwareInfoN hwinfoN; DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN); for (u32 i = 0; i < 0x14; i+=4) ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); for (u32 i = 0; i < 0x18; i+=4) - ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS[0x88+i]); + ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS.Bytes[0x88+i]); DSi_NAND::DeInit(); } diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index b7ab6205..7c3e7453 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -486,7 +486,7 @@ bool ESDecrypt(u8* data, u32 len) } -void ReadHardwareInfo(u8* dataS, u8* dataN) +void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) { FF_FIL file; FRESULT res; @@ -495,20 +495,20 @@ void ReadHardwareInfo(u8* dataS, u8* dataN) res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ); if (res == FR_OK) { - f_read(&file, dataS, 0xA4, &nread); + f_read(&file, &dataS, sizeof(DSiSerialData), &nread); f_close(&file); } res = f_open(&file, "0:/sys/HWINFO_N.dat", FA_OPEN_EXISTING | FA_READ); if (res == FR_OK) { - f_read(&file, dataN, 0x9C, &nread); + f_read(&file, dataN.data(), sizeof(dataN), &nread); f_close(&file); } } -void ReadUserData(u8* data) +void ReadUserData(DSiFirmwareSystemSettings& data) { FF_FIL file; FRESULT res; @@ -553,7 +553,7 @@ void ReadUserData(u8* data) } f_lseek(&file, 0); - f_read(&file, data, 0x1B0, &nread); + f_read(&file, &data, sizeof(DSiFirmwareSystemSettings), &nread); f_close(&file); } @@ -574,10 +574,10 @@ void PatchUserData() continue; } - u8 contents[0x1B0]; + DSiFirmwareSystemSettings contents; u32 nres; f_lseek(&file, 0); - f_read(&file, contents, 0x1B0, &nres); + f_read(&file, &contents, sizeof(DSiFirmwareSystemSettings), &nres); // override user settings, if needed if (Platform::GetConfigBool(Platform::Firm_OverrideSettings)) @@ -586,46 +586,39 @@ void PatchUserData() std::string orig_username = Platform::GetConfigString(Platform::Firm_Username); std::u16string username = std::wstring_convert, char16_t>{}.from_bytes(orig_username); size_t usernameLength = std::min(username.length(), (size_t) 10); - memset(contents + 0xD0, 0, 11 * sizeof(char16_t)); - memcpy(contents + 0xD0, username.data(), usernameLength * sizeof(char16_t)); + memset(&contents.Nickname, 0, sizeof(contents.Nickname)); + memcpy(&contents.Nickname, username.data(), usernameLength * sizeof(char16_t)); // setting language - contents[0x8E] = Platform::GetConfigInt(Platform::Firm_Language); + contents.Language = static_cast(Platform::GetConfigInt(Platform::Firm_Language)); // setting up color - contents[0xCC] = Platform::GetConfigInt(Platform::Firm_Color); + contents.FavoriteColor = Platform::GetConfigInt(Platform::Firm_Color); // setting up birthday - contents[0xCE] = Platform::GetConfigInt(Platform::Firm_BirthdayMonth); - contents[0xCF] = Platform::GetConfigInt(Platform::Firm_BirthdayDay); + contents.BirthdayMonth = Platform::GetConfigInt(Platform::Firm_BirthdayMonth); + contents.BirthdayDay = Platform::GetConfigInt(Platform::Firm_BirthdayDay); // setup message std::string orig_message = Platform::GetConfigString(Platform::Firm_Message); std::u16string message = std::wstring_convert, char16_t>{}.from_bytes(orig_message); size_t messageLength = std::min(message.length(), (size_t) 26); - memset(contents + 0xE6, 0, 27 * sizeof(char16_t)); - memcpy(contents + 0xE6, message.data(), messageLength * sizeof(char16_t)); + memset(&contents.Message, 0, sizeof(contents.Message)); + memcpy(&contents.Message, message.data(), messageLength * sizeof(char16_t)); // TODO: make other items configurable? } // fix touchscreen coords - *(u16*)&contents[0xB8] = 0; - *(u16*)&contents[0xBA] = 0; - contents[0xBC] = 0; - contents[0xBD] = 0; - *(u16*)&contents[0xBE] = 255<<4; - *(u16*)&contents[0xC0] = 191<<4; - contents[0xC2] = 255; - contents[0xC3] = 191; + contents.TouchCalibrationADC1 = {0, 0}; + contents.TouchCalibrationPixel1 = {0, 0}; + contents.TouchCalibrationADC2 = {255 << 4, 191 << 4}; + contents.TouchCalibrationPixel2 = {255, 191}; - SHA1_CTX sha; - SHA1Init(&sha); - SHA1Update(&sha, &contents[0x88], 0x128); - SHA1Final(&contents[0], &sha); + contents.UpdateHash(); f_lseek(&file, 0); - f_write(&file, contents, 0x1B0, &nres); + f_write(&file, &contents, sizeof(DSiFirmwareSystemSettings), &nres); f_close(&file); } @@ -1318,4 +1311,12 @@ bool ExportTitleData(u32 category, u32 titleid, int type, const char* file) return ExportFile(fname, file); } +void DSiFirmwareSystemSettings::UpdateHash() +{ + SHA1_CTX sha; + SHA1Init(&sha); + SHA1Update(&sha, &Bytes[0x88], 0x128); + SHA1Final(Hash.data(), &sha); +} + } diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 14599b26..9bff8f2b 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -22,6 +22,8 @@ #include "types.h" #include "NDS_Header.h" #include "DSi_TMD.h" +#include "SPI_Firmware.h" +#include #include #include @@ -35,6 +37,10 @@ enum TitleData_BannerSav, }; +union DSiFirmwareSystemSettings; +union DSiSerialData; +using DSiHardwareInfoN = std::array; + bool Init(u8* es_keyY); void DeInit(); @@ -42,9 +48,9 @@ Platform::FileHandle* GetFile(); void GetIDs(u8* emmc_cid, u64& consoleid); -void ReadHardwareInfo(u8* dataS, u8* dataN); +void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN); -void ReadUserData(u8* data); +void ReadUserData(DSiFirmwareSystemSettings& data); void PatchUserData(); void ListTitles(u32 category, std::vector& titlelist); @@ -58,6 +64,108 @@ u32 GetTitleDataMask(u32 category, u32 titleid); bool ImportTitleData(u32 category, u32 titleid, int type, const char* file); bool ExportTitleData(u32 category, u32 titleid, int type, const char* file); +typedef std::array SHA1Hash; +typedef std::array TitleID; + +/// Firmware settings for the DSi, saved to the NAND as TWLCFG0.dat or TWLCFG1.dat. +/// The DSi mirrors this information to its own firmware for compatibility with NDS games. +/// @note The file is normally 16KiB, but only the first 432 bytes are used; +/// the rest is FF-padded. +/// This struct excludes the padding. +/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaresystemsettingsdatafiles +union DSiFirmwareSystemSettings +{ + struct + { + SHA1Hash Hash; + u8 Zero00[108]; + u8 Version; + u8 UpdateCounter; + u8 Zero01[2]; + u32 BelowRAMAreaSize; + u32 ConfigFlags; + u8 Zero02; + u8 CountryCode; + SPI_Firmware::Language Language; + u8 RTCYear; + u32 RTCOffset; + u8 Zero3[4]; + u8 EULAVersion; + u8 Zero04[9]; + u8 AlarmHour; + u8 AlarmMinute; + u8 Zero05[2]; + bool AlarmEnable; + u8 Zero06[2]; + u8 SystemMenuUsedTitleSlots; + u8 SystemMenuFreeTitleSlots; + u8 Unknown0; + u8 Unknown1; + u8 Zero07[3]; + TitleID SystemMenuMostRecentTitleID; + std::array TouchCalibrationADC1; + std::array TouchCalibrationPixel1; + std::array TouchCalibrationADC2; + std::array TouchCalibrationPixel2; + u8 Unknown2[4]; + u8 Zero08[4]; + u8 FavoriteColor; + u8 Zero09; + u8 BirthdayMonth; + u8 BirthdayDay; + char16_t Nickname[11]; + char16_t Message[27]; + u8 ParentalControlsFlags; + u8 Zero10[6]; + u8 ParentalControlsRegion; + u8 ParentalControlsYearsOfAgeRating; + u8 ParentalControlsSecretQuestion; + u8 Unknown3; + u8 Zero11[2]; + char ParentalControlsPIN[5]; + char16_t ParentalControlsSecretAnswer[65]; + }; + u8 Bytes[432]; + + void UpdateHash(); +}; + +static_assert(sizeof(DSiFirmwareSystemSettings) == 432, "DSiFirmwareSystemSettings must be exactly 432 bytes"); + +enum class ConsoleRegion : u8 +{ + Japan, + USA, + Europe, + Australia, + China, + Korea, +}; + +/// Data file saved to 0:/sys/HWINFO_S.dat. +/// @note The file is normally 16KiB, but only the first 164 bytes are used; +/// the rest is FF-padded. +/// This struct excludes the padding. +/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaremiscfiles +union DSiSerialData +{ + struct + { + u8 RsaSha1HMAC[0x80]; + u32 Version; + u32 EntrySize; + u32 SupportedLanguages; + u8 Unknown0[4]; + ConsoleRegion Region; + char Serial[12]; + u8 Unknown1[3]; + u8 TitleIDLSBs[4]; + }; + u8 Bytes[164]; +}; + +static_assert(sizeof(DSiSerialData) == 164, "DSiSerialData must be exactly 164 bytes"); + } #endif // DSI_NAND_H