update branch season2 to modern standards

This commit is contained in:
Arisotura 2023-10-07 16:13:09 +02:00
parent 30186029e8
commit 22277d8e54
37 changed files with 3740 additions and 2512 deletions

View File

@ -330,6 +330,7 @@ void DeInit()
ARMJIT_Memory::DeInit();
delete JITCompiler;
JITCompiler = nullptr;
}
void Reset()

View File

@ -308,7 +308,7 @@ HANDLE MemoryFile;
LPVOID ExceptionHandlerHandle;
#else
u8* MemoryBase;
int MemoryFile;
int MemoryFile = -1;
#endif
bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size)
@ -811,25 +811,58 @@ void DeInit()
{
#if defined(__SWITCH__)
virtmemLock();
virtmemRemoveReservation(FastMem9Reservation);
virtmemRemoveReservation(FastMem7Reservation);
if (FastMem9Reservation)
virtmemRemoveReservation(FastMem9Reservation);
if (FastMem7Reservation)
virtmemRemoveReservation(FastMem7Reservation);
FastMem9Reservation = nullptr;
FastMem7Reservation = nullptr;
virtmemUnlock();
svcUnmapProcessCodeMemory(envGetOwnProcessHandle(), (u64)MemoryBaseCodeMem, (u64)MemoryBase, MemoryTotalSize);
free(MemoryBase);
MemoryBase = nullptr;
#elif defined(_WIN32)
assert(UnmapViewOfFile(MemoryBase));
CloseHandle(MemoryFile);
if (MemoryBase)
{
bool viewUnmapped = UnmapViewOfFile(MemoryBase);
assert(viewUnmapped);
MemoryBase = nullptr;
FastMem9Start = nullptr;
FastMem7Start = nullptr;
}
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
if (MemoryFile)
{
CloseHandle(MemoryFile);
MemoryFile = INVALID_HANDLE_VALUE;
}
if (ExceptionHandlerHandle)
{
RemoveVectoredExceptionHandler(ExceptionHandlerHandle);
ExceptionHandlerHandle = nullptr;
}
#else
sigaction(SIGSEGV, &OldSaSegv, nullptr);
#ifdef __APPLE__
sigaction(SIGBUS, &OldSaBus, nullptr);
#endif
if (MemoryBase)
{
munmap(MemoryBase, MemoryTotalSize);
MemoryBase = nullptr;
FastMem9Start = nullptr;
FastMem7Start = nullptr;
}
munmap(MemoryBase, MemoryTotalSize);
close(MemoryFile);
if (MemoryFile >= 0)
{
close(MemoryFile);
MemoryFile = -1;
}
#if defined(__ANDROID__)
if (Libandroid)

View File

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

View File

@ -128,6 +128,10 @@ void DeInit()
delete[] NWRAM_A;
delete[] NWRAM_B;
delete[] NWRAM_C;
NWRAM_A = nullptr;
NWRAM_B = nullptr;
NWRAM_C = nullptr;
#endif
DSi_I2C::DeInit();
@ -135,10 +139,16 @@ void DeInit()
DSi_AES::DeInit();
DSi_DSP::DeInit();
for (int i = 0; i < 8; i++) delete NDMAs[i];
for (int i = 0; i < 8; i++)
{
delete NDMAs[i];
NDMAs[i] = nullptr;
}
delete SDMMC;
SDMMC = nullptr;
delete SDIO;
SDIO = nullptr;
}
void Reset()
@ -520,30 +530,30 @@ 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();
}
u8 nwifiver = SPI_Firmware::GetNWifiVersion();
ARM9Write8(0x020005E0, nwifiver);
SPI_Firmware::WifiBoard nwifiver = SPI_Firmware::GetFirmware()->Header().WifiBoard;
ARM9Write8(0x020005E0, static_cast<u8>(nwifiver));
// TODO: these should be taken from the wifi firmware in NAND
// but, hey, this works too.
if (nwifiver == 1)
if (nwifiver == SPI_Firmware::WifiBoard::W015)
{
ARM9Write16(0x020005E2, 0xB57E);
ARM9Write32(0x020005E4, 0x00500400);
@ -716,64 +726,6 @@ void SoftReset()
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()
{
Log(LogLevel::Info, "Loading DSi NAND\n");

View File

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

View File

@ -63,6 +63,9 @@ void DeInit()
{
delete Camera0;
delete Camera1;
Camera0 = nullptr;
Camera1 = nullptr;
}
void Reset()

View File

@ -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);
}
@ -564,7 +564,7 @@ void PatchUserData()
for (int i = 0; i < 2; i++)
{
char filename[64];
sprintf(filename, "0:/shared1/TWLCFG%d.dat", i);
snprintf(filename, sizeof(filename), "0:/shared1/TWLCFG%d.dat", i);
FF_FIL file;
res = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ | FA_WRITE);
@ -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<std::codecvt_utf8_utf16<char16_t>, 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<SPI_Firmware::Language>(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<std::codecvt_utf8_utf16<char16_t>, 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);
}
@ -648,7 +641,7 @@ void debug_listfiles(const char* path)
if (!info.fname[0]) break;
char fullname[512];
sprintf(fullname, "%s/%s", path, info.fname);
snprintf(fullname, sizeof(fullname), "%s/%s", path, info.fname);
Log(LogLevel::Debug, "[%c] %s\n", (info.fattrib&AM_DIR)?'D':'F', fullname);
if (info.fattrib & AM_DIR)
@ -816,7 +809,7 @@ void RemoveDir(const char* path)
if (!info.fname[0]) break;
char fullname[512];
sprintf(fullname, "%s/%s", path, info.fname);
snprintf(fullname, sizeof(fullname), "%s/%s", path, info.fname);
if (info.fattrib & AM_RDO)
f_chmod(path, 0, AM_RDO);
@ -850,7 +843,7 @@ u32 GetTitleVersion(u32 category, u32 titleid)
{
FRESULT res;
char path[256];
sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid);
snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/title.tmd", category, titleid);
FF_FIL file;
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
if (res != FR_OK)
@ -872,7 +865,7 @@ void ListTitles(u32 category, std::vector<u32>& titlelist)
FF_DIR titledir;
char path[256];
sprintf(path, "0:/title/%08x", category);
snprintf(path, sizeof(path), "0:/title/%08x", category);
res = f_opendir(&titledir, path);
if (res != FR_OK)
{
@ -898,7 +891,7 @@ void ListTitles(u32 category, std::vector<u32>& titlelist)
if (version == 0xFFFFFFFF)
continue;
sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
FF_FILINFO appinfo;
res = f_stat(path, &appinfo);
if (res != FR_OK)
@ -918,7 +911,7 @@ void ListTitles(u32 category, std::vector<u32>& titlelist)
bool TitleExists(u32 category, u32 titleid)
{
char path[256];
sprintf(path, "0:/title/%08x/%08x/content/title.tmd", category, titleid);
snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/title.tmd", category, titleid);
FRESULT res = f_stat(path, nullptr);
return (res == FR_OK);
@ -933,7 +926,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND
FRESULT res;
char path[256];
sprintf(path, "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/%08x.app", category, titleid, version);
FF_FIL file;
res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ);
if (res != FR_OK)
@ -1098,10 +1091,10 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat
u32 nwrite;
// ticket
sprintf(fname, "0:/ticket/%08x", titleid0);
snprintf(fname, sizeof(fname), "0:/ticket/%08x", titleid0);
f_mkdir(fname);
sprintf(fname, "0:/ticket/%08x/%08x.tik", titleid0, titleid1);
snprintf(fname, sizeof(fname), "0:/ticket/%08x/%08x.tik", titleid0, titleid1);
if (!CreateTicket(fname, tmd.GetCategoryNoByteswap(), tmd.GetIDNoByteswap(), header.ROMVersion))
return false;
@ -1109,29 +1102,29 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat
// folder
sprintf(fname, "0:/title/%08x", titleid0);
snprintf(fname, sizeof(fname), "0:/title/%08x", titleid0);
f_mkdir(fname);
sprintf(fname, "0:/title/%08x/%08x", titleid0, titleid1);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x", titleid0, titleid1);
f_mkdir(fname);
sprintf(fname, "0:/title/%08x/%08x/content", titleid0, titleid1);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content", titleid0, titleid1);
f_mkdir(fname);
sprintf(fname, "0:/title/%08x/%08x/data", titleid0, titleid1);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data", titleid0, titleid1);
f_mkdir(fname);
// data
sprintf(fname, "0:/title/%08x/%08x/data/public.sav", titleid0, titleid1);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", titleid0, titleid1);
if (!CreateSaveFile(fname, header.DSiPublicSavSize))
return false;
sprintf(fname, "0:/title/%08x/%08x/data/private.sav", titleid0, titleid1);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", titleid0, titleid1);
if (!CreateSaveFile(fname, header.DSiPrivateSavSize))
return false;
if (header.AppFlags & 0x04)
{
// custom banner file
sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", titleid0, titleid1);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", titleid0, titleid1);
res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE);
if (res != FR_OK)
{
@ -1148,7 +1141,7 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat
// TMD
sprintf(fname, "0:/title/%08x/%08x/content/title.tmd", titleid0, titleid1);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/title.tmd", titleid0, titleid1);
res = f_open(&file, fname, FA_CREATE_ALWAYS | FA_WRITE);
if (res != FR_OK)
{
@ -1191,7 +1184,7 @@ bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool re
// executable
char fname[128];
sprintf(fname, "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
if (!ImportFile(fname, appfile))
{
Log(LogLevel::Error, "ImportTitle: failed to create executable\n");
@ -1227,7 +1220,7 @@ bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata&
// executable
char fname[128];
sprintf(fname, "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/content/%08x.app", titleid0, titleid1, version);
if (!ImportFile(fname, app, appLength))
{
Log(LogLevel::Error, "ImportTitle: failed to create executable\n");
@ -1243,10 +1236,10 @@ void DeleteTitle(u32 category, u32 titleid)
{
char fname[128];
sprintf(fname, "0:/ticket/%08x/%08x.tik", category, titleid);
snprintf(fname, sizeof(fname), "0:/ticket/%08x/%08x.tik", category, titleid);
RemoveFile(fname);
sprintf(fname, "0:/title/%08x/%08x", category, titleid);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x", category, titleid);
RemoveDir(fname);
}
@ -1274,15 +1267,15 @@ bool ImportTitleData(u32 category, u32 titleid, int type, const char* file)
switch (type)
{
case TitleData_PublicSav:
sprintf(fname, "0:/title/%08x/%08x/data/public.sav", category, titleid);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", category, titleid);
break;
case TitleData_PrivateSav:
sprintf(fname, "0:/title/%08x/%08x/data/private.sav", category, titleid);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", category, titleid);
break;
case TitleData_BannerSav:
sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", category, titleid);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", category, titleid);
break;
default:
@ -1300,15 +1293,15 @@ bool ExportTitleData(u32 category, u32 titleid, int type, const char* file)
switch (type)
{
case TitleData_PublicSav:
sprintf(fname, "0:/title/%08x/%08x/data/public.sav", category, titleid);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/public.sav", category, titleid);
break;
case TitleData_PrivateSav:
sprintf(fname, "0:/title/%08x/%08x/data/private.sav", category, titleid);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/private.sav", category, titleid);
break;
case TitleData_BannerSav:
sprintf(fname, "0:/title/%08x/%08x/data/banner.sav", category, titleid);
snprintf(fname, sizeof(fname), "0:/title/%08x/%08x/data/banner.sav", category, titleid);
break;
default:
@ -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);
}
}

View File

@ -22,6 +22,8 @@
#include "types.h"
#include "NDS_Header.h"
#include "DSi_TMD.h"
#include "SPI_Firmware.h"
#include <array>
#include <vector>
#include <string>
@ -35,6 +37,10 @@ enum
TitleData_BannerSav,
};
union DSiFirmwareSystemSettings;
union DSiSerialData;
using DSiHardwareInfoN = std::array<u8, 0x9C>;
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<u32>& 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<u8, 20> SHA1Hash;
typedef std::array<u8, 8> 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<u16, 2> TouchCalibrationADC1;
std::array<u8, 2> TouchCalibrationPixel1;
std::array<u16, 2> TouchCalibrationADC2;
std::array<u8, 2> 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

View File

@ -147,6 +147,7 @@ DSi_NWifi::~DSi_NWifi()
void DSi_NWifi::Reset()
{
using namespace SPI_Firmware;
TransferCmd = 0xFFFFFFFF;
RemSize = 0;
@ -163,26 +164,26 @@ void DSi_NWifi::Reset()
for (int i = 0; i < 9; i++)
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",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
u8 type = SPI_Firmware::GetNWifiVersion();
WifiBoard type = GetFirmware()->Header().WifiBoard;
switch (type)
{
case 1: // AR6002
case WifiBoard::W015: // AR6002
ROMID = 0x20000188;
ChipID = 0x02000001;
HostIntAddr = 0x00500400;
break;
case 2: // AR6013
case WifiBoard::W024: // AR6013
ROMID = 0x23000024;
ChipID = 0x0D000000;
HostIntAddr = 0x00520000;
break;
case 3: // AR6014 (3DS)
case WifiBoard::W028: // AR6014 (3DS)
ROMID = 0x2300006F;
ChipID = 0x0D000001;
HostIntAddr = 0x00520000;
@ -190,7 +191,7 @@ void DSi_NWifi::Reset()
break;
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;
ChipID = 0x02000001;
HostIntAddr = 0x00500400;
@ -201,7 +202,7 @@ void DSi_NWifi::Reset()
*(u32*)&EEPROM[0x000] = 0x300;
*(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;
memset(&EEPROM[0x03C], 0xFF, 0x70);
@ -894,8 +895,9 @@ void DSi_NWifi::HTC_Command()
case 0x0004: // setup complete
{
SPI_Firmware::MacAddress mac = SPI_Firmware::GetFirmware()->Header().MacAddress;
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[7] = 0;
*(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

@ -171,6 +171,15 @@ void DeInit()
if (Framebuffer[0][1]) delete[] Framebuffer[0][1];
if (Framebuffer[1][0]) delete[] Framebuffer[1][0];
if (Framebuffer[1][1]) delete[] Framebuffer[1][1];
Framebuffer[0][0] = nullptr;
Framebuffer[0][1] = nullptr;
Framebuffer[1][0] = nullptr;
Framebuffer[1][1] = nullptr;
#ifdef OGLRENDERER_ENABLED
CurGLCompositor = nullptr;
#endif
}
void ResetVRAMCache()
@ -388,20 +397,18 @@ void InitRenderer(int renderer)
#ifdef OGLRENDERER_ENABLED
if (renderer == 1)
{
CurGLCompositor = std::make_unique<GLCompositor>();
// Create opengl rendrerer
if (!CurGLCompositor->Init())
CurGLCompositor = GLCompositor::New();
// Create opengl renderer
if (!CurGLCompositor)
{
// Fallback on software renderer
renderer = 0;
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
GPU3D::CurrentRenderer->Init();
}
GPU3D::CurrentRenderer = std::make_unique<GPU3D::GLRenderer>();
if (!GPU3D::CurrentRenderer->Init())
GPU3D::CurrentRenderer = GPU3D::GLRenderer::New();
if (!GPU3D::CurrentRenderer)
{
// Fallback on software renderer
CurGLCompositor->DeInit();
CurGLCompositor.reset();
renderer = 0;
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
@ -411,7 +418,6 @@ void InitRenderer(int renderer)
#endif
{
GPU3D::CurrentRenderer = std::make_unique<GPU3D::SoftRenderer>();
GPU3D::CurrentRenderer->Init();
}
Renderer = renderer;
@ -419,12 +425,12 @@ void InitRenderer(int renderer)
void DeInitRenderer()
{
GPU3D::CurrentRenderer->DeInit();
// Delete the 3D renderer, if it exists
GPU3D::CurrentRenderer.reset();
#ifdef OGLRENDERER_ENABLED
if (Renderer == 1)
{
CurGLCompositor->DeInit();
}
// Delete the compositor, if one exists
CurGLCompositor.reset();
#endif
}

View File

@ -286,6 +286,7 @@ bool Init()
void DeInit()
{
CurrentRenderer = nullptr;
}
void ResetRenderingState()

View File

@ -137,14 +137,11 @@ void Write32(u32 addr, u32 val);
class Renderer3D
{
public:
Renderer3D(bool Accelerated);
virtual ~Renderer3D() {};
virtual ~Renderer3D() = default;
Renderer3D(const Renderer3D&) = delete;
Renderer3D& operator=(const Renderer3D&) = delete;
virtual bool Init() = 0;
virtual void DeInit() = 0;
virtual void Reset() = 0;
// This "Accelerated" flag currently communicates if the framebuffer should
@ -159,6 +156,8 @@ public:
virtual void RenderFrame() = 0;
virtual void RestartFrame() {};
virtual u32* GetLine(int line) = 0;
protected:
Renderer3D(bool Accelerated);
};
extern int Renderer;

View File

@ -18,6 +18,7 @@
#include "GPU3D_OpenGL.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "NDS.h"
@ -96,14 +97,20 @@ void SetupDefaultTexParams(GLuint tex)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
GLRenderer::GLRenderer()
: Renderer3D(true)
GLRenderer::GLRenderer() noexcept : Renderer3D(true)
{
// GLRenderer::New() will be used to actually initialize the renderer;
// The various glDelete* functions silently ignore invalid IDs,
// so we can just let the destructor clean up a half-initialized renderer.
}
bool GLRenderer::Init()
std::unique_ptr<GLRenderer> GLRenderer::New() noexcept
{
GLint uni_id;
assert(glEnable != nullptr);
// Will be returned if the initialization succeeds,
// or cleaned up via RAII if it fails.
std::unique_ptr<GLRenderer> result = std::unique_ptr<GLRenderer>(new GLRenderer());
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
@ -112,86 +119,93 @@ bool GLRenderer::Init()
glClearDepth(1.0);
if (!OpenGL::BuildShaderProgram(kClearVS, kClearFS, ClearShaderPlain, "ClearShader"))
return false;
if (!OpenGL::BuildShaderProgram(kClearVS, kClearFS, result->ClearShaderPlain, "ClearShader"))
return nullptr;
glBindAttribLocation(ClearShaderPlain[2], 0, "vPosition");
glBindFragDataLocation(ClearShaderPlain[2], 0, "oColor");
glBindFragDataLocation(ClearShaderPlain[2], 1, "oAttr");
glBindAttribLocation(result->ClearShaderPlain[2], 0, "vPosition");
glBindFragDataLocation(result->ClearShaderPlain[2], 0, "oColor");
glBindFragDataLocation(result->ClearShaderPlain[2], 1, "oAttr");
if (!OpenGL::LinkShaderProgram(ClearShaderPlain))
return false;
if (!OpenGL::LinkShaderProgram(result->ClearShaderPlain))
return nullptr;
ClearUniformLoc[0] = glGetUniformLocation(ClearShaderPlain[2], "uColor");
ClearUniformLoc[1] = glGetUniformLocation(ClearShaderPlain[2], "uDepth");
ClearUniformLoc[2] = glGetUniformLocation(ClearShaderPlain[2], "uOpaquePolyID");
ClearUniformLoc[3] = glGetUniformLocation(ClearShaderPlain[2], "uFogFlag");
result->ClearUniformLoc[0] = glGetUniformLocation(result->ClearShaderPlain[2], "uColor");
result->ClearUniformLoc[1] = glGetUniformLocation(result->ClearShaderPlain[2], "uDepth");
result->ClearUniformLoc[2] = glGetUniformLocation(result->ClearShaderPlain[2], "uOpaquePolyID");
result->ClearUniformLoc[3] = glGetUniformLocation(result->ClearShaderPlain[2], "uFogFlag");
memset(RenderShader, 0, sizeof(RenderShader));
memset(result->RenderShader, 0, sizeof(RenderShader));
if (!BuildRenderShader(0,
kRenderVS_Z, kRenderFS_ZO)) return false;
if (!BuildRenderShader(RenderFlag_WBuffer,
kRenderVS_W, kRenderFS_WO)) return false;
if (!BuildRenderShader(RenderFlag_Edge,
kRenderVS_Z, kRenderFS_ZE)) return false;
if (!BuildRenderShader(RenderFlag_Edge | RenderFlag_WBuffer,
kRenderVS_W, kRenderFS_WE)) return false;
if (!BuildRenderShader(RenderFlag_Trans,
kRenderVS_Z, kRenderFS_ZT)) return false;
if (!BuildRenderShader(RenderFlag_Trans | RenderFlag_WBuffer,
kRenderVS_W, kRenderFS_WT)) return false;
if (!BuildRenderShader(RenderFlag_ShadowMask,
kRenderVS_Z, kRenderFS_ZSM)) return false;
if (!BuildRenderShader(RenderFlag_ShadowMask | RenderFlag_WBuffer,
kRenderVS_W, kRenderFS_WSM)) return false;
if (!result->BuildRenderShader(0, kRenderVS_Z, kRenderFS_ZO))
return nullptr;
if (!result->BuildRenderShader(RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WO))
return nullptr;
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, FinalPassEdgeShader, "FinalPassEdgeShader"))
return false;
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, FinalPassFogShader, "FinalPassFogShader"))
return false;
if (!result->BuildRenderShader(RenderFlag_Edge, kRenderVS_Z, kRenderFS_ZE))
return nullptr;
glBindAttribLocation(FinalPassEdgeShader[2], 0, "vPosition");
glBindFragDataLocation(FinalPassEdgeShader[2], 0, "oColor");
if (!result->BuildRenderShader(RenderFlag_Edge | RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WE))
return nullptr;
if (!OpenGL::LinkShaderProgram(FinalPassEdgeShader))
return false;
if (!result->BuildRenderShader(RenderFlag_Trans, kRenderVS_Z, kRenderFS_ZT))
return nullptr;
uni_id = glGetUniformBlockIndex(FinalPassEdgeShader[2], "uConfig");
glUniformBlockBinding(FinalPassEdgeShader[2], uni_id, 0);
if (!result->BuildRenderShader(RenderFlag_Trans | RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WT))
return nullptr;
glUseProgram(FinalPassEdgeShader[2]);
if (!result->BuildRenderShader(RenderFlag_ShadowMask, kRenderVS_Z, kRenderFS_ZSM))
return nullptr;
uni_id = glGetUniformLocation(FinalPassEdgeShader[2], "DepthBuffer");
if (!result->BuildRenderShader(RenderFlag_ShadowMask | RenderFlag_WBuffer, kRenderVS_W, kRenderFS_WSM))
return nullptr;
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassEdgeFS, result->FinalPassEdgeShader, "FinalPassEdgeShader"))
return nullptr;
if (!OpenGL::BuildShaderProgram(kFinalPassVS, kFinalPassFogFS, result->FinalPassFogShader, "FinalPassFogShader"))
return nullptr;
glBindAttribLocation(result->FinalPassEdgeShader[2], 0, "vPosition");
glBindFragDataLocation(result->FinalPassEdgeShader[2], 0, "oColor");
if (!OpenGL::LinkShaderProgram(result->FinalPassEdgeShader))
return nullptr;
GLint uni_id = glGetUniformBlockIndex(result->FinalPassEdgeShader[2], "uConfig");
glUniformBlockBinding(result->FinalPassEdgeShader[2], uni_id, 0);
glUseProgram(result->FinalPassEdgeShader[2]);
uni_id = glGetUniformLocation(result->FinalPassEdgeShader[2], "DepthBuffer");
glUniform1i(uni_id, 0);
uni_id = glGetUniformLocation(FinalPassEdgeShader[2], "AttrBuffer");
uni_id = glGetUniformLocation(result->FinalPassEdgeShader[2], "AttrBuffer");
glUniform1i(uni_id, 1);
glBindAttribLocation(FinalPassFogShader[2], 0, "vPosition");
glBindFragDataLocation(FinalPassFogShader[2], 0, "oColor");
glBindAttribLocation(result->FinalPassFogShader[2], 0, "vPosition");
glBindFragDataLocation(result->FinalPassFogShader[2], 0, "oColor");
if (!OpenGL::LinkShaderProgram(FinalPassFogShader))
return false;
if (!OpenGL::LinkShaderProgram(result->FinalPassFogShader))
return nullptr;
uni_id = glGetUniformBlockIndex(FinalPassFogShader[2], "uConfig");
glUniformBlockBinding(FinalPassFogShader[2], uni_id, 0);
uni_id = glGetUniformBlockIndex(result->FinalPassFogShader[2], "uConfig");
glUniformBlockBinding(result->FinalPassFogShader[2], uni_id, 0);
glUseProgram(FinalPassFogShader[2]);
glUseProgram(result->FinalPassFogShader[2]);
uni_id = glGetUniformLocation(FinalPassFogShader[2], "DepthBuffer");
uni_id = glGetUniformLocation(result->FinalPassFogShader[2], "DepthBuffer");
glUniform1i(uni_id, 0);
uni_id = glGetUniformLocation(FinalPassFogShader[2], "AttrBuffer");
uni_id = glGetUniformLocation(result->FinalPassFogShader[2], "AttrBuffer");
glUniform1i(uni_id, 1);
memset(&ShaderConfig, 0, sizeof(ShaderConfig));
memset(&result->ShaderConfig, 0, sizeof(ShaderConfig));
glGenBuffers(1, &ShaderConfigUBO);
glBindBuffer(GL_UNIFORM_BUFFER, ShaderConfigUBO);
static_assert((sizeof(ShaderConfig) & 15) == 0, "");
glBufferData(GL_UNIFORM_BUFFER, sizeof(ShaderConfig), &ShaderConfig, GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, ShaderConfigUBO);
glGenBuffers(1, &result->ShaderConfigUBO);
glBindBuffer(GL_UNIFORM_BUFFER, result->ShaderConfigUBO);
static_assert((sizeof(ShaderConfig) & 15) == 0);
glBufferData(GL_UNIFORM_BUFFER, sizeof(ShaderConfig), &result->ShaderConfig, GL_STATIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, 0, result->ShaderConfigUBO);
float clearvtx[6*2] =
@ -205,22 +219,22 @@ bool GLRenderer::Init()
1.0, 1.0
};
glGenBuffers(1, &ClearVertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, ClearVertexBufferID);
glGenBuffers(1, &result->ClearVertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, result->ClearVertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(clearvtx), clearvtx, GL_STATIC_DRAW);
glGenVertexArrays(1, &ClearVertexArrayID);
glBindVertexArray(ClearVertexArrayID);
glGenVertexArrays(1, &result->ClearVertexArrayID);
glBindVertexArray(result->ClearVertexArrayID);
glEnableVertexAttribArray(0); // position
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0));
glGenBuffers(1, &VertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, VertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexBuffer), NULL, GL_DYNAMIC_DRAW);
glGenBuffers(1, &result->VertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, result->VertexBufferID);
glBufferData(GL_ARRAY_BUFFER, sizeof(VertexBuffer), nullptr, GL_DYNAMIC_DRAW);
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
glGenVertexArrays(1, &result->VertexArrayID);
glBindVertexArray(result->VertexArrayID);
glEnableVertexAttribArray(0); // position
glVertexAttribIPointer(0, 4, GL_UNSIGNED_SHORT, 7*4, (void*)(0));
glEnableVertexAttribArray(1); // color
@ -230,43 +244,43 @@ bool GLRenderer::Init()
glEnableVertexAttribArray(3); // attrib
glVertexAttribIPointer(3, 3, GL_UNSIGNED_INT, 7*4, (void*)(4*4));
glGenBuffers(1, &IndexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(IndexBuffer), NULL, GL_DYNAMIC_DRAW);
glGenBuffers(1, &result->IndexBufferID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, result->IndexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(IndexBuffer), nullptr, GL_DYNAMIC_DRAW);
glGenFramebuffers(4, &FramebufferID[0]);
glBindFramebuffer(GL_FRAMEBUFFER, FramebufferID[0]);
glGenFramebuffers(4, &result->FramebufferID[0]);
glBindFramebuffer(GL_FRAMEBUFFER, result->FramebufferID[0]);
glGenTextures(8, &FramebufferTex[0]);
FrontBuffer = 0;
glGenTextures(8, &result->FramebufferTex[0]);
result->FrontBuffer = 0;
// color buffers
SetupDefaultTexParams(FramebufferTex[0]);
SetupDefaultTexParams(FramebufferTex[1]);
SetupDefaultTexParams(result->FramebufferTex[0]);
SetupDefaultTexParams(result->FramebufferTex[1]);
// depth/stencil buffer
SetupDefaultTexParams(FramebufferTex[4]);
SetupDefaultTexParams(FramebufferTex[6]);
SetupDefaultTexParams(result->FramebufferTex[4]);
SetupDefaultTexParams(result->FramebufferTex[6]);
// attribute buffer
// R: opaque polyID (for edgemarking)
// G: edge flag
// B: fog flag
SetupDefaultTexParams(FramebufferTex[5]);
SetupDefaultTexParams(FramebufferTex[7]);
SetupDefaultTexParams(result->FramebufferTex[5]);
SetupDefaultTexParams(result->FramebufferTex[7]);
// downscale framebuffer for display capture (always 256x192)
SetupDefaultTexParams(FramebufferTex[3]);
SetupDefaultTexParams(result->FramebufferTex[3]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 192, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glEnable(GL_BLEND);
glBlendEquationSeparate(GL_FUNC_ADD, GL_MAX);
glGenBuffers(1, &PixelbufferID);
glGenBuffers(1, &result->PixelbufferID);
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &TexMemID);
glBindTexture(GL_TEXTURE_2D, TexMemID);
glGenTextures(1, &result->TexMemID);
glBindTexture(GL_TEXTURE_2D, result->TexMemID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@ -274,8 +288,8 @@ bool GLRenderer::Init()
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8UI, 1024, 512, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL);
glActiveTexture(GL_TEXTURE1);
glGenTextures(1, &TexPalMemID);
glBindTexture(GL_TEXTURE_2D, TexPalMemID);
glGenTextures(1, &result->TexPalMemID);
glBindTexture(GL_TEXTURE_2D, result->TexPalMemID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
@ -284,11 +298,13 @@ bool GLRenderer::Init()
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
return result;
}
void GLRenderer::DeInit()
GLRenderer::~GLRenderer()
{
assert(glDeleteTextures != nullptr);
glDeleteTextures(1, &TexMemID);
glDeleteTextures(1, &TexPalMemID);

View File

@ -28,10 +28,7 @@ namespace GPU3D
class GLRenderer : public Renderer3D
{
public:
GLRenderer();
virtual ~GLRenderer() override {};
virtual bool Init() override;
virtual void DeInit() override;
virtual ~GLRenderer() override;
virtual void Reset() override;
virtual void SetRenderSettings(GPU::RenderSettings& settings) override;
@ -42,7 +39,11 @@ public:
void SetupAccelFrame();
void PrepareCaptureFrame();
static std::unique_ptr<GLRenderer> New() noexcept;
private:
// Used by New()
GLRenderer() noexcept;
// GL version requirements
// * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS)
@ -62,7 +63,7 @@ private:
u32 RenderKey;
};
RendererPolygon PolygonList[2048];
RendererPolygon PolygonList[2048] {};
bool BuildRenderShader(u32 flags, const char* vs, const char* fs);
void UseRenderShader(u32 flags);
@ -83,13 +84,13 @@ private:
};
GLuint ClearShaderPlain[3];
GLuint ClearShaderPlain[3] {};
GLuint RenderShader[16][3];
GLuint RenderShader[16][3] {};
GLuint CurShaderID = -1;
GLuint FinalPassEdgeShader[3];
GLuint FinalPassFogShader[3];
GLuint FinalPassEdgeShader[3] {};
GLuint FinalPassFogShader[3] {};
// std140 compliant structure
struct
@ -104,13 +105,13 @@ private:
u32 uFogOffset; // int 304 / 1
u32 uFogShift; // int 305 / 1
u32 _pad1[2]; // int 306 / 2
} ShaderConfig;
} ShaderConfig {};
GLuint ShaderConfigUBO;
int NumFinalPolys, NumOpaqueFinalPolys;
GLuint ShaderConfigUBO {};
int NumFinalPolys {}, NumOpaqueFinalPolys {};
GLuint ClearVertexBufferID, ClearVertexArrayID;
GLint ClearUniformLoc[4];
GLuint ClearVertexBufferID = 0, ClearVertexArrayID {};
GLint ClearUniformLoc[4] {};
// vertex buffer
// * XYZW: 4x16bit
@ -124,28 +125,28 @@ private:
// * bit8: front-facing (?)
// * bit9: W-buffering (?)
GLuint VertexBufferID;
u32 VertexBuffer[10240 * 7];
u32 NumVertices;
GLuint VertexBufferID {};
u32 VertexBuffer[10240 * 7] {};
u32 NumVertices {};
GLuint VertexArrayID;
GLuint IndexBufferID;
u16 IndexBuffer[2048 * 40];
u32 NumIndices, NumEdgeIndices;
GLuint VertexArrayID {};
GLuint IndexBufferID {};
u16 IndexBuffer[2048 * 40] {};
u32 NumIndices {}, NumEdgeIndices {};
const u32 EdgeIndicesOffset = 2048 * 30;
GLuint TexMemID;
GLuint TexPalMemID;
GLuint TexMemID {};
GLuint TexPalMemID {};
int ScaleFactor;
bool BetterPolygons;
int ScreenW, ScreenH;
int ScaleFactor {};
bool BetterPolygons {};
int ScreenW {}, ScreenH {};
GLuint FramebufferTex[8];
int FrontBuffer;
GLuint FramebufferID[4], PixelbufferID;
u32 Framebuffer[256*192];
GLuint FramebufferTex[8] {};
int FrontBuffer {};
GLuint FramebufferID[4] {}, PixelbufferID {};
u32 Framebuffer[256*192] {};
};

View File

@ -39,6 +39,7 @@ void SoftRenderer::StopRenderThread()
Platform::Semaphore_Post(Sema_RenderStart);
Platform::Thread_Wait(RenderThread);
Platform::Thread_Free(RenderThread);
RenderThread = nullptr;
}
}
@ -71,13 +72,8 @@ void SoftRenderer::SetupRenderThread()
}
SoftRenderer::SoftRenderer()
SoftRenderer::SoftRenderer() noexcept
: Renderer3D(false)
{
}
bool SoftRenderer::Init()
{
Sema_RenderStart = Platform::Semaphore_Create();
Sema_RenderDone = Platform::Semaphore_Create();
@ -86,11 +82,10 @@ bool SoftRenderer::Init()
Threaded = false;
RenderThreadRunning = false;
RenderThreadRendering = false;
return true;
RenderThread = nullptr;
}
void SoftRenderer::DeInit()
SoftRenderer::~SoftRenderer()
{
StopRenderThread();

View File

@ -28,10 +28,8 @@ namespace GPU3D
class SoftRenderer : public Renderer3D
{
public:
SoftRenderer();
virtual ~SoftRenderer() override {};
virtual bool Init() override;
virtual void DeInit() override;
SoftRenderer() noexcept;
virtual ~SoftRenderer() override;
virtual void Reset() override;
virtual void SetRenderSettings(GPU::RenderSettings& settings) override;

View File

@ -18,6 +18,7 @@
#include "GPU_OpenGL.h"
#include <assert.h>
#include <cstdio>
#include <cstring>
@ -32,33 +33,36 @@ namespace GPU
using namespace OpenGL;
bool GLCompositor::Init()
std::unique_ptr<GLCompositor> GLCompositor::New() noexcept
{
if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, CompShader[0], "CompositorShader"))
//if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Linear, CompShader[0], "CompositorShader"))
//if (!OpenGL::BuildShaderProgram(kCompositorVS_xBRZ, kCompositorFS_xBRZ, CompShader[0], "CompositorShader"))
return false;
assert(glBindAttribLocation != nullptr);
for (int i = 0; i < 1; i++)
{
GLint uni_id;
std::array<GLuint, 3> CompShader {};
if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, &CompShader[0], "CompositorShader"))
return nullptr;
glBindAttribLocation(CompShader[i][2], 0, "vPosition");
glBindAttribLocation(CompShader[i][2], 1, "vTexcoord");
glBindFragDataLocation(CompShader[i][2], 0, "oColor");
glBindAttribLocation(CompShader[2], 0, "vPosition");
glBindAttribLocation(CompShader[2], 1, "vTexcoord");
glBindFragDataLocation(CompShader[2], 0, "oColor");
if (!OpenGL::LinkShaderProgram(CompShader[i]))
return false;
if (!OpenGL::LinkShaderProgram(CompShader.data()))
// OpenGL::LinkShaderProgram already deletes the shader program object
// if linking the shaders together failed.
return nullptr;
CompScaleLoc[i] = glGetUniformLocation(CompShader[i][2], "u3DScale");
Comp3DXPosLoc[i] = glGetUniformLocation(CompShader[i][2], "u3DXPos");
return std::unique_ptr<GLCompositor>(new GLCompositor(CompShader));
}
glUseProgram(CompShader[i][2]);
uni_id = glGetUniformLocation(CompShader[i][2], "ScreenTex");
glUniform1i(uni_id, 0);
uni_id = glGetUniformLocation(CompShader[i][2], "_3DTex");
glUniform1i(uni_id, 1);
}
GLCompositor::GLCompositor(std::array<GLuint, 3> compShader) noexcept : CompShader(compShader)
{
CompScaleLoc = glGetUniformLocation(CompShader[2], "u3DScale");
Comp3DXPosLoc = glGetUniformLocation(CompShader[2], "u3DXPos");
glUseProgram(CompShader[2]);
GLuint screenTextureUniform = glGetUniformLocation(CompShader[2], "ScreenTex");
glUniform1i(screenTextureUniform, 0);
GLuint _3dTextureUniform = glGetUniformLocation(CompShader[2], "_3DTex");
glUniform1i(_3dTextureUniform, 1);
// all this mess is to prevent bleeding
#define SETVERTEX(i, x, y, offset) \
@ -119,12 +123,12 @@ bool GLCompositor::Init()
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
return true;
}
void GLCompositor::DeInit()
GLCompositor::~GLCompositor()
{
assert(glDeleteFramebuffers != nullptr);
glDeleteFramebuffers(2, CompScreenOutputFB);
glDeleteTextures(1, &CompScreenInputTex);
glDeleteTextures(2, CompScreenOutputTex);
@ -132,8 +136,7 @@ void GLCompositor::DeInit()
glDeleteVertexArrays(1, &CompVertexArrayID);
glDeleteBuffers(1, &CompVertexBufferID);
for (int i = 0; i < 1; i++)
OpenGL::DeleteShaderProgram(CompShader[i]);
OpenGL::DeleteShaderProgram(CompShader.data());
}
void GLCompositor::Reset()
@ -197,11 +200,11 @@ void GLCompositor::RenderFrame()
glClear(GL_COLOR_BUFFER_BIT);
// TODO: select more shaders (filtering, etc)
OpenGL::UseShaderProgram(CompShader[0]);
glUniform1ui(CompScaleLoc[0], Scale);
OpenGL::UseShaderProgram(CompShader.data());
glUniform1ui(CompScaleLoc, Scale);
// TODO: support setting this midframe, if ever needed
glUniform1i(Comp3DXPosLoc[0], ((int)GPU3D::RenderXPos << 23) >> 23);
glUniform1i(Comp3DXPosLoc, ((int)GPU3D::RenderXPos << 23) >> 23);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, CompScreenInputTex);

View File

@ -20,6 +20,9 @@
#include "OpenGLSupport.h"
#include <array>
#include <memory>
namespace GPU
{
@ -28,12 +31,11 @@ struct RenderSettings;
class GLCompositor
{
public:
GLCompositor() = default;
static std::unique_ptr<GLCompositor> New() noexcept;
GLCompositor(const GLCompositor&) = delete;
GLCompositor& operator=(const GLCompositor&) = delete;
~GLCompositor();
bool Init();
void DeInit();
void Reset();
void SetRenderSettings(RenderSettings& settings);
@ -42,13 +44,14 @@ public:
void RenderFrame();
void BindOutputTexture(int buf);
private:
GLCompositor(std::array<GLuint, 3> CompShader) noexcept;
int Scale;
int ScreenH, ScreenW;
GLuint CompShader[1][3];
GLuint CompScaleLoc[1];
GLuint Comp3DXPosLoc[1];
std::array<GLuint, 3> CompShader;
GLuint CompScaleLoc;
GLuint Comp3DXPosLoc;
GLuint CompVertexBufferID;
GLuint CompVertexArrayID;

View File

@ -226,10 +226,16 @@ void DeInit()
#endif
delete ARM9;
ARM9 = nullptr;
delete ARM7;
ARM7 = nullptr;
for (int i = 0; i < 8; i++)
{
delete DMAs[i];
DMAs[i] = nullptr;
}
NDSCart::DeInit();
GBACart::DeInit();
@ -369,7 +375,7 @@ bool NeedsDirectBoot()
return true;
// DSi/3DS firmwares aren't bootable
if (SPI_Firmware::GetFirmwareLength() == 0x20000)
if (!SPI_Firmware::GetFirmware()->IsBootable())
return true;
return false;
@ -518,50 +524,7 @@ void Reset()
RunningGame = false;
LastSysClockCycles = 0;
// DS BIOSes are always loaded, even in DSi mode
// 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));
}
// BIOS files are now loaded by the frontend
#ifdef JIT_ENABLED
ARMJIT::Reset();
@ -569,7 +532,7 @@ void Reset()
if (ConsoleType == 1)
{
DSi::LoadBIOS();
// BIOS files are now loaded by the frontend
ARM9ClockShift = 2;
MainRAMMask = 0xFFFFFF;
@ -1042,6 +1005,15 @@ void LoadBIOS()
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()
{

View File

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

View File

@ -29,6 +29,12 @@ bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char*
int len;
int res;
if (!glCreateShader)
{
Log(LogLevel::Error, "OpenGL: Cannot build shader program, OpenGL hasn't been loaded\n");
return false;
}
ids[0] = glCreateShader(GL_VERTEX_SHADER);
len = strlen(vs);
glShaderSource(ids[0], 1, &vs, &len);
@ -87,6 +93,12 @@ bool LinkShaderProgram(GLuint* ids)
{
int res;
if (!glLinkProgram)
{
Log(LogLevel::Error, "OpenGL: Cannot link shader program, OpenGL hasn't been loaded\n");
return false;
}
glLinkProgram(ids[2]);
glDetachShader(ids[2], ids[0]);
@ -115,12 +127,18 @@ bool LinkShaderProgram(GLuint* ids)
void DeleteShaderProgram(GLuint* ids)
{
glDeleteProgram(ids[2]);
if (glDeleteProgram)
{ // If OpenGL isn't loaded, then there's no shader program to delete
glDeleteProgram(ids[2]);
}
}
void UseShaderProgram(GLuint* ids)
{
glUseProgram(ids[2]);
if (glUseProgram)
{ // If OpenGL isn't loaded, then there's no shader program to use
glUseProgram(ids[2]);
}
}
}

View File

@ -24,6 +24,11 @@
#include <functional>
#include <string>
namespace SPI_Firmware
{
class Firmware;
}
namespace Platform
{
@ -102,13 +107,6 @@ enum ConfigEntry
ExternalBIOSEnable,
BIOS9Path,
BIOS7Path,
FirmwarePath,
DSi_BIOS9Path,
DSi_BIOS7Path,
DSi_FirmwarePath,
DSi_NANDPath,
DLDI_Enable,
@ -125,7 +123,7 @@ enum ConfigEntry
DSiSD_FolderSync,
DSiSD_FolderPath,
Firm_OverrideSettings,
Firm_OverrideSettings [[deprecated("Individual fields can now be overridden")]],
Firm_Username,
Firm_Language,
Firm_BirthdayMonth,
@ -333,6 +331,13 @@ void Sleep(u64 usecs);
void WriteNDSSave(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
// packet type: DS-style TX header (12 bytes) + original 802.11 frame

View File

@ -19,10 +19,8 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <string>
#include <algorithm>
#include <codecvt>
#include <locale>
#include <memory>
#include <utility>
#include "NDS.h"
#include "DSi.h"
#include "SPI.h"
@ -34,12 +32,7 @@ using namespace Platform;
namespace SPI_Firmware
{
std::string FirmwarePath;
u8* Firmware;
u32 FirmwareLength;
u32 FirmwareMask;
u32 UserSettings;
std::unique_ptr<Firmware> Firmware;
u32 Hold;
u8 CurCmd;
@ -49,10 +42,9 @@ u8 Data;
u8 StatusReg;
u32 Addr;
u16 CRC16(u8* data, u32 len, u32 start)
u16 CRC16(const 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++)
{
@ -75,22 +67,20 @@ u16 CRC16(u8* data, u32 len, u32 start)
bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset)
{
u16 crc_stored = *(u16*)&Firmware[crcoffset];
u16 crc_calced = CRC16(&Firmware[offset], len, start);
u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset];
u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start);
return (crc_stored == crc_calced);
}
bool Init()
{
FirmwarePath = "";
Firmware = nullptr;
return true;
}
void DeInit()
{
if (Firmware) delete[] Firmware;
RemoveFirmware();
}
u32 FixFirmwareLength(u32 originalLength)
@ -116,335 +106,43 @@ u32 FixFirmwareLength(u32 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()
{
if (Firmware) delete[] Firmware;
Firmware = nullptr;
FirmwarePath = "";
bool firmoverride = false;
if (Platform::GetConfigBool(Platform::ExternalBIOSEnable))
if (!Firmware)
{
if (NDS::ConsoleType == 1)
FirmwarePath = Platform::GetConfigString(Platform::DSi_FirmwarePath);
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);
}
Log(LogLevel::Warn, "SPI firmware: no firmware loaded! Using default\n");
Firmware = std::make_unique<class Firmware>(NDS::ConsoleType);
}
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
*(u16*)&Firmware[userdata+0x58] = 0;
*(u16*)&Firmware[userdata+0x5A] = 0;
Firmware[userdata+0x5C] = 0;
Firmware[userdata+0x5D] = 0;
*(u16*)&Firmware[userdata+0x5E] = 255<<4;
*(u16*)&Firmware[userdata+0x60] = 191<<4;
Firmware[userdata+0x62] = 255;
Firmware[userdata+0x63] = 191;
for (UserData& u : Firmware->UserData())
{
u.TouchCalibrationADC1[0] = 0;
u.TouchCalibrationADC1[1] = 0;
u.TouchCalibrationPixel1[0] = 0;
u.TouchCalibrationPixel1[1] = 0;
u.TouchCalibrationADC2[0] = 255<<4;
u.TouchCalibrationADC2[1] = 191<<4;
u.TouchCalibrationPixel2[0] = 255;
u.TouchCalibrationPixel2[1] = 191;
}
Firmware->UpdateChecksums();
// disable autoboot
//Firmware[userdata+0x64] &= 0xBF;
*(u16*)&Firmware[userdata+0x72] = CRC16(&Firmware[userdata], 0x70, 0xFFFF);
//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]);
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]);
// verify shit
Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware[0x2C], 0x2A)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&FirmwareMask, 0xFE, 0x7FAFE&FirmwareMask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&FirmwareMask, 0xFE, 0x7FBFE&FirmwareMask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&FirmwareMask, 0xFE, 0x7FCFE&FirmwareMask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: USER0 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x7FE00&FirmwareMask, 0x70, 0x7FE72&FirmwareMask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: USER1 CRC16 = %s\n", VerifyCRC16(0xFFFF, 0x7FF00&FirmwareMask, 0x70, 0x7FF72&FirmwareMask)?"GOOD":"BAD");
u32 mask = Firmware->Mask();
Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware->Buffer()[0x2C], 0x2A)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP1 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FA00&mask, 0xFE, 0x7FAFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP2 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FB00&mask, 0xFE, 0x7FBFE&mask)?"GOOD":"BAD");
Log(LogLevel::Debug, "FW: AP3 CRC16 = %s\n", VerifyCRC16(0x0000, 0x7FC00&mask, 0xFE, 0x7FCFE&mask)?"GOOD":"BAD");
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;
CurCmd = 0;
@ -458,7 +156,7 @@ void DoSavestate(Savestate* file)
// CHECKME/TODO: trust the firmware to stay the same?????
// embedding the whole firmware in the savestate would be derpo tho??
file->Var32(&FirmwareLength);
/*file->Var32(&FirmwareLength);
if (file->Saving)
{
file->VarArray(Firmware, FirmwareLength);
@ -479,7 +177,7 @@ void DoSavestate(Savestate* file)
}
UserSettings = userdata;
}
}*/
file->Var32(&Hold);
file->Var8(&CurCmd);
@ -492,36 +190,85 @@ void DoSavestate(Savestate* file)
void SetupDirectBoot(bool dsi)
{
const FirmwareHeader& header = Firmware->Header();
const UserData& userdata = Firmware->EffectiveUserData();
if (dsi)
{
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
DSi::ARM9Write16(0x02FFFCFA, *(u16*)&Firmware[0x3C]); // enabled channels
DSi::ARM9Write16(0x02FFFCFA, header.EnabledChannels); // enabled channels
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
{
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(0x027FF876, *(u16*)&Firmware[0x04]); // CRC16 for GUI/wifi code
NDS::ARM9Write16(0x027FF874, header.DataGfxChecksum); // CRC16 for data/gfx
NDS::ARM9Write16(0x027FF876, header.GUIWifiCodeChecksum); // CRC16 for GUI/wifi code
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; }
u8 GetConsoleType() { return Firmware[0x1D]; }
u8 GetWifiVersion() { return Firmware[0x2F]; }
u8 GetNWifiVersion() { return Firmware[0x1FD]; } // for DSi; will return 0xFF on a DS
u8 GetRFVersion() { return Firmware[0x40]; }
u8* GetWifiMAC() { return &Firmware[0x36]; }
const class Firmware* GetFirmware()
{
return Firmware.get();
}
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()
{
@ -560,7 +307,7 @@ void Write(u8 val, u32 hold)
}
else
{
Data = Firmware[Addr & FirmwareMask];
Data = Firmware->Buffer()[Addr & Firmware->Mask()];
Addr++;
}
@ -586,14 +333,14 @@ void Write(u8 val, u32 hold)
{
// TODO: what happens if you write too many bytes? (max 256, they say)
if (DataPos < 4)
{
{ // If we're in the middle of writing the address...
Addr <<= 8;
Addr |= val;
Data = 0;
}
else
{
Firmware[Addr & FirmwareMask] = val;
Firmware->Buffer()[Addr & Firmware->Mask()] = val;
Data = val;
Addr++;
}
@ -622,31 +369,14 @@ void Write(u8 val, u32 hold)
}
if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A))
{
if (!FirmwarePath.empty())
{
FileHandle* f = Platform::OpenLocalFile(FirmwarePath, FileMode::ReadWriteExisting);
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();
{ // If the SPI firmware chip just finished a write...
// We only notify the frontend of changes to the Wi-fi/userdata settings region
// (although it might still decide to flush the whole thing)
u32 wifioffset = Firmware->WifiAccessPointOffset();
FileHandle* f = Platform::OpenLocalFile(wfcfile, FileMode::Write);
if (f)
{
u32 cutoff = 0x7F400 & FirmwareMask;
FileWrite(&Firmware[cutoff], 0x900, 1, f);
CloseFile(f);
}
}
// Request that the start of the Wi-fi/userdata settings region
// through the end of the firmware blob be flushed to disk
Platform::WriteFirmware(*Firmware, wifioffset, Firmware->Length() - wifioffset);
}
}

View File

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

379
src/SPI_Firmware.cpp Normal file
View File

@ -0,0 +1,379 @@
/*
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();
}
if (Header().ConsoleType == FirmwareConsoleType::DSi)
{
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

@ -135,12 +135,18 @@ bool Init()
void DeInit()
{
for (int i = 0; i < 16; i++)
{
delete Channels[i];
Channels[i] = nullptr;
}
delete Capture[0];
delete Capture[1];
Capture[0] = nullptr;
Capture[1] = nullptr;
Platform::Mutex_Free(AudioLock);
AudioLock = nullptr;
}
void Reset()

View File

@ -171,6 +171,7 @@ void DeInit()
void Reset()
{
using namespace SPI_Firmware;
memset(RAM, 0, 0x2000);
memset(IO, 0, 0x1000);
@ -210,15 +211,15 @@ void Reset()
}
#undef BBREG_FIXED
RFVersion = SPI_Firmware::GetRFVersion();
RFVersion = GetFirmware()->Header().RFChipType;
memset(RFRegs, 0, 4*0x40);
u8 console = SPI_Firmware::GetConsoleType();
if (console == 0xFF)
FirmwareConsoleType console = GetFirmware()->Header().ConsoleType;
if (console == FirmwareConsoleType::DS)
IOPORT(0x000) = 0x1440;
else if (console == 0x20)
else if (console == FirmwareConsoleType::DSLite)
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
else
{

View File

@ -334,12 +334,17 @@ void Init()
void DeInit()
{
if (audioDevice) SDL_CloseAudioDevice(audioDevice);
audioDevice = 0;
MicClose();
SDL_DestroyCond(audioSync);
SDL_DestroyMutex(audioSyncLock);
if (audioSync) SDL_DestroyCond(audioSync);
audioSync = nullptr;
if (audioSyncLock) SDL_DestroyMutex(audioSyncLock);
audioSyncLock = nullptr;
if (micWavBuffer) delete[] micWavBuffer;
micWavBuffer = nullptr;
}
void AudioSync()

View File

@ -40,6 +40,7 @@
#include "IPC.h"
#include "LAN.h"
#include "OSD.h"
#include "SPI_Firmware.h"
#ifdef __WIN32__
#define fseek _fseeki64
@ -193,13 +194,6 @@ std::string GetConfigString(ConfigEntry 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 DLDI_ImagePath: return Config::DLDISDPath;
@ -530,7 +524,36 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 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);
}
}
}
void MP_Begin()
{

View File

@ -16,9 +16,14 @@
with melonDS. If not, see http://www.gnu.org/licenses/.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <codecvt>
#include <locale>
#include <memory>
#include <tuple>
#include <string>
#include <utility>
#include <fstream>
@ -35,7 +40,13 @@
#include "DSi.h"
#include "SPI.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;
namespace ROMManager
@ -55,6 +66,7 @@ std::string BaseGBAAssetName = "";
SaveManager* NDSSave = nullptr;
SaveManager* GBASave = nullptr;
std::unique_ptr<SaveManager> FirmwareSave = nullptr;
std::unique_ptr<Savestate> BackupState = nullptr;
bool SavestateLoaded = false;
@ -464,6 +476,94 @@ void LoadCheats()
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)
{
CheatsOn = enable;
@ -494,6 +594,9 @@ void Reset()
{
NDS::SetConsoleType(Config::ConsoleType);
if (Config::ConsoleType == 1) EjectGBACart();
LoadBIOSFiles();
InstallFirmware();
NDS::Reset();
SetBatteryLevels();
@ -515,6 +618,28 @@ void Reset()
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 (Config::DirectBoot || NDS::NeedsDirectBoot())
@ -529,6 +654,11 @@ bool LoadBIOS()
{
NDS::SetConsoleType(Config::ConsoleType);
LoadBIOSFiles();
if (!InstallFirmware())
return false;
if (NDS::NeedsDirectBoot())
return false;
@ -640,6 +770,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)
{
if (filepath.empty()) return false;
@ -736,10 +1082,16 @@ bool LoadROM(QStringList filepath, bool reset)
BaseROMName = romname;
BaseAssetName = romname.substr(0, romname.rfind('.'));
if (!InstallFirmware())
{
return false;
}
if (reset)
{
NDS::SetConsoleType(Config::ConsoleType);
NDS::EjectCart();
LoadBIOSFiles();
NDS::Reset();
SetBatteryLevels();
}

View File

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

View File

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

View File

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

View File

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