update branch season2 to modern standards
This commit is contained in:
parent
30186029e8
commit
22277d8e54
|
@ -330,6 +330,7 @@ void DeInit()
|
|||
ARMJIT_Memory::DeInit();
|
||||
|
||||
delete JITCompiler;
|
||||
JITCompiler = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
86
src/DSi.cpp
86
src/DSi.cpp
|
@ -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");
|
||||
|
|
|
@ -65,7 +65,6 @@ void SetCartInserted(bool inserted);
|
|||
void SetupDirectBoot();
|
||||
void SoftReset();
|
||||
|
||||
bool LoadBIOS();
|
||||
bool LoadNAND();
|
||||
|
||||
void RunNDMAs(u32 cpu);
|
||||
|
|
|
@ -63,6 +63,9 @@ void DeInit()
|
|||
{
|
||||
delete Camera0;
|
||||
delete Camera1;
|
||||
|
||||
Camera0 = nullptr;
|
||||
Camera1 = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
|
|
113
src/DSi_NAND.cpp
113
src/DSi_NAND.cpp
|
@ -486,7 +486,7 @@ bool ESDecrypt(u8* data, u32 len)
|
|||
}
|
||||
|
||||
|
||||
void ReadHardwareInfo(u8* dataS, u8* dataN)
|
||||
void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN)
|
||||
{
|
||||
FF_FIL file;
|
||||
FRESULT res;
|
||||
|
@ -495,20 +495,20 @@ void ReadHardwareInfo(u8* dataS, u8* dataN)
|
|||
res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ);
|
||||
if (res == FR_OK)
|
||||
{
|
||||
f_read(&file, dataS, 0xA4, &nread);
|
||||
f_read(&file, &dataS, sizeof(DSiSerialData), &nread);
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
res = f_open(&file, "0:/sys/HWINFO_N.dat", FA_OPEN_EXISTING | FA_READ);
|
||||
if (res == FR_OK)
|
||||
{
|
||||
f_read(&file, dataN, 0x9C, &nread);
|
||||
f_read(&file, dataN.data(), sizeof(dataN), &nread);
|
||||
f_close(&file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ReadUserData(u8* data)
|
||||
void ReadUserData(DSiFirmwareSystemSettings& data)
|
||||
{
|
||||
FF_FIL file;
|
||||
FRESULT res;
|
||||
|
@ -553,7 +553,7 @@ void ReadUserData(u8* data)
|
|||
}
|
||||
|
||||
f_lseek(&file, 0);
|
||||
f_read(&file, data, 0x1B0, &nread);
|
||||
f_read(&file, &data, sizeof(DSiFirmwareSystemSettings), &nread);
|
||||
f_close(&file);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
112
src/DSi_NAND.h
112
src/DSi_NAND.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
1715
src/FreeBIOS.h
1715
src/FreeBIOS.h
File diff suppressed because it is too large
Load Diff
32
src/GPU.cpp
32
src/GPU.cpp
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -286,6 +286,7 @@ bool Init()
|
|||
|
||||
void DeInit()
|
||||
{
|
||||
CurrentRenderer = nullptr;
|
||||
}
|
||||
|
||||
void ResetRenderingState()
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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] {};
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
64
src/NDS.cpp
64
src/NDS.cpp
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
484
src/SPI.cpp
484
src/SPI.cpp
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
22
src/SPI.h
22
src/SPI.h
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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()
|
||||
|
|
11
src/Wifi.cpp
11
src/Wifi.cpp
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -538,6 +538,9 @@ void EmuThread::run()
|
|||
if (ROMManager::GBASave)
|
||||
ROMManager::GBASave->CheckFlush();
|
||||
|
||||
if (ROMManager::FirmwareSave)
|
||||
ROMManager::FirmwareSave->CheckFlush();
|
||||
|
||||
if (!oglContext)
|
||||
{
|
||||
FrontBufferLock.lock();
|
||||
|
|
Loading…
Reference in New Issue