From bb09ce7d708c3272f63077222fe6f728c637ebc2 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sun, 1 Oct 2023 15:58:56 -0400 Subject: [PATCH 001/157] Replace DSi_NAND's uses of sprintf with snprintf (#1841) - Now clang oughta stop complaining --- src/DSi_NAND.cpp | 56 ++++++++++++++++++++++++------------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index df9cdd2b..b7ab6205 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -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); @@ -648,7 +648,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 +816,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 +850,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 +872,7 @@ void ListTitles(u32 category, std::vector& 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 +898,7 @@ void ListTitles(u32 category, std::vector& 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 +918,7 @@ void ListTitles(u32 category, std::vector& 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 +933,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 +1098,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 +1109,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 +1148,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 +1191,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 +1227,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 +1243,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 +1274,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 +1300,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: From b2fcff97c186cc9db263089acd4810ea7d58517d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Mon, 2 Oct 2023 11:54:17 -0400 Subject: [PATCH 002/157] Add some structs for files that DSi_NAND reads (#1842) * Add DSiFirmwareSystemSettings * Replace DSiFirmwareSystemSettings::TouchCalibration fields with std::arrays - So assignment can be done in one line * Make DSiFirmwareSystemSettings a union - So its bytes can be accessed * Add a comment * Use DSiFirmwareSystemSettings instead of raw byte offsets * Add definitions for DSiSerialData and DSiHardwareInfoN * Move DSiFirmwareSystemSettings's hash update logic into its own method --- src/DSi.cpp | 10 ++--- src/DSi_NAND.cpp | 57 ++++++++++++------------ src/DSi_NAND.h | 112 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 144 insertions(+), 35 deletions(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index 64bc6d2d..cdec8199 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -530,20 +530,20 @@ void SetupDirectBoot() if (DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308])) { - u8 userdata[0x1B0]; + DSi_NAND::DSiFirmwareSystemSettings userdata {}; DSi_NAND::ReadUserData(userdata); for (u32 i = 0; i < 0x128; i+=4) - ARM9Write32(0x02000400+i, *(u32*)&userdata[0x88+i]); + ARM9Write32(0x02000400+i, *(u32*)&userdata.Bytes[0x88+i]); - u8 hwinfoS[0xA4]; - u8 hwinfoN[0x9C]; + DSi_NAND::DSiSerialData hwinfoS {}; + DSi_NAND::DSiHardwareInfoN hwinfoN; DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN); for (u32 i = 0; i < 0x14; i+=4) ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); for (u32 i = 0; i < 0x18; i+=4) - ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS[0x88+i]); + ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS.Bytes[0x88+i]); DSi_NAND::DeInit(); } diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index b7ab6205..7c3e7453 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -486,7 +486,7 @@ bool ESDecrypt(u8* data, u32 len) } -void ReadHardwareInfo(u8* dataS, u8* dataN) +void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) { FF_FIL file; FRESULT res; @@ -495,20 +495,20 @@ void ReadHardwareInfo(u8* dataS, u8* dataN) res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ); if (res == FR_OK) { - f_read(&file, dataS, 0xA4, &nread); + f_read(&file, &dataS, sizeof(DSiSerialData), &nread); f_close(&file); } res = f_open(&file, "0:/sys/HWINFO_N.dat", FA_OPEN_EXISTING | FA_READ); if (res == FR_OK) { - f_read(&file, dataN, 0x9C, &nread); + f_read(&file, dataN.data(), sizeof(dataN), &nread); f_close(&file); } } -void ReadUserData(u8* data) +void ReadUserData(DSiFirmwareSystemSettings& data) { FF_FIL file; FRESULT res; @@ -553,7 +553,7 @@ void ReadUserData(u8* data) } f_lseek(&file, 0); - f_read(&file, data, 0x1B0, &nread); + f_read(&file, &data, sizeof(DSiFirmwareSystemSettings), &nread); f_close(&file); } @@ -574,10 +574,10 @@ void PatchUserData() continue; } - u8 contents[0x1B0]; + DSiFirmwareSystemSettings contents; u32 nres; f_lseek(&file, 0); - f_read(&file, contents, 0x1B0, &nres); + f_read(&file, &contents, sizeof(DSiFirmwareSystemSettings), &nres); // override user settings, if needed if (Platform::GetConfigBool(Platform::Firm_OverrideSettings)) @@ -586,46 +586,39 @@ void PatchUserData() std::string orig_username = Platform::GetConfigString(Platform::Firm_Username); std::u16string username = std::wstring_convert, char16_t>{}.from_bytes(orig_username); size_t usernameLength = std::min(username.length(), (size_t) 10); - memset(contents + 0xD0, 0, 11 * sizeof(char16_t)); - memcpy(contents + 0xD0, username.data(), usernameLength * sizeof(char16_t)); + memset(&contents.Nickname, 0, sizeof(contents.Nickname)); + memcpy(&contents.Nickname, username.data(), usernameLength * sizeof(char16_t)); // setting language - contents[0x8E] = Platform::GetConfigInt(Platform::Firm_Language); + contents.Language = static_cast(Platform::GetConfigInt(Platform::Firm_Language)); // setting up color - contents[0xCC] = Platform::GetConfigInt(Platform::Firm_Color); + contents.FavoriteColor = Platform::GetConfigInt(Platform::Firm_Color); // setting up birthday - contents[0xCE] = Platform::GetConfigInt(Platform::Firm_BirthdayMonth); - contents[0xCF] = Platform::GetConfigInt(Platform::Firm_BirthdayDay); + contents.BirthdayMonth = Platform::GetConfigInt(Platform::Firm_BirthdayMonth); + contents.BirthdayDay = Platform::GetConfigInt(Platform::Firm_BirthdayDay); // setup message std::string orig_message = Platform::GetConfigString(Platform::Firm_Message); std::u16string message = std::wstring_convert, char16_t>{}.from_bytes(orig_message); size_t messageLength = std::min(message.length(), (size_t) 26); - memset(contents + 0xE6, 0, 27 * sizeof(char16_t)); - memcpy(contents + 0xE6, message.data(), messageLength * sizeof(char16_t)); + memset(&contents.Message, 0, sizeof(contents.Message)); + memcpy(&contents.Message, message.data(), messageLength * sizeof(char16_t)); // TODO: make other items configurable? } // fix touchscreen coords - *(u16*)&contents[0xB8] = 0; - *(u16*)&contents[0xBA] = 0; - contents[0xBC] = 0; - contents[0xBD] = 0; - *(u16*)&contents[0xBE] = 255<<4; - *(u16*)&contents[0xC0] = 191<<4; - contents[0xC2] = 255; - contents[0xC3] = 191; + contents.TouchCalibrationADC1 = {0, 0}; + contents.TouchCalibrationPixel1 = {0, 0}; + contents.TouchCalibrationADC2 = {255 << 4, 191 << 4}; + contents.TouchCalibrationPixel2 = {255, 191}; - SHA1_CTX sha; - SHA1Init(&sha); - SHA1Update(&sha, &contents[0x88], 0x128); - SHA1Final(&contents[0], &sha); + contents.UpdateHash(); f_lseek(&file, 0); - f_write(&file, contents, 0x1B0, &nres); + f_write(&file, &contents, sizeof(DSiFirmwareSystemSettings), &nres); f_close(&file); } @@ -1318,4 +1311,12 @@ bool ExportTitleData(u32 category, u32 titleid, int type, const char* file) return ExportFile(fname, file); } +void DSiFirmwareSystemSettings::UpdateHash() +{ + SHA1_CTX sha; + SHA1Init(&sha); + SHA1Update(&sha, &Bytes[0x88], 0x128); + SHA1Final(Hash.data(), &sha); +} + } diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 14599b26..9bff8f2b 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -22,6 +22,8 @@ #include "types.h" #include "NDS_Header.h" #include "DSi_TMD.h" +#include "SPI_Firmware.h" +#include #include #include @@ -35,6 +37,10 @@ enum TitleData_BannerSav, }; +union DSiFirmwareSystemSettings; +union DSiSerialData; +using DSiHardwareInfoN = std::array; + bool Init(u8* es_keyY); void DeInit(); @@ -42,9 +48,9 @@ Platform::FileHandle* GetFile(); void GetIDs(u8* emmc_cid, u64& consoleid); -void ReadHardwareInfo(u8* dataS, u8* dataN); +void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN); -void ReadUserData(u8* data); +void ReadUserData(DSiFirmwareSystemSettings& data); void PatchUserData(); void ListTitles(u32 category, std::vector& titlelist); @@ -58,6 +64,108 @@ u32 GetTitleDataMask(u32 category, u32 titleid); bool ImportTitleData(u32 category, u32 titleid, int type, const char* file); bool ExportTitleData(u32 category, u32 titleid, int type, const char* file); +typedef std::array SHA1Hash; +typedef std::array TitleID; + +/// Firmware settings for the DSi, saved to the NAND as TWLCFG0.dat or TWLCFG1.dat. +/// The DSi mirrors this information to its own firmware for compatibility with NDS games. +/// @note The file is normally 16KiB, but only the first 432 bytes are used; +/// the rest is FF-padded. +/// This struct excludes the padding. +/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaresystemsettingsdatafiles +union DSiFirmwareSystemSettings +{ + struct + { + SHA1Hash Hash; + u8 Zero00[108]; + u8 Version; + u8 UpdateCounter; + u8 Zero01[2]; + u32 BelowRAMAreaSize; + u32 ConfigFlags; + u8 Zero02; + u8 CountryCode; + SPI_Firmware::Language Language; + u8 RTCYear; + u32 RTCOffset; + u8 Zero3[4]; + u8 EULAVersion; + u8 Zero04[9]; + u8 AlarmHour; + u8 AlarmMinute; + u8 Zero05[2]; + bool AlarmEnable; + u8 Zero06[2]; + u8 SystemMenuUsedTitleSlots; + u8 SystemMenuFreeTitleSlots; + u8 Unknown0; + u8 Unknown1; + u8 Zero07[3]; + TitleID SystemMenuMostRecentTitleID; + std::array TouchCalibrationADC1; + std::array TouchCalibrationPixel1; + std::array TouchCalibrationADC2; + std::array TouchCalibrationPixel2; + u8 Unknown2[4]; + u8 Zero08[4]; + u8 FavoriteColor; + u8 Zero09; + u8 BirthdayMonth; + u8 BirthdayDay; + char16_t Nickname[11]; + char16_t Message[27]; + u8 ParentalControlsFlags; + u8 Zero10[6]; + u8 ParentalControlsRegion; + u8 ParentalControlsYearsOfAgeRating; + u8 ParentalControlsSecretQuestion; + u8 Unknown3; + u8 Zero11[2]; + char ParentalControlsPIN[5]; + char16_t ParentalControlsSecretAnswer[65]; + }; + u8 Bytes[432]; + + void UpdateHash(); +}; + +static_assert(sizeof(DSiFirmwareSystemSettings) == 432, "DSiFirmwareSystemSettings must be exactly 432 bytes"); + +enum class ConsoleRegion : u8 +{ + Japan, + USA, + Europe, + Australia, + China, + Korea, +}; + +/// Data file saved to 0:/sys/HWINFO_S.dat. +/// @note The file is normally 16KiB, but only the first 164 bytes are used; +/// the rest is FF-padded. +/// This struct excludes the padding. +/// @see https://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaremiscfiles +union DSiSerialData +{ + struct + { + u8 RsaSha1HMAC[0x80]; + u32 Version; + u32 EntrySize; + u32 SupportedLanguages; + u8 Unknown0[4]; + ConsoleRegion Region; + char Serial[12]; + u8 Unknown1[3]; + u8 TitleIDLSBs[4]; + }; + u8 Bytes[164]; +}; + +static_assert(sizeof(DSiSerialData) == 164, "DSiSerialData must be exactly 164 bytes"); + } #endif // DSI_NAND_H From d4e51f80601f57399db49f1010c45427bd2bf3c4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 11 Oct 2023 11:20:05 -0400 Subject: [PATCH 003/157] Refactor DSi_NAND (#1844) * Refactor diskio's contents - Change ff_disk_read_cb/write_cb into a std::function instead of a raw pointer - Add const specifiers as needed * Refactor DSi_NAND to manage the file system's mounted lifetime with RAII * Split NANDMount into NANDMount and NANDImage - NANDImage is used for information about the NAND that doesn't require decryption or filesystem access - NANDMount is used to actually access the file system - Both classes manage their respective resources (the NAND file handle and the NAND's mount) with RAII - Also split the file loading into another function that I will remove in a later PR * Make NANDMount immovable * Remove NAND-loading code that I had sectioned off into a function - Incomplete copypasta - I must have gotten distracted * Tidy up NANDImage's initialization - Don't unmount the disk image if the constructor fails (that's NANDMount's job now) - Only assign CurFile if the constructor succeeds * Add some const-correctness * Move DSi NAND initialization to the frontend - The NANDImage is now installed via a unique_ptr in DSi * Remove Platform::DSi_NANDPath - Not Config::DSiNANDPath; that can still be configured as usual - The core no longer needs to care --- src/CMakeLists.txt | 2 +- src/DSi.cpp | 100 ++++----- src/DSi.h | 9 +- src/DSi_AES.cpp | 14 +- src/DSi_AES.h | 4 +- src/DSi_NAND.cpp | 238 ++++++++++----------- src/DSi_NAND.h | 101 +++++++-- src/DSi_SD.cpp | 39 ++-- src/DSi_SD.h | 11 +- src/{fatfs/diskio.c => FATIO.cpp} | 37 ++-- src/FATIO.h | 34 +++ src/FATStorage.cpp | 5 +- src/FATStorage.h | 4 +- src/Platform.h | 2 - src/fatfs/ff.h | 10 - src/frontend/qt_sdl/Platform.cpp | 2 - src/frontend/qt_sdl/ROMManager.cpp | 51 +++++ src/frontend/qt_sdl/ROMManager.h | 2 + src/frontend/qt_sdl/TitleManagerDialog.cpp | 51 +++-- src/frontend/qt_sdl/TitleManagerDialog.h | 15 +- 20 files changed, 441 insertions(+), 290 deletions(-) rename src/{fatfs/diskio.c => FATIO.cpp} (78%) create mode 100644 src/FATIO.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 850ec7c8..0fa96848 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -25,6 +25,7 @@ add_library(core STATIC DSi_NWifi.cpp DSi_SD.cpp DSi_SPI_TSC.cpp + FATIO.cpp FATStorage.cpp FIFO.h GBACart.cpp @@ -51,7 +52,6 @@ add_library(core STATIC Wifi.cpp WifiAP.cpp - fatfs/diskio.c fatfs/ff.c fatfs/ffsystem.c fatfs/ffunicode.c diff --git a/src/DSi.cpp b/src/DSi.cpp index cdec8199..17d79a6f 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -75,12 +75,10 @@ u32 NWRAMMask[2][3]; u32 NDMACnt[2]; DSi_NDMA* NDMAs[8]; +std::unique_ptr NANDImage; DSi_SDHost* SDMMC; DSi_SDHost* SDIO; -u64 ConsoleID; -u8 eMMC_CID[16]; - // FIXME: these currently have no effect (and aren't stored in a savestate) // ... not that they matter all that much u8 GPIO_Data; @@ -149,6 +147,10 @@ void DeInit() SDMMC = nullptr; delete SDIO; SDIO = nullptr; + + NANDImage = nullptr; + // The NANDImage is cleaned up (and its underlying file closed) + // as part of unique_ptr's destructor } void Reset() @@ -528,24 +530,25 @@ void SetupDirectBoot() ARM9Write32(0x02FFE000+i, tmp); } - if (DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308])) - { - DSi_NAND::DSiFirmwareSystemSettings userdata {}; - DSi_NAND::ReadUserData(userdata); - for (u32 i = 0; i < 0x128; i+=4) - ARM9Write32(0x02000400+i, *(u32*)&userdata.Bytes[0x88+i]); + if (NANDImage && *NANDImage) + { // If a NAND image is installed, and it's valid... + if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*NANDImage)) + { + DSi_NAND::DSiFirmwareSystemSettings userdata {}; + nand.ReadUserData(userdata); + for (u32 i = 0; i < 0x128; i+=4) + ARM9Write32(0x02000400+i, *(u32*)&userdata.Bytes[0x88+i]); - DSi_NAND::DSiSerialData hwinfoS {}; - DSi_NAND::DSiHardwareInfoN hwinfoN; - DSi_NAND::ReadHardwareInfo(hwinfoS, hwinfoN); + DSi_NAND::DSiSerialData hwinfoS {}; + DSi_NAND::DSiHardwareInfoN hwinfoN; + nand.ReadHardwareInfo(hwinfoS, hwinfoN); - for (u32 i = 0; i < 0x14; i+=4) - ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); + 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.Bytes[0x88+i]); - - DSi_NAND::DeInit(); + for (u32 i = 0; i < 0x18; i+=4) + ARM9Write32(0x02FFFD68+i, *(u32*)&hwinfoS.Bytes[0x88+i]); + } } SPI_Firmware::WifiBoard nwifiver = SPI_Firmware::GetFirmware()->Header().WifiBoard; @@ -728,15 +731,21 @@ void SoftReset() bool LoadNAND() { + if (!NANDImage) + { + Log(LogLevel::Error, "No NAND image loaded\n"); + return false; + } Log(LogLevel::Info, "Loading DSi NAND\n"); - if (!DSi_NAND::Init(&DSi::ARM7iBIOS[0x8308])) + DSi_NAND::NANDMount nandmount(*NANDImage); + if (!nandmount) { Log(LogLevel::Error, "Failed to load DSi NAND\n"); return false; } - FileHandle* nand = DSi_NAND::GetFile(); + FileHandle* nand = NANDImage->GetFile(); // Make sure NWRAM is accessible. // The Bits are set to the startup values in Reset() and we might @@ -892,13 +901,9 @@ bool LoadNAND() } } -#define printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); } -#define printhex_rev(str, size) { for (int z = (size)-1; z >= 0; z--) printf("%02X", (str)[z]); printf("\n"); } - - DSi_NAND::GetIDs(eMMC_CID, ConsoleID); - - Log(LogLevel::Debug, "eMMC CID: "); printhex(eMMC_CID, 16); - Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", ConsoleID); + const DSi_NAND::DSiKey& emmccid = NANDImage->GetEMMCID(); + Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]); + Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", NANDImage->GetConsoleID()); if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) { @@ -909,10 +914,10 @@ bool LoadNAND() else { u32 eaddr = 0x03FFE6E4; - ARM7Write32(eaddr+0x00, *(u32*)&eMMC_CID[0]); - ARM7Write32(eaddr+0x04, *(u32*)&eMMC_CID[4]); - ARM7Write32(eaddr+0x08, *(u32*)&eMMC_CID[8]); - ARM7Write32(eaddr+0x0C, *(u32*)&eMMC_CID[12]); + ARM7Write32(eaddr+0x00, *(const u32*)&emmccid[0]); + ARM7Write32(eaddr+0x04, *(const u32*)&emmccid[4]); + ARM7Write32(eaddr+0x08, *(const u32*)&emmccid[8]); + ARM7Write32(eaddr+0x0C, *(const u32*)&emmccid[12]); ARM7Write16(eaddr+0x2C, 0x0001); ARM7Write16(eaddr+0x2E, 0x0001); ARM7Write16(eaddr+0x3C, 0x0100); @@ -939,9 +944,7 @@ bool LoadNAND() NDS::ARM7->JumpTo(bootparams[6]); } - DSi_NAND::PatchUserData(); - - DSi_NAND::DeInit(); + nandmount.PatchUserData(); return true; } @@ -2690,6 +2693,7 @@ void ARM9IOWrite32(u32 addr, u32 val) u8 ARM7IORead8(u32 addr) { + switch (addr) { case 0x04004000: @@ -2710,14 +2714,14 @@ u8 ARM7IORead8(u32 addr) case 0x04004500: return DSi_I2C::ReadData(); case 0x04004501: return DSi_I2C::Cnt; - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFF; - case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 8) & 0xFF; - case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 16) & 0xFF; - case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 24) & 0xFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 32) & 0xFF; - case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 40) & 0xFF; - case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 48) & 0xFF; - case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 56; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF; + case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF; + case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFF; + case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 24) & 0xFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFF; + case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 40) & 0xFF; + case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 48) & 0xFF; + case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56; case 0x04004D08: return 0; case 0x4004700: return DSi_DSP::SNDExCnt; @@ -2757,10 +2761,10 @@ u16 ARM7IORead16(u32 addr) CASE_READ16_32BIT(0x0400405C, MBK[1][7]) CASE_READ16_32BIT(0x04004060, MBK[1][8]) - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFFFF; - case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 16) & 0xFFFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (ConsoleID >> 32) & 0xFFFF; - case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 48; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFF; + case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFFFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFFFF; + case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48; case 0x04004D08: return 0; case 0x4004700: return DSi_DSP::SNDExCnt; @@ -2836,8 +2840,8 @@ u32 ARM7IORead32(u32 addr) case 0x04004400: return DSi_AES::ReadCnt(); case 0x0400440C: return DSi_AES::ReadOutputFIFO(); - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID & 0xFFFFFFFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return ConsoleID >> 32; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32; case 0x04004D08: return 0; case 0x4004700: diff --git a/src/DSi.h b/src/DSi.h index 1ea2cca5..8822266a 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -22,6 +22,11 @@ #include "NDS.h" #include "DSi_SD.h" +namespace DSi_NAND +{ + class NANDImage; +} + namespace DSi { @@ -33,9 +38,7 @@ extern u32 SCFG_EXT[2]; extern u8 ARM9iBIOS[0x10000]; extern u8 ARM7iBIOS[0x10000]; -extern u8 eMMC_CID[16]; -extern u64 ConsoleID; - +extern std::unique_ptr NANDImage; extern DSi_SDHost* SDMMC; extern DSi_SDHost* SDIO; diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 5b1fc539..67b84eca 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -19,6 +19,7 @@ #include #include #include "DSi.h" +#include "DSi_NAND.h" #include "DSi_AES.h" #include "FIFO.h" #include "tiny-AES-c/aes.hpp" @@ -129,6 +130,7 @@ void Reset() OutputMACDue = false; // initialize keys + u64 consoleid = DSi::NANDImage->GetConsoleID(); // slot 0: modcrypt *(u32*)&KeyX[0][0] = 0x746E694E; @@ -137,14 +139,14 @@ void Reset() // slot 1: 'Tad'/dev.kp *(u32*)&KeyX[1][0] = 0x4E00004A; *(u32*)&KeyX[1][4] = 0x4A00004E; - *(u32*)&KeyX[1][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xC80C4B72; - *(u32*)&KeyX[1][12] = (u32)DSi::ConsoleID; + *(u32*)&KeyX[1][8] = (u32)(consoleid >> 32) ^ 0xC80C4B72; + *(u32*)&KeyX[1][12] = (u32)consoleid; // slot 3: console-unique eMMC crypto - *(u32*)&KeyX[3][0] = (u32)DSi::ConsoleID; - *(u32*)&KeyX[3][4] = (u32)DSi::ConsoleID ^ 0x24EE6906; - *(u32*)&KeyX[3][8] = (u32)(DSi::ConsoleID >> 32) ^ 0xE65B601D; - *(u32*)&KeyX[3][12] = (u32)(DSi::ConsoleID >> 32); + *(u32*)&KeyX[3][0] = (u32)consoleid; + *(u32*)&KeyX[3][4] = (u32)consoleid ^ 0x24EE6906; + *(u32*)&KeyX[3][8] = (u32)(consoleid >> 32) ^ 0xE65B601D; + *(u32*)&KeyX[3][12] = (u32)(consoleid >> 32); *(u32*)&KeyY[3][0] = 0x0AB9DC76; *(u32*)&KeyY[3][4] = 0xBD4DC4D3; *(u32*)&KeyY[3][8] = 0x202DDD1D; diff --git a/src/DSi_AES.h b/src/DSi_AES.h index f3aa550c..4ee9bddc 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -26,12 +26,12 @@ #pragma GCC diagnostic ignored "-Wattributes" #if defined(__GNUC__) && (__GNUC__ >= 11) // gcc 11.* // NOTE: Yes, the compiler does *not* recognize this code pattern, so it is indeed an optimization. -__attribute((always_inline)) static void Bswap128(void* Dst, void* Src) +__attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) { *(__int128*)Dst = __builtin_bswap128(*(__int128*)Src); } #else -__attribute((always_inline)) static void Bswap128(void* Dst, void* Src) +__attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) { for (int i = 0; i < 16; ++i) { diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 7c3e7453..03bed9b8 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -22,6 +22,7 @@ #include "DSi.h" #include "DSi_AES.h" #include "DSi_NAND.h" +#include "FATIO.h" #include "Platform.h" #include "sha1/sha1.hpp" @@ -34,78 +35,16 @@ using namespace Platform; namespace DSi_NAND { -FileHandle* CurFile; -FATFS CurFS; - -u8 eMMC_CID[16]; -u64 ConsoleID; - -u8 FATIV[16]; -u8 FATKey[16]; - -u8 ESKey[16]; - - -UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num); -UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num); - - -bool Init(u8* es_keyY) +NANDImage::NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept : NANDImage(nandfile, es_keyY.data()) { - CurFile = nullptr; - - std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath); - std::string instnand = nandpath + Platform::InstanceFileSuffix(); - - FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); - if ((!nandfile) && (Platform::InstanceID() > 0)) - { - FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read); - if (!orig) - { - Log(LogLevel::Error, "Failed to open DSi NAND\n"); - return false; - } - - long len = FileLength(orig); - - nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWrite); - if (nandfile) - { - u8* tmpbuf = new u8[0x10000]; - for (long i = 0; i < len; i+=0x10000) - { - long blklen = 0x10000; - if ((i+blklen) > len) blklen = len-i; - - FileRead(tmpbuf, blklen, 1, orig); - FileWrite(tmpbuf, blklen, 1, nandfile); - } - delete[] tmpbuf; - } - - Platform::CloseFile(orig); - Platform::CloseFile(nandfile); - - nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); - } +} +NANDImage::NANDImage(Platform::FileHandle* nandfile, const u8* es_keyY) noexcept +{ if (!nandfile) - return false; + return; - u64 nandlen = FileLength(nandfile); - - ff_disk_open(FF_ReadNAND, FF_WriteNAND, (LBA_t)(nandlen>>9)); - - FRESULT res; - res = f_mount(&CurFS, "0:", 0); - if (res != FR_OK) - { - Log(LogLevel::Error, "NAND mounting failed: %d\n", res); - f_unmount("0:"); - ff_disk_close(); - return false; - } + Length = FileLength(nandfile); // read the nocash footer @@ -113,26 +52,24 @@ bool Init(u8* es_keyY) char nand_footer[16]; const char* nand_footer_ref = "DSi eMMC CID/CPU"; - FileRead(nand_footer, 1, 16, nandfile); - if (memcmp(nand_footer, nand_footer_ref, 16)) + FileRead(nand_footer, 1, sizeof(nand_footer), nandfile); + if (memcmp(nand_footer, nand_footer_ref, sizeof(nand_footer))) { // There is another copy of the footer at 000FF800h for the case // that by external tools the image was cut off // See https://problemkaputt.de/gbatek.htm#dsisdmmcimages FileSeek(nandfile, 0x000FF800, FileSeekOrigin::Start); - FileRead(nand_footer, 1, 16, nandfile); - if (memcmp(nand_footer, nand_footer_ref, 16)) + FileRead(nand_footer, 1, sizeof(nand_footer), nandfile); + if (memcmp(nand_footer, nand_footer_ref, sizeof(nand_footer))) { Log(LogLevel::Error, "ERROR: NAND missing nocash footer\n"); CloseFile(nandfile); - f_unmount("0:"); - ff_disk_close(); - return false; + return; } } - FileRead(eMMC_CID, 1, 16, nandfile); - FileRead(&ConsoleID, 1, 8, nandfile); + FileRead(eMMC_CID.data(), 1, sizeof(eMMC_CID), nandfile); + FileRead(&ConsoleID, 1, sizeof(ConsoleID), nandfile); // init NAND crypto @@ -141,10 +78,10 @@ bool Init(u8* es_keyY) u8 keyX[16], keyY[16]; SHA1Init(&sha); - SHA1Update(&sha, eMMC_CID, 16); + SHA1Update(&sha, eMMC_CID.data(), sizeof(eMMC_CID)); SHA1Final(tmp, &sha); - Bswap128(FATIV, tmp); + Bswap128(FATIV.data(), tmp); *(u32*)&keyX[0] = (u32)ConsoleID; *(u32*)&keyX[4] = (u32)ConsoleID ^ 0x24EE6906; @@ -157,7 +94,7 @@ bool Init(u8* es_keyY) *(u32*)&keyY[12] = 0xE1A00005; DSi_AES::DeriveNormalKey(keyX, keyY, tmp); - Bswap128(FATKey, tmp); + Bswap128(FATKey.data(), tmp); *(u32*)&keyX[0] = 0x4E00004A; @@ -165,42 +102,87 @@ bool Init(u8* es_keyY) *(u32*)&keyX[8] = (u32)(ConsoleID >> 32) ^ 0xC80C4B72; *(u32*)&keyX[12] = (u32)ConsoleID; - memcpy(keyY, es_keyY, 16); + memcpy(keyY, es_keyY, sizeof(keyY)); DSi_AES::DeriveNormalKey(keyX, keyY, tmp); - Bswap128(ESKey, tmp); + Bswap128(ESKey.data(), tmp); CurFile = nandfile; - return true; } -void DeInit() +NANDImage::~NANDImage() { - f_unmount("0:"); - ff_disk_close(); - if (CurFile) CloseFile(CurFile); CurFile = nullptr; } - -FileHandle* GetFile() +NANDImage::NANDImage(NANDImage&& other) noexcept : + CurFile(other.CurFile), + eMMC_CID(other.eMMC_CID), + ConsoleID(other.ConsoleID), + FATIV(other.FATIV), + FATKey(other.FATKey), + ESKey(other.ESKey) { - return CurFile; + other.CurFile = nullptr; +} + +NANDImage& NANDImage::operator=(NANDImage&& other) noexcept +{ + if (this != &other) + { + CurFile = other.CurFile; + eMMC_CID = other.eMMC_CID; + ConsoleID = other.ConsoleID; + FATIV = other.FATIV; + FATKey = other.FATKey; + ESKey = other.ESKey; + + other.CurFile = nullptr; + } + + return *this; +} + +NANDMount::NANDMount(NANDImage& nand) noexcept : Image(&nand) +{ + if (!nand) + return; + + CurFS = std::make_unique(); + ff_disk_open( + [this](BYTE* buf, LBA_t sector, UINT num) { + return this->FF_ReadNAND(buf, sector, num); + }, + [this](const BYTE* buf, LBA_t sector, UINT num) { + return this->FF_WriteNAND(buf, sector, num); + }, + (LBA_t)(nand.GetLength()>>9) + ); + + FRESULT res; + res = f_mount(CurFS.get(), "0:", 0); + if (res != FR_OK) + { + Log(LogLevel::Error, "NAND mounting failed: %d\n", res); + f_unmount("0:"); + ff_disk_close(); + return; + } } -void GetIDs(u8* emmc_cid, u64& consoleid) +NANDMount::~NANDMount() { - memcpy(emmc_cid, eMMC_CID, 16); - consoleid = ConsoleID; + f_unmount("0:"); + ff_disk_close(); } -void SetupFATCrypto(AES_ctx* ctx, u32 ctr) +void NANDImage::SetupFATCrypto(AES_ctx* ctx, u32 ctr) { u8 iv[16]; - memcpy(iv, FATIV, sizeof(iv)); + memcpy(iv, FATIV.data(), sizeof(iv)); u32 res; res = iv[15] + (ctr & 0xFF); @@ -218,10 +200,10 @@ void SetupFATCrypto(AES_ctx* ctx, u32 ctr) else break; } - AES_init_ctx_iv(ctx, FATKey, iv); + AES_init_ctx_iv(ctx, FATKey.data(), iv); } -u32 ReadFATBlock(u64 addr, u32 len, u8* buf) +u32 NANDImage::ReadFATBlock(u64 addr, u32 len, u8* buf) { u32 ctr = (u32)(addr >> 4); @@ -243,7 +225,7 @@ u32 ReadFATBlock(u64 addr, u32 len, u8* buf) return len; } -u32 WriteFATBlock(u64 addr, u32 len, u8* buf) +u32 NANDImage::WriteFATBlock(u64 addr, u32 len, const u8* buf) { u32 ctr = (u32)(addr >> 4); @@ -272,30 +254,30 @@ u32 WriteFATBlock(u64 addr, u32 len, u8* buf) } -UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num) +UINT NANDMount::FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num) { // TODO: allow selecting other partitions? u64 baseaddr = 0x10EE00; u64 blockaddr = baseaddr + (sector * 0x200ULL); - u32 res = ReadFATBlock(blockaddr, num*0x200, buf); + u32 res = Image->ReadFATBlock(blockaddr, num*0x200, buf); return res >> 9; } -UINT FF_WriteNAND(BYTE* buf, LBA_t sector, UINT num) +UINT NANDMount::FF_WriteNAND(const BYTE* buf, LBA_t sector, UINT num) { // TODO: allow selecting other partitions? u64 baseaddr = 0x10EE00; u64 blockaddr = baseaddr + (sector * 0x200ULL); - u32 res = WriteFATBlock(blockaddr, num*0x200, buf); + u32 res = Image->WriteFATBlock(blockaddr, num*0x200, buf); return res >> 9; } -bool ESEncrypt(u8* data, u32 len) +bool NANDImage::ESEncrypt(u8* data, u32 len) const { AES_ctx ctx; u8 iv[16]; @@ -307,7 +289,7 @@ bool ESEncrypt(u8* data, u32 len) iv[14] = 0x00; iv[15] = 0x01; - AES_init_ctx_iv(&ctx, ESKey, iv); + AES_init_ctx_iv(&ctx, ESKey.data(), iv); u32 blklen = (len + 0xF) & ~0xF; mac[0] = 0x3A; @@ -380,7 +362,7 @@ bool ESEncrypt(u8* data, u32 len) return true; } -bool ESDecrypt(u8* data, u32 len) +bool NANDImage::ESDecrypt(u8* data, u32 len) { AES_ctx ctx; u8 iv[16]; @@ -392,7 +374,7 @@ bool ESDecrypt(u8* data, u32 len) iv[14] = 0x00; iv[15] = 0x01; - AES_init_ctx_iv(&ctx, ESKey, iv); + AES_init_ctx_iv(&ctx, ESKey.data(), iv); u32 blklen = (len + 0xF) & ~0xF; mac[0] = 0x3A; @@ -486,7 +468,7 @@ bool ESDecrypt(u8* data, u32 len) } -void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) +void NANDMount::ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) { FF_FIL file; FRESULT res; @@ -508,7 +490,7 @@ void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) } -void ReadUserData(DSiFirmwareSystemSettings& data) +void NANDMount::ReadUserData(DSiFirmwareSystemSettings& data) { FF_FIL file; FRESULT res; @@ -557,7 +539,7 @@ void ReadUserData(DSiFirmwareSystemSettings& data) f_close(&file); } -void PatchUserData() +void NANDMount::PatchUserData() { FRESULT res; @@ -653,7 +635,7 @@ void debug_listfiles(const char* path) f_closedir(&dir); } -bool ImportFile(const char* path, const u8* data, size_t len) +bool NANDMount::ImportFile(const char* path, const u8* data, size_t len) { if (!data || !len || !path) return false; @@ -687,7 +669,7 @@ bool ImportFile(const char* path, const u8* data, size_t len) return true; } -bool ImportFile(const char* path, const char* in) +bool NANDMount::ImportFile(const char* path, const char* in) { FF_FIL file; FILE* fin; @@ -730,7 +712,7 @@ bool ImportFile(const char* path, const char* in) return true; } -bool ExportFile(const char* path, const char* out) +bool NANDMount::ExportFile(const char* path, const char* out) { FF_FIL file; FILE* fout; @@ -771,7 +753,7 @@ bool ExportFile(const char* path, const char* out) return true; } -void RemoveFile(const char* path) +void NANDMount::RemoveFile(const char* path) { FF_FILINFO info; FRESULT res = f_stat(path, &info); @@ -784,7 +766,7 @@ void RemoveFile(const char* path) Log(LogLevel::Debug, "Removed file at %s\n", path); } -void RemoveDir(const char* path) +void NANDMount::RemoveDir(const char* path) { FF_DIR dir; FF_FILINFO info; @@ -839,7 +821,7 @@ void RemoveDir(const char* path) } -u32 GetTitleVersion(u32 category, u32 titleid) +u32 NANDMount::GetTitleVersion(u32 category, u32 titleid) { FRESULT res; char path[256]; @@ -859,7 +841,7 @@ u32 GetTitleVersion(u32 category, u32 titleid) return version; } -void ListTitles(u32 category, std::vector& titlelist) +void NANDMount::ListTitles(u32 category, std::vector& titlelist) { FRESULT res; FF_DIR titledir; @@ -908,7 +890,7 @@ void ListTitles(u32 category, std::vector& titlelist) f_closedir(&titledir); } -bool TitleExists(u32 category, u32 titleid) +bool NANDMount::TitleExists(u32 category, u32 titleid) { char path[256]; snprintf(path, sizeof(path), "0:/title/%08x/%08x/content/title.tmd", category, titleid); @@ -917,7 +899,7 @@ bool TitleExists(u32 category, u32 titleid) return (res == FR_OK); } -void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner) +void NANDMount::GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner) { version = GetTitleVersion(category, titleid); if (version == 0xFFFFFFFF) @@ -953,7 +935,7 @@ void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, ND } -bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) +bool NANDMount::CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) { FF_FIL file; FRESULT res; @@ -979,7 +961,7 @@ bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) memset(&ticket[0x222], 0xFF, 0x20); - ESEncrypt(ticket, 0x2A4); + Image->ESEncrypt(ticket, 0x2A4); f_write(&file, ticket, 0x2C4, &nwrite); @@ -988,7 +970,7 @@ bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version) return true; } -bool CreateSaveFile(const char* path, u32 len) +bool NANDMount::CreateSaveFile(const char* path, u32 len) { if (len == 0) return true; if (len < 0x200) return false; @@ -1078,7 +1060,7 @@ bool CreateSaveFile(const char* path, u32 len) return true; } -bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly) { u32 titleid0 = tmd.GetCategory(); u32 titleid1 = tmd.GetID(); @@ -1158,7 +1140,7 @@ bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadat return true; } -bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly) { NDSHeader header {}; { @@ -1196,7 +1178,7 @@ bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool re return true; } -bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly) +bool NANDMount::ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly) { if (!app || appLength < sizeof(NDSHeader)) return false; @@ -1232,7 +1214,7 @@ bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& return true; } -void DeleteTitle(u32 category, u32 titleid) +void NANDMount::DeleteTitle(u32 category, u32 titleid) { char fname[128]; @@ -1243,7 +1225,7 @@ void DeleteTitle(u32 category, u32 titleid) RemoveDir(fname); } -u32 GetTitleDataMask(u32 category, u32 titleid) +u32 NANDMount::GetTitleDataMask(u32 category, u32 titleid) { u32 version; NDSHeader header; @@ -1260,7 +1242,7 @@ u32 GetTitleDataMask(u32 category, u32 titleid) return ret; } -bool ImportTitleData(u32 category, u32 titleid, int type, const char* file) +bool NANDMount::ImportTitleData(u32 category, u32 titleid, int type, const char* file) { char fname[128]; @@ -1286,7 +1268,7 @@ bool ImportTitleData(u32 category, u32 titleid, int type, const char* file) return ImportFile(fname, file); } -bool ExportTitleData(u32 category, u32 titleid, int type, const char* file) +bool NANDMount::ExportTitleData(u32 category, u32 titleid, int type, const char* file) { char fname[128]; diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 9bff8f2b..777afe09 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -20,6 +20,7 @@ #define DSI_NAND_H #include "types.h" +#include "fatfs/ff.h" #include "NDS_Header.h" #include "DSi_TMD.h" #include "SPI_Firmware.h" @@ -27,6 +28,8 @@ #include #include +struct AES_ctx; + namespace DSi_NAND { @@ -40,29 +43,95 @@ enum union DSiFirmwareSystemSettings; union DSiSerialData; using DSiHardwareInfoN = std::array; +using DSiKey = std::array; -bool Init(u8* es_keyY); -void DeInit(); +class NANDImage +{ +public: + explicit NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept; + explicit NANDImage(Platform::FileHandle* nandfile, const u8* es_keyY) noexcept; + ~NANDImage(); + NANDImage(const NANDImage&) = delete; + NANDImage& operator=(const NANDImage&) = delete; -Platform::FileHandle* GetFile(); + NANDImage(NANDImage&& other) noexcept; + NANDImage& operator=(NANDImage&& other) noexcept; -void GetIDs(u8* emmc_cid, u64& consoleid); + Platform::FileHandle* GetFile() { return CurFile; } -void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN); + [[nodiscard]] const DSiKey& GetEMMCID() const noexcept { return eMMC_CID; } + [[nodiscard]] u64 GetConsoleID() const noexcept { return ConsoleID; } + [[nodiscard]] u64 GetLength() const noexcept { return Length; } -void ReadUserData(DSiFirmwareSystemSettings& data); -void PatchUserData(); + explicit operator bool() const { return CurFile != nullptr; } +private: + friend class NANDMount; + void SetupFATCrypto(AES_ctx* ctx, u32 ctr); + u32 ReadFATBlock(u64 addr, u32 len, u8* buf); + u32 WriteFATBlock(u64 addr, u32 len, const u8* buf); + bool ESEncrypt(u8* data, u32 len) const; + bool ESDecrypt(u8* data, u32 len); + Platform::FileHandle* CurFile = nullptr; + DSiKey eMMC_CID; + u64 ConsoleID; + DSiKey FATIV; + DSiKey FATKey; + DSiKey ESKey; + u64 Length; +}; -void ListTitles(u32 category, std::vector& titlelist); -bool TitleExists(u32 category, u32 titleid); -void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner); -bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly); -bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly); -void DeleteTitle(u32 category, u32 titleid); +class NANDMount +{ +public: + explicit NANDMount(NANDImage& nand) noexcept; + ~NANDMount(); + NANDMount(const NANDMount&) = delete; + NANDMount& operator=(const NANDMount&) = delete; + + // Move constructor deleted so that the closure passed to FATFS can't be invalidated + NANDMount(NANDMount&&) = delete; + NANDMount& operator=(NANDMount&&) = delete; + + void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN); + + void ReadUserData(DSiFirmwareSystemSettings& data); + void PatchUserData(); + + void ListTitles(u32 category, std::vector& titlelist); + bool TitleExists(u32 category, u32 titleid); + void GetTitleInfo(u32 category, u32 titleid, u32& version, NDSHeader* header, NDSBanner* banner); + bool ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& tmd, bool readonly); + bool ImportTitle(const u8* app, size_t appLength, const DSi_TMD::TitleMetadata& tmd, bool readonly); + void DeleteTitle(u32 category, u32 titleid); + + 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); + + bool ImportFile(const char* path, const u8* data, size_t len); + bool ImportFile(const char* path, const char* in); + bool ExportFile(const char* path, const char* out); + void RemoveFile(const char* path); + void RemoveDir(const char* path); + + explicit operator bool() const { return Image != nullptr && CurFS != nullptr; } +private: + u32 GetTitleVersion(u32 category, u32 titleid); + bool CreateTicket(const char* path, u32 titleid0, u32 titleid1, u8 version); + bool CreateSaveFile(const char* path, u32 len); + bool InitTitleFileStructure(const NDSHeader& header, const DSi_TMD::TitleMetadata& tmd, bool readonly); + UINT FF_ReadNAND(BYTE* buf, LBA_t sector, UINT num); + UINT FF_WriteNAND(const BYTE* buf, LBA_t sector, UINT num); + + NANDImage* Image; + + // We keep a pointer to CurFS because fatfs maintains a global pointer to it; + // therefore if we embed the FATFS directly in the object, + // we can't give it move semantics. + std::unique_ptr CurFS; + +}; -u32 GetTitleDataMask(u32 category, u32 titleid); -bool ImportTitleData(u32 category, u32 titleid, int type, const char* file); -bool ExportTitleData(u32 category, u32 titleid, int type, const char* file); typedef std::array SHA1Hash; typedef std::array TitleID; diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 4f287b17..158ae1e0 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -20,6 +20,7 @@ #include #include "DSi.h" #include "DSi_SD.h" +#include "DSi_NAND.h" #include "DSi_NWifi.h" #include "Platform.h" @@ -137,11 +138,8 @@ void DSi_SDHost::Reset() else sd = nullptr; - std::string nandpath = Platform::GetConfigString(Platform::DSi_NANDPath); - std::string instnand = nandpath + Platform::InstanceFileSuffix(); - - mmc = new DSi_MMCStorage(this, true, instnand); - mmc->SetCID(DSi::eMMC_CID); + mmc = new DSi_MMCStorage(this, *DSi::NANDImage); + mmc->SetCID(DSi::NANDImage->GetEMMCID().data()); Ports[0] = sd; Ports[1] = mmc; @@ -768,14 +766,9 @@ void DSi_SDHost::CheckSwapFIFO() #define MMC_DESC (Internal?"NAND":"SDcard") -DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename) - : DSi_SDDevice(host) +DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand) + : DSi_SDDevice(host), Internal(true), NAND(&nand), SD(nullptr) { - Internal = internal; - File = Platform::OpenLocalFile(filename, FileMode::ReadWriteExisting); - - SD = nullptr; - ReadOnly = false; } @@ -783,7 +776,7 @@ DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::strin : DSi_SDDevice(host) { Internal = internal; - File = nullptr; + NAND = nullptr; SD = new FATStorage(filename, size, readonly, sourcedir); SD->Open(); @@ -798,10 +791,8 @@ DSi_MMCStorage::~DSi_MMCStorage() SD->Close(); delete SD; } - if (File) - { - CloseFile(File); - } + + // Do not close the NANDImage, it's not owned by this object } void DSi_MMCStorage::Reset() @@ -925,7 +916,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) case 12: // stop operation SetState(0x04); - if (File) FileFlush(File); + if (NAND) FileFlush(NAND->GetFile()); RWCommand = 0; Host->SendResponse(CSR, true); return; @@ -1052,10 +1043,10 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr) { SD->ReadSectors((u32)(addr >> 9), 1, data); } - else if (File) + else if (NAND) { - FileSeek(File, addr, FileSeekOrigin::Start); - FileRead(&data[addr & 0x1FF], 1, len, File); + FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start); + FileRead(&data[addr & 0x1FF], 1, len, NAND->GetFile()); } return Host->DataRX(&data[addr & 0x1FF], len); @@ -1082,10 +1073,10 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr) { SD->WriteSectors((u32)(addr >> 9), 1, data); } - else if (File) + else if (NAND) { - FileSeek(File, addr, FileSeekOrigin::Start); - FileWrite(&data[addr & 0x1FF], 1, len, File); + FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start); + FileWrite(&data[addr & 0x1FF], 1, len, NAND->GetFile()); } } } diff --git a/src/DSi_SD.h b/src/DSi_SD.h index fe9e23ab..8e53a77a 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -24,6 +24,11 @@ #include "FATStorage.h" #include "Savestate.h" +namespace DSi_NAND +{ + class NANDImage; +} + class DSi_SDDevice; @@ -125,7 +130,7 @@ protected: class DSi_MMCStorage : public DSi_SDDevice { public: - DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename); + DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand); DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir); ~DSi_MMCStorage(); @@ -133,7 +138,7 @@ public: void DoSavestate(Savestate* file); - void SetCID(u8* cid) { memcpy(CID, cid, 16); } + void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); } void SendCMD(u8 cmd, u32 param); void SendACMD(u8 cmd, u32 param); @@ -142,7 +147,7 @@ public: private: bool Internal; - Platform::FileHandle* File; + DSi_NAND::NANDImage* NAND; FATStorage* SD; u8 CID[16]; diff --git a/src/fatfs/diskio.c b/src/FATIO.cpp similarity index 78% rename from src/fatfs/diskio.c rename to src/FATIO.cpp index 5b5c0545..3e91fbad 100644 --- a/src/fatfs/diskio.c +++ b/src/FATIO.cpp @@ -1,15 +1,24 @@ -/*-----------------------------------------------------------------------*/ -/* Low level disk I/O module SKELETON for FatFs (C)ChaN, 2019 */ -/*-----------------------------------------------------------------------*/ -/* If a working storage control module is available, it should be */ -/* attached to the FatFs via a glue function rather than modifying it. */ -/* This is an example of glue functions to attach various exsisting */ -/* storage control modules to the FatFs module with a defined API. */ -/*-----------------------------------------------------------------------*/ +/* + Copyright 2016-2023 melonDS team -#include "ff.h" /* Obtains integer types */ -#include "diskio.h" /* Declarations of disk functions */ + 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 "FATIO.h" +#include "fatfs/ff.h" +#include "fatfs/diskio.h" static ff_disk_read_cb ReadCb; static ff_disk_write_cb WriteCb; @@ -17,7 +26,7 @@ static LBA_t SectorCount; static DSTATUS Status = STA_NOINIT | STA_NODISK; -void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt) +void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt) { if (!readcb) return; @@ -30,10 +39,10 @@ void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt else Status &= ~STA_PROTECT; } -void ff_disk_close(void) +void ff_disk_close() { - ReadCb = (void*)0; - WriteCb = (void*)0; + ReadCb = nullptr; + WriteCb = nullptr; SectorCount = 0; Status &= ~STA_PROTECT; diff --git a/src/FATIO.h b/src/FATIO.h new file mode 100644 index 00000000..b3b63f3f --- /dev/null +++ b/src/FATIO.h @@ -0,0 +1,34 @@ +/* + 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 FATIO_H +#define FATIO_H + +#include +#include "fatfs/ff.h" + +// extra additions for interfacing with melonDS + +using ff_disk_read_cb = std::function; +using ff_disk_write_cb = std::function; + +void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt); +void ff_disk_close(); + +#endif // FATIO_H diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index d882f8dc..1d8b741d 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -21,6 +21,7 @@ #include #include +#include "FATIO.h" #include "FATStorage.h" #include "Platform.h" @@ -122,7 +123,7 @@ UINT FATStorage::FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num) return ReadSectorsInternal(FF_File, FF_FileSize, sector, num, buf); } -UINT FATStorage::FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num) +UINT FATStorage::FF_WriteStorage(const BYTE* buf, LBA_t sector, UINT num) { return WriteSectorsInternal(FF_File, FF_FileSize, sector, num, buf); } @@ -157,7 +158,7 @@ u32 FATStorage::ReadSectorsInternal(FileHandle* file, u64 filelen, u32 start, u3 return res; } -u32 FATStorage::WriteSectorsInternal(FileHandle* file, u64 filelen, u32 start, u32 num, u8* data) +u32 FATStorage::WriteSectorsInternal(FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data) { if (!file) return 0; diff --git a/src/FATStorage.h b/src/FATStorage.h index 6b9beb50..7edad13e 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -55,10 +55,10 @@ private: static Platform::FileHandle* FF_File; static u64 FF_FileSize; static UINT FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num); - static UINT FF_WriteStorage(BYTE* buf, LBA_t sector, UINT num); + static UINT FF_WriteStorage(const BYTE* buf, LBA_t sector, UINT num); static u32 ReadSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data); - static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data); + static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data); void LoadIndex(); void SaveIndex(); diff --git a/src/Platform.h b/src/Platform.h index 97522393..67e6b335 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -107,8 +107,6 @@ enum ConfigEntry ExternalBIOSEnable, - DSi_NANDPath, - DLDI_Enable, DLDI_ImagePath, DLDI_ImageSize, diff --git a/src/fatfs/ff.h b/src/fatfs/ff.h index a8c34aa4..c2832be1 100644 --- a/src/fatfs/ff.h +++ b/src/fatfs/ff.h @@ -414,16 +414,6 @@ int ff_del_syncobj (FF_SYNC_t sobj); /* Delete a sync object */ #define AM_DIR 0x10 /* Directory */ #define AM_ARC 0x20 /* Archive */ - -// extra additions for interfacing with melonDS - -typedef UINT (*ff_disk_read_cb)(BYTE* buff, LBA_t sector, UINT count); -typedef UINT (*ff_disk_write_cb)(BYTE* buff, LBA_t sector, UINT count); - -void ff_disk_open(ff_disk_read_cb readcb, ff_disk_write_cb writecb, LBA_t seccnt); -void ff_disk_close(void); - - #ifdef __cplusplus } #endif diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 0574d5d0..7f6e1d56 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -250,8 +250,6 @@ std::string GetConfigString(ConfigEntry entry) { switch (entry) { - case DSi_NANDPath: return Config::DSiNANDPath; - case DLDI_ImagePath: return Config::DLDISDPath; case DLDI_FolderPath: return Config::DLDIFolderPath; diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 49f4c457..206332bb 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -595,6 +595,10 @@ void Reset() LoadBIOSFiles(); InstallFirmware(); + if (Config::ConsoleType == 1) + { + InstallNAND(&DSi::ARM7iBIOS[0x8308]); + } NDS::Reset(); SetBatteryLevels(); @@ -657,6 +661,9 @@ bool LoadBIOS() if (!InstallFirmware()) return false; + if (Config::ConsoleType == 1 && !InstallNAND(&DSi::ARM7iBIOS[0x8308])) + return false; + if (NDS::NeedsDirectBoot()) return false; @@ -948,6 +955,47 @@ void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) firmware.UpdateChecksums(); } +static Platform::FileHandle* OpenNANDFile() noexcept +{ + std::string nandpath = Config::DSiNANDPath; + std::string instnand = nandpath + Platform::InstanceFileSuffix(); + + FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); + if ((!nandfile) && (Platform::InstanceID() > 0)) + { + FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read); + if (!orig) + { + Log(LogLevel::Error, "Failed to open DSi NAND\n"); + return nullptr; + } + + QFile::copy(QString::fromStdString(nandpath), QString::fromStdString(instnand)); + + nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); + } + + return nandfile; +} + +bool InstallNAND(const u8* es_keyY) +{ + Platform::FileHandle* nandfile = OpenNANDFile(); + if (!nandfile) + return false; + + if (auto nand = std::make_unique(nandfile, es_keyY); *nand) + { + DSi::NANDImage = std::move(nand); + return true; + } + else + { + DSi::NANDImage = nullptr; + return false; + } +} + bool InstallFirmware() { using namespace SPI_Firmware; @@ -1089,6 +1137,9 @@ bool LoadROM(QStringList filepath, bool reset) NDS::SetConsoleType(Config::ConsoleType); NDS::EjectCart(); LoadBIOSFiles(); + if (Config::ConsoleType == 1) + InstallNAND(&DSi::ARM7iBIOS[0x8308]); + NDS::Reset(); SetBatteryLevels(); } diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 5faef1a8..2eeed0a2 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -22,6 +22,7 @@ #include "types.h" #include "SaveManager.h" #include "AREngine.h" +#include "DSi_NAND.h" #include #include @@ -40,6 +41,7 @@ bool LoadBIOS(); void ClearBackupState(); bool InstallFirmware(); +bool InstallNAND(const u8* es_keyY); bool LoadROM(QStringList filepath, bool reset); void EjectCart(); bool CartInserted(); diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index d5147fc6..84bd1efe 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -32,13 +32,13 @@ using namespace Platform; -bool TitleManagerDialog::NANDInited = false; +std::unique_ptr TitleManagerDialog::nand = nullptr; TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr; extern std::string EmuDirectory; -TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(new Ui::TitleManagerDialog) +TitleManagerDialog::TitleManagerDialog(QWidget* parent, DSi_NAND::NANDImage& image) : QDialog(parent), ui(new Ui::TitleManagerDialog), nandmount(image) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -47,7 +47,7 @@ TitleManagerDialog::TitleManagerDialog(QWidget* parent) : QDialog(parent), ui(ne const u32 category = 0x00030004; std::vector titlelist; - DSi_NAND::ListTitles(category, titlelist); + nandmount.ListTitles(category, titlelist); for (std::vector::iterator it = titlelist.begin(); it != titlelist.end(); it++) { @@ -109,7 +109,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) NDSHeader header; NDSBanner banner; - DSi_NAND::GetTitleInfo(category, titleid, version, &header, &banner); + nandmount.GetTitleInfo(category, titleid, version, &header, &banner); u32 icondata[32*32]; ROMManager::ROMIcon(banner.Icon, banner.Palette, icondata); @@ -137,7 +137,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) bool TitleManagerDialog::openNAND() { - NANDInited = false; + nand = nullptr; FileHandle* bios7i = Platform::OpenLocalFile(Config::DSiBIOS7Path, FileMode::Read); if (!bios7i) @@ -148,22 +148,25 @@ bool TitleManagerDialog::openNAND() FileRead(es_keyY, 16, 1, bios7i); CloseFile(bios7i); - if (!DSi_NAND::Init(es_keyY)) - { + FileHandle* nandfile = Platform::OpenLocalFile(Config::DSiNANDPath, FileMode::ReadWriteExisting); + if (!nandfile) return false; + + nand = std::make_unique(nandfile, es_keyY); + if (!*nand) + { // If loading and mounting the NAND image failed... + nand = nullptr; + return false; + // NOTE: The NANDImage takes ownership of the FileHandle, + // so it will be closed even if the NANDImage constructor fails. } - NANDInited = true; return true; } void TitleManagerDialog::closeNAND() { - if (NANDInited) - { - DSi_NAND::DeInit(); - NANDInited = false; - } + nand = nullptr; } void TitleManagerDialog::done(int r) @@ -175,7 +178,7 @@ void TitleManagerDialog::done(int r) void TitleManagerDialog::on_btnImportTitle_clicked() { - TitleImportDialog* importdlg = new TitleImportDialog(this, importAppPath, &importTmdData, importReadOnly); + TitleImportDialog* importdlg = new TitleImportDialog(this, importAppPath, &importTmdData, importReadOnly, nandmount); importdlg->open(); connect(importdlg, &TitleImportDialog::finished, this, &TitleManagerDialog::onImportTitleFinished); @@ -190,14 +193,16 @@ void TitleManagerDialog::onImportTitleFinished(int res) titleid[0] = importTmdData.GetCategory(); titleid[1] = importTmdData.GetID(); + assert(nand != nullptr); + assert(*nand); // remove anything that might hinder the install - DSi_NAND::DeleteTitle(titleid[0], titleid[1]); + nandmount.DeleteTitle(titleid[0], titleid[1]); - bool importres = DSi_NAND::ImportTitle(importAppPath.toStdString().c_str(), importTmdData, importReadOnly); + bool importres = nandmount.ImportTitle(importAppPath.toStdString().c_str(), importTmdData, importReadOnly); if (!importres) { // remove a potential half-completed install - DSi_NAND::DeleteTitle(titleid[0], titleid[1]); + nandmount.DeleteTitle(titleid[0], titleid[1]); QMessageBox::critical(this, "Import title - melonDS", @@ -224,7 +229,7 @@ void TitleManagerDialog::on_btnDeleteTitle_clicked() return; u64 titleid = cur->data(Qt::UserRole).toULongLong(); - DSi_NAND::DeleteTitle((u32)(titleid >> 32), (u32)titleid); + nandmount.DeleteTitle((u32)(titleid >> 32), (u32)titleid); delete cur; } @@ -317,7 +322,7 @@ void TitleManagerDialog::onImportTitleData() } u64 titleid = cur->data(Qt::UserRole).toULongLong(); - bool res = DSi_NAND::ImportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); + bool res = nandmount.ImportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); if (!res) { QMessageBox::critical(this, @@ -370,7 +375,7 @@ void TitleManagerDialog::onExportTitleData() if (file.isEmpty()) return; u64 titleid = cur->data(Qt::UserRole).toULongLong(); - bool res = DSi_NAND::ExportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); + bool res = nandmount.ExportTitleData((u32)(titleid >> 32), (u32)titleid, type, file.toStdString().c_str()); if (!res) { QMessageBox::critical(this, @@ -380,8 +385,8 @@ void TitleManagerDialog::onExportTitleData() } -TitleImportDialog::TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly) -: QDialog(parent), ui(new Ui::TitleImportDialog), appPath(apppath), tmdData(tmd), readOnly(readonly) +TitleImportDialog::TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly, DSi_NAND::NANDMount& nandmount) +: QDialog(parent), ui(new Ui::TitleImportDialog), appPath(apppath), tmdData(tmd), readOnly(readonly), nandmount(nandmount) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -455,7 +460,7 @@ void TitleImportDialog::accept() } } - if (DSi_NAND::TitleExists(titleid[1], titleid[0])) + if (nandmount.TitleExists(titleid[1], titleid[0])) { if (QMessageBox::question(this, "Import title - melonDS", diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h index fc92fd81..5182e1da 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.h +++ b/src/frontend/qt_sdl/TitleManagerDialog.h @@ -19,6 +19,7 @@ #ifndef TITLEMANAGERDIALOG_H #define TITLEMANAGERDIALOG_H +#include #include #include #include @@ -30,6 +31,7 @@ #include #include "DSi_TMD.h" +#include "DSi_NAND.h" namespace Ui { @@ -44,10 +46,10 @@ class TitleManagerDialog : public QDialog Q_OBJECT public: - explicit TitleManagerDialog(QWidget* parent); + explicit TitleManagerDialog(QWidget* parent, DSi_NAND::NANDImage& image); ~TitleManagerDialog(); - static bool NANDInited; + static std::unique_ptr nand; static bool openNAND(); static void closeNAND(); @@ -68,7 +70,10 @@ public: return nullptr; } - currentDlg = new TitleManagerDialog(parent); + assert(nand != nullptr); + assert(*nand); + + currentDlg = new TitleManagerDialog(parent, *nand); currentDlg->open(); return currentDlg; } @@ -89,6 +94,7 @@ private slots: void onExportTitleData(); private: + DSi_NAND::NANDMount nandmount; Ui::TitleManagerDialog* ui; QString importAppPath; @@ -106,7 +112,7 @@ class TitleImportDialog : public QDialog Q_OBJECT public: - explicit TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly); + explicit TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly, DSi_NAND::NANDMount& nand); ~TitleImportDialog(); private slots: @@ -119,6 +125,7 @@ private slots: private: Ui::TitleImportDialog* ui; + DSi_NAND::NANDMount& nandmount; QButtonGroup* grpTmdSource; From 3d58a338a16bc41b9106857645fabc0221de711d Mon Sep 17 00:00:00 2001 From: RSDuck Date: Sun, 22 Oct 2023 15:21:03 +0200 Subject: [PATCH 004/157] store pc+12 when storing r15 --- src/ARMInterpreter_LoadStore.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index e7b83eb9..81877f02 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -62,14 +62,20 @@ namespace ARMInterpreter #define A_STR \ offset += cpu->R[(cpu->CurInstr>>16) & 0xF]; \ - cpu->DataWrite32(offset, cpu->R[(cpu->CurInstr>>12) & 0xF]); \ + u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \ + if (((cpu->CurInstr>>12) & 0xF) == 0xF) \ + storeval += 4; \ + cpu->DataWrite32(offset, storeval); \ if (cpu->CurInstr & (1<<21)) cpu->R[(cpu->CurInstr>>16) & 0xF] = offset; \ cpu->AddCycles_CD(); // TODO: user mode (bit21) #define A_STR_POST \ u32 addr = cpu->R[(cpu->CurInstr>>16) & 0xF]; \ - cpu->DataWrite32(addr, cpu->R[(cpu->CurInstr>>12) & 0xF]); \ + u32 storeval = cpu->R[(cpu->CurInstr>>12) & 0xF]; \ + if (((cpu->CurInstr>>12) & 0xF) == 0xF) \ + storeval += 4; \ + cpu->DataWrite32(addr, storeval); \ cpu->R[(cpu->CurInstr>>16) & 0xF] += offset; \ cpu->AddCycles_CD(); From 3ab752b8ca7878246c3d7f8a338a8bc3b0de26dd Mon Sep 17 00:00:00 2001 From: PoroCYon <3253268+PoroCYon@users.noreply.github.com> Date: Sun, 22 Oct 2023 15:35:31 +0200 Subject: [PATCH 005/157] GDB stub (#1583) * gdbstub beginnings * gdbstub: finish gdb impl things, next up is integration with melonDS * holy fuck the gdbstub works * gdb breakpoints work, but there's a mysterious crash on continue * fix memory corruption that sometimes happened, and make resetting the console thru gdb work * remove some gdb debug printing * fix things in gdbstub * separate option for enabling gdbstub * add mode-dependent CPU registers * C++ize the GDBstub code * add gdbstub config in emu settings dialog * make sure gdb is disabled when jit is enabled * Remove unnecessary compiler flags, mark ARMJIT assembly code as no-execute-stack This hardens the binary a little bit against common exploitation methods * add option to wait for debugger attach on startup * only insert GNU stack notes on linux * disable gdbstub enable checkbox when jit is enabled * fix non-linux incompatibilities * enable gdbstub by default * fix issues with gdbstub settings disable stuff * format stuff * update gdb test code * Fix segfault when calling StubCallbacks->GetCPU() C++ overrides are hard. Please I'm just a lowly C programmer. * fix packet size not being sent correctly Thanks to @GlowingUmbreon on Github for troubleshooting this * fix select(2) calls (i should read docs more properly) * fix GDB command sequencing/parsing issue (hopefully) * [GDB] implement no-ack mode * fix sending ack on handshake * get lldb to work --- .gitignore | 2 +- CMakeLists.txt | 5 + src/ARM.cpp | 286 +++++++ src/ARM.h | 121 ++- src/ARMInterpreter.cpp | 10 + src/ARMJIT_A64/ARMJIT_Linkage.S | 5 + src/ARMJIT_x64/ARMJIT_Linkage.S | 5 + src/CMakeLists.txt | 22 +- src/NDS.cpp | 3 + src/Platform.h | 10 +- src/SPU.cpp | 10 +- src/debug/GdbArch.h | 62 ++ src/debug/GdbCmds.cpp | 924 ++++++++++++++++++++++ src/debug/GdbCmds.h | 53 ++ src/debug/GdbProto.cpp | 389 +++++++++ src/debug/GdbProto.h | 40 + src/debug/GdbStub.cpp | 693 ++++++++++++++++ src/debug/GdbStub.h | 184 +++++ src/debug/gdb_test/.gitignore | 2 + src/debug/gdb_test/Makefile | 28 + src/debug/gdb_test/main.cpp | 124 +++ src/debug/hexutil.h | 75 ++ src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/Config.cpp | 16 + src/frontend/qt_sdl/Config.h | 6 + src/frontend/qt_sdl/EmuSettingsDialog.cpp | 64 +- src/frontend/qt_sdl/EmuSettingsDialog.h | 2 + src/frontend/qt_sdl/EmuSettingsDialog.ui | 106 ++- src/frontend/qt_sdl/Platform.cpp | 11 + 29 files changed, 3210 insertions(+), 49 deletions(-) create mode 100644 src/debug/GdbArch.h create mode 100644 src/debug/GdbCmds.cpp create mode 100644 src/debug/GdbCmds.h create mode 100644 src/debug/GdbProto.cpp create mode 100644 src/debug/GdbProto.h create mode 100644 src/debug/GdbStub.cpp create mode 100644 src/debug/GdbStub.h create mode 100644 src/debug/gdb_test/.gitignore create mode 100644 src/debug/gdb_test/Makefile create mode 100644 src/debug/gdb_test/main.cpp create mode 100644 src/debug/hexutil.h diff --git a/.gitignore b/.gitignore index e76a0944..1aa5e3a1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ -build +build*/ bin obj *.depend diff --git a/CMakeLists.txt b/CMakeLists.txt index 57d82eff..598b3bdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,6 +99,11 @@ if (CCACHE) set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE}) endif() +option(ENABLE_GDBSTUB "Enable GDB stub" ON) +if (ENABLE_GDBSTUB) + add_definitions(-DGDBSTUB_ENABLED) +endif() + option(BUILD_QT_SDL "Build Qt/SDL frontend" ON) add_subdirectory(src) diff --git a/src/ARM.cpp b/src/ARM.cpp index b59530d1..2fd7c2d4 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -25,6 +25,7 @@ #include "AREngine.h" #include "ARMJIT.h" #include "Platform.h" +#include "GPU.h" #ifdef JIT_ENABLED #include "ARMJIT.h" @@ -34,6 +35,45 @@ using Platform::Log; using Platform::LogLevel; +#ifdef GDBSTUB_ENABLED +void ARM::GdbCheckA() +{ + if (!IsSingleStep && !BreakReq) + { // check if eg. break signal is incoming etc. + Gdb::StubState st = GdbStub.Enter(false, Gdb::TgtStatus::NoEvent, ~(u32)0u, BreakOnStartup); + BreakOnStartup = false; + IsSingleStep = st == Gdb::StubState::Step; + BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break; + } +} +void ARM::GdbCheckB() +{ + if (IsSingleStep || BreakReq) + { // use else here or we single-step the same insn twice in gdb + u32 pc_real = R[15] - ((CPSR & 0x20) ? 2 : 4); + Gdb::StubState st = GdbStub.Enter(true, Gdb::TgtStatus::SingleStep, pc_real); + IsSingleStep = st == Gdb::StubState::Step; + BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break; + } +} +void ARM::GdbCheckC() +{ + u32 pc_real = R[15] - ((CPSR & 0x20) ? 2 : 4); + Gdb::StubState st = GdbStub.CheckBkpt(pc_real, true, true); + if (st != Gdb::StubState::CheckNoHit) + { + IsSingleStep = st == Gdb::StubState::Step; + BreakReq = st == Gdb::StubState::Attach || st == Gdb::StubState::Break; + } + else GdbCheckB(); +} +#else +ARM::GdbCheckA() {} +ARM::GdbCheckB() {} +ARM::GdbCheckC() {} +#endif + + // instruction timing notes // // * simple instruction: 1S (code) @@ -70,9 +110,22 @@ u32 ARM::ConditionTable[16] = ARM::ARM(u32 num) +#ifdef GDBSTUB_ENABLED + : GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)) +#endif { // well uh Num = num; + +#ifdef GDBSTUB_ENABLED + if (Platform::GetConfigBool(Platform::GdbEnabled) +#ifdef JIT_ENABLED + && !Platform::GetConfigBool(Platform::JIT_Enable) +#endif + ) + GdbStub.Init(); + IsSingleStep = false; +#endif } ARM::~ARM() @@ -139,6 +192,13 @@ void ARM::Reset() FastBlockLookupSize = 0; #endif +#ifdef GDBSTUB_ENABLED + IsSingleStep = false; + BreakReq = false; + BreakOnStartup = Platform::GetConfigBool( + Num ? Platform::GdbARM7BreakOnStartup : Platform::GdbARM9BreakOnStartup); +#endif + // zorp JumpTo(ExceptionBase); } @@ -571,8 +631,15 @@ void ARMv5::DataAbort() JumpTo(ExceptionBase + 0x10); } +void ARM::CheckGdbIncoming() +{ + GdbCheckA(); +} + void ARMv5::Execute() { + GdbCheckB(); + if (Halted) { if (Halted == 2) @@ -596,6 +663,8 @@ void ARMv5::Execute() { if (CPSR & 0x20) // THUMB { + GdbCheckC(); + // prefetch R[15] += 2; CurInstr = NextInstr[0]; @@ -609,6 +678,8 @@ void ARMv5::Execute() } else { + GdbCheckC(); + // prefetch R[15] += 4; CurInstr = NextInstr[0]; @@ -723,6 +794,8 @@ void ARMv5::ExecuteJIT() void ARMv4::Execute() { + GdbCheckB(); + if (Halted) { if (Halted == 2) @@ -746,6 +819,8 @@ void ARMv4::Execute() { if (CPSR & 0x20) // THUMB { + GdbCheckC(); + // prefetch R[15] += 2; CurInstr = NextInstr[0]; @@ -758,6 +833,8 @@ void ARMv4::Execute() } else { + GdbCheckC(); + // prefetch R[15] += 4; CurInstr = NextInstr[0]; @@ -916,3 +993,212 @@ void ARMv4::FillPipeline() NextInstr[1] = CodeRead32(R[15]); } } + +#ifdef GDBSTUB_ENABLED +u32 ARM::ReadReg(Gdb::Register reg) +{ + using Gdb::Register; + int r = static_cast(reg); + + if (reg < Register::pc) return R[r]; + else if (reg == Register::pc) + { + return R[r] - ((CPSR & 0x20) ? 2 : 4); + } + else if (reg == Register::cpsr) return CPSR; + else if (reg == Register::sp_usr || reg == Register::lr_usr) + { + r -= static_cast(Register::sp_usr); + if (ModeIs(0x10) || ModeIs(0x1f)) + { + return R[13 + r]; + } + else switch (CPSR & 0x1f) + { + case 0x11: return R_FIQ[5 + r]; + case 0x12: return R_IRQ[0 + r]; + case 0x13: return R_SVC[0 + r]; + case 0x17: return R_ABT[0 + r]; + case 0x1b: return R_UND[0 + r]; + } + } + else if (reg >= Register::r8_fiq && reg <= Register::lr_fiq) + { + r -= static_cast(Register::r8_fiq); + return ModeIs(0x11) ? R[ 8 + r] : R_FIQ[r]; + } + else if (reg == Register::sp_irq || reg == Register::lr_irq) + { + r -= static_cast(Register::sp_irq); + return ModeIs(0x12) ? R[13 + r] : R_IRQ[r]; + } + else if (reg == Register::sp_svc || reg == Register::lr_svc) + { + r -= static_cast(Register::sp_svc); + return ModeIs(0x13) ? R[13 + r] : R_SVC[r]; + } + else if (reg == Register::sp_abt || reg == Register::lr_abt) + { + r -= static_cast(Register::sp_abt); + return ModeIs(0x17) ? R[13 + r] : R_ABT[r]; + } + else if (reg == Register::sp_und || reg == Register::lr_und) + { + r -= static_cast(Register::sp_und); + return ModeIs(0x1b) ? R[13 + r] : R_UND[r]; + } + else if (reg == Register::spsr_fiq) return ModeIs(0x11) ? CPSR : R_FIQ[7]; + else if (reg == Register::spsr_irq) return ModeIs(0x12) ? CPSR : R_IRQ[2]; + else if (reg == Register::spsr_svc) return ModeIs(0x13) ? CPSR : R_SVC[2]; + else if (reg == Register::spsr_abt) return ModeIs(0x17) ? CPSR : R_ABT[2]; + else if (reg == Register::spsr_und) return ModeIs(0x1b) ? CPSR : R_UND[2]; + + Log(LogLevel::Warn, "GDB reg read: unknown reg no %d\n", r); + return 0xdeadbeef; +} +void ARM::WriteReg(Gdb::Register reg, u32 v) +{ + using Gdb::Register; + int r = static_cast(reg); + + if (reg < Register::pc) R[r] = v; + else if (reg == Register::pc) JumpTo(v); + else if (reg == Register::cpsr) CPSR = v; + else if (reg == Register::sp_usr || reg == Register::lr_usr) + { + r -= static_cast(Register::sp_usr); + if (ModeIs(0x10) || ModeIs(0x1f)) + { + R[13 + r] = v; + } + else switch (CPSR & 0x1f) + { + case 0x11: R_FIQ[5 + r] = v; break; + case 0x12: R_IRQ[0 + r] = v; break; + case 0x13: R_SVC[0 + r] = v; break; + case 0x17: R_ABT[0 + r] = v; break; + case 0x1b: R_UND[0 + r] = v; break; + } + } + else if (reg >= Register::r8_fiq && reg <= Register::lr_fiq) + { + r -= static_cast(Register::r8_fiq); + *(ModeIs(0x11) ? &R[ 8 + r] : &R_FIQ[r]) = v; + } + else if (reg == Register::sp_irq || reg == Register::lr_irq) + { + r -= static_cast(Register::sp_irq); + *(ModeIs(0x12) ? &R[13 + r] : &R_IRQ[r]) = v; + } + else if (reg == Register::sp_svc || reg == Register::lr_svc) + { + r -= static_cast(Register::sp_svc); + *(ModeIs(0x13) ? &R[13 + r] : &R_SVC[r]) = v; + } + else if (reg == Register::sp_abt || reg == Register::lr_abt) + { + r -= static_cast(Register::sp_abt); + *(ModeIs(0x17) ? &R[13 + r] : &R_ABT[r]) = v; + } + else if (reg == Register::sp_und || reg == Register::lr_und) + { + r -= static_cast(Register::sp_und); + *(ModeIs(0x1b) ? &R[13 + r] : &R_UND[r]) = v; + } + else if (reg == Register::spsr_fiq) + { + *(ModeIs(0x11) ? &CPSR : &R_FIQ[7]) = v; + } + else if (reg == Register::spsr_irq) + { + *(ModeIs(0x12) ? &CPSR : &R_IRQ[2]) = v; + } + else if (reg == Register::spsr_svc) + { + *(ModeIs(0x13) ? &CPSR : &R_SVC[2]) = v; + } + else if (reg == Register::spsr_abt) + { + *(ModeIs(0x17) ? &CPSR : &R_ABT[2]) = v; + } + else if (reg == Register::spsr_und) + { + *(ModeIs(0x1b) ? &CPSR : &R_UND[2]) = v; + } + else Log(LogLevel::Warn, "GDB reg write: unknown reg no %d (write 0x%08x)\n", r, v); +} +u32 ARM::ReadMem(u32 addr, int size) +{ + if (size == 8) return BusRead8(addr); + else if (size == 16) return BusRead16(addr); + else if (size == 32) return BusRead32(addr); + else return 0xfeedface; +} +void ARM::WriteMem(u32 addr, int size, u32 v) +{ + if (size == 8) BusWrite8(addr, (u8)v); + else if (size == 16) BusWrite16(addr, (u16)v); + else if (size == 32) BusWrite32(addr, v); +} + +void ARM::ResetGdb() +{ + NDS::Reset(); + GPU::StartFrame(); // need this to properly kick off the scheduler & frame output +} +int ARM::RemoteCmd(const u8* cmd, size_t len) +{ + (void)len; + + Log(LogLevel::Info, "[ARMGDB] Rcmd: \"%s\"\n", cmd); + if (!strcmp((const char*)cmd, "reset") || !strcmp((const char*)cmd, "r")) + { + Reset(); + return 0; + } + + return 1; // not implemented (yet) +} + +void ARMv5::WriteMem(u32 addr, int size, u32 v) +{ + if (addr < ITCMSize) + { + if (size == 8) *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u8)v; + else if (size == 16) *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u16)v; + else if (size == 32) *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = (u32)v; + else {} + return; + } + else if ((addr & DTCMMask) == DTCMBase) + { + if (size == 8) *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u8)v; + else if (size == 16) *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u16)v; + else if (size == 32) *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)] = (u32)v; + else {} + return; + } + + ARM::WriteMem(addr, size, v); +} +u32 ARMv5::ReadMem(u32 addr, int size) +{ + if (addr < ITCMSize) + { + if (size == 8) return *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)]; + else if (size == 16) return *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)]; + else if (size == 32) return *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)]; + else return 0xfeedface; + } + else if ((addr & DTCMMask) == DTCMBase) + { + if (size == 8) return *(u8*)&DTCM[addr & (DTCMPhysicalSize - 1)]; + else if (size == 16) return *(u16*)&DTCM[addr & (DTCMPhysicalSize - 1)]; + else if (size == 32) return *(u32*)&DTCM[addr & (DTCMPhysicalSize - 1)]; + else return 0xfeedface; + } + + return ARM::ReadMem(addr, size); +} +#endif + diff --git a/src/ARM.h b/src/ARM.h index 6cfb6772..8690f313 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -24,6 +24,10 @@ #include "types.h" #include "NDS.h" +#ifdef GDBSTUB_ENABLED +#include "debug/GdbStub.h" +#endif + inline u32 ROR(u32 x, u32 n) { return (x >> (n&0x1F)) | (x << ((32-n)&0x1F)); @@ -39,6 +43,9 @@ const u32 ITCMPhysicalSize = 0x8000; const u32 DTCMPhysicalSize = 0x4000; class ARM +#ifdef GDBSTUB_ENABLED + : public Gdb::StubCallbacks +#endif { public: ARM(u32 num); @@ -93,6 +100,18 @@ public: if (v) CPSR |= 0x10000000; } + inline bool ModeIs(u32 mode) + { + u32 cm = CPSR & 0x1f; + mode &= 0x1f; + + if (mode == cm) return true; + if (mode == 0x17) return cm >= 0x14 && cm <= 0x17; // abt + if (mode == 0x1b) return cm >= 0x18 && cm <= 0x1b; // und + + return false; + } + void UpdateMode(u32 oldmode, u32 newmode, bool phony = false); void TriggerIRQ(); @@ -114,6 +133,7 @@ public: virtual void AddCycles_CDI() = 0; virtual void AddCycles_CD() = 0; + void CheckGdbIncoming(); u32 Num; @@ -155,6 +175,9 @@ public: #endif static u32 ConditionTable[16]; +#ifdef GDBSTUB_ENABLED + Gdb::GdbStub GdbStub; +#endif protected: u8 (*BusRead8)(u32 addr); @@ -163,6 +186,29 @@ protected: void (*BusWrite8)(u32 addr, u8 val); void (*BusWrite16)(u32 addr, u16 val); void (*BusWrite32)(u32 addr, u32 val); + +#ifdef GDBSTUB_ENABLED + bool IsSingleStep; + bool BreakReq; + bool BreakOnStartup; + +public: + int GetCPU() const override { return Num ? 7 : 9; } + + u32 ReadReg(Gdb::Register reg) override; + void WriteReg(Gdb::Register reg, u32 v) override; + u32 ReadMem(u32 addr, int size) override; + void WriteMem(u32 addr, int size, u32 v) override; + + void ResetGdb() override; + int RemoteCmd(const u8* cmd, size_t len) override; + +protected: +#endif + + void GdbCheckA(); + void GdbCheckB(); + void GdbCheckC(); }; class ARMv5 : public ARM @@ -171,51 +217,51 @@ public: ARMv5(); ~ARMv5(); - void Reset(); + void Reset() override; - void DoSavestate(Savestate* file); + void DoSavestate(Savestate* file) override; void UpdateRegionTimings(u32 addrstart, u32 addrend); - void FillPipeline(); + void FillPipeline() override; - void JumpTo(u32 addr, bool restorecpsr = false); + void JumpTo(u32 addr, bool restorecpsr = false) override; void PrefetchAbort(); void DataAbort(); - void Execute(); + void Execute() override; #ifdef JIT_ENABLED - void ExecuteJIT(); + void ExecuteJIT() override; #endif // all code accesses are forced nonseq 32bit u32 CodeRead32(u32 addr, bool branch); - void DataRead8(u32 addr, u32* val); - void DataRead16(u32 addr, u32* val); - void DataRead32(u32 addr, u32* val); - void DataRead32S(u32 addr, u32* val); - void DataWrite8(u32 addr, u8 val); - void DataWrite16(u32 addr, u16 val); - void DataWrite32(u32 addr, u32 val); - void DataWrite32S(u32 addr, u32 val); + void DataRead8(u32 addr, u32* val) override; + void DataRead16(u32 addr, u32* val) override; + void DataRead32(u32 addr, u32* val) override; + void DataRead32S(u32 addr, u32* val) override; + void DataWrite8(u32 addr, u8 val) override; + void DataWrite16(u32 addr, u16 val) override; + void DataWrite32(u32 addr, u32 val) override; + void DataWrite32S(u32 addr, u32 val) override; - void AddCycles_C() + void AddCycles_C() override { // code only. always nonseq 32-bit for ARM9. s32 numC = (R[15] & 0x2) ? 0 : CodeCycles; Cycles += numC; } - void AddCycles_CI(s32 numI) + void AddCycles_CI(s32 numI) override { // code+internal s32 numC = (R[15] & 0x2) ? 0 : CodeCycles; Cycles += numC + numI; } - void AddCycles_CDI() + void AddCycles_CDI() override { // LDR/LDM cycles. ARM9 seems to skip the internal cycle there. // TODO: ITCM data fetches shouldn't be parallelized, they say @@ -228,7 +274,7 @@ public: // Cycles += numC + numD; } - void AddCycles_CD() + void AddCycles_CD() override { // TODO: ITCM data fetches shouldn't be parallelized, they say s32 numC = (R[15] & 0x2) ? 0 : CodeCycles; @@ -302,6 +348,11 @@ public: u8* CurICacheLine; bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region); + +#ifdef GDBSTUB_ENABLED + u32 ReadMem(u32 addr, int size) override; + void WriteMem(u32 addr, int size, u32 v) override; +#endif }; class ARMv4 : public ARM @@ -309,15 +360,15 @@ class ARMv4 : public ARM public: ARMv4(); - void Reset(); + void Reset() override; - void FillPipeline(); + void FillPipeline() override; - void JumpTo(u32 addr, bool restorecpsr = false); + void JumpTo(u32 addr, bool restorecpsr = false) override; - void Execute(); + void Execute() override; #ifdef JIT_ENABLED - void ExecuteJIT(); + void ExecuteJIT() override; #endif u16 CodeRead16(u32 addr) @@ -330,14 +381,14 @@ public: return BusRead32(addr); } - void DataRead8(u32 addr, u32* val) + void DataRead8(u32 addr, u32* val) override { *val = BusRead8(addr); DataRegion = addr; DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; } - void DataRead16(u32 addr, u32* val) + void DataRead16(u32 addr, u32* val) override { addr &= ~1; @@ -346,7 +397,7 @@ public: DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; } - void DataRead32(u32 addr, u32* val) + void DataRead32(u32 addr, u32* val) override { addr &= ~3; @@ -355,7 +406,7 @@ public: DataCycles = NDS::ARM7MemTimings[addr >> 15][2]; } - void DataRead32S(u32 addr, u32* val) + void DataRead32S(u32 addr, u32* val) override { addr &= ~3; @@ -363,14 +414,14 @@ public: DataCycles += NDS::ARM7MemTimings[addr >> 15][3]; } - void DataWrite8(u32 addr, u8 val) + void DataWrite8(u32 addr, u8 val) override { BusWrite8(addr, val); DataRegion = addr; DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; } - void DataWrite16(u32 addr, u16 val) + void DataWrite16(u32 addr, u16 val) override { addr &= ~1; @@ -379,7 +430,7 @@ public: DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; } - void DataWrite32(u32 addr, u32 val) + void DataWrite32(u32 addr, u32 val) override { addr &= ~3; @@ -388,7 +439,7 @@ public: DataCycles = NDS::ARM7MemTimings[addr >> 15][2]; } - void DataWrite32S(u32 addr, u32 val) + void DataWrite32S(u32 addr, u32 val) override { addr &= ~3; @@ -397,19 +448,19 @@ public: } - void AddCycles_C() + void AddCycles_C() override { // code only. this code fetch is sequential. Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3]; } - void AddCycles_CI(s32 num) + void AddCycles_CI(s32 num) override { // code+internal. results in a nonseq code fetch. Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num; } - void AddCycles_CDI() + void AddCycles_CDI() override { // LDR/LDM cycles. s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; @@ -436,7 +487,7 @@ public: } } - void AddCycles_CD() + void AddCycles_CD() override { // TODO: max gain should be 5c when writing to mainRAM s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 35854d16..0451894f 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -27,6 +27,10 @@ using Platform::Log; using Platform::LogLevel; +#ifdef GDBSTUB_ENABLED +#include "debug/GdbStub.h" +#endif + namespace ARMInterpreter { @@ -34,6 +38,9 @@ namespace ARMInterpreter void A_UNK(ARM* cpu) { Log(LogLevel::Warn, "undefined ARM%d instruction %08X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-8); +#ifdef GDBSTUB_ENABLED + cpu->GdbStub.Enter(true, Gdb::TgtStatus::FaultInsn, cpu->R[15]-8); +#endif //for (int i = 0; i < 16; i++) printf("R%d: %08X\n", i, cpu->R[i]); //NDS::Halt(); u32 oldcpsr = cpu->CPSR; @@ -49,6 +56,9 @@ void A_UNK(ARM* cpu) void T_UNK(ARM* cpu) { Log(LogLevel::Warn, "undefined THUMB%d instruction %04X @ %08X\n", cpu->Num?7:9, cpu->CurInstr, cpu->R[15]-4); +#ifdef GDBSTUB_ENABLED + cpu->GdbStub.Enter(true, Gdb::TgtStatus::FaultInsn, cpu->R[15]-4); +#endif //NDS::Halt(); u32 oldcpsr = cpu->CPSR; cpu->CPSR &= ~0xBF; diff --git a/src/ARMJIT_A64/ARMJIT_Linkage.S b/src/ARMJIT_A64/ARMJIT_Linkage.S index c1ecd7c8..ad1a3f0f 100644 --- a/src/ARMJIT_A64/ARMJIT_Linkage.S +++ b/src/ARMJIT_A64/ARMJIT_Linkage.S @@ -94,3 +94,8 @@ ARM_RestoreContext: mov sp, x17 br x18 + +#ifndef __APPLE__ +.section .note.GNU-stack,"",@progbits +#endif + diff --git a/src/ARMJIT_x64/ARMJIT_Linkage.S b/src/ARMJIT_x64/ARMJIT_Linkage.S index fe7b1435..ff24e58c 100644 --- a/src/ARMJIT_x64/ARMJIT_Linkage.S +++ b/src/ARMJIT_x64/ARMJIT_Linkage.S @@ -104,3 +104,8 @@ ARM_Ret: #endif ret + +#if !defined(__APPLE__) && !defined(WIN64) +.section .note.GNU-stack,"",@progbits +#endif + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0fa96848..eb5f81e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -61,6 +61,15 @@ add_library(core STATIC tiny-AES-c/aes.c xxhash/xxhash.c) +if (ENABLE_GDBSTUB) + message(NOTICE "Enabling GDB stub") + target_sources(core PRIVATE + debug/GdbStub.cpp + debug/GdbProto.cpp + debug/GdbCmds.cpp + ) +endif() + if (ENABLE_OGLRENDERER) target_sources(core PRIVATE GPU_OpenGL.cpp @@ -131,7 +140,7 @@ if (ENABLE_JIT) endif() if (WIN32) - target_link_libraries(core PRIVATE ole32 comctl32 ws2_32) + target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32) elseif(NOT APPLE) check_library_exists(rt shm_open "" NEED_LIBRT) if (NEED_LIBRT) @@ -143,3 +152,14 @@ if (ENABLE_JIT_PROFILING) target_include_directories(core PRIVATE "${VTUNE_INCLUDE_DIR}") target_link_libraries(core PRIVATE "${VTUNE_LIBRARY}") endif() + +#if(CMAKE_BUILD_TYPE MATCHES "Debug") +# set( +# CMAKE_C_FLAGS +# "${CMAKE_C_FLAGS} -fsanitize=undefined -fsanitize=address" +# ) +# target_link_options(core +# BEFORE PUBLIC -fsanitize=undefined PUBLIC -fsanitize=address +# ) +#endif() + diff --git a/src/NDS.cpp b/src/NDS.cpp index b1ee4550..ae45d3da 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1068,6 +1068,9 @@ u32 RunFrame() bool runFrame = Running && !(CPUStop & 0x40000000); if (runFrame) { + ARM9->CheckGdbIncoming(); + ARM7->CheckGdbIncoming(); + GPU::StartFrame(); while (Running && GPU::TotalScanlines==0) diff --git a/src/Platform.h b/src/Platform.h index 67e6b335..b40dce9e 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -134,7 +134,15 @@ enum ConfigEntry AudioBitDepth, - DSi_FullBIOSBoot + DSi_FullBIOSBoot, + +#ifdef GDBSTUB_ENABLED + GdbEnabled, + GdbPortARM7, + GdbPortARM9, + GdbARM7BreakOnStartup, + GdbARM9BreakOnStartup, +#endif }; int GetConfigInt(ConfigEntry entry); diff --git a/src/SPU.cpp b/src/SPU.cpp index e83a0c71..3939aef7 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -856,9 +856,13 @@ void Mix(u32 dummy) // OutputBufferFrame can never get full because it's // transfered to OutputBuffer at the end of the frame - OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1; - OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1; - OutputBackbufferWritePosition += 2; + // FIXME: apparently this does happen!!! + if (OutputBackbufferWritePosition * 2 < OutputBufferSize - 1) + { + OutputBackbuffer[OutputBackbufferWritePosition ] = leftoutput >> 1; + OutputBackbuffer[OutputBackbufferWritePosition + 1] = rightoutput >> 1; + OutputBackbufferWritePosition += 2; + } NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0); } diff --git a/src/debug/GdbArch.h b/src/debug/GdbArch.h new file mode 100644 index 00000000..45f1c1b2 --- /dev/null +++ b/src/debug/GdbArch.h @@ -0,0 +1,62 @@ + +#ifndef GDBARCH_H_ +#define GDBARCH_H_ + +namespace Gdb +{ + +enum class Register : int +{ + r0, + r1, + r2, + r3, + r4, + r5, + r6, + r7, + r8, + r9, + r10, + r11, + r12, + sp, + lr, + pc, + + cpsr, + sp_usr, + lr_usr, + + r8_fiq, + r9_fiq, + r10_fiq, + r11_fiq, + r12_fiq, + + sp_fiq, + lr_fiq, + sp_irq, + lr_irq, + sp_svc, + lr_svc, + sp_abt, + lr_abt, + sp_und, + lr_und, + + spsr_fiq, + spsr_irq, + spsr_svc, + spsr_abt, + spsr_und, + + COUNT +}; + +constexpr int GDB_ARCH_N_REG = (int)Register::COUNT; + +} + +#endif + diff --git a/src/debug/GdbCmds.cpp b/src/debug/GdbCmds.cpp new file mode 100644 index 00000000..057502f2 --- /dev/null +++ b/src/debug/GdbCmds.cpp @@ -0,0 +1,924 @@ + +#include +#include + +#include "../CRC32.h" +#include "../Platform.h" +#include "hexutil.h" + +#include "GdbProto.h" + +using Platform::Log; +using Platform::LogLevel; + +namespace Gdb +{ + +enum class GdbSignal : int +{ + INT = 2, + TRAP = 5, + EMT = 7, // "emulation trap" + SEGV = 11, + ILL = 4 +}; + +// 12: llvm::MachO::CPU_TYPE_ARM +// 5: llvm::MachO::CPU_SUBTYPE_ARM_V4T +// 7: llvm::MachO::CPU_SUBTYPE_ARM_V5TEJ +const char* TARGET_INFO_ARM7 = "cputype:12;cpusubtype:5;triple:arm-none-eabi;ostype:none;vendor:none;endian:little;ptrsize:4;"; +const char* TARGET_INFO_ARM9 = "cputype:12;cpusubtype:7;triple:arm-none-eabi;ostype:none;vendor:none;endian:little;ptrsize:4;"; + + +#define TARGET_XML__CORE_REGS \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + /* 16 regs */ \ + +#define TARGET_XML__MODE_REGS \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + "" \ + /* 23 regs */ \ + + +const char* TARGET_XML_ARM7 = + "" + "armv4t" + "none" + "" + TARGET_XML__CORE_REGS + TARGET_XML__MODE_REGS + // 39 regs total + "" + ""; + + +const char* TARGET_XML_ARM9 = + "" + "armv5te" + "none" + "" + TARGET_XML__CORE_REGS + TARGET_XML__MODE_REGS + // 39 regs total + "" + ""; + // TODO: CP15? + + +static int DoQResponse(GdbStub* stub, const u8* query, const char* data, const size_t len) +{ + size_t qaddr, qlen; + + Log(LogLevel::Debug, "[GDB qresp] query='%s'\n", query); + if (sscanf((const char*)query, "%zx,%zx", &qaddr, &qlen) != 2) + { + return stub->RespStr("E01"); + } + else if (qaddr > len) + { + return stub->RespStr("E01"); + } + else if (qaddr == len) + { + return stub->RespStr("l"); + } + + size_t bleft = len - qaddr; + size_t outlen = qlen; + if (outlen > bleft) outlen = bleft; + Log(LogLevel::Debug, "[GDB qresp] qaddr=%zu qlen=%zu left=%zu outlen=%zu\n", + qaddr, qlen, bleft, outlen); + + return stub->RespC("m", 1, (const u8*)&data[qaddr], outlen); +} + +__attribute__((__aligned__(4))) +static u8 tempdatabuf[1024]; + +ExecResult GdbStub::Handle_g(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u8* regstrbuf = tempdatabuf; + + for (size_t i = 0; i < GDB_ARCH_N_REG; ++i) + { + u32 v = stub->Cb->ReadReg(static_cast(i)); + hexfmt32(®strbuf[i*4*2], v); + } + + stub->Resp(regstrbuf, GDB_ARCH_N_REG*4*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_G(GdbStub* stub, const u8* cmd, ssize_t len) +{ + if (len != GDB_ARCH_N_REG*4*2) + { + Log(LogLevel::Error, "[GDB] REG WRITE ERR: BAD LEN: %zd != %d!\n", len, GDB_ARCH_N_REG*4*2); + stub->RespStr("E01"); + return ExecResult::Ok; + } + + for (int i = 0; i < GDB_ARCH_N_REG; ++i) + { + u32 v = unhex32(&cmd[i*4*2]); + stub->Cb->WriteReg(static_cast(i), v); + } + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_m(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr = 0, llen = 0, end; + + if (sscanf((const char*)cmd, "%08X,%08X", &addr, &llen) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + u8* datastr = tempdatabuf; + u8* dataptr = datastr; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u32 v = stub->Cb->ReadMem(addr, 16); + hexfmt16(dataptr, v&0xffff); + addr += 2; + dataptr += 4; + } + else if ((end-addr) == 1) + { // last byte + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = stub->Cb->ReadMem(addr, 32); + hexfmt32(dataptr, v); + addr += 4; + dataptr += 8; + } + + // post-align: short + if ((end-addr) & 2) + { + u32 v = stub->Cb->ReadMem(addr, 16); + hexfmt16(dataptr, v&0xffff); + addr += 2; + dataptr += 4; + } + + // post-align: byte + if ((end-addr) == 1) + { + u32 v = stub->Cb->ReadMem(addr, 8); + hexfmt8(dataptr, v&0xff); + ++addr; + dataptr += 2; + } + +end: + assert(addr == end); + + stub->Resp(datastr, llen*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_M(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr, llen, end; + int inoff; + + if (sscanf((const char*)cmd, "%08X,%08X:%n", &addr, &llen, &inoff) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + const u8* dataptr = cmd + inoff; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u16 v = unhex16(dataptr); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 4; + } + else if ((end-addr) == 1) + { // last byte + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = unhex32(dataptr); + stub->Cb->WriteMem(addr, 32, v); + addr += 4; + dataptr += 8; + } + + // post-align: short + if ((end-addr) & 2) + { + u16 v = unhex16(dataptr); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 4; + } + + // post-align: byte + if ((end-addr) == 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 2; + } + +end: + assert(addr == end); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_X(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr, llen, end; + int inoff; + + if (sscanf((const char*)cmd, "%08X,%08X:%n", &addr, &llen, &inoff) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + else if (llen > (GDBPROTO_BUFFER_CAPACITY/2)) + { + stub->RespStr("E02"); + return ExecResult::Ok; + } + end = addr + llen; + + const u8* dataptr = cmd + inoff; + + // pre-align: byte + if ((addr & 1)) + { + if ((end-addr) >= 1) + { + u8 v = *dataptr; + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + else goto end; + } + + // pre-align: short + if ((addr & 2)) + { + if ((end-addr) >= 2) + { + u16 v = dataptr[0] | ((u16)dataptr[1] << 8); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 2; + } + else if ((end-addr) == 1) + { // last byte + u8 v = *dataptr; + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + else goto end; + } + + // main loop: 4-byte chunks + while (addr < end) + { + if (end - addr < 4) break; // post-align stuff + + u32 v = dataptr[0] | ((u32)dataptr[1] << 8) + | ((u32)dataptr[2] << 16) | ((u32)dataptr[3] << 24); + stub->Cb->WriteMem(addr, 32, v); + addr += 4; + dataptr += 4; + } + + // post-align: short + if ((end-addr) & 2) + { + u16 v = dataptr[0] | ((u16)dataptr[1] << 8); + stub->Cb->WriteMem(addr, 16, v); + addr += 2; + dataptr += 2; + } + + // post-align: byte + if ((end-addr) == 1) + { + u8 v = unhex8(dataptr); + stub->Cb->WriteMem(addr, 8, v); + ++addr; + dataptr += 1; + } + +end: + assert(addr == end); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_c(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u32 addr = ~(u32)0; + + if (len > 0) + { + if (len <= 8) + { + if (sscanf((const char*)cmd, "%08X", &addr) != 1) + { + stub->RespStr("E01"); + } // else: ok + } + else + { + stub->RespStr("E01"); + } + } // else: continue at current + + if (~addr) + { + stub->Cb->WriteReg(Register::pc, addr); + } + + return ExecResult::Continue; +} + +ExecResult GdbStub::Handle_s(GdbStub* stub, const u8* cmd, ssize_t len) { + u32 addr = ~(u32)0; + + if (len > 0) + { + if (len <= 8) + { + if (sscanf((const char*)cmd, "%08X", &addr) != 1) { + stub->RespStr("E01"); + return ExecResult::Ok; + } // else: ok + } + else + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + } // else: continue at current + + if (~addr != 0) + { + stub->Cb->WriteReg(Register::pc, addr); + } + + return ExecResult::Step; +} + +ExecResult GdbStub::Handle_p(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int reg; + if (sscanf((const char*)cmd, "%x", ®) != 1 || reg < 0 || reg >= GDB_ARCH_N_REG) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 v = stub->Cb->ReadReg(static_cast(reg)); + hexfmt32(tempdatabuf, v); + stub->Resp(tempdatabuf, 4*2); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_P(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int reg, dataoff; + + if (sscanf((const char*)cmd, "%x=%n", ®, &dataoff) != 1 || reg < 0 + || reg >= GDB_ARCH_N_REG || dataoff + 4*2 > len) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 v = unhex32(&cmd[dataoff]); + stub->Cb->WriteReg(static_cast(reg), v); + + stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_H(GdbStub* stub, const u8* cmd, ssize_t len) +{ + u8 operation = cmd[0]; + u32 thread_id; + sscanf((const char*)&cmd[1], "%u", &thread_id); + + (void)operation; + if (thread_id <= 1) + { + stub->RespStr("OK"); + } + else + { + stub->RespStr("E01"); + } + + return ExecResult::Ok; +} + + +ExecResult GdbStub::Handle_Question(GdbStub* stub, const u8* cmd, ssize_t len) +{ + // "request reason for target halt" (which must also halt) + + TgtStatus st = stub->Stat; + u32 arg = ~(u32)0; + int typ = 0; + + switch (st) + { + case TgtStatus::None: // no target! + stub->RespStr("W00"); + break; + + case TgtStatus::Running: // will break very soon due to retval + case TgtStatus::BreakReq: + stub->RespFmt("S%02X", GdbSignal::INT); + break; + + case TgtStatus::SingleStep: + stub->RespFmt("S%02X", GdbSignal::TRAP); + break; + + case TgtStatus::Bkpt: + arg = stub->CurBkpt; + typ = 1; + goto bkpt_rest; + case TgtStatus::Watchpt: + arg = stub->CurWatchpt; + typ = 2; + bkpt_rest: + if (!~arg) + { + stub->RespFmt("S%02X", GdbSignal::TRAP); + } + else + { + switch (typ) + { + case 1: + stub->RespFmt("S%02X", GdbSignal::TRAP); + //stub->RespFmt("T%02Xhwbreak:"/*"%08X"*/";", GdbSignal::TRAP/*, arg*/); + break; + case 2: + stub->RespFmt("S%02X", GdbSignal::TRAP); + //stub->RespFmt("T%02Xwatch:"/*"%08X"*/";", GdbSignal::TRAP/*, arg*/); + break; + default: + stub->RespFmt("S%02X", GdbSignal::TRAP); + break; + } + } + break; + case TgtStatus::BkptInsn: + stub->RespFmt("T%02Xswbreak:%08X;", GdbSignal::TRAP, + stub->Cb->ReadReg(Register::pc)); + break; + + // these three should technically be a SIGBUS but gdb etc don't really + // like that (plus it sounds confusing) + case TgtStatus::FaultData: + case TgtStatus::FaultIAcc: + stub->RespFmt("S%02X", GdbSignal::SEGV); + break; + case TgtStatus::FaultInsn: + stub->RespFmt("S%02X", GdbSignal::ILL); + break; + default: break; + } + + return ExecResult::InitialBreak; +} + +ExecResult GdbStub::Handle_Exclamation(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_D(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("OK"); + return ExecResult::Detached; +} + +ExecResult GdbStub::Handle_r(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Cb->ResetGdb(); + return ExecResult::Ok; +} +ExecResult GdbStub::Handle_R(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Cb->ResetGdb(); + return ExecResult::Ok; +} +ExecResult GdbStub::Handle_k(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return ExecResult::Detached; +} + + +ExecResult GdbStub::Handle_z(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int typ; + u32 addr, kind; + + if (sscanf((const char*)cmd, "%d,%x,%u", &typ, &addr, &kind) != 3) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (typ) + { + case 0: case 1: // remove breakpoint (we cheat & always insert a hardware breakpoint) + stub->DelBkpt(addr, kind); + break; + case 2: case 3: case 4: // watchpoint. currently not distinguishing between reads & writes oops + stub->DelWatchpt(addr, kind, typ); + break; + default: + stub->RespStr("E02"); + return ExecResult::Ok; + } + + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_Z(GdbStub* stub, const u8* cmd, ssize_t len) +{ + int typ; + u32 addr, kind; + + if (sscanf((const char*)cmd, "%d,%x,%u", &typ, &addr, &kind) != 3) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (typ) + { + case 0: case 1: // insert breakpoint (we cheat & always insert a hardware breakpoint) + stub->AddBkpt(addr, kind); + break; + case 2: case 3: case 4: // watchpoint. currently not distinguishing between reads & writes oops + stub->AddWatchpt(addr, kind, typ); + break; + default: + stub->RespStr("E02"); + return ExecResult::Ok; + } + + stub->RespStr("OK"); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_HostInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + const char* resp = ""; + + switch (stub->Cb->GetCPU()) + { + case 7: resp = TARGET_INFO_ARM7; break; + case 9: resp = TARGET_INFO_ARM9; break; + default: break; + } + + stub->RespStr(resp); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Rcmd(GdbStub* stub, const u8* cmd, ssize_t len) +{ + + memset(tempdatabuf, 0, sizeof tempdatabuf); + for (ssize_t i = 0; i < len/2; ++i) + { + tempdatabuf[i] = unhex8(&cmd[i*2]); + } + + int r = stub->Cb->RemoteCmd(tempdatabuf, len/2); + + if (r) stub->RespFmt("E%02X", r&0xff); + else stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Supported(GdbStub* stub, + const u8* cmd, ssize_t len) { + // TODO: support Xfer:memory-map:read:: + // but NWRAM is super annoying with that + stub->RespFmt("PacketSize=%X;qXfer:features:read+;swbreak-;hwbreak+;QStartNoAckMode+", GDBPROTO_BUFFER_CAPACITY-1); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_CRC(GdbStub* stub, + const u8* cmd, ssize_t llen) +{ + static u8 crcbuf[128]; + + u32 addr, len; + if (sscanf((const char*)cmd, "%x,%x", &addr, &len) != 2) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + u32 val = 0; // start at 0 + u32 caddr = addr; + u32 realend = addr + len; + + for (; caddr < addr + len; ) + { + // calc partial CRC in 128-byte chunks + u32 end = caddr + sizeof(crcbuf)/sizeof(crcbuf[0]); + if (end > realend) end = realend; + u32 clen = end - caddr; + + for (size_t i = 0; caddr < end; ++caddr, ++i) + { + crcbuf[i] = stub->Cb->ReadMem(caddr, 8); + } + + val = CRC32(crcbuf, clen, val); + } + + stub->RespFmt("C%x", val); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_C(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("QC1"); // current thread ID is 1 + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_fThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("m1"); // one thread + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_sThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("l"); // end of thread list + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_features(GdbStub* stub, const u8* cmd, ssize_t len) +{ + const char* resp; + + Log(LogLevel::Debug, "[GDB] CPU type = %d\n", stub->Cb->GetCPU()); + switch (stub->Cb->GetCPU()) + { + case 7: resp = TARGET_XML_ARM7; break; + case 9: resp = TARGET_XML_ARM9; break; + default: resp = ""; break; + } + + DoQResponse(stub, cmd, resp, strlen(resp)); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_q_Attached(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("1"); // always "attach to a process" + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Attach(GdbStub* stub, const u8* cmd, ssize_t len) +{ + + TgtStatus st = stub->Stat; + + if (st == TgtStatus::None) + { + // no target + stub->RespStr("E01"); + return ExecResult::Ok; + } + + stub->RespStr("T05thread:1;"); + + if (st == TgtStatus::Running) return ExecResult::MustBreak; + else return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Kill(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + stub->Cb->ResetGdb(); + + stub->RespStr("OK"); + + return (st != TgtStatus::Running && st != TgtStatus::None) ? ExecResult::Detached : ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Run(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + stub->Cb->ResetGdb(); + + // TODO: handle cmdline for homebrew? + + return (st != TgtStatus::Running && st != TgtStatus::None) ? ExecResult::Continue : ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len) +{ + TgtStatus st = stub->Stat; + + static bool notified = true; + + // not sure if i understand this correctly + if (st != TgtStatus::Running) + { + if (notified) stub->RespStr("OK"); + else stub->RespStr("W00"); + + notified = !notified; + } + else stub->RespStr("OK"); + + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->Resp(NULL, 0); + return ExecResult::Ok; +} + +ExecResult GdbStub::Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len) +{ + if (len < 1) + { + stub->RespStr("E01"); + return ExecResult::Ok; + } + + switch (cmd[0]) + { + case 'c': + stub->RespStr("OK"); + return ExecResult::Continue; + case 's': + stub->RespStr("OK"); + return ExecResult::Step; + case 't': + stub->RespStr("OK"); + return ExecResult::MustBreak; + default: + stub->RespStr("E01"); + return ExecResult::Ok; + } +} + +ExecResult GdbStub::Handle_v_ContQuery(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->RespStr("vCont;c;s;t"); + return ExecResult::Ok; +} + + +ExecResult GdbStub::Handle_Q_StartNoAckMode(GdbStub* stub, const u8* cmd, ssize_t len) +{ + stub->NoAck = true; + stub->RespStr("OK"); + return ExecResult::Ok; +} + +} + diff --git a/src/debug/GdbCmds.h b/src/debug/GdbCmds.h new file mode 100644 index 00000000..4f300606 --- /dev/null +++ b/src/debug/GdbCmds.h @@ -0,0 +1,53 @@ + +#ifndef GDBSTUB_H_ +#error "DO NOT INCLUDE THIS FILE YOURSELF!" +#endif + +private: + static ExecResult Handle_g(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_G(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_m(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_M(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_X(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_c(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_s(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_p(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_P(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_H(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_Question(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_Exclamation(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_D(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_r(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_R(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_k(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_z(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_Z(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_q(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_Q(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_q_HostInfo(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_Rcmd(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_Supported(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_CRC(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_C(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_fThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_sThreadInfo(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_TStatus(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_features(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_q_Attached(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_v_Attach(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_Kill(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_Run(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_Stopped(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_MustReplyEmpty(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_Cont(GdbStub* stub, const u8* cmd, ssize_t len); + static ExecResult Handle_v_ContQuery(GdbStub* stub, const u8* cmd, ssize_t len); + + static ExecResult Handle_Q_StartNoAckMode(GdbStub* stub, const u8* cmd, ssize_t len); + diff --git a/src/debug/GdbProto.cpp b/src/debug/GdbProto.cpp new file mode 100644 index 00000000..dc803649 --- /dev/null +++ b/src/debug/GdbProto.cpp @@ -0,0 +1,389 @@ + +#ifdef _WIN32 +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#endif + +#include "../Platform.h" +#include "hexutil.h" + +#include "GdbProto.h" + + +using Platform::Log; +using Platform::LogLevel; + +namespace Gdb +{ + +/* + * TODO commands to support: + * m M g G c s p P H + * ? D r + * qC qfThreadInfo qsThreadInfo + * z0 Z0 z1 Z1 z4 Z4 + * qCRC + * vAttach;addr + * vKill;pid + * qRcmd? qSupported? + */ +u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY]; +ssize_t Cmdlen; + +namespace Proto +{ + +u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY]; +u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5]; + +ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]) +{ + static ssize_t dataoff = 0; + + ssize_t recv_total = dataoff; + ssize_t cksumoff = -1; + u8 sum = 0; + + bool first = true; + + //printf("--- dataoff=%zd\n", dataoff); + if (dataoff != 0) { + printf("--- got preexisting: %s\n", PacketBuf); + + ssize_t datastart = 0; + while (true) + { + if (PacketBuf[datastart] == '\x04') return ReadResult::Eof; + else if (PacketBuf[datastart] == '+' || PacketBuf[datastart] == '-') + { + /*if (PacketBuf[datastart] == '+') SendAck(connfd); + else SendNak(connfd);*/ + ++datastart; + continue; + } + else if (PacketBuf[datastart] == '$') + { + ++datastart; + break; + } + else + { + __builtin_trap(); + return ReadResult::Wut; + } + } + printf("--- datastart=%zd\n", datastart); + + for (ssize_t i = datastart; i < dataoff; ++i) + { + if (PacketBuf[i] == '#') + { + cksumoff = i + 1; + printf("--- cksumoff=%zd\n", cksumoff); + break; + } + + sum += PacketBuf[i]; + } + + if (cksumoff >= 0) + { + recv_total = dataoff - datastart + 1; + dataoff = cksumoff + 2 - datastart + 1; + cksumoff -= datastart - 1; + + memmove(&PacketBuf[1], &PacketBuf[datastart], recv_total); + PacketBuf[0] = '$'; + PacketBuf[recv_total] = 0; + + printf("=== cksumoff=%zi recv_total=%zi datastart=%zi dataoff=%zi\n==> %s\n", + cksumoff, recv_total, datastart, dataoff, PacketBuf); + //break; + } + } + + while (cksumoff < 0) + { + u8* pkt = &PacketBuf[dataoff]; + ssize_t n, blehoff = 0; + + memset(pkt, 0, sizeof(PacketBuf) - dataoff); + int flag = 0; +#if MOCKTEST + static bool FIRST = false; + if (FIRST) { + printf("%s", "[==>] TEST DONE\n"); + __builtin_trap(); + } + FIRST = true; + + const char* testinp1 = "+$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77"; + const char* testinp2 = "+$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77$qSupported:multiprocess+;swbreak+;hwbreak+;qRelocInsn+;fork-events+;vfork-events+;exec-events+;vContSupported+;QThreadEvents+;no-resumed+;memory-tagging+;xmlRegisters=i386#77---+$vMustReplyEmpty#3a"; + + const char* testinp = testinp1; + + n = strlen(testinp); + memcpy(pkt, testinp, strlen(testinp)); +#else +#ifndef _WIN32 + if (first) flag |= MSG_DONTWAIT; + n = recv(connfd, pkt, sizeof(PacketBuf) - dataoff, flag); +#else + // fuck windows + n = recv(connfd, (char*)pkt, sizeof(PacketBuf) - dataoff, flag); +#endif +#endif + + if (n <= 0) + { + if (first) return ReadResult::NoPacket; + else + { + Log(LogLevel::Debug, "[GDB] recv() error %zi, errno=%d (%s)\n", n, errno, strerror(errno)); + return ReadResult::Eof; + } + } + + Log(LogLevel::Debug, "[GDB] recv() %zd bytes: '%s' (%02x)\n", n, pkt, pkt[0]); + first = false; + + do + { + if (dataoff == 0) + { + if (pkt[blehoff] == '\x04') return ReadResult::Eof; + else if (pkt[blehoff] == '\x03') return ReadResult::Break; + else if (pkt[blehoff] != '$') + { + ++blehoff; + --n; + } + else break; + + if (n == 0) goto next_outer; + } + } + while (true); + + if (blehoff > 0) + { + memmove(pkt, &pkt[blehoff], n - blehoff + 1); + n -= blehoff - 1; // ??? + } + + recv_total += n; + + Log(LogLevel::Debug, "[GDB] recv() after skipping: n=%zd, recv_total=%zd\n", n, recv_total); + + for (ssize_t i = (dataoff == 0) ? 1 : 0; i < n; ++i) + { + u8 v = pkt[i]; + if (v == '#') + { + cksumoff = dataoff + i + 1; + break; + } + + sum += pkt[i]; + } + + if (cksumoff < 0) + { + // oops, need more data + dataoff += n; + } + + next_outer:; + } + + u8 ck = (hex2nyb(PacketBuf[cksumoff+0]) << 4) + | hex2nyb(PacketBuf[cksumoff+1]); + + Log(LogLevel::Debug, "[GDB] got pkt, checksum: %02x vs %02x\n", ck, sum); + + if (ck != sum) + { + //__builtin_trap(); + return ReadResult::CksumErr; + } + + if (cksumoff + 2 > recv_total) + { + Log(LogLevel::Error, "[GDB] BIG MISTAKE: %zi > %zi which shouldn't happen!\n", cksumoff + 2, recv_total); + //__builtin_trap(); + return ReadResult::Wut; + } + else + { + Cmdlen = cksumoff - 2; + memcpy(Cmdbuf, &PacketBuf[1], Cmdlen); + Cmdbuf[Cmdlen] = 0; + + if (cksumoff + 2 < recv_total) { + // huh, we have the start of the next packet + dataoff = recv_total - (cksumoff + 2); + memmove(PacketBuf, &PacketBuf[cksumoff + 2], (size_t)dataoff); + PacketBuf[dataoff] = 0; + Log(LogLevel::Debug, "[GDB] got more: cksumoff=%zd, recvtotal=%zd, remain=%zd\n==> %s\n", cksumoff, recv_total, dataoff, PacketBuf); + } + else dataoff = 0; + } + + return ReadResult::CmdRecvd; +} + +int SendAck(int connfd) +{ + Log(LogLevel::Debug, "[GDB] send ack\n"); + u8 v = '+'; +#if MOCKTEST + return 1; +#endif + +#ifdef _WIN32 + // fuck windows + return send(connfd, (const char*)&v, 1, 0); +#else + return send(connfd, &v, 1, 0); +#endif +} + +int SendNak(int connfd) +{ + Log(LogLevel::Debug, "[GDB] send nak\n"); + u8 v = '-'; +#if MOCKTEST + return 1; +#endif + +#ifdef _WIN32 + // fuck windows + return send(connfd, (const char*)&v, 1, 0); +#else + return send(connfd, &v, 1, 0); +#endif +} + +int WaitAckBlocking(int connfd, u8* ackp, int to_ms) +{ +#if MOCKTEST + *ackp = '+'; + return 0; +#endif + +#ifdef _WIN32 + fd_set infd, outfd, errfd; + FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); + FD_SET(connfd, &infd); + + struct timeval to; + to.tv_sec = to_ms / 1000; + to.tv_usec = (to_ms % 1000) * 1000; + + int r = select(connfd+1, &infd, &outfd, &errfd, &to); + + if (FD_ISSET(connfd, &errfd)) return -1; + else if (FD_ISSET(connfd, &infd)) + { + r = recv(connfd, (char*)ackp, 1, 0); + if (r < 0) return r; + return 0; + } + + return -1; +#else + struct pollfd pfd; + + pfd.fd = connfd; + pfd.events = POLLIN; + pfd.revents = 0; + + ssize_t r = (ssize_t)poll(&pfd, 1, to_ms); + if (r < 0) return r; + if (r == 0) return -1; + + if (pfd.revents & (POLLHUP|POLLERR)) return -69; + + r = recv(connfd, ackp, 1, 0); + if (r < 0) return r; + + return (r == 1) ? 0 : -1; +#endif +} + +int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack) +{ + u8 cksum = 0; + int tries = 0; + + size_t totallen = len1 + len2; + + if (totallen >= GDBPROTO_BUFFER_CAPACITY) + { + Log(LogLevel::Error, "[GDB] packet with len %zu can't fit in buffer!\n", totallen); + return -42; + } + + RespBuf[0] = '$'; + for (size_t i = 0; i < len1; ++i) + { + cksum += data1[i]; + RespBuf[i+1] = data1[i]; + } + for (size_t i = 0; i < len2; ++i) + { + cksum += data2[i]; + RespBuf[len1+i+1] = data2[i]; + } + RespBuf[totallen+1] = '#'; + hexfmt8(&RespBuf[totallen+2], cksum); + RespBuf[totallen+4] = 0; + + do + { + ssize_t r; + u8 ack; + + Log(LogLevel::Debug, "[GDB] send resp: '%s'\n", RespBuf); +#if MOCKTEST + r = totallen+4; +#else +#ifdef _WIN32 + r = send(connfd, (const char*)RespBuf, totallen+4, 0); +#else + r = send(connfd, RespBuf, totallen+4, 0); +#endif +#endif + if (r < 0) return r; + + if (noack) break; + + r = WaitAckBlocking(connfd, &ack, 2000); + //Log(LogLevel::Debug, "[GDB] got ack: '%c'\n", ack); + if (r == 0 && ack == '+') break; + + ++tries; + } + while (tries < 3); + + return 0; +} + +} + +} + diff --git a/src/debug/GdbProto.h b/src/debug/GdbProto.h new file mode 100644 index 00000000..ef8bdec9 --- /dev/null +++ b/src/debug/GdbProto.h @@ -0,0 +1,40 @@ + +#ifndef GDBPROTO_H_ +#define GDBPROTO_H_ + +#include +#include + +#include "GdbStub.h" /* class GdbStub */ + + +#define MOCKTEST 0 + + +namespace Gdb { + +constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128; + +extern u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY]; +extern ssize_t Cmdlen; + +namespace Proto { + +extern u8 PacketBuf[GDBPROTO_BUFFER_CAPACITY]; +extern u8 RespBuf[GDBPROTO_BUFFER_CAPACITY+5]; + +Gdb::ReadResult MsgRecv(int connfd, u8 cmd_dest[/*static GDBPROTO_BUFFER_CAPACITY*/]); + +int SendAck(int connfd); +int SendNak(int connfd); + +int Resp(int connfd, const u8* data1, size_t len1, const u8* data2, size_t len2, bool noack); + +int WaitAckBlocking(int connfd, u8* ackp, int to_ms); + +} + +} + +#endif + diff --git a/src/debug/GdbStub.cpp b/src/debug/GdbStub.cpp new file mode 100644 index 00000000..d756ce44 --- /dev/null +++ b/src/debug/GdbStub.cpp @@ -0,0 +1,693 @@ + +#ifdef _WIN32 +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#include +#include +#include +#include +#include +#endif + + +#include "../Platform.h" +#include "GdbProto.h" + +using Platform::Log; +using Platform::LogLevel; + +static int SocketSetBlocking(int fd, bool block) +{ +#if MOCKTEST + return 0; +#endif + + if (fd < 0) return -1; + +#ifdef _WIN32 + unsigned long mode = block ? 0 : 1; + return ioctlsocket(fd, FIONBIO, &mode); +#else + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) return -1; + flags = block ? (flags & ~O_NONBLOCK) : (flags | O_NONBLOCK); + return fcntl(fd, F_SETFL, flags); +#endif +} + +namespace Gdb +{ + +GdbStub::GdbStub(StubCallbacks* cb, int port) + : Cb(cb), Port(port) + , SockFd(0), ConnFd(0) + , Stat(TgtStatus::None), CurBkpt(0), CurWatchpt(0), StatFlag(false), NoAck(false) + , ServerSA((void*)new struct sockaddr_in()) + , ClientSA((void*)new struct sockaddr_in()) +{ } + +bool GdbStub::Init() +{ + Log(LogLevel::Info, "[GDB] initializing GDB stub for core %d on port %d\n", + Cb->GetCPU(), Port); + +#if MOCKTEST + SockFd = 0; + return true; +#endif + +#ifndef _WIN32 + /*void* fn = SIG_IGN; + struct sigaction act = { 0 }; + act.sa_flags = SA_SIGINFO; + act.sa_sigaction = (sighandler_t)fn; + if (sigaction(SIGPIPE, &act, NULL) == -1) { + Log(LogLevel::Warn, "[GDB] couldn't ignore SIGPIPE, stuff may fail on GDB disconnect.\n"); + }*/ + signal(SIGPIPE, SIG_IGN); +#else + WSADATA wsa; + if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) + { + Log(LogLevel::Error, "[GDB] winsock could not be initialized (%d).\n", WSAGetLastError()); + return false; + } +#endif + + int r; + struct sockaddr_in* server = (struct sockaddr_in*)ServerSA; + struct sockaddr_in* client = (struct sockaddr_in*)ClientSA; + + int typ = SOCK_STREAM; +#ifdef __linux__ + typ |= SOCK_NONBLOCK; +#endif + SockFd = socket(AF_INET, SOCK_STREAM, 0); + if (SockFd < 0) + { + Log(LogLevel::Error, "[GDB] err: can't create a socket fd\n"); + goto err; + } +#ifndef __linux__ + SocketSetBlocking(SockFd, false); +#endif + + server->sin_family = AF_INET; + server->sin_addr.s_addr = htonl(INADDR_ANY); + server->sin_port = htons(Port); + + r = bind(SockFd, (const sockaddr*)server, sizeof(*server)); + if (r < 0) + { + Log(LogLevel::Error, "[GDB] err: can't bind to address and port %d\n", Port); + goto err; + } + + r = listen(SockFd, 5); + if (r < 0) + { + Log(LogLevel::Error, "[GDB] err: can't listen to SockFd\n"); + goto err; + } + + return true; + +err: + if (SockFd != 0) + { +#ifdef _WIN32 + closesocket(SockFd); +#else + close(SockFd); +#endif + SockFd = 0; + } + + return false; +} + +void GdbStub::Close() +{ + Disconnect(); + if (SockFd > 0) close(SockFd); + SockFd = 0; +} + +void GdbStub::Disconnect() +{ + if (ConnFd > 0) close(ConnFd); + ConnFd = 0; +} + +GdbStub::~GdbStub() +{ + Close(); + delete (struct sockaddr_in*)ServerSA; + delete (struct sockaddr_in*)ClientSA; +} + +SubcmdHandler GdbStub::Handlers_v[] = { + { .MainCmd = 'v', .SubStr = "Attach;" , .Handler = GdbStub::Handle_v_Attach }, + { .MainCmd = 'v', .SubStr = "Kill;" , .Handler = GdbStub::Handle_v_Kill }, + { .MainCmd = 'v', .SubStr = "Run" , .Handler = GdbStub::Handle_v_Run }, + { .MainCmd = 'v', .SubStr = "Stopped" , .Handler = GdbStub::Handle_v_Stopped }, + { .MainCmd = 'v', .SubStr = "MustReplyEmpty", .Handler = GdbStub::Handle_v_MustReplyEmpty }, + { .MainCmd = 'v', .SubStr = "Cont?" , .Handler = GdbStub::Handle_v_ContQuery }, + { .MainCmd = 'v', .SubStr = "Cont" , .Handler = GdbStub::Handle_v_Cont }, + + { .MainCmd = 'v', .SubStr = NULL, .Handler = NULL } +}; + +SubcmdHandler GdbStub::Handlers_q[] = { + { .MainCmd = 'q', .SubStr = "HostInfo" , .Handler = GdbStub::Handle_q_HostInfo }, + { .MainCmd = 'q', .SubStr = "ProcessInfo", .Handler = GdbStub::Handle_q_HostInfo }, + { .MainCmd = 'q', .SubStr = "Rcmd," , .Handler = GdbStub::Handle_q_Rcmd }, + { .MainCmd = 'q', .SubStr = "Supported:" , .Handler = GdbStub::Handle_q_Supported }, + { .MainCmd = 'q', .SubStr = "CRC:" , .Handler = GdbStub::Handle_q_CRC }, + { .MainCmd = 'q', .SubStr = "C" , .Handler = GdbStub::Handle_q_C }, + { .MainCmd = 'q', .SubStr = "fThreadInfo", .Handler = GdbStub::Handle_q_fThreadInfo }, + { .MainCmd = 'q', .SubStr = "sThreadInfo", .Handler = GdbStub::Handle_q_sThreadInfo }, + { .MainCmd = 'q', .SubStr = "Attached" , .Handler = GdbStub::Handle_q_Attached }, + { .MainCmd = 'q', .SubStr = "Xfer:features:read:target.xml:", .Handler = GdbStub::Handle_q_features }, + + { .MainCmd = 'q', .SubStr = NULL, .Handler = NULL }, +}; + +SubcmdHandler GdbStub::Handlers_Q[] = { + { .MainCmd = 'Q', .SubStr = "StartNoAckMode", .Handler = GdbStub::Handle_Q_StartNoAckMode }, + + { .MainCmd = 'Q', .SubStr = NULL, .Handler = NULL }, +}; + +ExecResult GdbStub::Handle_q(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return stub->SubcmdExec(cmd, len, Handlers_q); +} + +ExecResult GdbStub::Handle_v(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return stub->SubcmdExec(cmd, len, Handlers_v); +} + +ExecResult GdbStub::Handle_Q(GdbStub* stub, const u8* cmd, ssize_t len) +{ + return stub->SubcmdExec(cmd, len, Handlers_Q); +} + +CmdHandler GdbStub::Handlers_top[] = { + { .Cmd = 'g', .Handler = GdbStub::Handle_g }, + { .Cmd = 'G', .Handler = GdbStub::Handle_G }, + { .Cmd = 'm', .Handler = GdbStub::Handle_m }, + { .Cmd = 'M', .Handler = GdbStub::Handle_M }, + { .Cmd = 'X', .Handler = GdbStub::Handle_X }, + { .Cmd = 'c', .Handler = GdbStub::Handle_c }, + { .Cmd = 's', .Handler = GdbStub::Handle_s }, + { .Cmd = 'p', .Handler = GdbStub::Handle_p }, + { .Cmd = 'P', .Handler = GdbStub::Handle_P }, + { .Cmd = 'H', .Handler = GdbStub::Handle_H }, + { .Cmd = 'T', .Handler = GdbStub::Handle_H }, + + { .Cmd = '?', .Handler = GdbStub::Handle_Question }, + { .Cmd = '!', .Handler = GdbStub::Handle_Exclamation }, + { .Cmd = 'D', .Handler = GdbStub::Handle_D }, + { .Cmd = 'r', .Handler = GdbStub::Handle_r }, + { .Cmd = 'R', .Handler = GdbStub::Handle_R }, + { .Cmd = 'k', .Handler = GdbStub::Handle_k }, + + { .Cmd = 'z', .Handler = GdbStub::Handle_z }, + { .Cmd = 'Z', .Handler = GdbStub::Handle_Z }, + + { .Cmd = 'q', .Handler = GdbStub::Handle_q }, + { .Cmd = 'v', .Handler = GdbStub::Handle_v }, + { .Cmd = 'Q', .Handler = GdbStub::Handle_Q }, + + { .Cmd = 0, .Handler = NULL } +}; + + +StubState GdbStub::HandlePacket() +{ + ExecResult r = CmdExec(Handlers_top); + + if (r == ExecResult::MustBreak) + { + if (Stat == TgtStatus::None || Stat == TgtStatus::Running) + Stat = TgtStatus::BreakReq; + return StubState::Break; + } + else if (r == ExecResult::InitialBreak) + { + Stat = TgtStatus::BreakReq; + return StubState::Attach; + /*} + else if (r == ExecResult::Detached) + { + Stat = TgtStatus::None; + return StubState::Disconnect;*/ + } + else if (r == ExecResult::Continue) + { + Stat = TgtStatus::Running; + return StubState::Continue; + } + else if (r == ExecResult::Step) + { + return StubState::Step; + } + else if (r == ExecResult::Ok || r == ExecResult::UnkCmd) + { + return StubState::None; + } + else + { + Stat = TgtStatus::None; + return StubState::Disconnect; + } +} + +StubState GdbStub::Poll(bool wait) +{ + int r; + + if (ConnFd <= 0) + { + SocketSetBlocking(SockFd, wait); + + // not yet connected, so let's wait for one + // nonblocking only done in part of read_packet(), so that it can still + // quickly handle partly-received packets + struct sockaddr_in* client = (struct sockaddr_in*)ClientSA; + socklen_t len = sizeof(*client); +#if MOCKTEST + ConnFd = 0; +#else +#ifdef __linux__ + ConnFd = accept4(SockFd, (struct sockaddr*)client, &len, /*SOCK_NONBLOCK|*/SOCK_CLOEXEC); +#else + ConnFd = accept(SockFd, (struct sockaddr*)client, &len); +#endif +#endif + + if (ConnFd < 0) return StubState::NoConn; + + u8 a; + if (Proto::WaitAckBlocking(ConnFd, &a, 1000) < 0) + { + Log(LogLevel::Error, "[GDB] inital handshake: didn't receive inital ack!\n"); + close(ConnFd); + ConnFd = 0; + return StubState::Disconnect; + } + + if (a != '+') + { + Log(LogLevel::Error, "[GDB] inital handshake: unexpected character '%c'!\n", a); + } + SendAck(); + + Stat = TgtStatus::Running; // on connected + StatFlag = false; + } + + if (StatFlag) + { + StatFlag = false; + //Log(LogLevel::Debug, "[GDB] STAT FLAG WAS TRUE\n"); + + Handle_Question(this, NULL, 0); // ugly hack but it should work + } + +#if MOCKTEST + // nothing... +#else +#ifndef _WIN32 + struct pollfd pfd; + pfd.fd = ConnFd; + pfd.events = POLLIN; + pfd.revents = 0; + + r = poll(&pfd, 1, wait ? -1 : 0); + + if (r == 0) return StubState::None; // nothing is happening + + if (pfd.revents & (POLLHUP|POLLERR|POLLNVAL)) + { + // oopsie, something happened + Disconnect(); + return StubState::Disconnect; + } +#else + fd_set infd, outfd, errfd; + FD_ZERO(&infd); FD_ZERO(&outfd); FD_ZERO(&errfd); + FD_SET(ConnFd, &infd); + + struct timeval to; + if (wait) + { + to.tv_sec = ~(time_t)0; + to.tv_usec = ~(long)0; + } + else + { + to.tv_sec = 0; + to.tv_usec = 0; + } + + r = select(ConnFd+1, &infd, &outfd, &errfd, &to); + + if (FD_ISSET(ConnFd, &errfd)) + { + Disconnect(); + return StubState::Disconnect; + } + else if (!FD_ISSET(ConnFd, &infd)) + { + return StubState::None; + } +#endif +#endif + + ReadResult res = Proto::MsgRecv(ConnFd, Cmdbuf); + + switch (res) + { + case ReadResult::NoPacket: + return StubState::None; + case ReadResult::Break: + return StubState::Break; + case ReadResult::Wut: + Log(LogLevel::Info, "[GDB] WUT\n"); + case_gdbp_eof: + case ReadResult::Eof: + Log(LogLevel::Info, "[GDB] EOF!\n"); + close(ConnFd); + ConnFd = 0; + return StubState::Disconnect; + case ReadResult::CksumErr: + Log(LogLevel::Info, "[GDB] checksum err!\n"); + if (SendNak() < 0) { + Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n"); + goto case_gdbp_eof; + } + return StubState::None; + case ReadResult::CmdRecvd: + /*if (SendAck() < 0) { + Log(LogLevel::Error, "[GDB] send packet ack failed!\n"); + goto case_gdbp_eof; + }*/ + break; + } + + return HandlePacket(); +} + +ExecResult GdbStub::SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler* handlers) +{ + //Log(LogLevel::Debug, "[GDB] subcommand in: '%s'\n", cmd); + + for (size_t i = 0; handlers[i].Handler != NULL; ++i) { + // check if prefix matches + if (!strncmp((const char*)cmd, handlers[i].SubStr, strlen(handlers[i].SubStr))) + { + if (SendAck() < 0) + { + Log(LogLevel::Error, "[GDB] send packet ack failed!\n"); + return ExecResult::NetErr; + } + return handlers[i].Handler(this, &cmd[strlen(handlers[i].SubStr)], len-strlen(handlers[i].SubStr)); + } + } + + Log(LogLevel::Info, "[GDB] unknown subcommand '%s'!\n", cmd); + /*if (SendNak() < 0) + { + Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n"); + return ExecResult::NetErr; + }*/ + //Resp("E99"); + Resp(NULL, 0); + return ExecResult::UnkCmd; +} + +ExecResult GdbStub::CmdExec(const CmdHandler* handlers) +{ + Log(LogLevel::Debug, "[GDB] command in: '%s'\n", Cmdbuf); + + for (size_t i = 0; handlers[i].Handler != NULL; ++i) + { + if (handlers[i].Cmd == Cmdbuf[0]) + { + if (SendAck() < 0) + { + Log(LogLevel::Error, "[GDB] send packet ack failed!\n"); + return ExecResult::NetErr; + } + return handlers[i].Handler(this, &Cmdbuf[1], Cmdlen-1); + } + } + + Log(LogLevel::Info, "[GDB] unknown command '%c'!\n", Cmdbuf[0]); + /*if (SendNak() < 0) + { + Log(LogLevel::Error, "[GDB] send nak after cksum fail errored!\n"); + return ExecResult::NetErr; + }*/ + //RespStr("E99"); + Resp(NULL, 0); + return ExecResult::UnkCmd; +} + + +void GdbStub::SignalStatus(TgtStatus stat, u32 arg) +{ + //Log(LogLevel::Debug, "[GDB] SIGNAL STATUS %d!\n", stat); + + this->Stat = stat; + StatFlag = true; + + if (stat == TgtStatus::Bkpt) CurBkpt = arg; + else if (stat == TgtStatus::Watchpt) CurWatchpt = arg; +} + + +StubState GdbStub::Enter(bool stay, TgtStatus stat, u32 arg, bool wait_for_conn) +{ + if (stat != TgtStatus::NoEvent) SignalStatus(stat, arg); + + StubState st; + bool do_next = true; + do + { + bool was_conn = ConnFd > 0; + st = Poll(wait_for_conn); + bool has_conn = ConnFd > 0; + + if (has_conn && !was_conn) stay = true; + + switch (st) + { + case StubState::Break: + Log(LogLevel::Info, "[GDB] break execution\n"); + SignalStatus(TgtStatus::BreakReq, ~(u32)0); + break; + case StubState::Continue: + Log(LogLevel::Info, "[GDB] continue execution\n"); + do_next = false; + break; + case StubState::Step: + Log(LogLevel::Info, "[GDB] single-step\n"); + do_next = false; + break; + case StubState::Disconnect: + Log(LogLevel::Info, "[GDB] disconnect\n"); + SignalStatus(TgtStatus::None, ~(u32)0); + do_next = false; + break; + default: break; + } + } + while (do_next && stay); + + if (st != StubState::None && st != StubState::NoConn) + { + Log(LogLevel::Debug, "[GDB] enter exit: %d\n", st); + } + return st; +} + +void GdbStub::AddBkpt(u32 addr, int kind) +{ + BpWp np; + np.addr = addr ^ (addr & 1); // clear lowest bit to not break on thumb mode weirdnesses + np.len = 0; + np.kind = kind; + + { + // already in the map + auto search = BpList.find(np.addr); + if (search != BpList.end()) return; + } + + BpList.insert({np.addr, np}); + + Log(LogLevel::Debug, "[GDB] added bkpt:\n"); + size_t i = 0; + for (auto search = BpList.begin(); search != BpList.end(); ++search, ++i) + { + Log(LogLevel::Debug, "\t[%zu]: addr=%08x, kind=%d\n", i, search->first, search->second.kind); + } +} +void GdbStub::AddWatchpt(u32 addr, u32 len, int kind) +{ + BpWp np; + np.addr = addr; + np.len = len; + np.kind = kind; + + for (auto search = WpList.begin(); search != WpList.end(); ++search) + { + if (search->addr > addr) + { + WpList.insert(search, np); + return; + } + else if (search->addr == addr && search->kind == kind) + { + if (search->len < len) search->len = len; + return; + } + } + + WpList.push_back(np); +} + +void GdbStub::DelBkpt(u32 addr, int kind) +{ + addr = addr ^ (addr & 1); + + auto search = BpList.find(addr); + if (search != BpList.end()) + { + BpList.erase(search); + } +} +void GdbStub::DelWatchpt(u32 addr, u32 len, int kind) +{ + (void)len; (void)kind; + + for (auto search = WpList.begin(); search != WpList.end(); ++search) + { + if (search->addr == addr && search->kind == kind) + { + WpList.erase(search); + return; + } + else if (search->addr > addr) return; + } +} + +void GdbStub::DelAllBpWp() +{ + BpList.erase(BpList.begin(), BpList.end()); + WpList.erase(WpList.begin(), WpList.end()); +} + +StubState GdbStub::CheckBkpt(u32 addr, bool enter, bool stay) +{ + addr ^= (addr & 1); // clear lowest bit to not break on thumb mode weirdnesses + + auto search = BpList.find(addr); + if (search == BpList.end()) return StubState::CheckNoHit; + + if (enter) + { + StubState r = Enter(stay, TgtStatus::Bkpt, addr); + Log(LogLevel::Debug, "[GDB] ENTER st=%d\n", r); + return r; + } + else + { + SignalStatus(TgtStatus::Bkpt, addr); + return StubState::None; + } +} +StubState GdbStub::CheckWatchpt(u32 addr, int kind, bool enter, bool stay) +{ + for (auto search = WpList.begin(); search != WpList.end(); ++search) + { + if (search->addr > addr) break; + + if (addr >= search->addr && addr < search->addr + search->len && search->kind == kind) + { + if (enter) return Enter(stay, TgtStatus::Watchpt, addr); + else + { + SignalStatus(TgtStatus::Watchpt, addr); + return StubState::None; + } + } + } + + return StubState::CheckNoHit; +} + +int GdbStub::SendAck() +{ + if (NoAck) return 1; + return Proto::SendAck(ConnFd); +} +int GdbStub::SendNak() +{ + if (NoAck) return 1; + return Proto::SendNak(ConnFd); +} + +int GdbStub::Resp(const u8* data1, size_t len1, const u8* data2, size_t len2) +{ + return Proto::Resp(ConnFd, data1, len1, data2, len2, NoAck); +} +int GdbStub::RespC(const char* data1, size_t len1, const u8* data2, size_t len2) +{ + return Proto::Resp(ConnFd, (const u8*)data1, len1, data2, len2, NoAck); +} +#if defined(__GCC__) || defined(__clang__) +__attribute__((__format__(printf, 2/*includes implicit this*/, 3))) +#endif +int GdbStub::RespFmt(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + int r = vsnprintf((char*)&Proto::RespBuf[1], sizeof(Proto::RespBuf)-5, fmt, args); + va_end(args); + + if (r < 0) return r; + + if ((size_t)r >= sizeof(Proto::RespBuf)-5) + { + Log(LogLevel::Error, "[GDB] truncated response in send_fmt()! (lost %zd bytes)\n", + (ssize_t)r - (ssize_t)(sizeof(Proto::RespBuf)-5)); + r = sizeof(Proto::RespBuf)-5; + } + + return Resp(&Proto::RespBuf[1], r); +} + +int GdbStub::RespStr(const char* str) +{ + return Resp((const u8*)str, strlen(str)); +} + +} + diff --git a/src/debug/GdbStub.h b/src/debug/GdbStub.h new file mode 100644 index 00000000..b3bdab72 --- /dev/null +++ b/src/debug/GdbStub.h @@ -0,0 +1,184 @@ + +#ifndef GDBSTUB_H_ +#define GDBSTUB_H_ + +#include +#include +#include + +#include "../types.h" + +#include "GdbArch.h" + +namespace Gdb +{ + +enum class TgtStatus +{ + NoEvent, + + None, + Running, + SingleStep, + BreakReq, // "break" command from gdb client + Bkpt, + Watchpt, + BkptInsn, // "bkpt" instruction + FaultData, // data abort + FaultIAcc, // instruction fetch abort + FaultInsn, // illegal instruction +}; + +class StubCallbacks +{ +public: + StubCallbacks(){} + virtual ~StubCallbacks(){}; + + virtual int GetCPU() const = 0; // 7 or 9 (currently, maybe also xtensa in the future?) + + // 0..14: as usual + // 15: pc *pipeline-corrected* + // 16: cpsr + virtual u32 ReadReg (Register reg) = 0; + virtual void WriteReg(Register reg, u32 value) = 0; + + virtual u32 ReadMem (u32 addr, int len) = 0; + virtual void WriteMem(u32 addr, int len, u32 value) = 0; + + virtual void ResetGdb() = 0; + virtual int RemoteCmd(const u8* cmd, size_t len) = 0; +}; + +enum class StubState +{ + NoConn, + None, + Break, + Continue, + Step, + Disconnect, + Attach, + CheckNoHit +}; + +enum class ReadResult +{ + NoPacket, + Eof, + CksumErr, + CmdRecvd, + Wut, + Break +}; + +enum class ExecResult +{ + Ok, + UnkCmd, + NetErr, + InitialBreak, + MustBreak, + Detached, + Step, + Continue +}; + +class GdbStub; + +typedef ExecResult (*GdbProtoCmd)(GdbStub* stub, const u8* cmd, ssize_t len); + +struct SubcmdHandler +{ + char MainCmd; + const char* SubStr; + GdbProtoCmd Handler; +}; + +struct CmdHandler +{ + char Cmd; + GdbProtoCmd Handler; +}; + +class GdbStub +{ +public: + struct BpWp + { + public: + u32 addr, len; + int kind; + }; + + GdbStub(StubCallbacks* cb, int port); + ~GdbStub(); + + bool Init(); + void Close(); + + StubState Poll(bool wait = false); + void SignalStatus(TgtStatus stat, u32 arg); + StubState Enter(bool stay, TgtStatus stat=TgtStatus::NoEvent, u32 arg=~(u32)0u, bool wait_for_conn=false); + + // kind: 2=thumb, 3=thumb2 (not relevant), 4=arm + void AddBkpt(u32 addr, int kind); + void DelBkpt(u32 addr, int kind); + // kind: 2=read, 3=write, 4=rdwr + void AddWatchpt(u32 addr, u32 len, int kind); + void DelWatchpt(u32 addr, u32 len, int kind); + + void DelAllBpWp(); + + StubState CheckBkpt(u32 addr, bool enter, bool stay); + StubState CheckWatchpt(u32 addr, int kind, bool enter, bool stay); + +#include "GdbCmds.h" + + Gdb::ExecResult SubcmdExec(const u8* cmd, ssize_t len, const SubcmdHandler* handlers); + Gdb::ExecResult CmdExec(const CmdHandler* handlers); + +public: + int SendAck(); + int SendNak(); + + int Resp(const u8* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0); + int RespC(const char* data1, size_t len1, const u8* data2 = NULL, size_t len2 = 0); +#if defined(__GCC__) || defined(__clang__) + __attribute__((__format__(printf, 2, 3))) +#endif + int RespFmt(const char* fmt, ...); + + int RespStr(const char* str); + +private: + void Disconnect(); + StubState HandlePacket(); + +private: + StubCallbacks* Cb; + + //struct sockaddr_in server, client; + void *ServerSA, *ClientSA; + int Port; + int SockFd; + int ConnFd; + + TgtStatus Stat; + u32 CurBkpt, CurWatchpt; + bool StatFlag; + bool NoAck; + + std::map BpList; + std::vector WpList; + + static SubcmdHandler Handlers_v[]; + static SubcmdHandler Handlers_q[]; + static SubcmdHandler Handlers_Q[]; + static CmdHandler Handlers_top[]; +}; + +} + +#endif + diff --git a/src/debug/gdb_test/.gitignore b/src/debug/gdb_test/.gitignore new file mode 100644 index 00000000..218500b3 --- /dev/null +++ b/src/debug/gdb_test/.gitignore @@ -0,0 +1,2 @@ +obj/ +test-gdb diff --git a/src/debug/gdb_test/Makefile b/src/debug/gdb_test/Makefile new file mode 100644 index 00000000..e8357955 --- /dev/null +++ b/src/debug/gdb_test/Makefile @@ -0,0 +1,28 @@ + +default: all + +all: test-gdb + +CPPFLAGS += -Werror=implicit-function-declaration -Werror=int-conversion \ + -Werror=return-type -Werror=uninitialized \ + -I../ -I../../ -Og -g -Wall \ + -Wno-switch -Wno-pointer-sign + +obj/: + @mkdir -vp "$@" + +test-gdb: obj/GdbProto.o obj/GdbStub.o obj/GdbCmds.o obj/main.o obj/CRC32.o + $(CXX) $(CPPFLAGS) $(LDFLAGS) -o "$@" $^ + +obj/Gdb%.o: ../Gdb%.cpp obj/ + $(CXX) $(CPPFLAGS) -c -o "$@" "$<" + +obj/main.o: main.cpp obj/ + $(CXX) $(CPPFLAGS) -c -o "$@" "$<" + +obj/CRC32.o: ../../CRC32.cpp obj/ + $(CXX) $(CPPFLAGS) -c -o "$@" "$<" + +clean: + @$(RM) -rv obj/ test-gdb + diff --git a/src/debug/gdb_test/main.cpp b/src/debug/gdb_test/main.cpp new file mode 100644 index 00000000..afdfa2cd --- /dev/null +++ b/src/debug/gdb_test/main.cpp @@ -0,0 +1,124 @@ + +#include +#include +#include +#include + +#include "GdbStub.h" +#include "Platform.h" + +class Debug : public Gdb::StubCallbacks +{ +public: + Debug(){} + ~Debug(){} + + int GetCPU() const override { return 9; } + + u32 ReadReg(Gdb::Register reg) override + { + printf("[==>] read reg %d\n", (int)reg); + if (reg == Gdb::Register::pc) return 0x000000df; // cpsr: irq,fiq disabled, arm, sys mode + else return 0x69420; + } + void WriteReg(Gdb::Register reg, u32 value) override + { + printf("[==>] write reg %d: 0x%08x\n", (int)reg, value); + } + + u32 ReadMem(u32 addr, int len) override + { + static const u32 words[] = { + 0xeafffffe, + 0xe0211002, + 0xe12fff1e, + 0 + }; + + printf("[==>] read mem 0x%08x (size %u)\n", addr, len); + + // $: b $ (arm) + return words[(addr>>2)&3] & ((1uLL<] write addr 0x%08x (size %u): 0x%08x\n", addr, len, value); + } + + void ResetGdb() override + { + printf("[==>] RESET!!!\n"); + } + int RemoteCmd(const u8* cmd, size_t len) override + { + printf("[==>] Rcmd: %s\n", cmd); + return 0; + } +}; + +int main(int argc, char** argv) { + Debug debug; + + Gdb::GdbStub stub(&debug, (argc > 1) ? atoi(argv[1]) : 3333); + if (!stub.Init()) return 1; + + do + { + while (true) + { + Gdb::StubState s = stub.Poll(); + + if (s == Gdb::StubState::None || s == Gdb::StubState::NoConn) + { + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 1000*1000; // 1 ms + nanosleep(&ts, NULL); + continue; + } + + switch (s) + { + case Gdb::StubState::Attach: + printf("[==>] attached\n"); + break; + case Gdb::StubState::Break: + printf("[==>] break execution\n"); + stub.SignalStatus(Gdb::TgtStatus::BreakReq, ~(u32)0); + break; + case Gdb::StubState::Continue: + printf("[==>] continue execution\n"); + // TODO: send signal status on SIGSTOP? eh. + break; + case Gdb::StubState::Step: + printf("[==>] single-step\n"); + stub.SignalStatus(Gdb::TgtStatus::SingleStep, ~(u32)0); + break; + case Gdb::StubState::Disconnect: + printf("[==>] disconnect\n"); + stub.SignalStatus(Gdb::TgtStatus::None, ~(u32)0); + break; + } + + if (s == Gdb::StubState::Disconnect) break; + } + } + while (false); + + stub.Close(); + return 0; +} + +namespace Platform +{ +void Log(LogLevel level, const char* fmt, ...) +{ + if (fmt == nullptr) return; + + va_list args; + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); +} +} + diff --git a/src/debug/hexutil.h b/src/debug/hexutil.h new file mode 100644 index 00000000..9eb4ad22 --- /dev/null +++ b/src/debug/hexutil.h @@ -0,0 +1,75 @@ + +#ifndef HEXUTIL_GDB_H_ +#define HEXUTIL_GDB_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include + +inline static uint8_t hex2nyb(uint8_t v) +{ + if (v >= '0' && v <= '9') return v - '0'; + else if (v >= 'A' && v <= 'F') return v - 'A' + 0xa; + else if (v >= 'a' && v <= 'f') return v - 'a' + 0xa; + else + { + __builtin_trap(); + return 0xcc; + } +} +inline static uint8_t nyb2hex(uint8_t v) +{ + v &= 0xf; + if (v >= 0xa) return v - 0xa + 'a'; + else return v - 0x0 + '0'; +} + +inline static void hexfmt8(uint8_t* dst, uint8_t v) +{ + dst[0] = nyb2hex(v>>4); + dst[1] = nyb2hex(v>>0); +} +inline static uint8_t unhex8(const uint8_t* src) +{ + return (hex2nyb(src[0]) << 4) | hex2nyb(src[1]); +} + +inline static void hexfmt16(uint8_t* dst, uint16_t v) +{ + dst[0] = nyb2hex(v>> 4); + dst[1] = nyb2hex(v>> 0); + dst[2] = nyb2hex(v>>12); + dst[3] = nyb2hex(v>> 8); +} +inline static uint16_t unhex16(const uint8_t* src) +{ + return unhex8(&src[0*2]) | ((uint16_t)unhex8(&src[1*2]) << 8); +} + +inline static void hexfmt32(uint8_t* dst, uint32_t v) +{ + for (size_t i = 0; i < 4; ++i, v >>= 8) + { + dst[2*i+0] = nyb2hex(v>>4); + dst[2*i+1] = nyb2hex(v>>0); + } +} +inline static uint32_t unhex32(const uint8_t* src) +{ + uint32_t v = 0; + for (size_t i = 0; i < 4; ++i) + { + v |= (uint32_t)unhex8(&src[i*2]) << (i*8); + } + return v; +} + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 24261030..3923f379 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -139,6 +139,7 @@ else() ) target_link_libraries(melonDS PRIVATE "${X11_LIBRARIES}" "${EGL_LIBRARIES}") target_include_directories(melonDS PRIVATE "${X11_INCLUDE_DIR}") + add_compile_definitions(QAPPLICATION_CLASS=QApplication) endif() diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 898e4a16..da08c285 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -146,6 +146,14 @@ bool DSiBatteryCharging; bool DSiFullBIOSBoot; +#ifdef GDBSTUB_ENABLED +bool GdbEnabled; +int GdbPortARM7; +int GdbPortARM9; +bool GdbARM7BreakOnStartup; +bool GdbARM9BreakOnStartup; +#endif + CameraConfig Camera[2]; @@ -337,6 +345,14 @@ ConfigEntry ConfigFile[] = {"DSiFullBIOSBoot", 1, &DSiFullBIOSBoot, false, true}, +#ifdef GDBSTUB_ENABLED + {"GdbEnabled", 1, &GdbEnabled, false, false}, + {"GdbPortARM7", 0, &GdbPortARM7, 3334, true}, + {"GdbPortARM9", 0, &GdbPortARM9, 3333, true}, + {"GdbARM7BreakOnStartup", 1, &GdbARM7BreakOnStartup, false, true}, + {"GdbARM9BreakOnStartup", 1, &GdbARM9BreakOnStartup, false, true}, +#endif + // TODO!! // we need a more elegant way to deal with this {"Camera0_InputType", 0, &Camera[0].InputType, 0, false}, diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 504c068d..b1d9532e 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -193,6 +193,12 @@ extern bool DSiFullBIOSBoot; extern CameraConfig Camera[2]; +extern bool GdbEnabled; +extern int GdbPortARM7; +extern int GdbPortARM9; +extern bool GdbARM7BreakOnStartup; +extern bool GdbARM9BreakOnStartup; + void Load(); void Save(); diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index 0bdbb5c7..571f36a2 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -89,7 +89,22 @@ EmuSettingsDialog::EmuSettingsDialog(QWidget* parent) : QDialog(parent), ui(new ui->spnJITMaximumBlockSize->setDisabled(true); #endif +#ifdef GDBSTUB_ENABLED + ui->cbGdbEnabled->setChecked(Config::GdbEnabled); + ui->intGdbPortA7->setValue(Config::GdbPortARM7); + ui->intGdbPortA9->setValue(Config::GdbPortARM9); + ui->cbGdbBOSA7->setChecked(Config::GdbARM7BreakOnStartup); + ui->cbGdbBOSA9->setChecked(Config::GdbARM9BreakOnStartup); +#else + ui->cbGdbEnabled->setDisabled(true); + ui->intGdbPortA7->setDisabled(true); + ui->intGdbPortA9->setDisabled(true); + ui->cbGdbBOSA7->setDisabled(true); + ui->cbGdbBOSA9->setDisabled(true); +#endif + on_chkEnableJIT_toggled(); + on_cbGdbEnabled_toggled(); on_chkExternalBIOS_toggled(); const int imgsizes[] = {256, 512, 1024, 2048, 4096, 0}; @@ -223,6 +238,12 @@ void EmuSettingsDialog::done(int r) bool dsiSDFolderSync = ui->cbDSiSDFolder->isChecked(); std::string dsiSDFolderPath = ui->txtDSiSDFolder->text().toStdString(); + bool gdbEnabled = ui->cbGdbEnabled->isChecked(); + int gdbPortA7 = ui->intGdbPortA7->value(); + int gdbPortA9 = ui->intGdbPortA9->value(); + bool gdbBOSA7 = ui->cbGdbBOSA7->isChecked(); + bool gdbBOSA9 = ui->cbGdbBOSA9->isChecked(); + if (consoleType != Config::ConsoleType || directBoot != Config::DirectBoot #ifdef JIT_ENABLED @@ -231,6 +252,13 @@ void EmuSettingsDialog::done(int r) || jitBranchOptimisations != Config::JIT_BranchOptimisations || jitLiteralOptimisations != Config::JIT_LiteralOptimisations || jitFastMemory != Config::JIT_FastMemory +#endif +#ifdef GDBSTUB_ENABLED + || gdbEnabled != Config::GdbEnabled + || gdbPortA7 != Config::GdbPortARM7 + || gdbPortA9 != Config::GdbPortARM9 + || gdbBOSA7 != Config::GdbARM7BreakOnStartup + || gdbBOSA9 != Config::GdbARM9BreakOnStartup #endif || externalBiosEnable != Config::ExternalBIOSEnable || bios9Path != Config::BIOS9Path @@ -285,13 +313,20 @@ void EmuSettingsDialog::done(int r) Config::DSiSDFolderSync = dsiSDFolderSync; Config::DSiSDFolderPath = dsiSDFolderPath; - #ifdef JIT_ENABLED +#ifdef JIT_ENABLED Config::JIT_Enable = jitEnable; Config::JIT_MaxBlockSize = jitMaxBlockSize; Config::JIT_BranchOptimisations = jitBranchOptimisations; Config::JIT_LiteralOptimisations = jitLiteralOptimisations; Config::JIT_FastMemory = jitFastMemory; - #endif +#endif +#ifdef GDBSTUB_ENABLED + Config::GdbEnabled = gdbEnabled; + Config::GdbPortARM7 = gdbPortA7; + Config::GdbPortARM9 = gdbPortA9; + Config::GdbARM7BreakOnStartup = gdbBOSA7; + Config::GdbARM9BreakOnStartup = gdbBOSA9; +#endif Config::ConsoleType = consoleType; Config::DirectBoot = directBoot; @@ -506,6 +541,31 @@ void EmuSettingsDialog::on_chkEnableJIT_toggled() ui->chkJITFastMemory->setDisabled(disabled); #endif ui->spnJITMaximumBlockSize->setDisabled(disabled); + + on_cbGdbEnabled_toggled(); +} + +void EmuSettingsDialog::on_cbGdbEnabled_toggled() +{ +#ifdef GDBSTUB_ENABLED + bool disabled = !ui->cbGdbEnabled->isChecked(); + bool jitenable = ui->chkEnableJIT->isChecked(); + + if (jitenable && !disabled) { + ui->cbGdbEnabled->setChecked(false); + disabled = true; + } +#else + bool disabled = true; + bool jitenable = true; + ui->cbGdbEnabled->setChecked(false); +#endif + + ui->cbGdbEnabled->setDisabled(jitenable); + ui->intGdbPortA7->setDisabled(disabled); + ui->intGdbPortA9->setDisabled(disabled); + ui->cbGdbBOSA7->setDisabled(disabled); + ui->cbGdbBOSA9->setDisabled(disabled); } void EmuSettingsDialog::on_chkExternalBIOS_toggled() diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h index 6a796267..2ebfd2fc 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.h +++ b/src/frontend/qt_sdl/EmuSettingsDialog.h @@ -77,6 +77,8 @@ private slots: void on_chkEnableJIT_toggled(); void on_chkExternalBIOS_toggled(); + void on_cbGdbEnabled_toggled(); + private: void verifyFirmware(); diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.ui b/src/frontend/qt_sdl/EmuSettingsDialog.ui index b434bbe2..74bc0865 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.ui +++ b/src/frontend/qt_sdl/EmuSettingsDialog.ui @@ -26,7 +26,7 @@ - 0 + 5 @@ -568,6 +568,101 @@ + + + Devtools + + + + + + ARM9 port + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + ARM7 port + + + + + + + Enable GDB stub + + + + + + + Note: melonDS must be restarted in order for these changes to have effect + + + + + + + Note: GDB stub cannot be used together with the JIT recompiler + + + + + + + Break on startup + + + + + + + 1000 + + + 65535 + + + 3333 + + + + + + + 1000 + + + 65535 + + + 3334 + + + + + + + Break on startup + + + + + @@ -590,7 +685,6 @@ - tabWidget cbxConsoleType chkDirectBoot chkExternalBIOS @@ -639,8 +733,8 @@ accept() - 257 - 349 + 266 + 379 157 @@ -655,8 +749,8 @@ reject() - 325 - 349 + 334 + 379 286 diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 7f6e1d56..2fa0b182 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -213,6 +213,11 @@ int GetConfigInt(ConfigEntry entry) case Firm_Color: return Config::FirmwareFavouriteColour; case AudioBitDepth: return Config::AudioBitDepth; + +#ifdef GDBSTUB_ENABLED + case GdbPortARM7: return Config::GdbPortARM7; + case GdbPortARM9: return Config::GdbPortARM9; +#endif } return 0; @@ -241,6 +246,12 @@ bool GetConfigBool(ConfigEntry entry) case Firm_OverrideSettings: return Config::FirmwareOverrideSettings != 0; case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0; + +#ifdef GDBSTUB_ENABLED + case GdbEnabled: return Config::GdbEnabled; + case GdbARM7BreakOnStartup: return Config::GdbARM7BreakOnStartup; + case GdbARM9BreakOnStartup: return Config::GdbARM9BreakOnStartup; +#endif } return false; From bf81b87a60373d82bedc76d316a980a700f89031 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Oct 2023 15:49:36 -0400 Subject: [PATCH 006/157] Generalize a path in .gitignore (#1862) - Covers all of CLion's default CMake build paths --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1aa5e3a1..d7001e4a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ obj melon_grc.c melon_grc.h melon.rc -cmake-build +cmake-build* cmake-build-debug compile_commands.json .idea From 8c4e5af737ab0dddb91bd593f51d815d1a04a7b1 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Oct 2023 17:27:55 -0400 Subject: [PATCH 007/157] Slight polish to DMA (#1856) * Slight polish to DMA - Default-initialize members explicitly - Mark some methods as const noexcept - Initialize DMA::MRAMBurstTable to DMATiming::MRAMDummy - Use the default destructor * Move DMA_Timings definitions to a source file - To ensure constant and unique addresses * Include some extra DMA members in the savestate * Simplify serializing the DMA table - Extend the dummy table to 256 bytes (same length as the real ones) * Revert the type change to DMA::DoSavestate * Keep the MRAMBurstTable inside the DMA class, instead of using a pointer - If we use a pointer to an external table, then we can't use it in savestates (else that external table gets overwritten) --- src/CMakeLists.txt | 1 + src/DMA.cpp | 16 +-- src/DMA.h | 47 +++++---- src/DMA_Timings.cpp | 243 ++++++++++++++++++++++++++++++++++++++++++++ src/DMA_Timings.h | 198 ++---------------------------------- src/Savestate.h | 2 +- 6 files changed, 284 insertions(+), 223 deletions(-) create mode 100644 src/DMA_Timings.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eb5f81e0..9fe93ae5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(core STATIC CRC32.cpp DMA.cpp DMA_Timings.h + DMA_Timings.cpp DSi.cpp DSi_AES.cpp DSi_Camera.cpp diff --git a/src/DMA.cpp b/src/DMA.cpp index d296511a..a7558ff4 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -47,21 +47,16 @@ using Platform::LogLevel; // TODO: timings are nonseq when address is fixed/decrementing -DMA::DMA(u32 cpu, u32 num) +DMA::DMA(u32 cpu, u32 num) : + CPU(cpu), + Num(num) { - CPU = cpu; - Num = num; - if (cpu == 0) CountMask = 0x001FFFFF; else CountMask = (num==3 ? 0x0000FFFF : 0x00003FFF); } -DMA::~DMA() -{ -} - void DMA::Reset() { SrcAddr = 0; @@ -82,6 +77,7 @@ void DMA::Reset() Executing = false; InProgress = false; MRAMBurstCount = 0; + MRAMBurstTable = DMATiming::MRAMDummy; } void DMA::DoSavestate(Savestate* file) @@ -106,6 +102,10 @@ void DMA::DoSavestate(Savestate* file) file->Bool32(&InProgress); file->Bool32(&IsGXFIFODMA); file->Var32(&MRAMBurstCount); + file->Bool32(&Executing); + file->Bool32(&Stall); + + file->VarArray(MRAMBurstTable.data(), sizeof(MRAMBurstTable)); } void DMA::WriteCnt(u32 val) diff --git a/src/DMA.h b/src/DMA.h index ad194c11..b0e8f8a3 100644 --- a/src/DMA.h +++ b/src/DMA.h @@ -19,14 +19,16 @@ #ifndef DMA_H #define DMA_H +#include #include "types.h" #include "Savestate.h" +#include "DMA_Timings.h" class DMA { public: DMA(u32 cpu, u32 num); - ~DMA(); + ~DMA() = default; void Reset(); @@ -48,12 +50,12 @@ public: template void Run7(); - bool IsInMode(u32 mode) + bool IsInMode(u32 mode) const noexcept { return ((mode == StartMode) && (Cnt & 0x80000000)); } - bool IsRunning() { return Running!=0; } + bool IsRunning() const noexcept { return Running!=0; } void StartIfNeeded(u32 mode) { @@ -72,32 +74,33 @@ public: if (Executing) Stall = true; } - u32 SrcAddr; - u32 DstAddr; - u32 Cnt; + u32 SrcAddr {}; + u32 DstAddr {}; + u32 Cnt {}; private: - u32 CPU, Num; + u32 CPU {}; + u32 Num {}; - u32 StartMode; - u32 CurSrcAddr; - u32 CurDstAddr; - u32 RemCount; - u32 IterCount; - s32 SrcAddrInc; - s32 DstAddrInc; - u32 CountMask; + u32 StartMode {}; + u32 CurSrcAddr {}; + u32 CurDstAddr {}; + u32 RemCount {}; + u32 IterCount {}; + s32 SrcAddrInc {}; + s32 DstAddrInc {}; + u32 CountMask {}; - u32 Running; - bool InProgress; + u32 Running {}; + bool InProgress {}; - bool Executing; - bool Stall; + bool Executing {}; + bool Stall {}; - bool IsGXFIFODMA; + bool IsGXFIFODMA {}; - u32 MRAMBurstCount; - const u8* MRAMBurstTable; + u32 MRAMBurstCount {}; + std::array MRAMBurstTable; }; #endif diff --git a/src/DMA_Timings.cpp b/src/DMA_Timings.cpp new file mode 100644 index 00000000..cea32820 --- /dev/null +++ b/src/DMA_Timings.cpp @@ -0,0 +1,243 @@ +/* + 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 "DMA_Timings.h" +#include "types.h" + +namespace DMATiming +{ + +// DMA timing tables +// +// DMA timings on the DS are normally straightforward, except in one case: when +// main RAM is involved. +// Main RAM to main RAM is the easy case: 16c/unit in 16bit mode, 18c/unit in 32bit +// mode. +// It gets more complicated when transferring from main RAM to somewhere else, or +// vice versa: main RAM supports burst accesses, but the rules dictating how long +// bursts can be are weird and inconsistent. Main RAM also supports parallel +// memory operations, to some extent. +// I haven't figured out the full logic behind it, let alone how to emulate it +// efficiently, so for now we will use these tables. +// A zero denotes the end of a burst pattern. +// +// Note: burst patterns only apply when the main RAM address is incrementing. +// A fixed or decrementing address results in nonsequential accesses. +// +// Note about GBA slot/wifi timings: these take into account the sequential timing +// setting. Timings are such that the nonseq setting only matters for the first +// access, and minor edge cases (like the last of a 0x20000-byte block). + +extern const std::array MRAMDummy = {0}; + +extern const std::array MRAMRead16Bursts[] = +{ + // main RAM to regular 16bit or 32bit bus (similar) + {7, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, + 7, 3, + 0}, + // main RAM to GBA/wifi, seq=4 + {8, 6, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, + 0}, + // main RAM to GBA/wifi, seq=6 + {10, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, + 12, 8, + 0}, +}; + +extern const std::array MRAMRead32Bursts[] = +{ + // main RAM to regular 16bit bus + {9, 4, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, + 0}, + // main RAM to regular 32bit bus + {9, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, + 0}, + // main RAM to GBA/wifi, seq=4 + {14, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, + 13, + 0}, + // main RAM to GBA/wifi, seq=6 + {18, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, + 17, + 0}, +}; + +extern const std::array MRAMWrite16Bursts[] = +{ + // regular 16bit or 32bit bus to main RAM (similar) + {8, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 0}, + // GBA/wifi to main RAM, seq=4 + {10, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 0}, + // GBA/wifi to main RAM, seq=6 + {9, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, + 0}, +}; + +extern const std::array MRAMWrite32Bursts[4] = +{ + // regular 16bit bus to main RAM + {9, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 0}, + // regular 32bit bus to main RAM + {9, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 0}, + // GBA/wifi to main RAM, seq=4 + {15, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, + 0}, + // GBA/wifi to main RAM, seq=6 + {16, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, + 0}, +}; + +} \ No newline at end of file diff --git a/src/DMA_Timings.h b/src/DMA_Timings.h index 4281c783..83af9ad6 100644 --- a/src/DMA_Timings.h +++ b/src/DMA_Timings.h @@ -19,6 +19,7 @@ #ifndef DMA_TIMINGS_H #define DMA_TIMINGS_H +#include #include "types.h" namespace DMATiming @@ -45,202 +46,15 @@ namespace DMATiming // setting. Timings are such that the nonseq setting only matters for the first // access, and minor edge cases (like the last of a 0x20000-byte block). -constexpr u8 MRAMDummy[1] = {0}; +extern const std::array MRAMDummy; -constexpr u8 MRAMRead16Bursts[][256] = -{ - // main RAM to regular 16bit or 32bit bus (similar) - {7, 3, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, - 7, 3, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, - 7, 3, - 0}, - // main RAM to GBA/wifi, seq=4 - {8, 6, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, - 0}, - // main RAM to GBA/wifi, seq=6 - {10, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, - 12, 8, - 0}, -}; +extern const std::array MRAMRead16Bursts[3]; -constexpr u8 MRAMRead32Bursts[][256] = -{ - // main RAM to regular 16bit bus - {9, 4, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 9, - 0}, - // main RAM to regular 32bit bus - {9, 3, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 0}, - // main RAM to GBA/wifi, seq=4 - {14, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, 10, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, - 9, 9, 9, 9, 9, 9, 9, - 13, - 0}, - // main RAM to GBA/wifi, seq=6 - {18, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, 14, 13, 13, 13, 13, 13, 13, 13, 13, - 13, 13, 13, 13, 13, 13, 13, 13, 13, - 17, - 0}, -}; +extern const std::array MRAMRead32Bursts[4]; -constexpr u8 MRAMWrite16Bursts[][256] = -{ - // regular 16bit or 32bit bus to main RAM (similar) - {8, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 0}, - // GBA/wifi to main RAM, seq=4 - {10, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 0}, - // GBA/wifi to main RAM, seq=6 - {9, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, - 7, 7, 7, 7, 7, - 0}, -}; +extern const std::array MRAMWrite16Bursts[3]; -constexpr u8 MRAMWrite32Bursts[][256] = -{ - // regular 16bit bus to main RAM - {9, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 0}, - // regular 32bit bus to main RAM - {9, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 0}, - // GBA/wifi to main RAM, seq=4 - {15, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, - 10, 10, 10, 10, - 0}, - // GBA/wifi to main RAM, seq=6 - {16, 14, 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 0}, -}; +extern const std::array MRAMWrite32Bursts[4]; } diff --git a/src/Savestate.h b/src/Savestate.h index 0aef517e..235d1fbc 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -25,7 +25,7 @@ #include "types.h" #define SAVESTATE_MAJOR 10 -#define SAVESTATE_MINOR 0 +#define SAVESTATE_MINOR 1 class Savestate { From 21590b070976f8fe343fdc44d4fee599cbda94f4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 24 Oct 2023 17:28:14 -0400 Subject: [PATCH 008/157] Miscellaneous DSi NAND fixes (#1852) * Replace some standard I/O calls with Platform equivalents - I missed a spot when I submitted that PR a few months ago * Include in DSi_NAND.h - Because it uses unique_ptr * Split DSi_NAND::ReadHardwareInfo into ReadSerialData and ReadHardwareInfoN * Add a RegionMask enum * Move DSi NAND patching to the frontend * Add DSiSupportedLanguageMask - Not currently used by the frontend, but I use it in melonDS DS * Remove some Platform::ConfigEntry values - The core no longer needs to know about them - The corresponding Config values are unchanged * Mark NANDMount's destructor as noexcept --- src/DSi.cpp | 5 +- src/DSi_NAND.cpp | 148 ++++++++++++----------------- src/DSi_NAND.h | 37 +++++++- src/NDSCart.cpp | 3 +- src/NDS_Header.h | 17 +++- src/OpenGLSupport.cpp | 6 +- src/Platform.h | 7 -- src/frontend/qt_sdl/Platform.cpp | 8 -- src/frontend/qt_sdl/ROMManager.cpp | 90 +++++++++++++++--- 9 files changed, 194 insertions(+), 127 deletions(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index 17d79a6f..15160b29 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -540,8 +540,9 @@ void SetupDirectBoot() ARM9Write32(0x02000400+i, *(u32*)&userdata.Bytes[0x88+i]); DSi_NAND::DSiSerialData hwinfoS {}; + nand.ReadSerialData(hwinfoS); DSi_NAND::DSiHardwareInfoN hwinfoN; - nand.ReadHardwareInfo(hwinfoS, hwinfoN); + nand.ReadHardwareInfoN(hwinfoN); for (u32 i = 0; i < 0x14; i+=4) ARM9Write32(0x02000600+i, *(u32*)&hwinfoN[0x88+i]); @@ -944,7 +945,7 @@ bool LoadNAND() NDS::ARM7->JumpTo(bootparams[6]); } - nandmount.PatchUserData(); + // user data is now expected to be patched by the frontend return true; } diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 03bed9b8..79458cbc 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -172,7 +172,7 @@ NANDMount::NANDMount(NANDImage& nand) noexcept : Image(&nand) } -NANDMount::~NANDMount() +NANDMount::~NANDMount() noexcept { f_unmount("0:"); ff_disk_close(); @@ -467,30 +467,43 @@ bool NANDImage::ESDecrypt(u8* data, u32 len) return true; } - -void NANDMount::ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) +bool NANDMount::ReadSerialData(DSiSerialData& dataS) { FF_FIL file; - FRESULT res; - u32 nread; + FRESULT res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ); - res = f_open(&file, "0:/sys/HWINFO_S.dat", FA_OPEN_EXISTING | FA_READ); if (res == FR_OK) { + u32 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); + return res == FR_OK; +} + +bool NANDMount::ReadHardwareInfoN(DSiHardwareInfoN& dataN) +{ + FF_FIL file; + FRESULT res = f_open(&file, "0:/sys/HWINFO_N.dat", FA_OPEN_EXISTING | FA_READ); + if (res == FR_OK) { + u32 nread; f_read(&file, dataN.data(), sizeof(dataN), &nread); f_close(&file); } + + return res == FR_OK; } +void NANDMount::ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN) +{ + ReadSerialData(dataS); + ReadHardwareInfoN(dataN); +} -void NANDMount::ReadUserData(DSiFirmwareSystemSettings& data) +bool NANDMount::ReadUserData(DSiFirmwareSystemSettings& data) { FF_FIL file; FRESULT res; @@ -521,7 +534,7 @@ void NANDMount::ReadUserData(DSiFirmwareSystemSettings& data) v2 = tmp; } - if (v1 < 0 && v2 < 0) return; + if (v1 < 0 && v2 < 0) return false; if (v2 > v1) { @@ -537,73 +550,40 @@ void NANDMount::ReadUserData(DSiFirmwareSystemSettings& data) f_lseek(&file, 0); f_read(&file, &data, sizeof(DSiFirmwareSystemSettings), &nread); f_close(&file); + + return true; } -void NANDMount::PatchUserData() +static bool SaveUserData(const char* filename, const DSiFirmwareSystemSettings& data) { - FRESULT res; - - for (int i = 0; i < 2; i++) + FF_FIL file; + if (FRESULT res = f_open(&file, filename, FA_OPEN_EXISTING | FA_READ | FA_WRITE); res != FR_OK) { - char filename[64]; - 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); - if (res != FR_OK) - { - Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res); - continue; - } - - DSiFirmwareSystemSettings contents; - u32 nres; - f_lseek(&file, 0); - f_read(&file, &contents, sizeof(DSiFirmwareSystemSettings), &nres); - - // override user settings, if needed - if (Platform::GetConfigBool(Platform::Firm_OverrideSettings)) - { - // setting up username - std::string orig_username = Platform::GetConfigString(Platform::Firm_Username); - std::u16string username = std::wstring_convert, char16_t>{}.from_bytes(orig_username); - size_t usernameLength = std::min(username.length(), (size_t) 10); - memset(&contents.Nickname, 0, sizeof(contents.Nickname)); - memcpy(&contents.Nickname, username.data(), usernameLength * sizeof(char16_t)); - - // setting language - contents.Language = static_cast(Platform::GetConfigInt(Platform::Firm_Language)); - - // setting up color - contents.FavoriteColor = Platform::GetConfigInt(Platform::Firm_Color); - - // setting up birthday - contents.BirthdayMonth = Platform::GetConfigInt(Platform::Firm_BirthdayMonth); - contents.BirthdayDay = Platform::GetConfigInt(Platform::Firm_BirthdayDay); - - // setup message - std::string orig_message = Platform::GetConfigString(Platform::Firm_Message); - std::u16string message = std::wstring_convert, char16_t>{}.from_bytes(orig_message); - size_t messageLength = std::min(message.length(), (size_t) 26); - memset(&contents.Message, 0, sizeof(contents.Message)); - memcpy(&contents.Message, message.data(), messageLength * sizeof(char16_t)); - - // TODO: make other items configurable? - } - - // fix touchscreen coords - contents.TouchCalibrationADC1 = {0, 0}; - contents.TouchCalibrationPixel1 = {0, 0}; - contents.TouchCalibrationADC2 = {255 << 4, 191 << 4}; - contents.TouchCalibrationPixel2 = {255, 191}; - - contents.UpdateHash(); - - f_lseek(&file, 0); - f_write(&file, &contents, sizeof(DSiFirmwareSystemSettings), &nres); - - f_close(&file); + Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res); + return false; } + // TODO: If the file couldn't be opened, try creating a new one in its place + // (after all, we have the data for that) + + u32 bytes_written = 0; + FRESULT res = f_write(&file, &data, sizeof(DSiFirmwareSystemSettings), &bytes_written); + f_close(&file); + + if (res != FR_OK || bytes_written != sizeof(DSiFirmwareSystemSettings)) + { + Log(LogLevel::Error, "NAND: editing file %s failed: %d\n", filename, res); + return false; + } + + return true; +} + +bool NANDMount::ApplyUserData(const DSiFirmwareSystemSettings& data) +{ + bool ok0 = SaveUserData("0:/shared1/TWLCFG0.dat", data); + bool ok1 = SaveUserData("0:/shared1/TWLCFG1.dat", data); + + return ok0 && ok1; } @@ -672,21 +652,18 @@ bool NANDMount::ImportFile(const char* path, const u8* data, size_t len) bool NANDMount::ImportFile(const char* path, const char* in) { FF_FIL file; - FILE* fin; FRESULT res; - fin = fopen(in, "rb"); + Platform::FileHandle* fin = OpenLocalFile(in, FileMode::Read); if (!fin) return false; - fseek(fin, 0, SEEK_END); - u32 len = (u32)ftell(fin); - fseek(fin, 0, SEEK_SET); + u32 len = FileLength(fin); res = f_open(&file, path, FA_CREATE_ALWAYS | FA_WRITE); if (res != FR_OK) { - fclose(fin); + CloseFile(fin); return false; } @@ -700,11 +677,11 @@ bool NANDMount::ImportFile(const char* path, const char* in) blocklen = sizeof(buf); u32 nwrite; - fread(buf, blocklen, 1, fin); + FileRead(buf, blocklen, 1, fin); f_write(&file, buf, blocklen, &nwrite); } - fclose(fin); + CloseFile(fin); f_close(&file); Log(LogLevel::Debug, "Imported file from %s to %s\n", in, path); @@ -715,7 +692,6 @@ bool NANDMount::ImportFile(const char* path, const char* in) bool NANDMount::ExportFile(const char* path, const char* out) { FF_FIL file; - FILE* fout; FRESULT res; res = f_open(&file, path, FA_OPEN_EXISTING | FA_READ); @@ -724,7 +700,7 @@ bool NANDMount::ExportFile(const char* path, const char* out) u32 len = f_size(&file); - fout = fopen(out, "wb"); + Platform::FileHandle* fout = OpenLocalFile(out, FileMode::Write); if (!fout) { f_close(&file); @@ -742,10 +718,10 @@ bool NANDMount::ExportFile(const char* path, const char* out) u32 nread; f_read(&file, buf, blocklen, &nread); - fwrite(buf, blocklen, 1, fout); + FileWrite(buf, blocklen, 1, fout); } - fclose(fout); + CloseFile(fout); f_close(&file); Log(LogLevel::Debug, "Exported file from %s to %s\n", path, out); @@ -1144,10 +1120,10 @@ bool NANDMount::ImportTitle(const char* appfile, const DSi_TMD::TitleMetadata& t { NDSHeader header {}; { - FILE* f = fopen(appfile, "rb"); + Platform::FileHandle* f = OpenLocalFile(appfile, FileMode::Read); if (!f) return false; - fread(&header, sizeof(header), 1, f); - fclose(f); + FileRead(&header, sizeof(header), 1, f); + CloseFile(f); } u32 version = tmd.Contents.GetVersion(); diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 777afe09..0077eacb 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -25,6 +25,7 @@ #include "DSi_TMD.h" #include "SPI_Firmware.h" #include +#include #include #include @@ -84,7 +85,7 @@ class NANDMount { public: explicit NANDMount(NANDImage& nand) noexcept; - ~NANDMount(); + ~NANDMount() noexcept; NANDMount(const NANDMount&) = delete; NANDMount& operator=(const NANDMount&) = delete; @@ -92,10 +93,15 @@ public: NANDMount(NANDMount&&) = delete; NANDMount& operator=(NANDMount&&) = delete; + bool ReadSerialData(DSiSerialData& dataS); + bool ReadHardwareInfoN(DSiHardwareInfoN& dataN); void ReadHardwareInfo(DSiSerialData& dataS, DSiHardwareInfoN& dataN); - void ReadUserData(DSiFirmwareSystemSettings& data); - void PatchUserData(); + bool ReadUserData(DSiFirmwareSystemSettings& data); + + /// Saves the given system settings to the DSi NAND, + /// to both TWLCFG0.dat and TWLCFG1.dat. + bool ApplyUserData(const DSiFirmwareSystemSettings& data); void ListTitles(u32 category, std::vector& titlelist); bool TitleExists(u32 category, u32 titleid); @@ -211,6 +217,29 @@ enum class ConsoleRegion : u8 Korea, }; +/// Languages that the given NAND image supports. +/// @see https://problemkaputt.de/gbatek.htm#dsiregions +enum DSiSupportedLanguageMask : u32 { + NoLanguagesSet = 0, + JapaneseSupported = 1 << 0, + EnglishSupported = 1 << 1, + FrenchSupported = 1 << 2, + GermanSupported = 1 << 3, + ItalianSupported = 1 << 4, + SpanishSupported = 1 << 5, + ChineseSupported = 1 << 6, + KoreanSupported = 1 << 7, + + JapanLanguages = JapaneseSupported, + AmericaLanguages = EnglishSupported | FrenchSupported | SpanishSupported, + EuropeLanguages = EnglishSupported | FrenchSupported | GermanSupported | ItalianSupported | SpanishSupported, + AustraliaLanguages = EnglishSupported, + + // "Unknown (supposedly Chinese/Mandarin?, and maybe English or so)" + ChinaLanguages = ChineseSupported | EnglishSupported, + KoreaLanguages = KoreanSupported, +}; + /// 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. @@ -223,7 +252,7 @@ union DSiSerialData u8 RsaSha1HMAC[0x80]; u32 Version; u32 EntrySize; - u32 SupportedLanguages; + DSiSupportedLanguageMask SupportedLanguages; u8 Unknown0[4]; ConsoleRegion Region; char Serial[12]; diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index f1598eb2..400f7d6d 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1650,8 +1650,7 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen) bool dsi = header.IsDSi(); bool badDSiDump = false; - u32 dsiRegion = header.DSiRegionMask; - if (dsi && dsiRegion == 0) + if (dsi && header.DSiRegionMask == RegionMask::NoRegion) { Log(LogLevel::Info, "DS header indicates DSi, but region is zero. Going in bad dump mode.\n"); badDSiDump = true; diff --git a/src/NDS_Header.h b/src/NDS_Header.h index 4496b957..626f80cb 100644 --- a/src/NDS_Header.h +++ b/src/NDS_Header.h @@ -22,6 +22,21 @@ #include #include "types.h" +/// Set to indicate the console regions that a ROM (including DSiWare) +/// can be played on. +enum RegionMask : u32 +{ + NoRegion = 0, + Japan = 1 << 0, + USA = 1 << 1, + Europe = 1 << 2, + Australia = 1 << 3, + China = 1 << 4, + Korea = 1 << 5, + Reserved = ~(Japan | USA | Europe | Australia | China | Korea), + RegionFree = 0xFFFFFFFF, +}; + // Consult GBATEK for info on what these are struct NDSHeader { @@ -105,7 +120,7 @@ struct NDSHeader u8 DSiMBKWriteProtect[3]; // global MBK9 setting u8 DSiWRAMCntSetting; // global WRAMCNT setting - u32 DSiRegionMask; + RegionMask DSiRegionMask; u32 DSiPermissions[2]; u8 Reserved6[3]; u8 AppFlags; // flags at 1BF diff --git a/src/OpenGLSupport.cpp b/src/OpenGLSupport.cpp index f1914fc1..5a8da116 100644 --- a/src/OpenGLSupport.cpp +++ b/src/OpenGLSupport.cpp @@ -72,9 +72,9 @@ bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* //printf("shader source:\n--\n%s\n--\n", fs); delete[] log; - FILE* logf = fopen("shaderfail.log", "w"); - fwrite(fs, len+1, 1, logf); - fclose(logf); + Platform::FileHandle* logf = Platform::OpenFile("shaderfail.log", Platform::FileMode::WriteText); + Platform::FileWrite(fs, len+1, 1, logf); + Platform::CloseFile(logf); glDeleteShader(ids[0]); glDeleteShader(ids[1]); diff --git a/src/Platform.h b/src/Platform.h index b40dce9e..a379d853 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -121,13 +121,6 @@ enum ConfigEntry DSiSD_FolderSync, DSiSD_FolderPath, - Firm_OverrideSettings [[deprecated("Individual fields can now be overridden")]], - Firm_Username, - Firm_Language, - Firm_BirthdayMonth, - Firm_BirthdayDay, - Firm_Color, - Firm_Message, Firm_MAC, WifiSettingsPath, diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 2fa0b182..0cb95740 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -207,11 +207,6 @@ int GetConfigInt(ConfigEntry entry) case DSiSD_ImageSize: return imgsizes[Config::DSiSDSize]; - case Firm_Language: return Config::FirmwareLanguage; - case Firm_BirthdayMonth: return Config::FirmwareBirthdayMonth; - case Firm_BirthdayDay: return Config::FirmwareBirthdayDay; - case Firm_Color: return Config::FirmwareFavouriteColour; - case AudioBitDepth: return Config::AudioBitDepth; #ifdef GDBSTUB_ENABLED @@ -244,7 +239,6 @@ bool GetConfigBool(ConfigEntry entry) case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0; case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0; - case Firm_OverrideSettings: return Config::FirmwareOverrideSettings != 0; case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0; #ifdef GDBSTUB_ENABLED @@ -267,8 +261,6 @@ std::string GetConfigString(ConfigEntry entry) case DSiSD_ImagePath: return Config::DSiSDPath; case DSiSD_FolderPath: return Config::DSiSDFolderPath; - case Firm_Username: return Config::FirmwareUsername; - case Firm_Message: return Config::FirmwareMessage; case WifiSettingsPath: return Config::WifiSettingsPath; } diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 206332bb..cc65dfd8 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -47,6 +47,7 @@ using std::pair; using std::string; using std::tie; using std::unique_ptr; +using std::wstring_convert; using namespace Platform; namespace ROMManager @@ -875,7 +876,7 @@ void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) UserData& currentData = firmware.EffectiveUserData(); // setting up username - std::string orig_username = Platform::GetConfigString(Platform::Firm_Username); + std::string orig_username = Config::FirmwareUsername; if (!orig_username.empty()) { // If the frontend defines a username, take it. If not, leave the existing one. std::u16string username = std::wstring_convert, char16_t>{}.from_bytes(orig_username); @@ -884,7 +885,7 @@ void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) memcpy(currentData.Nickname, username.data(), usernameLength * sizeof(char16_t)); } - auto language = static_cast(Platform::GetConfigInt(Platform::Firm_Language)); + auto language = static_cast(Config::FirmwareLanguage); if (language != Language::Reserved) { // If the frontend specifies a language (rather than using the existing value)... currentData.Settings &= ~Language::Reserved; // ..clear the existing language... @@ -892,26 +893,26 @@ void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) } // setting up color - u8 favoritecolor = Platform::GetConfigInt(Platform::Firm_Color); + u8 favoritecolor = Config::FirmwareFavouriteColour; if (favoritecolor != 0xFF) { currentData.FavoriteColor = favoritecolor; } - u8 birthmonth = Platform::GetConfigInt(Platform::Firm_BirthdayMonth); + u8 birthmonth = Config::FirmwareBirthdayMonth; 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); + u8 birthday = Config::FirmwareBirthdayDay; 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); + std::string orig_message = Config::FirmwareMessage; if (!orig_message.empty()) { std::u16string message = std::wstring_convert, char16_t>{}.from_bytes(orig_message); @@ -966,7 +967,7 @@ static Platform::FileHandle* OpenNANDFile() noexcept FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read); if (!orig) { - Log(LogLevel::Error, "Failed to open DSi NAND\n"); + Log(LogLevel::Error, "Failed to open DSi NAND from %s\n", nandpath.c_str()); return nullptr; } @@ -984,16 +985,77 @@ bool InstallNAND(const u8* es_keyY) if (!nandfile) return false; - if (auto nand = std::make_unique(nandfile, es_keyY); *nand) + DSi_NAND::NANDImage nandImage(nandfile, es_keyY); + if (!nandImage) { - DSi::NANDImage = std::move(nand); - return true; - } - else - { - DSi::NANDImage = nullptr; + Log(LogLevel::Error, "Failed to parse DSi NAND\n"); return false; } + + // scoped so that mount isn't alive when we move the NAND image to DSi::NANDImage + { + auto mount = DSi_NAND::NANDMount(nandImage); + if (!mount) + { + Log(LogLevel::Error, "Failed to mount DSi NAND\n"); + return false; + } + + DSi_NAND::DSiFirmwareSystemSettings settings {}; + if (!mount.ReadUserData(settings)) + { + Log(LogLevel::Error, "Failed to read DSi NAND user data\n"); + return false; + } + + // override user settings, if needed + if (Config::FirmwareOverrideSettings) + { + // we store relevant strings as UTF-8, so we need to convert them to UTF-16 + auto converter = wstring_convert, char16_t>{}; + + // setting up username + std::u16string username = converter.from_bytes(Config::FirmwareUsername); + size_t usernameLength = std::min(username.length(), (size_t) 10); + memset(&settings.Nickname, 0, sizeof(settings.Nickname)); + memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t)); + + // setting language + settings.Language = static_cast(Config::FirmwareLanguage); + + // setting up color + settings.FavoriteColor = Config::FirmwareFavouriteColour; + + // setting up birthday + settings.BirthdayMonth = Config::FirmwareBirthdayMonth; + settings.BirthdayDay = Config::FirmwareBirthdayDay; + + // setup message + std::u16string message = converter.from_bytes(Config::FirmwareMessage); + size_t messageLength = std::min(message.length(), (size_t) 26); + memset(&settings.Message, 0, sizeof(settings.Message)); + memcpy(&settings.Message, message.data(), messageLength * sizeof(char16_t)); + + // TODO: make other items configurable? + } + + // fix touchscreen coords + settings.TouchCalibrationADC1 = {0, 0}; + settings.TouchCalibrationPixel1 = {0, 0}; + settings.TouchCalibrationADC2 = {255 << 4, 191 << 4}; + settings.TouchCalibrationPixel2 = {255, 191}; + + settings.UpdateHash(); + + if (!mount.ApplyUserData(settings)) + { + Log(LogLevel::Error, "Failed to write patched DSi NAND user data\n"); + return false; + } + } + + DSi::NANDImage = std::make_unique(std::move(nandImage)); + return true; } bool InstallFirmware() From 9a450f5f28969cfa601ceda5f225cc1f03243374 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 30 Oct 2023 18:37:49 +0100 Subject: [PATCH 009/157] RTC revamp (#1867) * get this started * implement DSi RTC commands * set up RTC clock timer. lay down basic idea of a clock. * make the date/time registers writable * move RTC state to its own structure, to make it easier to deal with * more RTC work lay base for date/time dialog * get the bulk of the RTC functionality going * much simpler design for RTC stuff * aha, that is what it is * start working on the RTC IRQ * implement all types of RTC IRQ * start refining sleep mode. code still kinda sucks. * implement keypad IRQ * refine it some more * shut the fuck uuuuuupppppppppppppp --- src/DSi.cpp | 8 +- src/GPU.cpp | 18 + src/GPU.h | 1 + src/NDS.cpp | 312 ++++++--- src/NDS.h | 29 + src/Platform.h | 3 + src/RTC.cpp | 881 +++++++++++++++++++++---- src/RTC.h | 29 + src/SPI_Firmware.h | 2 +- src/SPU.cpp | 2 +- src/Savestate.h | 2 +- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/Config.cpp | 8 + src/frontend/qt_sdl/Config.h | 6 +- src/frontend/qt_sdl/DateTimeDialog.cpp | 91 +++ src/frontend/qt_sdl/DateTimeDialog.h | 70 ++ src/frontend/qt_sdl/DateTimeDialog.ui | 148 +++++ src/frontend/qt_sdl/Platform.cpp | 10 + src/frontend/qt_sdl/ROMManager.cpp | 15 + src/frontend/qt_sdl/main.cpp | 42 +- src/frontend/qt_sdl/main.h | 6 +- 21 files changed, 1470 insertions(+), 214 deletions(-) create mode 100644 src/frontend/qt_sdl/DateTimeDialog.cpp create mode 100644 src/frontend/qt_sdl/DateTimeDialog.h create mode 100644 src/frontend/qt_sdl/DateTimeDialog.ui diff --git a/src/DSi.cpp b/src/DSi.cpp index 15160b29..17bfb8ff 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -959,10 +959,10 @@ void RunNDMAs(u32 cpu) { if (NDS::ARM9Timestamp >= NDS::ARM9Target) return; - if (!(NDS::CPUStop & 0x80000000)) NDMAs[0]->Run(); - if (!(NDS::CPUStop & 0x80000000)) NDMAs[1]->Run(); - if (!(NDS::CPUStop & 0x80000000)) NDMAs[2]->Run(); - if (!(NDS::CPUStop & 0x80000000)) NDMAs[3]->Run(); + if (!(NDS::CPUStop & NDS::CPUStop_GXStall)) NDMAs[0]->Run(); + if (!(NDS::CPUStop & NDS::CPUStop_GXStall)) NDMAs[1]->Run(); + if (!(NDS::CPUStop & NDS::CPUStop_GXStall)) NDMAs[2]->Run(); + if (!(NDS::CPUStop & NDS::CPUStop_GXStall)) NDMAs[3]->Run(); } else { diff --git a/src/GPU.cpp b/src/GPU.cpp index 07748beb..630e88d2 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -1096,6 +1096,24 @@ void FinishFrame(u32 lines) } } +void BlankFrame() +{ + int backbuf = FrontBuffer ? 0 : 1; + int fbsize; + if (GPU3D::CurrentRenderer->Accelerated) + fbsize = (256*3 + 1) * 192; + else + fbsize = 256 * 192; + + memset(Framebuffer[backbuf][0], 0, fbsize*4); + memset(Framebuffer[backbuf][1], 0, fbsize*4); + + FrontBuffer = backbuf; + AssignFramebuffers(); + + TotalScanlines = 263; +} + void StartScanline(u32 line) { if (line == 0) diff --git a/src/GPU.h b/src/GPU.h index 96867044..cec8a2dd 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -607,6 +607,7 @@ void SetPowerCnt(u32 val); void StartFrame(); void FinishFrame(u32 lines); +void BlankFrame(); void StartScanline(u32 line); void StartHBlank(u32 line); diff --git a/src/NDS.cpp b/src/NDS.cpp index ae45d3da..832661a4 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -167,7 +167,7 @@ u32 SqrtVal[2]; u32 SqrtRes; u32 KeyInput; -u16 KeyCnt; +u16 KeyCnt[2]; u16 RCnt; bool Running; @@ -612,7 +612,8 @@ void Reset() SchedListMask = 0; KeyInput = 0x007F03FF; - KeyCnt = 0; + KeyCnt[0] = 0; + KeyCnt[1] = 0; RCnt = 0; NDSCart::Reset(); @@ -715,6 +716,7 @@ bool DoSavestate_Scheduler(Savestate* file) GPU::StartScanline, GPU::StartHBlank, GPU::FinishFrame, SPU::Mix, Wifi::USTimer, + RTC::ClockTimer, GPU::DisplayFIFO, NDSCart::ROMPrepareData, NDSCart::ROMEndTransfer, @@ -887,7 +889,7 @@ bool DoSavestate(Savestate* file) file->Bool32(&LagFrameFlag); // TODO: save KeyInput???? - file->Var16(&KeyCnt); + file->VarArray(KeyCnt, 2*sizeof(u16)); file->Var16(&RCnt); file->Var8(&WRAMCnt); @@ -1059,93 +1061,192 @@ void RunSystem(u64 timestamp) } } +u64 NextTargetSleep() +{ + u64 minEvent = UINT64_MAX; + + u32 mask = SchedListMask; + for (int i = 0; i < Event_MAX; i++) + { + if (!mask) break; + if (i == Event_SPU || i == Event_RTC) + { + if (mask & 0x1) + { + if (SchedList[i].Timestamp < minEvent) + minEvent = SchedList[i].Timestamp; + } + } + + mask >>= 1; + } + + return minEvent; +} + +void RunSystemSleep(u64 timestamp) +{ + u64 offset = timestamp - SysTimestamp; + SysTimestamp = timestamp; + + u32 mask = SchedListMask; + for (int i = 0; i < Event_MAX; i++) + { + if (!mask) break; + if (i == Event_SPU || i == Event_RTC) + { + if (mask & 0x1) + { + if (SchedList[i].Timestamp <= SysTimestamp) + { + SchedListMask &= ~(1<>= 1; + } +} + template u32 RunFrame() { FrameStartTimestamp = SysTimestamp; + GPU::TotalScanlines = 0; + LagFrameFlag = true; - bool runFrame = Running && !(CPUStop & 0x40000000); - if (runFrame) + bool runFrame = Running && !(CPUStop & CPUStop_Sleep); + while (Running) { - ARM9->CheckGdbIncoming(); - ARM7->CheckGdbIncoming(); + u64 frametarget = SysTimestamp + 560190; - GPU::StartFrame(); - - while (Running && GPU::TotalScanlines==0) + if (CPUStop & CPUStop_Sleep) { - u64 target = NextTarget(); - ARM9Target = target << ARM9ClockShift; - CurCPU = 0; + // we are running in sleep mode + // we still need to run the RTC during this mode + // we also keep outputting audio, so that frontends using audio sync don't skyrocket to 1000+FPS - if (CPUStop & 0x80000000) + while (Running && (SysTimestamp < frametarget)) { - // GXFIFO stall - s32 cycles = GPU3D::CyclesToRunFor(); + u64 target = NextTargetSleep(); + if (target > frametarget) + target = frametarget; - ARM9Timestamp = std::min(ARM9Target, ARM9Timestamp+(cycles<Run(); - if (!(CPUStop & 0x80000000)) DMAs[1]->Run(); - if (!(CPUStop & 0x80000000)) DMAs[2]->Run(); - if (!(CPUStop & 0x80000000)) DMAs[3]->Run(); - if (ConsoleType == 1) DSi::RunNDMAs(0); - } - else - { -#ifdef JIT_ENABLED - if (EnableJIT) - ARM9->ExecuteJIT(); - else -#endif - ARM9->Execute(); + ARM9Timestamp = target << ARM9ClockShift; + ARM7Timestamp = target; + TimerTimestamp[0] = target; + TimerTimestamp[1] = target; + GPU3D::Timestamp = target; + RunSystemSleep(target); + + if (!(CPUStop & CPUStop_Sleep)) + break; } - RunTimers(0); - GPU3D::Run(); + if (SysTimestamp >= frametarget) + GPU::BlankFrame(); + } + else + { + ARM9->CheckGdbIncoming(); + ARM7->CheckGdbIncoming(); - target = ARM9Timestamp >> ARM9ClockShift; - CurCPU = 1; - - while (ARM7Timestamp < target) + if (!(CPUStop & CPUStop_Wakeup)) { - ARM7Target = target; // might be changed by a reschedule + GPU::StartFrame(); + } + CPUStop &= ~CPUStop_Wakeup; - if (CPUStop & 0x0FFF0000) + while (Running && GPU::TotalScanlines==0) + { + u64 target = NextTarget(); + ARM9Target = target << ARM9ClockShift; + CurCPU = 0; + + if (CPUStop & CPUStop_GXStall) { - DMAs[4]->Run(); - DMAs[5]->Run(); - DMAs[6]->Run(); - DMAs[7]->Run(); - if (ConsoleType == 1) DSi::RunNDMAs(1); + // GXFIFO stall + s32 cycles = GPU3D::CyclesToRunFor(); + + ARM9Timestamp = std::min(ARM9Target, ARM9Timestamp+(cycles<Run(); + if (!(CPUStop & CPUStop_GXStall)) DMAs[1]->Run(); + if (!(CPUStop & CPUStop_GXStall)) DMAs[2]->Run(); + if (!(CPUStop & CPUStop_GXStall)) DMAs[3]->Run(); + if (ConsoleType == 1) DSi::RunNDMAs(0); } else { #ifdef JIT_ENABLED if (EnableJIT) - ARM7->ExecuteJIT(); + ARM9->ExecuteJIT(); else #endif - ARM7->Execute(); + ARM9->Execute(); } - RunTimers(1); - } + RunTimers(0); + GPU3D::Run(); - RunSystem(target); + target = ARM9Timestamp >> ARM9ClockShift; + CurCPU = 1; - if (CPUStop & 0x40000000) - { - // checkme: when is sleep mode effective? - CancelEvent(Event_LCD); - GPU::TotalScanlines = 263; - break; + while (ARM7Timestamp < target) + { + ARM7Target = target; // might be changed by a reschedule + + if (CPUStop & CPUStop_DMA7) + { + DMAs[4]->Run(); + DMAs[5]->Run(); + DMAs[6]->Run(); + DMAs[7]->Run(); + if (ConsoleType == 1) DSi::RunNDMAs(1); + } + else + { +#ifdef JIT_ENABLED + if (EnableJIT) + ARM7->ExecuteJIT(); + else +#endif + ARM7->Execute(); + } + + RunTimers(1); + } + + RunSystem(target); + + if (CPUStop & CPUStop_Sleep) + { + break; + } } } + if (GPU::TotalScanlines == 0) + continue; + #ifdef DEBUG_CHECK_DESYNC Log(LogLevel::Debug, "[%08X%08X] ARM9=%ld, ARM7=%ld, GPU=%ld\n", (u32)(SysTimestamp>>32), (u32)SysTimestamp, @@ -1154,6 +1255,7 @@ u32 RunFrame() GPU3D::Timestamp-SysTimestamp); #endif SPU::TransferOutput(); + break; } // In the context of TASes, frame count is traditionally the primary measure of emulated time, @@ -1162,7 +1264,7 @@ u32 RunFrame() if (LagFrameFlag) NumLagFrames++; - if (runFrame) + if (Running) return GPU::TotalScanlines; else return 263; @@ -1276,13 +1378,47 @@ void ReleaseScreen() } +void CheckKeyIRQ(u32 cpu, u32 oldkey, u32 newkey) +{ + u16 cnt = KeyCnt[cpu]; + if (!(cnt & (1<<14))) // IRQ disabled + return; + + u32 mask = (cnt & 0x03FF); + oldkey &= mask; + newkey &= mask; + + bool oldmatch, newmatch; + if (cnt & (1<<15)) + { + // logical AND + + oldmatch = (oldkey == 0); + newmatch = (newkey == 0); + } + else + { + // logical OR + + oldmatch = (oldkey != mask); + newmatch = (newkey != mask); + } + + if ((!oldmatch) && newmatch) + SetIRQ(cpu, IRQ_Keypad); +} + void SetKeyMask(u32 mask) { u32 key_lo = mask & 0x3FF; u32 key_hi = (mask >> 10) & 0x3; + u32 oldkey = KeyInput; KeyInput &= 0xFFFCFC00; KeyInput |= key_lo | (key_hi << 16); + + CheckKeyIRQ(0, oldkey, KeyInput); + CheckKeyIRQ(1, oldkey, KeyInput); } bool IsLidClosed() @@ -1301,8 +1437,6 @@ void SetLidClosed(bool closed) { KeyInput &= ~(1<<23); SetIRQ(1, IRQ_LidOpen); - CPUStop &= ~0x40000000; - GPU3D::RestartFrame(); } } @@ -1467,6 +1601,16 @@ void SetIRQ(u32 cpu, u32 irq) { IF[cpu] |= (1 << irq); UpdateIRQ(cpu); + + if ((cpu == 1) && (CPUStop & CPUStop_Sleep)) + { + if (IE[1] & (1 << irq)) + { + CPUStop &= ~CPUStop_Sleep; + CPUStop |= CPUStop_Wakeup; + GPU3D::RestartFrame(); + } + } } void ClearIRQ(u32 cpu, u32 irq) @@ -1526,9 +1670,9 @@ void ResumeCPU(u32 cpu, u32 mask) void GXFIFOStall() { - if (CPUStop & 0x80000000) return; + if (CPUStop & CPUStop_GXStall) return; - CPUStop |= 0x80000000; + CPUStop |= CPUStop_GXStall; if (CurCPU == 1) ARM9->Halt(2); else @@ -1543,14 +1687,14 @@ void GXFIFOStall() void GXFIFOUnstall() { - CPUStop &= ~0x80000000; + CPUStop &= ~CPUStop_GXStall; } void EnterSleepMode() { - if (CPUStop & 0x40000000) return; + if (CPUStop & CPUStop_Sleep) return; - CPUStop |= 0x40000000; + CPUStop |= CPUStop_Sleep; ARM7->Halt(2); } @@ -2017,7 +2161,7 @@ void debug(u32 param) // printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]); FILE* - shit = fopen("debug/crayon.bin", "wb"); + shit = fopen("debug/DSfirmware.bin", "wb"); fwrite(ARM9->ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x02400000; i+=4) { @@ -2942,8 +3086,8 @@ u8 ARM9IORead8(u32 addr) { case 0x04000130: LagFrameFlag = false; return KeyInput & 0xFF; case 0x04000131: LagFrameFlag = false; return (KeyInput >> 8) & 0xFF; - case 0x04000132: return KeyCnt & 0xFF; - case 0x04000133: return KeyCnt >> 8; + case 0x04000132: return KeyCnt[0] & 0xFF; + case 0x04000133: return KeyCnt[0] >> 8; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) @@ -3079,7 +3223,7 @@ u16 ARM9IORead16(u32 addr) case 0x0400010E: return Timers[3].Cnt; case 0x04000130: LagFrameFlag = false; return KeyInput & 0xFFFF; - case 0x04000132: return KeyCnt; + case 0x04000132: return KeyCnt[0]; case 0x04000180: return IPCSync9; case 0x04000184: @@ -3221,7 +3365,7 @@ u32 ARM9IORead32(u32 addr) case 0x04000108: return TimerGetCounter(2) | (Timers[2].Cnt << 16); case 0x0400010C: return TimerGetCounter(3) | (Timers[3].Cnt << 16); - case 0x04000130: LagFrameFlag = false; return (KeyInput & 0xFFFF) | (KeyCnt << 16); + case 0x04000130: LagFrameFlag = false; return (KeyInput & 0xFFFF) | (KeyCnt[0] << 16); case 0x04000180: return IPCSync9; case 0x04000184: return ARM9IORead16(addr); @@ -3341,10 +3485,10 @@ void ARM9IOWrite8(u32 addr, u8 val) case 0x0400106D: GPU::GPU2D_B.Write8(addr, val); return; case 0x04000132: - KeyCnt = (KeyCnt & 0xFF00) | val; + KeyCnt[0] = (KeyCnt[0] & 0xFF00) | val; return; case 0x04000133: - KeyCnt = (KeyCnt & 0x00FF) | (val << 8); + KeyCnt[0] = (KeyCnt[0] & 0x00FF) | (val << 8); return; case 0x04000188: @@ -3454,7 +3598,7 @@ void ARM9IOWrite16(u32 addr, u16 val) case 0x0400010E: TimerStart(3, val); return; case 0x04000132: - KeyCnt = val; + KeyCnt[0] = val; return; case 0x04000180: @@ -3647,7 +3791,7 @@ void ARM9IOWrite32(u32 addr, u32 val) return; case 0x04000130: - KeyCnt = val >> 16; + KeyCnt[0] = val >> 16; return; case 0x04000180: @@ -3800,8 +3944,8 @@ u8 ARM7IORead8(u32 addr) { case 0x04000130: return KeyInput & 0xFF; case 0x04000131: return (KeyInput >> 8) & 0xFF; - case 0x04000132: return KeyCnt & 0xFF; - case 0x04000133: return KeyCnt >> 8; + case 0x04000132: return KeyCnt[1] & 0xFF; + case 0x04000133: return KeyCnt[1] >> 8; case 0x04000134: return RCnt & 0xFF; case 0x04000135: return RCnt >> 8; case 0x04000136: return (KeyInput >> 16) & 0xFF; @@ -3894,7 +4038,7 @@ u16 ARM7IORead16(u32 addr) case 0x0400010E: return Timers[7].Cnt; case 0x04000130: return KeyInput & 0xFFFF; - case 0x04000132: return KeyCnt; + case 0x04000132: return KeyCnt[1]; case 0x04000134: return RCnt; case 0x04000136: return KeyInput >> 16; @@ -3986,8 +4130,8 @@ u32 ARM7IORead32(u32 addr) case 0x04000108: return TimerGetCounter(6) | (Timers[6].Cnt << 16); case 0x0400010C: return TimerGetCounter(7) | (Timers[7].Cnt << 16); - case 0x04000130: return (KeyInput & 0xFFFF) | (KeyCnt << 16); - case 0x04000134: return RCnt | (KeyCnt & 0xFFFF0000); + case 0x04000130: return (KeyInput & 0xFFFF) | (KeyCnt[1] << 16); + case 0x04000134: return RCnt | (KeyInput & 0xFFFF0000); case 0x04000138: return RTC::Read(); case 0x04000180: return IPCSync7; @@ -4068,10 +4212,10 @@ void ARM7IOWrite8(u32 addr, u8 val) switch (addr) { case 0x04000132: - KeyCnt = (KeyCnt & 0xFF00) | val; + KeyCnt[1] = (KeyCnt[1] & 0xFF00) | val; return; case 0x04000133: - KeyCnt = (KeyCnt & 0x00FF) | (val << 8); + KeyCnt[1] = (KeyCnt[1] & 0x00FF) | (val << 8); return; case 0x04000134: RCnt = (RCnt & 0xFF00) | val; @@ -4165,7 +4309,7 @@ void ARM7IOWrite16(u32 addr, u16 val) case 0x0400010C: Timers[7].Reload = val; return; case 0x0400010E: TimerStart(7, val); return; - case 0x04000132: KeyCnt = val; return; + case 0x04000132: KeyCnt[1] = val; return; case 0x04000134: RCnt = val; return; case 0x04000138: RTC::Write(val, false); return; @@ -4334,7 +4478,7 @@ void ARM7IOWrite32(u32 addr, u32 val) TimerStart(7, val>>16); return; - case 0x04000130: KeyCnt = val >> 16; return; + case 0x04000130: KeyCnt[1] = val >> 16; return; case 0x04000134: RCnt = val & 0xFFFF; return; case 0x04000138: RTC::Write(val & 0xFFFF, false); return; diff --git a/src/NDS.h b/src/NDS.h index e81e952b..de038b2b 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -37,6 +37,7 @@ enum Event_LCD = 0, Event_SPU, Event_Wifi, + Event_RTC, Event_DisplayFIFO, Event_ROMTransfer, @@ -122,6 +123,33 @@ enum IRQ2_DSi_MicExt }; +enum +{ + CPUStop_DMA9_0 = (1<<0), + CPUStop_DMA9_1 = (1<<1), + CPUStop_DMA9_2 = (1<<2), + CPUStop_DMA9_3 = (1<<3), + CPUStop_NDMA9_0 = (1<<4), + CPUStop_NDMA9_1 = (1<<5), + CPUStop_NDMA9_2 = (1<<6), + CPUStop_NDMA9_3 = (1<<7), + CPUStop_DMA9 = 0xFFF, + + CPUStop_DMA7_0 = (1<<16), + CPUStop_DMA7_1 = (1<<17), + CPUStop_DMA7_2 = (1<<18), + CPUStop_DMA7_3 = (1<<19), + CPUStop_NDMA7_0 = (1<<20), + CPUStop_NDMA7_1 = (1<<21), + CPUStop_NDMA7_2 = (1<<22), + CPUStop_NDMA7_3 = (1<<23), + CPUStop_DMA7 = (0xFFF<<16), + + CPUStop_Wakeup = (1<<29), + CPUStop_Sleep = (1<<30), + CPUStop_GXStall = (1<<31), +}; + struct Timer { u16 Reload; @@ -219,6 +247,7 @@ extern MemRegion SWRAM_ARM9; extern MemRegion SWRAM_ARM7; extern u32 KeyInput; +extern u16 RCnt; const u32 ARM7WRAMSize = 0x10000; extern u8* ARM7WRAM; diff --git a/src/Platform.h b/src/Platform.h index a379d853..144fce19 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -337,6 +337,9 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen /// @param writelen The number of bytes that were written to firmware. void WriteFirmware(const SPI_Firmware::Firmware& firmware, u32 writeoffset, u32 writelen); +// called when the RTC date/time is changed and the frontend might need to take it into account +void WriteDateTime(int year, int month, int day, int hour, int minute, int second); + // local multiplayer comm interface // packet type: DS-style TX header (12 bytes) + original 802.11 frame diff --git a/src/RTC.cpp b/src/RTC.cpp index 94d7ae7c..021cde72 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -20,7 +20,7 @@ #define _POSIX_THREAD_SAFE_FUNCTIONS #include -#include +#include "NDS.h" #include "RTC.h" #include "Platform.h" @@ -45,16 +45,23 @@ u32 OutputPos; u8 CurCmd; -u8 StatusReg1; -u8 StatusReg2; -u8 Alarm1[3]; -u8 Alarm2[3]; -u8 ClockAdjust; -u8 FreeReg; +StateData State; + +s32 TimerError; +u32 ClockCount; + + +void WriteDateTime(int num, u8 val); bool Init() { + ResetState(); + + // indicate the power was off + // this will be changed if a previously saved RTC state is loaded + State.StatusReg1 = 0x80; + return true; } @@ -73,12 +80,8 @@ void Reset() CurCmd = 0; - StatusReg1 = 0; - StatusReg2 = 0; - memset(Alarm1, 0, sizeof(Alarm1)); - memset(Alarm2, 0, sizeof(Alarm2)); - ClockAdjust = 0; - FreeReg = 0; + ClockCount = 0; + ScheduleTimer(true); } void DoSavestate(Savestate* file) @@ -97,12 +100,10 @@ void DoSavestate(Savestate* file) file->Var8(&CurCmd); - file->Var8(&StatusReg1); - file->Var8(&StatusReg2); - file->VarArray(Alarm1, sizeof(Alarm1)); - file->VarArray(Alarm2, sizeof(Alarm2)); - file->Var8(&ClockAdjust); - file->Var8(&FreeReg); + file->VarArray(&State, sizeof(State)); + + file->Var32((u32*)&TimerError); + file->Var32(&ClockCount); } @@ -111,6 +112,740 @@ u8 BCD(u8 val) return (val % 10) | ((val / 10) << 4); } +u8 BCDIncrement(u8 val) +{ + val++; + if ((val & 0x0F) >= 0x0A) + val += 0x06; + if ((val & 0xF0) >= 0xA0) + val += 0x60; + return val; +} + +u8 BCDSanitize(u8 val, u8 vmin, u8 vmax) +{ + if (val < vmin || val > vmax) + val = vmin; + else if ((val & 0x0F) >= 0x0A) + val = vmin; + else if ((val & 0xF0) >= 0xA0) + val = vmin; + + return val; +} + + +void GetState(StateData& state) +{ + memcpy(&state, &State, sizeof(State)); +} + +void SetState(StateData& state) +{ + memcpy(&State, &state, sizeof(State)); + + // sanitize the input state + + for (int i = 0; i < 7; i++) + WriteDateTime(i+1, State.DateTime[i]); +} + +void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) +{ + int val; + + val = State.DateTime[0]; + year = (val & 0xF) + ((val >> 4) * 10); + year += 2000; + + val = State.DateTime[1] & 0x3F; + month = (val & 0xF) + ((val >> 4) * 10); + + val = State.DateTime[2] & 0x3F; + day = (val & 0xF) + ((val >> 4) * 10); + + val = State.DateTime[4] & 0x3F; + hour = (val & 0xF) + ((val >> 4) * 10); + + if (!(State.StatusReg1 & (1<<1))) + { + // 12-hour mode + + if (State.DateTime[4] & 0x40) + hour += 12; + } + + val = State.DateTime[5] & 0x7F; + minute = (val & 0xF) + ((val >> 4) * 10); + + val = State.DateTime[6] & 0x7F; + second = (val & 0xF) + ((val >> 4) * 10); +} + +void SetDateTime(int year, int month, int day, int hour, int minute, int second) +{ + int monthdays[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + + // the year range of the DS RTC is limited to 2000-2099 + year %= 100; + if (year < 0) year = 0; + + if (!(year & 3)) monthdays[2] = 29; + + if (month < 1 || month > 12) month = 1; + if (day < 1 || day > monthdays[month]) day = 1; + if (hour < 0 || hour > 23) hour = 0; + if (minute < 0 || minute > 59) minute = 0; + if (second < 0 || second > 59) second = 0; + + // note on day-of-week value + // that RTC register is a simple incrementing counter and the assignation is defined by software + // DS/DSi firmware counts from 0=Sunday + + int numdays = (year * 365) + ((year+3) / 4); // account for leap years + + for (int m = 1; m < month; m++) + { + numdays += monthdays[m]; + } + numdays += (day-1); + + // 01/01/2000 is a Saturday, so the starting value is 6 + int dayofweek = (6 + numdays) % 7; + + int pm = (hour >= 12) ? 0x40 : 0; + if (!(State.StatusReg1 & (1<<1))) + { + // 12-hour mode + + if (pm) hour -= 12; + } + + State.DateTime[0] = BCD(year); + State.DateTime[1] = BCD(month); + State.DateTime[2] = BCD(day); + State.DateTime[3] = dayofweek; + State.DateTime[4] = BCD(hour) | pm; + State.DateTime[5] = BCD(minute); + State.DateTime[6] = BCD(second); + + State.StatusReg1 &= ~0x80; +} + +void ResetState() +{ + memset(&State, 0, sizeof(State)); + State.DateTime[1] = 1; + State.DateTime[2] = 1; +} + + +void SetIRQ(u8 irq) +{ + u8 oldstat = State.IRQFlag; + State.IRQFlag |= irq; + State.StatusReg1 |= irq; + + if ((!(oldstat & 0x30)) && (State.IRQFlag & 0x30)) + { + if ((NDS::RCnt & 0xC100) == 0x8100) + { + // CHECKME: is the IRQ status readable in RCNT? + NDS::SetIRQ(1, NDS::IRQ_RTC); + } + } +} + +void ClearIRQ(u8 irq) +{ + State.IRQFlag &= ~irq; +} + +void ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write +{ + // INT1 + + switch (State.StatusReg2 & 0x0F) + { + case 0b0000: // none + if (type == 2) + { + ClearIRQ(0x10); + } + break; + + case 0b0001: + case 0b0101: // selected frequency steady interrupt + if ((type == 1 && (!(ClockCount & 0x3FF))) || (type == 2)) + { + u32 mask = 0; + if (State.Alarm1[2] & (1<<0)) mask |= 0x4000; + if (State.Alarm1[2] & (1<<1)) mask |= 0x2000; + if (State.Alarm1[2] & (1<<2)) mask |= 0x1000; + if (State.Alarm1[2] & (1<<3)) mask |= 0x0800; + if (State.Alarm1[2] & (1<<4)) mask |= 0x0400; + + if (mask && ((ClockCount & mask) != mask)) + SetIRQ(0x10); + else + ClearIRQ(0x10); + } + break; + + case 0b0010: + case 0b0110: // per-minute edge interrupt + if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01))) + { + SetIRQ(0x10); + } + break; + + case 0b0011: // per-minute steady interrupt 1 (duty 30s) + if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01))) + { + SetIRQ(0x10); + } + else if ((type == 1) && (State.DateTime[6] == 0x30)) + { + ClearIRQ(0x10); + } + break; + + case 0b0111: // per-minute steady interrupt 2 (duty 256 cycles) + if ((type == 0) || (type == 2 && (State.IRQFlag & 0x01))) + { + SetIRQ(0x10); + } + else if ((type == 1) && (State.DateTime[6] == 0x00) && ((ClockCount & 0x7FFF) == 256)) + { + ClearIRQ(0x10); + } + break; + + case 0b0100: // alarm interrupt + if (type == 0) + { + bool cond = true; + if (State.Alarm1[0] & (1<<7)) + cond = cond && ((State.Alarm1[0] & 0x07) == State.DateTime[3]); + if (State.Alarm1[1] & (1<<7)) + cond = cond && ((State.Alarm1[1] & 0x7F) == State.DateTime[4]); + if (State.Alarm1[2] & (1<<7)) + cond = cond && ((State.Alarm1[2] & 0x7F) == State.DateTime[5]); + + if (cond) + SetIRQ(0x10); + else + ClearIRQ(0x10); + } + break; + + default: // 32KHz output + if (type == 1) + { + SetIRQ(0x10); + ClearIRQ(0x10); + } + break; + } + + // INT2 + + if (State.StatusReg2 & (1<<6)) + { + // alarm interrupt + + if (type == 0) + { + bool cond = true; + if (State.Alarm2[0] & (1<<7)) + cond = cond && ((State.Alarm2[0] & 0x07) == State.DateTime[3]); + if (State.Alarm2[1] & (1<<7)) + cond = cond && ((State.Alarm2[1] & 0x7F) == State.DateTime[4]); + if (State.Alarm2[2] & (1<<7)) + cond = cond && ((State.Alarm2[2] & 0x7F) == State.DateTime[5]); + + if (cond) + SetIRQ(0x20); + else + ClearIRQ(0x20); + } + } + else + { + if (type == 2) + { + ClearIRQ(0x20); + } + } +} + + +u8 DaysInMonth() +{ + u8 numdays; + + switch (State.DateTime[1]) + { + case 0x01: // Jan + case 0x03: // Mar + case 0x05: // May + case 0x07: // Jul + case 0x08: // Aug + case 0x10: // Oct + case 0x12: // Dec + numdays = 0x31; + break; + + case 0x04: // Apr + case 0x06: // Jun + case 0x09: // Sep + case 0x11: // Nov + numdays = 0x30; + break; + + case 0x02: // Feb + { + numdays = 0x28; + + // leap year: if year divisible by 4 and not divisible by 100 unless divisible by 400 + // the limited year range (2000-2099) simplifies this + int year = State.DateTime[0]; + year = (year & 0xF) + ((year >> 4) * 10); + if (!(year & 3)) + numdays = 0x29; + } + break; + + default: // ??? + return 0; + } + + return numdays; +} + +void CountYear() +{ + State.DateTime[0] = BCDIncrement(State.DateTime[0]); +} + +void CountMonth() +{ + State.DateTime[1] = BCDIncrement(State.DateTime[1]); + if (State.DateTime[1] > 0x12) + { + State.DateTime[1] = 1; + CountYear(); + } +} + +void CheckEndOfMonth() +{ + if (State.DateTime[2] > DaysInMonth()) + { + State.DateTime[2] = 1; + CountMonth(); + } +} + +void CountDay() +{ + // day-of-week counter + State.DateTime[3]++; + if (State.DateTime[3] >= 7) + State.DateTime[3] = 0; + + // day counter + State.DateTime[2] = BCDIncrement(State.DateTime[2]); + CheckEndOfMonth(); +} + +void CountHour() +{ + u8 hour = BCDIncrement(State.DateTime[4] & 0x3F); + u8 pm = State.DateTime[4] & 0x40; + + if (State.StatusReg1 & (1<<1)) + { + // 24-hour mode + + if (hour >= 0x24) + { + hour = 0; + CountDay(); + } + + pm = (hour >= 0x12) ? 0x40 : 0; + } + else + { + // 12-hour mode + + if (hour >= 0x12) + { + hour = 0; + if (pm) CountDay(); + pm ^= 0x40; + } + } + + State.DateTime[4] = hour | pm; +} + +void CountMinute() +{ + State.MinuteCount++; + State.DateTime[5] = BCDIncrement(State.DateTime[5]); + if (State.DateTime[5] >= 0x60) + { + State.DateTime[5] = 0; + CountHour(); + } + + State.IRQFlag |= 0x01; // store minute carry flag + ProcessIRQ(0); +} + +void CountSecond() +{ + State.DateTime[6] = BCDIncrement(State.DateTime[6]); + if (State.DateTime[6] >= 0x60) + { + State.DateTime[6] = 0; + CountMinute(); + } +} + + +void ScheduleTimer(bool first) +{ + if (first) TimerError = 0; + + // the RTC clock runs at 32768Hz + // cycles = 33513982 / 32768 + s32 sysclock = 33513982 + TimerError; + s32 delay = sysclock >> 15; + TimerError = sysclock & 0x7FFF; + + NDS::ScheduleEvent(NDS::Event_RTC, !first, delay, ClockTimer, 0); +} + +void ClockTimer(u32 param) +{ + ClockCount++; + + if (!(ClockCount & 0x7FFF)) + { + // count up one second + CountSecond(); + } + else if ((ClockCount & 0x7FFF) == 4) + { + // minute-carry flag lasts 4 cycles + State.IRQFlag &= ~0x01; + } + + ProcessIRQ(1); + + ScheduleTimer(false); +} + + +void WriteDateTime(int num, u8 val) +{ + switch (num) + { + case 1: // year + State.DateTime[0] = BCDSanitize(val, 0x00, 0x99); + break; + + case 2: // month + State.DateTime[1] = BCDSanitize(val & 0x1F, 0x01, 0x12); + break; + + case 3: // day + State.DateTime[2] = BCDSanitize(val & 0x3F, 0x01, 0x31); + CheckEndOfMonth(); + break; + + case 4: // day of week + State.DateTime[3] = BCDSanitize(val & 0x07, 0x00, 0x06); + break; + + case 5: // hour + { + u8 hour = val & 0x3F; + u8 pm = val & 0x40; + + if (State.StatusReg1 & (1<<1)) + { + // 24-hour mode + + hour = BCDSanitize(hour, 0x00, 0x23); + pm = (hour >= 0x12) ? 0x40 : 0; + } + else + { + // 12-hour mode + + hour = BCDSanitize(hour, 0x00, 0x11); + } + + State.DateTime[4] = hour | pm; + } + break; + + case 6: // minute + State.DateTime[5] = BCDSanitize(val & 0x7F, 0x00, 0x59); + break; + + case 7: // second + State.DateTime[6] = BCDSanitize(val & 0x7F, 0x00, 0x59); + break; + } +} + +void SaveDateTime() +{ + int y, m, d, h, i, s; + GetDateTime(y, m, d, h, i, s); + Platform::WriteDateTime(y, m, d, h, i, s); +} + +void CmdRead() +{ + if ((CurCmd & 0x0F) == 0x06) + { + switch (CurCmd & 0x70) + { + case 0x00: + Output[0] = State.StatusReg1; + State.StatusReg1 &= 0x0F; // clear auto-clearing bit4-7 + break; + + case 0x40: + Output[0] = State.StatusReg2; + break; + + case 0x20: + memcpy(Output, &State.DateTime[0], 7); + break; + + case 0x60: + memcpy(Output, &State.DateTime[4], 3); + break; + + case 0x10: + if (State.StatusReg2 & 0x04) + memcpy(Output, &State.Alarm1[0], 3); + else + Output[0] = State.Alarm1[2]; + break; + + case 0x50: + memcpy(Output, &State.Alarm2[0], 3); + break; + + case 0x30: Output[0] = State.ClockAdjust; break; + case 0x70: Output[0] = State.FreeReg; break; + } + + return; + } + else if ((CurCmd & 0x0F) == 0x0E) + { + if (NDS::ConsoleType != 1) + { + Log(LogLevel::Debug, "RTC: unknown read command %02X\n", CurCmd); + return; + } + + switch (CurCmd & 0x70) + { + case 0x00: + Output[0] = (State.MinuteCount >> 16) & 0xFF; + Output[1] = (State.MinuteCount >> 8) & 0xFF; + Output[2] = State.MinuteCount & 0xFF; + break; + + case 0x40: Output[0] = State.FOUT1; break; + case 0x20: Output[0] = State.FOUT2; break; + + case 0x10: + memcpy(Output, &State.AlarmDate1[0], 3); + break; + + case 0x50: + memcpy(Output, &State.AlarmDate2[0], 3); + break; + + default: + Log(LogLevel::Debug, "RTC: unknown read command %02X\n", CurCmd); + break; + } + + return; + } + + Log(LogLevel::Debug, "RTC: unknown read command %02X\n", CurCmd); +} + +void CmdWrite(u8 val) +{ + if ((CurCmd & 0x0F) == 0x06) + { + switch (CurCmd & 0x70) + { + case 0x00: + if (InputPos == 1) + { + u8 oldval = State.StatusReg1; + + if (val & (1<<0)) // reset + ResetState(); + + State.StatusReg1 = (State.StatusReg1 & 0xF0) | (val & 0x0E); + + if ((State.StatusReg1 ^ oldval) & (1<<1)) + { + // AM/PM changed + + u8 hour = State.DateTime[4] & 0x3F; + u8 pm = State.DateTime[4] & 0x40; + + if (State.StatusReg1 & (1<<1)) + { + // 24-hour mode + + if (pm) + { + hour += 0x12; + if ((hour & 0x0F) >= 0x0A) + hour += 0x06; + } + + hour = BCDSanitize(hour, 0x00, 0x23); + } + else + { + // 12-hour mode + + if (hour >= 0x12) + { + pm = 0x40; + + hour -= 0x12; + if ((hour & 0x0F) >= 0x0A) + hour -= 0x06; + } + else + pm = 0; + + hour = BCDSanitize(hour, 0x00, 0x11); + } + + State.DateTime[4] = hour | pm; + } + } + break; + + case 0x40: + if (InputPos == 1) + { + State.StatusReg2 = val; + ProcessIRQ(2); + } + break; + + case 0x20: + if (InputPos <= 7) + WriteDateTime(InputPos, val); + if (InputPos == 7) + SaveDateTime(); + break; + + case 0x60: + if (InputPos <= 3) + WriteDateTime(InputPos+4, val); + if (InputPos == 3) + SaveDateTime(); + break; + + case 0x10: + if (State.StatusReg2 & 0x04) + { + if (InputPos <= 3) + State.Alarm1[InputPos-1] = val; + } + else + { + if (InputPos == 1) + State.Alarm1[2] = val; + } + break; + + case 0x50: + if (InputPos <= 3) + State.Alarm2[InputPos-1] = val; + break; + + case 0x30: + if (InputPos == 1) + State.ClockAdjust = val; + break; + + case 0x70: + if (InputPos == 1) + State.FreeReg = val; + break; + } + + return; + } + else if ((CurCmd & 0x0F) == 0x0E) + { + if (NDS::ConsoleType != 1) + { + Log(LogLevel::Debug, "RTC: unknown write command %02X\n", CurCmd); + return; + } + + switch (CurCmd & 0x70) + { + case 0x00: + Log(LogLevel::Debug, "RTC: trying to write read-only minute counter\n"); + break; + + case 0x40: + if (InputPos == 1) + State.FOUT1 = val; + break; + + case 0x20: + if (InputPos == 1) + State.FOUT2 = val; + break; + + case 0x10: + if (InputPos <= 3) + State.AlarmDate1[InputPos-1] = val; + break; + + case 0x50: + if (InputPos <= 3) + State.AlarmDate2[InputPos-1] = val; + break; + + default: + Log(LogLevel::Debug, "RTC: unknown write command %02X\n", CurCmd); + break; + } + + return; + } + + Log(LogLevel::Debug, "RTC: unknown write command %02X\n", CurCmd); +} void ByteIn(u8 val) { @@ -124,107 +859,25 @@ void ByteIn(u8 val) else CurCmd = val; + if (NDS::ConsoleType == 1) + { + // for DSi: handle extra commands + + if (((CurCmd & 0xF0) == 0x70) && ((CurCmd & 0xFE) != 0x76)) + { + u8 rev[16] = {0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE}; + CurCmd = rev[CurCmd & 0xF]; + } + } + if (CurCmd & 0x80) { - switch (CurCmd & 0x70) - { - case 0x00: Output[0] = StatusReg1; break; - case 0x40: Output[0] = StatusReg2; break; - - case 0x20: - { - time_t timestamp = time(NULL); - struct tm timedata; - localtime_r(×tamp, &timedata); - - Output[0] = BCD(timedata.tm_year - 100); - Output[1] = BCD(timedata.tm_mon + 1); - Output[2] = BCD(timedata.tm_mday); - Output[3] = BCD(timedata.tm_wday); - Output[4] = BCD(timedata.tm_hour); - Output[5] = BCD(timedata.tm_min); - Output[6] = BCD(timedata.tm_sec); - } - break; - - case 0x60: - { - time_t timestamp = time(NULL); - struct tm timedata; - localtime_r(×tamp, &timedata); - - Output[0] = BCD(timedata.tm_hour); - Output[1] = BCD(timedata.tm_min); - Output[2] = BCD(timedata.tm_sec); - } - break; - - case 0x10: - if (StatusReg2 & 0x04) - { - Output[0] = Alarm1[0]; - Output[1] = Alarm1[1]; - Output[2] = Alarm1[2]; - } - else - Output[0] = Alarm1[2]; - break; - - case 0x50: - Output[0] = Alarm2[0]; - Output[1] = Alarm2[1]; - Output[2] = Alarm2[2]; - break; - - case 0x30: Output[0] = ClockAdjust; break; - case 0x70: Output[0] = FreeReg; break; - } + CmdRead(); } return; } - switch (CurCmd & 0x70) - { - case 0x00: - if (InputPos == 1) StatusReg1 = val & 0x0E; - break; - - case 0x40: - if (InputPos == 1) StatusReg2 = val; - if (StatusReg2 & 0x4F) Log(LogLevel::Debug, "RTC INTERRUPT ON: %02X\n", StatusReg2); - break; - - case 0x20: - // TODO: set time somehow?? - break; - - case 0x60: - // same shit - break; - - case 0x10: - if (StatusReg2 & 0x04) - { - if (InputPos <= 3) Alarm1[InputPos-1] = val; - } - else - { - if (InputPos == 1) Alarm1[2] = val; - } - break; - - case 0x50: - if (InputPos <= 3) Alarm2[InputPos-1] = val; - break; - - case 0x30: - if (InputPos == 1) ClockAdjust = val; - break; - - case 0x70: - if (InputPos == 1) FreeReg = val; - break; - } + CmdWrite(val); } diff --git a/src/RTC.h b/src/RTC.h index 0fed9fd1..ff842711 100644 --- a/src/RTC.h +++ b/src/RTC.h @@ -25,11 +25,40 @@ namespace RTC { +struct StateData +{ + u8 StatusReg1; + u8 StatusReg2; + u8 DateTime[7]; + u8 Alarm1[3]; + u8 Alarm2[3]; + u8 ClockAdjust; + u8 FreeReg; + + u8 IRQFlag; + + // DSi registers + u32 MinuteCount; + u8 FOUT1; + u8 FOUT2; + u8 AlarmDate1[3]; + u8 AlarmDate2[3]; +}; + bool Init(); void DeInit(); void Reset(); void DoSavestate(Savestate* file); +void GetState(StateData& state); +void SetState(StateData& state); +void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second); +void SetDateTime(int year, int month, int day, int hour, int minute, int second); +void ResetState(); + +void ScheduleTimer(bool first); +void ClockTimer(u32 param); + u16 Read(); void Write(u16 val, bool byte); diff --git a/src/SPI_Firmware.h b/src/SPI_Firmware.h index 14771b27..85576047 100644 --- a/src/SPI_Firmware.h +++ b/src/SPI_Firmware.h @@ -396,7 +396,7 @@ union UserData u8 TouchCalibrationPixel2[2]; u16 Settings; u8 Year; - u8 Unknown1; + u8 RTCClockAdjust; u32 RTCOffset; u8 Unused2[4]; u16 UpdateCounter; diff --git a/src/SPU.cpp b/src/SPU.cpp index 3939aef7..8148bac2 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -726,7 +726,7 @@ void Mix(u32 dummy) s32 left = 0, right = 0; s32 leftoutput = 0, rightoutput = 0; - if (Cnt & (1<<15)) + if ((Cnt & (1<<15)) && (!dummy)) { s32 ch0 = Channels[0]->DoRun(); s32 ch1 = Channels[1]->DoRun(); diff --git a/src/Savestate.h b/src/Savestate.h index 235d1fbc..6707f9f4 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -24,7 +24,7 @@ #include #include "types.h" -#define SAVESTATE_MAJOR 10 +#define SAVESTATE_MAJOR 11 #define SAVESTATE_MINOR 1 class Savestate diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 3923f379..cdc96dd5 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES_QT_SDL main_shaders.h CheatsDialog.cpp Config.cpp + DateTimeDialog.cpp EmuSettingsDialog.cpp PowerManagement/PowerManagementDialog.cpp PowerManagement/resources/battery.qrc diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index da08c285..c28f63af 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "Platform.h" #include "Config.h" @@ -140,6 +141,8 @@ int MouseHideSeconds; bool PauseLostFocus; +int64_t RTCOffset; + bool DSBatteryLevelOkay; int DSiBatteryLevel; bool DSiBatteryCharging; @@ -339,6 +342,8 @@ ConfigEntry ConfigFile[] = {"MouseHideSeconds", 0, &MouseHideSeconds, 5, false}, {"PauseLostFocus", 1, &PauseLostFocus, false, false}, + {"RTCOffset", 3, &RTCOffset, (int64_t)0, true}, + {"DSBatteryLevelOkay", 1, &DSBatteryLevelOkay, true, true}, {"DSiBatteryLevel", 0, &DSiBatteryLevel, 0xF, true}, {"DSiBatteryCharging", 1, &DSiBatteryCharging, true, true}, @@ -406,6 +411,7 @@ void LoadFile(int inst) case 0: *(int*)entry->Value = strtol(entryval, NULL, 10); break; case 1: *(bool*)entry->Value = strtol(entryval, NULL, 10) ? true:false; break; case 2: *(std::string*)entry->Value = entryval; break; + case 3: *(int64_t*)entry->Value = strtoll(entryval, NULL, 10); break; } break; @@ -426,6 +432,7 @@ void Load() case 0: *(int*)entry->Value = std::get(entry->Default); break; case 1: *(bool*)entry->Value = std::get(entry->Default); break; case 2: *(std::string*)entry->Value = std::get(entry->Default); break; + case 3: *(int64_t*)entry->Value = std::get(entry->Default); break; } } @@ -462,6 +469,7 @@ void Save() case 0: Platform::FileWriteFormatted(f, "%s=%d\r\n", entry->Name, *(int*)entry->Value); break; case 1: Platform::FileWriteFormatted(f, "%s=%d\r\n", entry->Name, *(bool*)entry->Value ? 1:0); break; case 2: Platform::FileWriteFormatted(f, "%s=%s\r\n", entry->Name, (*(std::string*)entry->Value).c_str()); break; + case 3: Platform::FileWriteFormatted(f, "%s=%" PRId64 "\r\n", entry->Name, *(int64_t*)entry->Value); break; } } diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index b1d9532e..fba9bfb0 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -57,9 +57,9 @@ namespace Config struct ConfigEntry { char Name[32]; - int Type; // 0=int 1=bool 2=string + int Type; // 0=int 1=bool 2=string 3=64bit int void* Value; // pointer to the value variable - std::variant Default; + std::variant Default; bool InstanceUnique; // whether the setting can exist individually for each instance in multiplayer }; @@ -185,6 +185,8 @@ extern bool MouseHide; extern int MouseHideSeconds; extern bool PauseLostFocus; +extern int64_t RTCOffset; + extern bool DSBatteryLevelOkay; extern int DSiBatteryLevel; extern bool DSiBatteryCharging; diff --git a/src/frontend/qt_sdl/DateTimeDialog.cpp b/src/frontend/qt_sdl/DateTimeDialog.cpp new file mode 100644 index 00000000..2ef96881 --- /dev/null +++ b/src/frontend/qt_sdl/DateTimeDialog.cpp @@ -0,0 +1,91 @@ +/* + Copyright 2016-2022 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 + +#include "types.h" +#include "Config.h" + +#include "DateTimeDialog.h" +#include "ui_DateTimeDialog.h" + +DateTimeDialog* DateTimeDialog::currentDlg = nullptr; + + +DateTimeDialog::DateTimeDialog(QWidget* parent) : QDialog(parent), ui(new Ui::DateTimeDialog) +{ + ui->setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + + QDateTime now = QDateTime::currentDateTime(); + customTime = now.addSecs(Config::RTCOffset); + + ui->chkChangeTime->setChecked(false); + ui->chkResetTime->setChecked(false); + + ui->lblCustomTime->setText(customTime.toString(ui->txtNewCustomTime->displayFormat())); + startTimer(1000); + + ui->txtNewCustomTime->setEnabled(ui->chkChangeTime->isChecked()); +} + +DateTimeDialog::~DateTimeDialog() +{ + delete ui; +} + +void DateTimeDialog::timerEvent(QTimerEvent* event) +{ + customTime = customTime.addSecs(1); + ui->lblCustomTime->setText(customTime.toString(ui->txtNewCustomTime->displayFormat())); +} + +void DateTimeDialog::done(int r) +{ + if (r == QDialog::Accepted) + { + if (ui->chkChangeTime->isChecked()) + { + QDateTime now = QDateTime::currentDateTime(); + Config::RTCOffset = now.secsTo(ui->txtNewCustomTime->dateTime()); + } + else if (ui->chkResetTime->isChecked()) + Config::RTCOffset = 0; + + Config::Save(); + } + + QDialog::done(r); + + closeDlg(); +} + +void DateTimeDialog::on_chkChangeTime_clicked(bool checked) +{ + if (checked) ui->chkResetTime->setChecked(false); + ui->txtNewCustomTime->setEnabled(checked); +} + +void DateTimeDialog::on_chkResetTime_clicked(bool checked) +{ + if (checked) + { + ui->chkChangeTime->setChecked(false); + ui->txtNewCustomTime->setEnabled(false); + } +} diff --git a/src/frontend/qt_sdl/DateTimeDialog.h b/src/frontend/qt_sdl/DateTimeDialog.h new file mode 100644 index 00000000..f13dbd09 --- /dev/null +++ b/src/frontend/qt_sdl/DateTimeDialog.h @@ -0,0 +1,70 @@ +/* + Copyright 2016-2022 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 DATETIMEDIALOG_H +#define DATETIMEDIALOG_H + +#include +#include +#include + +namespace Ui {class DateTimeDialog; } +class DateTimeDialog; + +class DateTimeDialog : public QDialog +{ + Q_OBJECT + +public: + explicit DateTimeDialog(QWidget* parent); + ~DateTimeDialog(); + + static DateTimeDialog* currentDlg; + static DateTimeDialog* openDlg(QWidget* parent) + { + if (currentDlg) + { + currentDlg->activateWindow(); + return currentDlg; + } + + currentDlg = new DateTimeDialog(parent); + currentDlg->open(); + return currentDlg; + } + static void closeDlg() + { + currentDlg = nullptr; + } + +protected: + void timerEvent(QTimerEvent* event) override; + +private slots: + void done(int r); + + void on_chkChangeTime_clicked(bool checked); + void on_chkResetTime_clicked(bool checked); + +private: + Ui::DateTimeDialog* ui; + + QDateTime customTime; +}; + +#endif // DATETIMEDIALOG_H diff --git a/src/frontend/qt_sdl/DateTimeDialog.ui b/src/frontend/qt_sdl/DateTimeDialog.ui new file mode 100644 index 00000000..59eb677e --- /dev/null +++ b/src/frontend/qt_sdl/DateTimeDialog.ui @@ -0,0 +1,148 @@ + + + DateTimeDialog + + + + 0 + 0 + 357 + 161 + + + + Date and time - melonDS + + + + + + Date and time + + + + + + Current value: + + + + + + + [placeholder] + + + + + + + + + + + + + + Change to: + + + + + + + + 0 + 0 + 0 + 2000 + 1 + 1 + + + + + 23 + 59 + 59 + 2099 + 12 + 31 + + + + + 0 + 0 + 0 + 2000 + 1 + 1 + + + + dd/MM/yyyy HH:mm:ss + + + true + + + + + + + Reset to system date and time + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + DateTimeDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + DateTimeDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 0cb95740..252594c7 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -610,6 +611,15 @@ void WriteFirmware(const SPI_Firmware::Firmware& firmware, u32 writeoffset, u32 } +void WriteDateTime(int year, int month, int day, int hour, int minute, int second) +{ + QDateTime hosttime = QDateTime::currentDateTime(); + QDateTime time = QDateTime(QDate(year, month, day), QTime(hour, minute, second)); + + Config::RTCOffset = hosttime.secsTo(time); + Config::Save(); +} + bool MP_Init() { return LocalMP::Init(); diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index cc65dfd8..648ab676 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -28,6 +28,8 @@ #include #include +#include + #include #ifdef ARCHIVE_SUPPORT_ENABLED #include "ArchiveUtil.h" @@ -39,6 +41,7 @@ #include "NDS.h" #include "DSi.h" #include "SPI.h" +#include "RTC.h" #include "DSi_I2C.h" #include "FreeBIOS.h" @@ -589,6 +592,15 @@ void SetBatteryLevels() } } +void SetDateTime() +{ + QDateTime hosttime = QDateTime::currentDateTime(); + QDateTime time = hosttime.addSecs(Config::RTCOffset); + + RTC::SetDateTime(time.date().year(), time.date().month(), time.date().day(), + time.time().hour(), time.time().minute(), time.time().second()); +} + void Reset() { NDS::SetConsoleType(Config::ConsoleType); @@ -602,6 +614,7 @@ void Reset() } NDS::Reset(); SetBatteryLevels(); + SetDateTime(); if ((CartType != -1) && NDSSave) { @@ -678,6 +691,7 @@ bool LoadBIOS() NDS::Reset(); SetBatteryLevels(); + SetDateTime(); return true; } @@ -1204,6 +1218,7 @@ bool LoadROM(QStringList filepath, bool reset) NDS::Reset(); SetBatteryLevels(); + SetDateTime(); } u32 savelen = 0; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a6cc2482..f6704779 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -58,6 +58,7 @@ #include "main.h" #include "Input.h" #include "CheatsDialog.h" +#include "DateTimeDialog.h" #include "EmuSettingsDialog.h" #include "InputConfig/InputConfigDialog.h" #include "VideoSettingsDialog.h" @@ -90,6 +91,7 @@ #include "LocalMP.h" #include "Config.h" #include "DSi_I2C.h" +#include "RTC.h" #include "Savestate.h" @@ -313,6 +315,7 @@ void EmuThread::deinitOpenGL() void EmuThread::run() { u32 mainScreenPos[3]; + Platform::FileHandle* file; NDS::Init(); @@ -352,6 +355,15 @@ void EmuThread::run() u32 winUpdateCount = 0, winUpdateFreq = 1; u8 dsiVolumeLevel = 0x1F; + file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read); + if (file) + { + RTC::StateData state; + Platform::FileRead(&state, sizeof(state), 1, file); + Platform::CloseFile(file); + RTC::SetState(state); + } + char melontitle[100]; while (EmuRunning != emuStatus_Exit) @@ -650,6 +662,15 @@ void EmuThread::run() } } + file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write); + if (file) + { + RTC::StateData state; + RTC::GetState(state); + Platform::FileWrite(&state, sizeof(state), 1, file); + Platform::CloseFile(file); + } + EmuStatus = emuStatus_Exit; GPU::DeInitRenderer(); @@ -1525,6 +1546,9 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actPowerManagement = menu->addAction("Power management"); connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); + actDateTime = menu->addAction("Date and time"); + connect(actDateTime, &QAction::triggered, this, &MainWindow::onOpenDateTime); + menu->addSeparator(); actEnableCheats = menu->addAction("Enable cheats"); @@ -1787,6 +1811,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) actStop->setEnabled(false); actFrameStep->setEnabled(false); + actDateTime->setEnabled(true); actPowerManagement->setEnabled(false); actSetupCheats->setEnabled(false); @@ -2737,6 +2762,16 @@ void MainWindow::onFrameStep() emuThread->emuFrameStep(); } +void MainWindow::onOpenDateTime() +{ + DateTimeDialog* dlg = DateTimeDialog::openDlg(this); +} + +void MainWindow::onOpenPowerManagement() +{ + PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this); +} + void MainWindow::onEnableCheats(bool checked) { Config::EnableCheats = checked?1:0; @@ -2824,11 +2859,6 @@ void MainWindow::onEmuSettingsDialogFinished(int res) actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); } -void MainWindow::onOpenPowerManagement() -{ - PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this); -} - void MainWindow::onOpenInputConfig() { emuThread->emuPause(); @@ -3148,6 +3178,7 @@ void MainWindow::onEmuStart() actStop->setEnabled(true); actFrameStep->setEnabled(true); + actDateTime->setEnabled(false); actPowerManagement->setEnabled(true); actTitleManager->setEnabled(false); @@ -3169,6 +3200,7 @@ void MainWindow::onEmuStop() actStop->setEnabled(false); actFrameStep->setEnabled(false); + actDateTime->setEnabled(true); actPowerManagement->setEnabled(false); actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 073a4da0..5832ed3b 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -299,6 +299,8 @@ private slots: void onReset(); void onStop(); void onFrameStep(); + void onOpenPowerManagement(); + void onOpenDateTime(); void onEnableCheats(bool checked); void onSetupCheats(); void onCheatsDialogFinished(int res); @@ -309,7 +311,6 @@ private slots: void onOpenEmuSettings(); void onEmuSettingsDialogFinished(int res); - void onOpenPowerManagement(); void onOpenInputConfig(); void onInputConfigFinished(int res); void onOpenVideoSettings(); @@ -397,6 +398,8 @@ public: QAction* actReset; QAction* actStop; QAction* actFrameStep; + QAction* actPowerManagement; + QAction* actDateTime; QAction* actEnableCheats; QAction* actSetupCheats; QAction* actROMInfo; @@ -408,7 +411,6 @@ public: #ifdef __APPLE__ QAction* actPreferences; #endif - QAction* actPowerManagement; QAction* actInputConfig; QAction* actVideoSettings; QAction* actCameraSettings; From e89b8a871bc40a3902bfa6be620fba3dea9dbaf3 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 30 Oct 2023 18:40:26 +0100 Subject: [PATCH 010/157] fdsfd --- src/frontend/qt_sdl/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index cdc96dd5..40815638 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -7,7 +7,7 @@ set(SOURCES_QT_SDL main_shaders.h CheatsDialog.cpp Config.cpp - DateTimeDialog.cpp + DateTimeDialog.cpp EmuSettingsDialog.cpp PowerManagement/PowerManagementDialog.cpp PowerManagement/resources/battery.qrc From b8963b07384a273e446760274662e8819af37c58 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Mon, 30 Oct 2023 19:37:33 +0100 Subject: [PATCH 011/157] use a grid layout in the date/time dialog so Qt will behave --- src/frontend/qt_sdl/DateTimeDialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/DateTimeDialog.ui b/src/frontend/qt_sdl/DateTimeDialog.ui index 59eb677e..80b8d56e 100644 --- a/src/frontend/qt_sdl/DateTimeDialog.ui +++ b/src/frontend/qt_sdl/DateTimeDialog.ui @@ -19,7 +19,7 @@ Date and time - + From 3b4fdea376ee23c2bee05a421a12b95a54e22274 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 1 Nov 2023 12:03:35 +0100 Subject: [PATCH 012/157] minor cleanup --- src/RTC.cpp | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/RTC.cpp b/src/RTC.cpp index 021cde72..ff403222 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -16,9 +16,6 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ -// Required by MinGW to enable localtime_r in time.h -#define _POSIX_THREAD_SAFE_FUNCTIONS - #include #include "NDS.h" #include "RTC.h" @@ -112,6 +109,11 @@ u8 BCD(u8 val) return (val % 10) | ((val / 10) << 4); } +u8 FromBCD(u8 val) +{ + return (val & 0xF) + ((val >> 4) * 10); +} + u8 BCDIncrement(u8 val) { val++; @@ -152,20 +154,12 @@ void SetState(StateData& state) void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) { - int val; - - val = State.DateTime[0]; - year = (val & 0xF) + ((val >> 4) * 10); + year = FromBCD(State.DateTime[0]); year += 2000; + month = FromBCD(State.DateTime[1] & 0x3F); + day = FromBCD(State.DateTime[2] & 0x3F); - val = State.DateTime[1] & 0x3F; - month = (val & 0xF) + ((val >> 4) * 10); - - val = State.DateTime[2] & 0x3F; - day = (val & 0xF) + ((val >> 4) * 10); - - val = State.DateTime[4] & 0x3F; - hour = (val & 0xF) + ((val >> 4) * 10); + hour = FromBCD(State.DateTime[4] & 0x3F); if (!(State.StatusReg1 & (1<<1))) { @@ -175,11 +169,8 @@ void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& s hour += 12; } - val = State.DateTime[5] & 0x7F; - minute = (val & 0xF) + ((val >> 4) * 10); - - val = State.DateTime[6] & 0x7F; - second = (val & 0xF) + ((val >> 4) * 10); + minute = FromBCD(State.DateTime[5] & 0x7F); + second = FromBCD(State.DateTime[6] & 0x7F); } void SetDateTime(int year, int month, int day, int hour, int minute, int second) @@ -305,7 +296,7 @@ void ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write { SetIRQ(0x10); } - else if ((type == 1) && (State.DateTime[6] == 0x30)) + else if ((type == 1) && (State.DateTime[6] == 0x30) && ((ClockCount & 0x7FFF) == 0)) { ClearIRQ(0x10); } From d11ba63bb0c385131eadba758345e0022ae3001a Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 1 Nov 2023 11:02:15 -0400 Subject: [PATCH 013/157] Fix compilation with the GDB stub disabled (#1863) --- src/ARM.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index 2fd7c2d4..e24dc2ff 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -68,9 +68,9 @@ void ARM::GdbCheckC() else GdbCheckB(); } #else -ARM::GdbCheckA() {} -ARM::GdbCheckB() {} -ARM::GdbCheckC() {} +void ARM::GdbCheckA() {} +void ARM::GdbCheckB() {} +void ARM::GdbCheckC() {} #endif From eb13bce6e7300ffb71f76dec11ce422e05154c28 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 2 Nov 2023 12:21:59 +0100 Subject: [PATCH 014/157] RTC: add the DSi alarm expansion registers --- src/RTC.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/RTC.cpp b/src/RTC.cpp index ff403222..3d25226c 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -324,6 +324,16 @@ void ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write if (State.Alarm1[2] & (1<<7)) cond = cond && ((State.Alarm1[2] & 0x7F) == State.DateTime[5]); + if (NDS::ConsoleType == 1) + { + if (State.AlarmDate1[1] & (1<<6)) + cond = cond && (State.AlarmDate1[0] == State.DateTime[0]); + if (State.AlarmDate1[1] & (1<<7)) + cond = cond && ((State.AlarmDate1[1] & 0x1F) == State.DateTime[1]); + if (State.AlarmDate1[2] & (1<<7)) + cond = cond && ((State.AlarmDate1[2] & 0x3F) == State.DateTime[2]); + } + if (cond) SetIRQ(0x10); else @@ -356,6 +366,16 @@ void ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write if (State.Alarm2[2] & (1<<7)) cond = cond && ((State.Alarm2[2] & 0x7F) == State.DateTime[5]); + if (NDS::ConsoleType == 1) + { + if (State.AlarmDate2[1] & (1<<6)) + cond = cond && (State.AlarmDate2[0] == State.DateTime[0]); + if (State.AlarmDate2[1] & (1<<7)) + cond = cond && ((State.AlarmDate2[1] & 0x1F) == State.DateTime[1]); + if (State.AlarmDate2[2] & (1<<7)) + cond = cond && ((State.AlarmDate2[2] & 0x3F) == State.DateTime[2]); + } + if (cond) SetIRQ(0x20); else From bff7a0d1148026876871ac2b6133823a044614dc Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 2 Nov 2023 12:40:49 +0100 Subject: [PATCH 015/157] make linebreaks in text files not weird --- src/ARCodeFile.cpp | 10 +++++----- src/FATStorage.cpp | 6 +++--- src/frontend/qt_sdl/Config.cpp | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/ARCodeFile.cpp b/src/ARCodeFile.cpp index d1f34fb6..80833e37 100644 --- a/src/ARCodeFile.cpp +++ b/src/ARCodeFile.cpp @@ -162,20 +162,20 @@ bool ARCodeFile::Save() { ARCodeCat& cat = *it; - if (it != Categories.begin()) FileWriteFormatted(f, "\r\n"); - FileWriteFormatted(f, "CAT %s\r\n\r\n", cat.Name.c_str()); + if (it != Categories.begin()) FileWriteFormatted(f, "\n"); + FileWriteFormatted(f, "CAT %s\n\n", cat.Name.c_str()); for (ARCodeList::iterator jt = cat.Codes.begin(); jt != cat.Codes.end(); jt++) { ARCode& code = *jt; - FileWriteFormatted(f, "CODE %d %s\r\n", code.Enabled, code.Name.c_str()); + FileWriteFormatted(f, "CODE %d %s\n", code.Enabled, code.Name.c_str()); for (size_t i = 0; i < code.Code.size(); i+=2) { - FileWriteFormatted(f, "%08X %08X\r\n", code.Code[i], code.Code[i + 1]); + FileWriteFormatted(f, "%08X %08X\n", code.Code[i], code.Code[i + 1]); } - FileWriteFormatted(f, "\r\n"); + FileWriteFormatted(f, "\n"); } } diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index 1d8b741d..ec72fb60 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -319,17 +319,17 @@ void FATStorage::SaveIndex() FileHandle* f = OpenLocalFile(IndexPath, FileMode::WriteText); if (!f) return; - FileWriteFormatted(f, "SIZE %" PRIu64 "\r\n", FileSize); + FileWriteFormatted(f, "SIZE %" PRIu64 "\n", FileSize); for (const auto& [key, val] : DirIndex) { - FileWriteFormatted(f, "DIR %u %s\r\n", + FileWriteFormatted(f, "DIR %u %s\n", val.IsReadOnly?1:0, val.Path.c_str()); } for (const auto& [key, val] : FileIndex) { - FileWriteFormatted(f, "FILE %u %" PRIu64 " %" PRId64 " %u %s\r\n", + FileWriteFormatted(f, "FILE %u %" PRIu64 " %" PRId64 " %u %s\n", val.IsReadOnly?1:0, val.Size, val.LastModified, val.LastModifiedInternal, val.Path.c_str()); } diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index c28f63af..b46c4bf3 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -466,10 +466,10 @@ void Save() switch (entry->Type) { - case 0: Platform::FileWriteFormatted(f, "%s=%d\r\n", entry->Name, *(int*)entry->Value); break; - case 1: Platform::FileWriteFormatted(f, "%s=%d\r\n", entry->Name, *(bool*)entry->Value ? 1:0); break; - case 2: Platform::FileWriteFormatted(f, "%s=%s\r\n", entry->Name, (*(std::string*)entry->Value).c_str()); break; - case 3: Platform::FileWriteFormatted(f, "%s=%" PRId64 "\r\n", entry->Name, *(int64_t*)entry->Value); break; + case 0: Platform::FileWriteFormatted(f, "%s=%d\n", entry->Name, *(int*)entry->Value); break; + case 1: Platform::FileWriteFormatted(f, "%s=%d\n", entry->Name, *(bool*)entry->Value ? 1:0); break; + case 2: Platform::FileWriteFormatted(f, "%s=%s\n", entry->Name, (*(std::string*)entry->Value).c_str()); break; + case 3: Platform::FileWriteFormatted(f, "%s=%" PRId64 "\n", entry->Name, *(int64_t*)entry->Value); break; } } From 2959d089fecb1a56261052b3733be280536b24f9 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 2 Nov 2023 15:31:26 +0100 Subject: [PATCH 016/157] fix weird hang when returning to the DSi menu --- src/DSi_NWifi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 04b905a4..132936a6 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -908,7 +908,7 @@ void DSi_NWifi::HTC_Command() SendWMIEvent(1, 0x1006, regdomain_evt, 4); BootPhase = 2; - NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, MSTimer, 0); + NDS::ScheduleEvent(NDS::Event_DSi_NWifi, false, 33611, MSTimer, 0); } break; From 5ccd3916ff719ea4ed49030077794fadda048cb9 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 2 Nov 2023 15:46:35 +0100 Subject: [PATCH 017/157] better be safe than sorry --- src/DSi_Camera.cpp | 2 +- src/SPU.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index c5fa29df..80e578f7 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -85,7 +85,7 @@ void Reset() BufferNumLines = 0; CurCamera = nullptr; - NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0); + NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, false, kIRQInterval, IRQ, 0); } void Stop() diff --git a/src/SPU.cpp b/src/SPU.cpp index 8148bac2..76fdd2d7 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -163,7 +163,7 @@ void Reset() Capture[0]->Reset(); Capture[1]->Reset(); - NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0); + NDS::ScheduleEvent(NDS::Event_SPU, false, 1024, Mix, 0); } void Stop() From 70c6750561b0c130b430e2ec3d076708b3767674 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 2 Nov 2023 21:04:09 +0100 Subject: [PATCH 018/157] better, less hacky, more OOP-friendly scheduler design --- src/DSi_Camera.cpp | 16 ++-- src/DSi_DSP.cpp | 6 +- src/DSi_NWifi.cpp | 60 ++++++-------- src/DSi_NWifi.h | 4 +- src/DSi_SD.cpp | 42 ++++++---- src/DSi_SD.h | 4 +- src/GPU.cpp | 27 ++++-- src/NDS.cpp | 202 ++++++++++++++------------------------------- src/NDS.h | 14 ++-- src/NDSCart.cpp | 22 ++++- src/RTC.cpp | 5 +- src/SPI.cpp | 6 +- src/SPU.cpp | 8 +- src/Wifi.cpp | 6 +- 14 files changed, 203 insertions(+), 219 deletions(-) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 80e578f7..2471f8cc 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -53,6 +53,9 @@ const u32 kTransferStart = 60000; bool Init() { + NDS::RegisterEventFunc(NDS::Event_DSi_CamIRQ, 0, IRQ); + NDS::RegisterEventFunc(NDS::Event_DSi_CamTransfer, 0, TransferScanline); + Camera0 = new Camera(0); Camera1 = new Camera(1); @@ -66,6 +69,9 @@ void DeInit() Camera0 = nullptr; Camera1 = nullptr; + + NDS::UnregisterEventFunc(NDS::Event_DSi_CamIRQ, 0); + NDS::UnregisterEventFunc(NDS::Event_DSi_CamTransfer, 0); } void Reset() @@ -85,7 +91,7 @@ void Reset() BufferNumLines = 0; CurCamera = nullptr; - NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, false, kIRQInterval, IRQ, 0); + NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, false, kIRQInterval, 0, 0); } void Stop() @@ -132,11 +138,11 @@ void IRQ(u32 param) BufferWritePos = 0; BufferNumLines = 0; CurCamera = activecam; - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, TransferScanline, 0); + NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, 0, 0); } } - NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, IRQ, 0); + NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, 0, 0); } void TransferScanline(u32 line) @@ -162,7 +168,7 @@ void TransferScanline(u32 line) if (line < ystart || line > yend) { if (!CurCamera->TransferDone()) - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1); + NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, 0, line+1); return; } @@ -242,7 +248,7 @@ void TransferScanline(u32 line) if (CurCamera->TransferDone()) return; - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, TransferScanline, line+1); + NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, 0, line+1); } diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index eaa80f9b..d4747635 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -129,6 +129,8 @@ void AudioCb(std::array frame) bool Init() { + NDS::RegisterEventFunc(NDS::Event_DSi_DSP, 0, DSPCatchUpU32); + TeakraCore = new Teakra::Teakra(); SCFG_RST = false; @@ -171,6 +173,8 @@ void DeInit() //PDATAReadFifo = NULL; //PDATAWriteFifo = NULL; TeakraCore = NULL; + + NDS::UnregisterEventFunc(NDS::Event_DSi_DSP, 0); } void Reset() @@ -579,7 +583,7 @@ void Run(u32 cycles) NDS::CancelEvent(NDS::Event_DSi_DSP); NDS::ScheduleEvent(NDS::Event_DSi_DSP, false, - 16384/*from citra (TeakraSlice)*/, DSPCatchUpU32, 0); + 16384/*from citra (TeakraSlice)*/, 0, 0); } void DoSavestate(Savestate* file) diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 132936a6..558cd6f7 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -116,9 +116,6 @@ const u8 CIS1[256] = }; -DSi_NWifi* Ctx = nullptr; - - DSi_NWifi::DSi_NWifi(DSi_SDHost* host) : DSi_SDDevice(host), Mailbox @@ -133,16 +130,17 @@ DSi_NWifi::DSi_NWifi(DSi_SDHost* host) DynamicFIFO(0x8000) } { + NDS::RegisterEventFunc(NDS::Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer)); + // this seems to control whether the firmware upload is done EEPROMReady = 0; - - Ctx = this; } DSi_NWifi::~DSi_NWifi() { NDS::CancelEvent(NDS::Event_DSi_NWifi); - Ctx = nullptr; + + NDS::UnregisterEventFunc(NDS::Event_DSi_NWifi, 0); } void DSi_NWifi::Reset() @@ -908,7 +906,7 @@ void DSi_NWifi::HTC_Command() SendWMIEvent(1, 0x1006, regdomain_evt, 4); BootPhase = 2; - NDS::ScheduleEvent(NDS::Event_DSi_NWifi, false, 33611, MSTimer, 0); + NDS::ScheduleEvent(NDS::Event_DSi_NWifi, false, 33611, 0, 0); } break; @@ -1549,7 +1547,26 @@ void DSi_NWifi::WindowWrite(u32 addr, u32 val) } -void DSi_NWifi::_MSTimer() +void DSi_NWifi::DrainRXBuffer() +{ + while (Mailbox[8].Level() >= 6) + { + u16 len = Mailbox[8].Peek(2) | (Mailbox[8].Peek(3) << 8); + u32 totallen = len + 6; + u32 required = (totallen + 0x7F) & ~0x7F; + + if (!Mailbox[4].CanFit(required)) + break; + + u32 i = 0; + for (; i < totallen; i++) Mailbox[4].Write(Mailbox[8].Read()); + for (; i < required; i++) Mailbox[4].Write(0); + } + + UpdateIRQ_F1(); +} + +void DSi_NWifi::MSTimer(u32 param) { BeaconTimer++; @@ -1587,29 +1604,6 @@ void DSi_NWifi::_MSTimer() //if (Mailbox[4].IsEmpty()) CheckRX(); } -} - -void DSi_NWifi::DrainRXBuffer() -{ - while (Mailbox[8].Level() >= 6) - { - u16 len = Mailbox[8].Peek(2) | (Mailbox[8].Peek(3) << 8); - u32 totallen = len + 6; - u32 required = (totallen + 0x7F) & ~0x7F; - - if (!Mailbox[4].CanFit(required)) - break; - - u32 i = 0; - for (; i < totallen; i++) Mailbox[4].Write(Mailbox[8].Read()); - for (; i < required; i++) Mailbox[4].Write(0); - } - - UpdateIRQ_F1(); -} - -void DSi_NWifi::MSTimer(u32 param) -{ - Ctx->_MSTimer(); - NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, MSTimer, 0); + + NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, 0, 0); } diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h index ffd56476..a758e961 100644 --- a/src/DSi_NWifi.h +++ b/src/DSi_NWifi.h @@ -40,9 +40,7 @@ public: void SetIRQ_F1_Counter(u32 n); - void _MSTimer(); - - static void MSTimer(u32 param); + void MSTimer(u32 param); private: u32 TransferCmd; diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 158ae1e0..5587034b 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -48,11 +48,22 @@ using namespace Platform; #define SD_DESC Num?"SDIO":"SD/MMC" +enum +{ + Transfer_TX = 0, + Transfer_RX, +}; + DSi_SDHost::DSi_SDHost(u32 num) { Num = num; + NDS::RegisterEventFunc(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); + NDS::RegisterEventFunc(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); + Ports[0] = nullptr; Ports[1] = nullptr; } @@ -61,6 +72,11 @@ DSi_SDHost::~DSi_SDHost() { if (Ports[0]) delete Ports[0]; if (Ports[1]) delete Ports[1]; + + NDS::UnregisterEventFunc(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + Transfer_TX); + NDS::UnregisterEventFunc(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + Transfer_RX); } void DSi_SDHost::CloseHandles() @@ -281,14 +297,12 @@ void DSi_SDHost::SendResponse(u32 val, bool last) void DSi_SDHost::FinishRX(u32 param) { - DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC; + CheckSwapFIFO(); - host->CheckSwapFIFO(); - - if (host->DataMode == 1) - host->UpdateFIFO32(); + if (DataMode == 1) + UpdateFIFO32(); else - host->SetIRQ(24); + SetIRQ(24); } u32 DSi_SDHost::DataRX(u8* data, u32 len) @@ -308,21 +322,19 @@ u32 DSi_SDHost::DataRX(u8* data, u32 len) // we need a delay because DSi boot2 will send a command and then wait for IRQ0 // but if IRQ24 is thrown instantly, the handler clears IRQ0 before the // send-command function starts polling IRQ status - u32 param = Num | (last << 1); NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, - false, 512, FinishRX, param); + false, 512, Transfer_RX, 0); return len; } void DSi_SDHost::FinishTX(u32 param) { - DSi_SDHost* host = (param & 0x1) ? DSi::SDIO : DSi::SDMMC; - DSi_SDDevice* dev = host->Ports[host->PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1]; - if (host->BlockCountInternal == 0) + if (BlockCountInternal == 0) { - if (host->StopAction & (1<<8)) + if (StopAction & (1<<8)) { if (dev) dev->SendCMD(12, 0); } @@ -330,8 +342,8 @@ void DSi_SDHost::FinishTX(u32 param) // CHECKME: presumably IRQ2 should not trigger here, but rather // when the data transfer is done //SetIRQ(0); - host->SetIRQ(2); - host->TXReq = false; + SetIRQ(2); + TXReq = false; } else { @@ -392,7 +404,7 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len) BlockCountInternal--; NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, - false, 512, FinishTX, Num); + false, 512, Transfer_TX, 0); return len; } diff --git a/src/DSi_SD.h b/src/DSi_SD.h index 8e53a77a..577572c4 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -43,8 +43,8 @@ public: void DoSavestate(Savestate* file); - static void FinishRX(u32 param); - static void FinishTX(u32 param); + void FinishRX(u32 param); + void FinishTX(u32 param); void SendResponse(u32 val, bool last); u32 DataRX(u8* data, u32 len); u32 DataTX(u8* data, u32 len); diff --git a/src/GPU.cpp b/src/GPU.cpp index 630e88d2..0cbc5996 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -36,6 +36,13 @@ namespace GPU #define HBLANK_CYCLES (48+(256*6)) #define FRAME_CYCLES (LINE_CYCLES * 263) +enum +{ + LCD_StartHBlank = 0, + LCD_StartScanline, + LCD_FinishFrame, +}; + u16 VCount; u32 NextVCount; u16 TotalScanlines; @@ -151,6 +158,11 @@ std::unique_ptr CurGLCompositor = {}; bool Init() { + NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartHBlank, StartHBlank); + NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartScanline, StartScanline); + NDS::RegisterEventFunc(NDS::Event_LCD, LCD_FinishFrame, FinishFrame); + NDS::RegisterEventFunc(NDS::Event_DisplayFIFO, 0, DisplayFIFO); + GPU2D_Renderer = std::make_unique(); if (!GPU3D::Init()) return false; @@ -180,6 +192,11 @@ void DeInit() #ifdef OGLRENDERER_ENABLED CurGLCompositor = nullptr; #endif + + NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_StartHBlank); + NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_StartScanline); + NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_FinishFrame); + NDS::UnregisterEventFunc(NDS::Event_DisplayFIFO, 0); } void ResetVRAMCache() @@ -1022,7 +1039,7 @@ void DisplayFIFO(u32 x) { // transfer the next 8 pixels NDS::CheckDMAs(0, 0x04); - NDS::ScheduleEvent(NDS::Event_DisplayFIFO, true, 6*8, DisplayFIFO, x+8); + NDS::ScheduleEvent(NDS::Event_DisplayFIFO, true, 6*8, 0, x+8); } else GPU2D_A.SampleFIFO(253, 3); // sample the remaining pixels @@ -1077,9 +1094,9 @@ void StartHBlank(u32 line) if (DispStat[1] & (1<<4)) NDS::SetIRQ(1, NDS::IRQ_HBlank); if (VCount < 262) - NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), StartScanline, line+1); + NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_StartScanline, line+1); else - NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), FinishFrame, line+1); + NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_FinishFrame, line+1); } void FinishFrame(u32 lines) @@ -1164,7 +1181,7 @@ void StartScanline(u32 line) } if (RunFIFO) - NDS::ScheduleEvent(NDS::Event_DisplayFIFO, false, 32, DisplayFIFO, 0); + NDS::ScheduleEvent(NDS::Event_DisplayFIFO, false, 32, 0, 0); } if (VCount == 262) @@ -1210,7 +1227,7 @@ void StartScanline(u32 line) } } - NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, StartHBlank, line); + NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, LCD_StartHBlank, line); } diff --git a/src/NDS.cpp b/src/NDS.cpp index 832661a4..0efd0e99 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -104,6 +104,14 @@ u64 ARM9Timestamp, ARM9Target; u64 ARM7Timestamp, ARM7Target; u64 SysTimestamp; +struct SchedEvent +{ + std::map Funcs; + u64 Timestamp; + u32 FuncID; + u32 Param; +}; + SchedEvent SchedList[Event_MAX]; u32 SchedListMask; @@ -184,6 +192,9 @@ void SetGBASlotTimings(); bool Init() { + RegisterEventFunc(Event_Div, 0, DivDone); + RegisterEventFunc(Event_Sqrt, 0, SqrtDone); + ARM9 = new ARMv5(); ARM7 = new ARMv4(); @@ -248,6 +259,9 @@ void DeInit() DSi::DeInit(); AREngine::DeInit(); + + UnregisterEventFunc(Event_Div, 0); + UnregisterEventFunc(Event_Sqrt, 0); } @@ -608,7 +622,14 @@ void Reset() for (i = 0; i < 8; i++) DMAs[i]->Reset(); memset(DMA9Fill, 0, 4*4); - memset(SchedList, 0, sizeof(SchedList)); + for (i = 0; i < Event_MAX; i++) + { + SchedEvent& evt = SchedList[i]; + + evt.Timestamp = 0; + evt.FuncID = 0; + evt.Param = 0; + } SchedListMask = 0; KeyInput = 0x007F03FF; @@ -699,106 +720,6 @@ void Stop(Platform::StopReason reason) DSi::Stop(); } -bool DoSavestate_Scheduler(Savestate* file) -{ - // this is a bit of a hack - // but uh, your local coder realized that the scheduler list contains function pointers - // and that storing those as-is is not a very good idea - // unless you want it to crash and burn - - // this is the solution your local coder came up with. - // it's gross but I think it's the best solution for this problem. - // just remember to add here if you add more event callbacks, kay? - // atleast until we come up with something more elegant. - - void (*eventfuncs[])(u32) = - { - GPU::StartScanline, GPU::StartHBlank, GPU::FinishFrame, - SPU::Mix, - Wifi::USTimer, - RTC::ClockTimer, - - GPU::DisplayFIFO, - NDSCart::ROMPrepareData, NDSCart::ROMEndTransfer, - NDSCart::SPITransferDone, - SPI::TransferDone, - DivDone, - SqrtDone, - - DSi_SDHost::FinishRX, - DSi_SDHost::FinishTX, - DSi_NWifi::MSTimer, - DSi_CamModule::IRQ, - DSi_CamModule::TransferScanline, - DSi_DSP::DSPCatchUpU32, - - nullptr - }; - - int len = Event_MAX; - if (file->Saving) - { - for (int i = 0; i < len; i++) - { - SchedEvent* evt = &SchedList[i]; - - u32 funcid = 0xFFFFFFFF; - if (evt->Func) - { - for (int j = 0; eventfuncs[j]; j++) - { - if (evt->Func == eventfuncs[j]) - { - funcid = j; - break; - } - } - if (funcid == 0xFFFFFFFF) - { - Log(LogLevel::Error, "savestate: VERY BAD!!!!! FUNCTION POINTER FOR EVENT %d NOT IN HACKY LIST. CANNOT SAVE. SMACK ARISOTURA.\n", i); - return false; - } - } - - file->Var32(&funcid); - file->Var64(&evt->Timestamp); - file->Var32(&evt->Param); - } - } - else - { - for (int i = 0; i < len; i++) - { - SchedEvent* evt = &SchedList[i]; - - u32 funcid; - file->Var32(&funcid); - - if (funcid != 0xFFFFFFFF) - { - for (int j = 0; ; j++) - { - if (!eventfuncs[j]) - { - Log(LogLevel::Error, "savestate: VERY BAD!!!!!! EVENT FUNCTION POINTER ID %d IS OUT OF RANGE. HAX?????\n", j); - return false; - } - if (j == funcid) break; - } - - evt->Func = eventfuncs[funcid]; - } - else - evt->Func = nullptr; - - file->Var64(&evt->Timestamp); - file->Var32(&evt->Param); - } - } - - return true; -} - bool DoSavestate(Savestate* file) { file->Section("NDSG"); @@ -871,10 +792,13 @@ bool DoSavestate(Savestate* file) file->VarArray(DMA9Fill, 4*sizeof(u32)); - if (!DoSavestate_Scheduler(file)) + for (int i = 0; i < Event_MAX; i++) { - Platform::Log(Platform::LogLevel::Error, "savestate: failed to %s scheduler state\n", file->Saving ? "save" : "load"); - return false; + SchedEvent& evt = SchedList[i]; + + file->Var64(&evt.Timestamp); + file->Var32(&evt.FuncID); + file->Var32(&evt.Param); } file->Var32(&SchedListMask); file->Var64(&ARM9Timestamp); @@ -1050,10 +974,14 @@ void RunSystem(u64 timestamp) if (!mask) break; if (mask & 0x1) { - if (SchedList[i].Timestamp <= SysTimestamp) + SchedEvent& evt = SchedList[i]; + + if (evt.Timestamp <= SysTimestamp) { SchedListMask &= ~(1<Timestamp += delay; + evt.Timestamp += delay; else { if (CurCPU == 0) - evt->Timestamp = (ARM9Timestamp >> ARM9ClockShift) + delay; + evt.Timestamp = (ARM9Timestamp >> ARM9ClockShift) + delay; else - evt->Timestamp = ARM7Timestamp + delay; + evt.Timestamp = ARM7Timestamp + delay; } - evt->Func = func; - evt->Param = param; + evt.FuncID = funcid; + evt.Param = param; SchedListMask |= (1<Timestamp); -} - -void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param) -{ - if (SchedListMask & (1<Timestamp = timestamp; - evt->Func = func; - evt->Param = param; - - SchedListMask |= (1<Timestamp); + Reschedule(evt.Timestamp); } void CancelEvent(u32 id) @@ -2096,7 +2022,7 @@ void StartDiv() { NDS::CancelEvent(NDS::Event_Div); DivCnt |= 0x8000; - NDS::ScheduleEvent(NDS::Event_Div, false, ((DivCnt&0x3)==0) ? 18:34, DivDone, 0); + NDS::ScheduleEvent(NDS::Event_Div, false, ((DivCnt&0x3)==0) ? 18:34, 0, 0); } // http://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2 @@ -2144,7 +2070,7 @@ void StartSqrt() { NDS::CancelEvent(NDS::Event_Sqrt); SqrtCnt |= 0x8000; - NDS::ScheduleEvent(NDS::Event_Sqrt, false, 13, SqrtDone, 0); + NDS::ScheduleEvent(NDS::Event_Sqrt, false, 13, 0, 0); } diff --git a/src/NDS.h b/src/NDS.h index de038b2b..89ebb1ce 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -20,6 +20,7 @@ #define NDS_H #include +#include #include "Platform.h" #include "Savestate.h" @@ -57,12 +58,8 @@ enum Event_MAX }; -struct SchedEvent -{ - void (*Func)(u32 param); - u64 Timestamp; - u32 Param; -}; +typedef std::function EventFunc; +#define MemberEventFunc(cls,func) std::bind(&cls::func,this,std::placeholders::_1) enum { @@ -297,8 +294,9 @@ void SetLidClosed(bool closed); void CamInputFrame(int cam, u32* data, int width, int height, bool rgb); void MicInputFrame(s16* data, int samples); -void ScheduleEvent(u32 id, bool periodic, s32 delay, void (*func)(u32), u32 param); -void ScheduleEvent(u32 id, u64 timestamp, void (*func)(u32), u32 param); +void RegisterEventFunc(u32 id, u32 funcid, EventFunc func); +void UnregisterEventFunc(u32 id, u32 funcid); +void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param); void CancelEvent(u32 id); void debug(u32 p); diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 400f7d6d..33d77951 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -34,6 +34,12 @@ using Platform::LogLevel; namespace NDSCart { +enum +{ + ROMTransfer_PrepareData = 0, + ROMTransfer_End +}; + // SRAM TODO: emulate write delays??? u16 SPICnt; @@ -1468,6 +1474,10 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) bool Init() { + NDS::RegisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_PrepareData, ROMPrepareData); + NDS::RegisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_End, ROMEndTransfer); + NDS::RegisterEventFunc(NDS::Event_ROMSPITransfer, 0, SPITransferDone); + Cart = nullptr; return true; @@ -1476,6 +1486,10 @@ bool Init() void DeInit() { Cart = nullptr; + + NDS::UnregisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_PrepareData); + NDS::UnregisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_End); + NDS::UnregisterEventFunc(NDS::Event_ROMSPITransfer, 0); } void Reset() @@ -1967,9 +1981,9 @@ void WriteROMCnt(u32 val) } if (datasize == 0) - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*cmddelay, ROMEndTransfer, 0); + NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*cmddelay, ROMTransfer_End, 0); else - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*(cmddelay+4), ROMPrepareData, 0); + NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*(cmddelay+4), ROMTransfer_PrepareData, 0); } void AdvanceROMTransfer() @@ -1986,7 +2000,7 @@ void AdvanceROMTransfer() delay += ((ROMCnt >> 16) & 0x3F); } - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*delay, ROMPrepareData, 0); + NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*delay, ROMTransfer_PrepareData, 0); } else ROMEndTransfer(0); @@ -2088,7 +2102,7 @@ void WriteSPIData(u8 val) // SPI transfers one bit per cycle -> 8 cycles per byte u32 delay = 8 * (8 << (SPICnt & 0x3)); - NDS::ScheduleEvent(NDS::Event_ROMSPITransfer, false, delay, SPITransferDone, 0); + NDS::ScheduleEvent(NDS::Event_ROMSPITransfer, false, delay, 0, 0); } } diff --git a/src/RTC.cpp b/src/RTC.cpp index 3d25226c..527b7f47 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -53,6 +53,8 @@ void WriteDateTime(int num, u8 val); bool Init() { + NDS::RegisterEventFunc(NDS::Event_RTC, 0, ClockTimer); + ResetState(); // indicate the power was off @@ -64,6 +66,7 @@ bool Init() void DeInit() { + NDS::UnregisterEventFunc(NDS::Event_RTC, 0); } void Reset() @@ -538,7 +541,7 @@ void ScheduleTimer(bool first) s32 delay = sysclock >> 15; TimerError = sysclock & 0x7FFF; - NDS::ScheduleEvent(NDS::Event_RTC, !first, delay, ClockTimer, 0); + NDS::ScheduleEvent(NDS::Event_RTC, !first, delay, 0, 0); } void ClockTimer(u32 param) diff --git a/src/SPI.cpp b/src/SPI.cpp index a755c440..1a4c12e5 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -612,6 +612,8 @@ u32 CurDevice; // remove me bool Init() { + NDS::RegisterEventFunc(NDS::Event_SPITransfer, 0, TransferDone); + if (!SPI_Firmware::Init()) return false; if (!SPI_Powerman::Init()) return false; if (!SPI_TSC::Init()) return false; @@ -626,6 +628,8 @@ void DeInit() SPI_Powerman::DeInit(); SPI_TSC::DeInit(); DSi_SPI_TSC::DeInit(); + + NDS::UnregisterEventFunc(NDS::Event_SPITransfer, 0); } void Reset() @@ -725,7 +729,7 @@ void WriteData(u8 val) // SPI transfers one bit per cycle -> 8 cycles per byte u32 delay = 8 * (8 << (Cnt & 0x3)); - NDS::ScheduleEvent(NDS::Event_SPITransfer, false, delay, TransferDone, 0); + NDS::ScheduleEvent(NDS::Event_SPITransfer, false, delay, 0, 0); } } diff --git a/src/SPU.cpp b/src/SPU.cpp index 76fdd2d7..cdfc2442 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -93,6 +93,8 @@ CaptureUnit* Capture[2]; bool Init() { + NDS::RegisterEventFunc(NDS::Event_SPU, 0, Mix); + for (int i = 0; i < 16; i++) Channels[i] = new Channel(i); @@ -147,6 +149,8 @@ void DeInit() Platform::Mutex_Free(AudioLock); AudioLock = nullptr; + + NDS::UnregisterEventFunc(NDS::Event_SPU, 0); } void Reset() @@ -163,7 +167,7 @@ void Reset() Capture[0]->Reset(); Capture[1]->Reset(); - NDS::ScheduleEvent(NDS::Event_SPU, false, 1024, Mix, 0); + NDS::ScheduleEvent(NDS::Event_SPU, false, 1024, 0, 0); } void Stop() @@ -864,7 +868,7 @@ void Mix(u32 dummy) OutputBackbufferWritePosition += 2; } - NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, Mix, 0); + NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, 0, 0); } void TransferOutput() diff --git a/src/Wifi.cpp b/src/Wifi.cpp index d786cd05..c5a67abf 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -150,6 +150,8 @@ u64 RXTimestamp; bool Init() { + NDS::RegisterEventFunc(NDS::Event_Wifi, 0, USTimer); + //MPInited = false; //LANInited = false; @@ -172,6 +174,8 @@ void DeInit() Platform::LAN_DeInit(); WifiAP::DeInit(); + + NDS::UnregisterEventFunc(NDS::Event_Wifi, 0); } void Reset() @@ -359,7 +363,7 @@ void ScheduleTimer(bool first) s32 delay = (cycles + 999999) / 1000000; TimerError = (delay * 1000000) - cycles; - NDS::ScheduleEvent(NDS::Event_Wifi, !first, delay, USTimer, 0); + NDS::ScheduleEvent(NDS::Event_Wifi, !first, delay, 0, 0); } void UpdatePowerOn() From 440b3566745bbf967210bd6c51e791e1df472ff2 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 3 Nov 2023 20:17:00 +0100 Subject: [PATCH 019/157] get this started: refactor SPI in OOP --- src/DSi.cpp | 11 +- src/DSi_I2C.cpp | 2 +- src/DSi_NAND.h | 2 +- src/DSi_NWifi.cpp | 16 +- src/DSi_SPI_TSC.cpp | 67 +- src/DSi_SPI_TSC.h | 33 +- src/NDS.cpp | 57 +- src/NDS.h | 4 + src/Platform.h | 7 +- src/SPI.cpp | 358 +++----- src/SPI.h | 159 +++- src/SPI_Firmware.cpp | 99 ++- src/SPI_Firmware.h | 796 +++++++++--------- src/Wifi.cpp | 13 +- src/frontend/qt_sdl/Platform.cpp | 16 +- .../PowerManagement/PowerManagementDialog.cpp | 12 +- src/frontend/qt_sdl/ROMManager.cpp | 49 +- 17 files changed, 855 insertions(+), 846 deletions(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index 17bfb8ff..0a62db2d 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -403,6 +403,7 @@ void SetupDirectBoot() NDSHeader& header = NDSCart::Cart->GetHeader(); const u8* cartrom = NDSCart::Cart->GetROM(); u32 cartid = NDSCart::Cart->ID(); + DSi_TSC* tsc = (DSi_TSC*)NDS::SPI->GetTSC(); // TODO: add controls for forcing DS or DSi mode? if (!(header.UnitCode & 0x02)) @@ -429,7 +430,7 @@ void SetupDirectBoot() NDS::MapSharedWRAM(3); - DSi_SPI_TSC::SetMode(0x00); + tsc->SetMode(0x00); Set_SCFG_Clock9(0x0000); } else @@ -481,7 +482,7 @@ void SetupDirectBoot() NDS::MapSharedWRAM(mbk[11] >> 24); if (!(header.AppFlags & (1<<0))) - DSi_SPI_TSC::SetMode(0x00); + tsc->SetMode(0x00); } // setup main RAM data @@ -552,12 +553,12 @@ void SetupDirectBoot() } } - SPI_Firmware::WifiBoard nwifiver = SPI_Firmware::GetFirmware()->Header().WifiBoard; + Firmware::WifiBoard nwifiver = NDS::SPI->GetFirmware()->GetHeader().WifiBoard; ARM9Write8(0x020005E0, static_cast(nwifiver)); // TODO: these should be taken from the wifi firmware in NAND // but, hey, this works too. - if (nwifiver == SPI_Firmware::WifiBoard::W015) + if (nwifiver == Firmware::WifiBoard::W015) { ARM9Write16(0x020005E2, 0xB57E); ARM9Write32(0x020005E4, 0x00500400); @@ -642,7 +643,7 @@ void SetupDirectBoot() NDS::ARM7BIOSProt = 0x20; - SPI_Firmware::SetupDirectBoot(true); + NDS::SPI->GetFirmwareMem()->SetupDirectBoot(true); NDS::ARM9->CP15Write(0x100, 0x00056078); NDS::ARM9->CP15Write(0x200, 0x0000004A); diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index c4ad50c5..26f3e4b8 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -144,7 +144,7 @@ u8 GetBatteryLevel() { return Registers[0x20] & 0xF; } void SetBatteryLevel(u8 batteryLevel) { Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F)); - SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false); + //SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false); if (batteryLevel <= 1) { diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 0077eacb..99a9ee7c 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -161,7 +161,7 @@ union DSiFirmwareSystemSettings u32 ConfigFlags; u8 Zero02; u8 CountryCode; - SPI_Firmware::Language Language; + Firmware::Language Language; u8 RTCYear; u32 RTCOffset; u8 Zero3[4]; diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 558cd6f7..d95def5a 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -145,7 +145,6 @@ DSi_NWifi::~DSi_NWifi() void DSi_NWifi::Reset() { - using namespace SPI_Firmware; TransferCmd = 0xFFFFFFFF; RemSize = 0; @@ -162,26 +161,28 @@ void DSi_NWifi::Reset() for (int i = 0; i < 9; i++) Mailbox[i].Clear(); - MacAddress mac = GetFirmware()->Header().MacAddress; + const Firmware* fw = NDS::SPI->GetFirmware(); + + MacAddress mac = fw->GetHeader().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]); - WifiBoard type = GetFirmware()->Header().WifiBoard; + Firmware::WifiBoard type = fw->GetHeader().WifiBoard; switch (type) { - case WifiBoard::W015: // AR6002 + case Firmware::WifiBoard::W015: // AR6002 ROMID = 0x20000188; ChipID = 0x02000001; HostIntAddr = 0x00500400; break; - case WifiBoard::W024: // AR6013 + case Firmware::WifiBoard::W024: // AR6013 ROMID = 0x23000024; ChipID = 0x0D000000; HostIntAddr = 0x00520000; break; - case WifiBoard::W028: // AR6014 (3DS) + case Firmware::WifiBoard::W028: // AR6014 (3DS) ROMID = 0x2300006F; ChipID = 0x0D000001; HostIntAddr = 0x00520000; @@ -893,9 +894,8 @@ 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], &mac, mac.size()); + memcpy(&ready_evt[0], &EEPROM[0xA], 6); // MAC address ready_evt[6] = 0x02; ready_evt[7] = 0; *(u32*)&ready_evt[8] = 0x2300006C; diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index c690f9c9..13044405 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -19,38 +19,25 @@ #include #include #include "DSi.h" -#include "SPI.h" #include "DSi_SPI_TSC.h" #include "Platform.h" using Platform::Log; using Platform::LogLevel; -namespace DSi_SPI_TSC -{ -u32 DataPos; -u8 Index; -u8 Bank; -u8 Data; - -u8 Bank3Regs[0x80]; -u8 TSCMode; - -u16 TouchX, TouchY; - - -bool Init() -{ - return true; -} - -void DeInit() +DSi_TSC::DSi_TSC(SPIHost* host) : TSC(host) { } -void Reset() +DSi_TSC::~DSi_TSC() { +} + +void DSi_TSC::Reset() +{ + TSC::Reset(); + DataPos = 0; Bank = 0; @@ -72,8 +59,10 @@ void Reset() TSCMode = 0x01; // DSi mode } -void DoSavestate(Savestate* file) +void DSi_TSC::DoSavestate(Savestate* file) { + TSC::DoSavestate(file); + file->Section("SPTi"); file->Var32(&DataPos); @@ -85,19 +74,14 @@ void DoSavestate(Savestate* file) file->Var8(&TSCMode); } -void SetMode(u8 mode) +void DSi_TSC::SetMode(u8 mode) { TSCMode = mode; } -void SetTouchCoords(u16 x, u16 y) +void DSi_TSC::SetTouchCoords(u16 x, u16 y) { - if (TSCMode == 0x00) - { - if (y == 0xFFF) NDS::KeyInput |= (1 << (16+6)); - else NDS::KeyInput &= ~(1 << (16+6)); - return SPI_TSC::SetTouchCoords(x, y); - } + if (TSCMode == 0x00) return TSC::SetTouchCoords(x, y); TouchX = x; TouchY = y; @@ -135,24 +119,17 @@ void SetTouchCoords(u16 x, u16 y) } } -void MicInputFrame(s16* data, int samples) +void DSi_TSC::MicInputFrame(s16* data, int samples) { - if (TSCMode == 0x00) return SPI_TSC::MicInputFrame(data, samples); + if (TSCMode == 0x00) return TSC::MicInputFrame(data, samples); // otherwise we don't handle mic input // TODO: handle it where it needs to be } -u8 Read() +void DSi_TSC::Write(u8 val) { - if (TSCMode == 0x00) return SPI_TSC::Read(); - - return Data; -} - -void Write(u8 val, u32 hold) -{ - if (TSCMode == 0x00) return SPI_TSC::Write(val, hold); + if (TSCMode == 0x00) return TSC::Write(val); #define READWRITE(var) { if (Index & 0x01) Data = var; else var = val; } @@ -233,8 +210,12 @@ void Write(u8 val, u32 hold) Index += (1<<1); // increment index } - if (hold) DataPos++; - else DataPos = 0; + DataPos++; } +void DSi_TSC::Release() +{ + if (TSCMode == 0x00) return TSC::Release(); + + DataPos = 0; } diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h index 7a3acf43..c7b9bfe6 100644 --- a/src/DSi_SPI_TSC.h +++ b/src/DSi_SPI_TSC.h @@ -21,26 +21,33 @@ #include "types.h" #include "Savestate.h" +#include "SPI.h" -namespace DSi_SPI_TSC +class DSi_TSC : public TSC { +public: + DSi_TSC(SPIHost* host); + ~DSi_TSC() override; -extern u32 DataPos; + void Reset() override; -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); + void DoSavestate(Savestate* file) override; -// 00=DS-mode 01=normal -void SetMode(u8 mode); + // 00=DS-mode 01=normal + void SetMode(u8 mode); -void SetTouchCoords(u16 x, u16 y); -void MicInputFrame(s16* data, int samples); + void SetTouchCoords(u16 x, u16 y) override; + void MicInputFrame(s16* data, int samples) override; -u8 Read(); -void Write(u8 val, u32 hold); + void Write(u8 val) override; + void Release() override; -} +private: + u8 Index; + u8 Bank; + + u8 Bank3Regs[0x80]; + u8 TSCMode; +}; #endif // DSI_SPI_TSC diff --git a/src/NDS.cpp b/src/NDS.cpp index 0efd0e99..9b9e1e71 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -178,6 +178,8 @@ u32 KeyInput; u16 KeyCnt[2]; u16 RCnt; +SPIHost* SPI; + bool Running; bool RunningGame; @@ -215,11 +217,12 @@ bool Init() DMAs[6] = new DMA(1, 2); DMAs[7] = new DMA(1, 3); + SPI = new SPIHost(); + if (!NDSCart::Init()) return false; if (!GBACart::Init()) return false; if (!GPU::Init()) return false; if (!SPU::Init()) return false; - if (!SPI::Init()) return false; if (!RTC::Init()) return false; if (!Wifi::Init()) return false; @@ -248,11 +251,13 @@ void DeInit() DMAs[i] = nullptr; } + delete SPI; + SPI = nullptr; + NDSCart::DeInit(); GBACart::DeInit(); GPU::DeInit(); SPU::DeInit(); - SPI::DeInit(); RTC::DeInit(); Wifi::DeInit(); @@ -389,7 +394,7 @@ bool NeedsDirectBoot() return true; // DSi/3DS firmwares aren't bootable - if (!SPI_Firmware::GetFirmware()->IsBootable()) + if (!SPI->GetFirmware()->IsBootable()) return true; return false; @@ -465,7 +470,7 @@ void SetupDirectBoot(const std::string& romname) ARM7BIOSProt = 0x1204; - SPI_Firmware::SetupDirectBoot(false); + SPI->GetFirmwareMem()->SetupDirectBoot(false); ARM9->CP15Write(0x100, 0x00012078); ARM9->CP15Write(0x200, 0x00000042); @@ -641,7 +646,7 @@ void Reset() GBACart::Reset(); GPU::Reset(); SPU::Reset(); - SPI::Reset(); + SPI->Reset(); RTC::Reset(); Wifi::Reset(); @@ -843,7 +848,7 @@ bool DoSavestate(Savestate* file) GBACart::DoSavestate(file); GPU::DoSavestate(file); SPU::DoSavestate(file); - SPI::DoSavestate(file); + SPI->DoSavestate(file); RTC::DoSavestate(file); Wifi::DoSavestate(file); @@ -1279,28 +1284,12 @@ void CancelEvent(u32 id) void TouchScreen(u16 x, u16 y) { - if (ConsoleType == 1) - { - DSi_SPI_TSC::SetTouchCoords(x, y); - } - else - { - SPI_TSC::SetTouchCoords(x, y); - KeyInput &= ~(1 << (16+6)); - } + SPI->GetTSC()->SetTouchCoords(x, y); } void ReleaseScreen() { - if (ConsoleType == 1) - { - DSi_SPI_TSC::SetTouchCoords(0x000, 0xFFF); - } - else - { - SPI_TSC::SetTouchCoords(0x000, 0xFFF); - KeyInput |= (1 << (16+6)); - } + SPI->GetTSC()->SetTouchCoords(0x000, 0xFFF); } @@ -1383,7 +1372,7 @@ void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) void MicInputFrame(s16* data, int samples) { - return SPI_TSC::MicInputFrame(data, samples); + return SPI->GetTSC()->MicInputFrame(data, samples); } /*int ImportSRAM(u8* data, u32 length) @@ -3917,7 +3906,7 @@ u8 ARM7IORead8(u32 addr) return NDSCart::ROMCommand[7]; return 0; - case 0x040001C2: return SPI::ReadData(); + case 0x040001C2: return SPI->ReadData(); case 0x04000208: return IME[1]; @@ -4005,8 +3994,8 @@ u16 ARM7IORead16(u32 addr) (NDSCart::ROMCommand[7] << 8); return 0; - case 0x040001C0: return SPI::Cnt; - case 0x040001C2: return SPI::ReadData(); + case 0x040001C0: return SPI->ReadCnt(); + case 0x040001C2: return SPI->ReadData(); case 0x04000204: return ExMemCnt[1]; case 0x04000206: @@ -4088,7 +4077,7 @@ u32 ARM7IORead32(u32 addr) return 0; case 0x040001C0: - return SPI::Cnt | (SPI::ReadData() << 16); + return SPI->ReadCnt() | (SPI->ReadData() << 16); case 0x04000208: return IME[1]; case 0x04000210: return IE[1]; @@ -4181,7 +4170,7 @@ void ARM7IOWrite8(u32 addr, u8 val) case 0x040001AF: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[7] = val; return; case 0x040001C2: - SPI::WriteData(val); + SPI->WriteData(val); return; case 0x04000208: IME[1] = val & 0x1; UpdateIRQ(1); return; @@ -4309,10 +4298,10 @@ void ARM7IOWrite16(u32 addr, u16 val) case 0x040001BA: ROMSeed1[12] = val & 0x7F; return; case 0x040001C0: - SPI::WriteCnt(val); + SPI->WriteCnt(val); return; case 0x040001C2: - SPI::WriteData(val & 0xFF); + SPI->WriteData(val & 0xFF); return; case 0x04000204: @@ -4462,8 +4451,8 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x040001B4: *(u32*)&ROMSeed1[8] = val; return; case 0x040001C0: - SPI::WriteCnt(val & 0xFFFF); - SPI::WriteData((val >> 16) & 0xFF); + SPI->WriteCnt(val & 0xFFFF); + SPI->WriteData((val >> 16) & 0xFF); return; case 0x04000208: IME[1] = val & 0x1; UpdateIRQ(1); return; diff --git a/src/NDS.h b/src/NDS.h index 89ebb1ce..b0126ed3 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -30,6 +30,8 @@ // with this enabled, to make sure it doesn't desync //#define DEBUG_CHECK_DESYNC +class SPIHost; + namespace NDS { @@ -246,6 +248,8 @@ extern MemRegion SWRAM_ARM7; extern u32 KeyInput; extern u16 RCnt; +extern SPIHost* SPI; + const u32 ARM7WRAMSize = 0x10000; extern u8* ARM7WRAM; diff --git a/src/Platform.h b/src/Platform.h index 144fce19..7fa8fbd7 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -24,10 +24,7 @@ #include #include -namespace SPI_Firmware -{ - class Firmware; -} +class Firmware; namespace Platform { @@ -335,7 +332,7 @@ void WriteGBASave(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen /// @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); +void WriteFirmware(const Firmware& firmware, u32 writeoffset, u32 writelen); // called when the RTC date/time is changed and the frontend might need to take it into account void WriteDateTime(int year, int month, int day, int hour, int minute, int second); diff --git a/src/SPI.cpp b/src/SPI.cpp index 1a4c12e5..8eb8b0e0 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -29,18 +29,6 @@ using namespace Platform; -namespace SPI_Firmware -{ - -std::unique_ptr Firmware; - -u32 Hold; -u8 CurCmd; -u32 DataPos; -u8 Data; - -u8 StatusReg; -u32 Addr; u16 CRC16(const u8* data, u32 len, u32 start) { @@ -65,7 +53,9 @@ u16 CRC16(const u8* data, u32 len, u32 start) return start & 0xFFFF; } -bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) + + +bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) { u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset]; u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start); @@ -73,40 +63,16 @@ bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) } -bool Init() +FirmwareMem::FirmwareMem(SPIHost* host) : SPIDevice(host) { - return true; } -void DeInit() +FirmwareMem::~FirmwareMem() { RemoveFirmware(); } -u32 FixFirmwareLength(u32 originalLength) -{ - if (originalLength != 0x20000 && originalLength != 0x40000 && originalLength != 0x80000) - { - Log(LogLevel::Warn, "Bad firmware size %d, ", originalLength); - - // pick the nearest power-of-two length - originalLength |= (originalLength >> 1); - originalLength |= (originalLength >> 2); - originalLength |= (originalLength >> 4); - originalLength |= (originalLength >> 8); - originalLength |= (originalLength >> 16); - originalLength++; - - // ensure it's a sane length - if (originalLength > 0x80000) originalLength = 0x80000; - else if (originalLength < 0x20000) originalLength = 0x20000; - - Log(LogLevel::Debug, "assuming %d\n", originalLength); - } - return originalLength; -} - -void Reset() +void FirmwareMem::Reset() { if (!Firmware) { @@ -115,7 +81,7 @@ void Reset() } // fix touchscreen coords - for (UserData& u : Firmware->UserData()) + for (auto& u : Firmware->GetUserData()) { u.TouchCalibrationADC1[0] = 0; u.TouchCalibrationADC1[1] = 0; @@ -132,7 +98,7 @@ void Reset() // disable autoboot //Firmware[userdata+0x64] &= 0xBF; - MacAddress mac = Firmware->Header().MacAddress; + MacAddress mac = Firmware->GetHeader().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 @@ -150,14 +116,14 @@ void Reset() StatusReg = 0x00; } -void DoSavestate(Savestate* file) +void FirmwareMem::DoSavestate(Savestate* file) { file->Section("SPFW"); // CHECKME/TODO: trust the firmware to stay the same????? // embedding the whole firmware in the savestate would be derpo tho?? - file->Var32(&Hold); + file->Bool32(&Hold); file->Var8(&CurCmd); file->Var32(&DataPos); file->Var8(&Data); @@ -166,10 +132,10 @@ void DoSavestate(Savestate* file) file->Var32(&Addr); } -void SetupDirectBoot(bool dsi) +void FirmwareMem::SetupDirectBoot(bool dsi) { - const FirmwareHeader& header = Firmware->Header(); - const UserData& userdata = Firmware->EffectiveUserData(); + const auto& header = Firmware->GetHeader(); + const auto& userdata = Firmware->GetEffectiveUserData(); if (dsi) { for (u32 i = 0; i < 6; i += 2) @@ -194,17 +160,17 @@ void SetupDirectBoot(bool dsi) } } -const class Firmware* GetFirmware() +const class Firmware* FirmwareMem::GetFirmware() { return Firmware.get(); } -bool IsLoadedFirmwareBuiltIn() +bool FirmwareMem::IsLoadedFirmwareBuiltIn() { - return Firmware->Header().Identifier == GENERATED_FIRMWARE_IDENTIFIER; + return Firmware->GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER; } -bool InstallFirmware(class Firmware&& firmware) +bool FirmwareMem::InstallFirmware(class Firmware&& firmware) { if (!firmware.Buffer()) { @@ -214,13 +180,13 @@ bool InstallFirmware(class Firmware&& firmware) Firmware = std::make_unique(std::move(firmware)); - FirmwareIdentifier id = Firmware->Header().Identifier; + FirmwareIdentifier id = Firmware->GetHeader().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&& firmware) +bool FirmwareMem::InstallFirmware(std::unique_ptr&& firmware) { if (!firmware) { @@ -236,40 +202,42 @@ bool InstallFirmware(std::unique_ptr&& firmware) Firmware = std::move(firmware); - FirmwareIdentifier id = Firmware->Header().Identifier; + FirmwareIdentifier id = Firmware->GetHeader().Identifier; Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]); return true; } -void RemoveFirmware() +void FirmwareMem::RemoveFirmware() { Firmware.reset(); Log(LogLevel::Debug, "Removed installed firmware (if any)\n"); } -u8 Read() +void FirmwareMem::Write(u8 val) { - return Data; -} - -void Write(u8 val, u32 hold) -{ - if (!hold) - { - if (!Hold) // commands with no paramters - CurCmd = val; - - Hold = 0; - } - - if (hold && (!Hold)) + if (!Hold) { CurCmd = val; - Hold = 1; + Hold = true; Data = 0; DataPos = 1; Addr = 0; + + // handle commands with no parameters + switch (CurCmd) + { + case 0x04: // write disable + StatusReg &= ~(1<<1); + Data = 0; + break; + + case 0x06: // write enable + StatusReg |= (1<<1); + Data = 0; + break; + } + return; } @@ -293,20 +261,10 @@ void Write(u8 val, u32 hold) } break; - case 0x04: // write disable - StatusReg &= ~(1<<1); - Data = 0; - break; - case 0x05: // read status reg Data = StatusReg; break; - case 0x06: // write enable - StatusReg |= (1<<1); - Data = 0; - break; - case 0x0A: // write { // TODO: what happens if you write too many bytes? (max 256, they say) @@ -345,45 +303,38 @@ void Write(u8 val, u32 hold) Data = 0xFF; break; } +} - if (!hold && (CurCmd == 0x02 || CurCmd == 0x0A)) +void FirmwareMem::Release() +{ + if (CurCmd == 0x02 || CurCmd == 0x0A) { // 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(); + u32 wifioffset = Firmware->GetWifiAccessPointOffset(); // 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); } + + SPIDevice::Release(); + CurCmd = 0; } -} - -namespace SPI_Powerman -{ - -u32 Hold; -u32 DataPos; -u8 Index; -u8 Data; - -u8 Registers[8]; -u8 RegMasks[8]; -bool Init() -{ - return true; -} - -void DeInit() +PowerMan::PowerMan(SPIHost* host) : SPIDevice(host) { } -void Reset() +PowerMan::~PowerMan() { - Hold = 0; +} + +void PowerMan::Reset() +{ + Hold = false; Index = 0; Data = 0; @@ -399,14 +350,11 @@ void Reset() RegMasks[4] = 0x0F; } -bool GetBatteryLevelOkay() { return !Registers[1]; } -void SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; } - -void DoSavestate(Savestate* file) +void PowerMan::DoSavestate(Savestate* file) { file->Section("SPPW"); - file->Var32(&Hold); + file->Bool32(&Hold); file->Var32(&DataPos); file->Var8(&Index); file->Var8(&Data); @@ -415,22 +363,15 @@ void DoSavestate(Savestate* file) file->VarArray(RegMasks, 8); // is that needed?? } -u8 Read() -{ - return Data; -} +bool PowerMan::GetBatteryLevelOkay() { return !Registers[1]; } +void PowerMan::SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; } -void Write(u8 val, u32 hold) +void PowerMan::Write(u8 val) { - if (!hold) - { - Hold = 0; - } - - if (hold && (!Hold)) + if (!Hold) { Index = val; - Hold = 1; + Hold = true; Data = 0; DataPos = 1; return; @@ -465,35 +406,19 @@ void Write(u8 val, u32 hold) Data = 0; } -} -namespace SPI_TSC -{ - -u32 DataPos; -u8 ControlByte; -u8 Data; - -u16 ConvResult; - -u16 TouchX, TouchY; - -s16 MicBuffer[1024]; -int MicBufferLen; - - -bool Init() -{ - return true; -} - -void DeInit() +TSC::TSC(SPIHost* host) : SPIDevice(host) { } -void Reset() +TSC::~TSC() { +} + +void TSC::Reset() +{ + Hold = false; ControlByte = 0; Data = 0; @@ -502,7 +427,7 @@ void Reset() MicBufferLen = 0; } -void DoSavestate(Savestate* file) +void TSC::DoSavestate(Savestate* file) { file->Section("SPTS"); @@ -513,7 +438,7 @@ void DoSavestate(Savestate* file) file->Var16(&ConvResult); } -void SetTouchCoords(u16 x, u16 y) +void TSC::SetTouchCoords(u16 x, u16 y) { // scr.x = (adc.x-adc.x1) * (scr.x2-scr.x1) / (adc.x2-adc.x1) + (scr.x1-1) // scr.y = (adc.y-adc.y1) * (scr.y2-scr.y1) / (adc.y2-adc.y1) + (scr.y1-1) @@ -522,13 +447,19 @@ void SetTouchCoords(u16 x, u16 y) TouchX = x; TouchY = y; - if (y == 0xFFF) return; + if (y == 0xFFF) + { + // released + NDS::KeyInput |= (1 << (16+6)); + return; + } TouchX <<= 4; TouchY <<= 4; + NDS::KeyInput &= ~(1 << (16+6)); } -void MicInputFrame(s16* data, int samples) +void TSC::MicInputFrame(s16* data, int samples) { if (!data) { @@ -541,12 +472,7 @@ void MicInputFrame(s16* data, int samples) MicBufferLen = samples; } -u8 Read() -{ - return Data; -} - -void Write(u8 val, u32 hold) +void TSC::Write(u8 val) { if (DataPos == 1) Data = (ConvResult >> 5) & 0xFF; @@ -599,79 +525,71 @@ void Write(u8 val, u32 hold) DataPos++; } + + +SPIHost::SPIHost() +{ + NDS::RegisterEventFunc(NDS::Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone)); + + Devices[SPIDevice_FirmwareMem] = new FirmwareMem(this); + Devices[SPIDevice_PowerMan] = new PowerMan(this); + Devices[SPIDevice_TSC] = nullptr; } - -namespace SPI +SPIHost::~SPIHost() { + for (int i = 0; i < SPIDevice_MAX; i++) + { + if (Devices[i]) + delete Devices[i]; -u16 Cnt; - -u32 CurDevice; // remove me - - -bool Init() -{ - NDS::RegisterEventFunc(NDS::Event_SPITransfer, 0, TransferDone); - - if (!SPI_Firmware::Init()) return false; - if (!SPI_Powerman::Init()) return false; - if (!SPI_TSC::Init()) return false; - if (!DSi_SPI_TSC::Init()) return false; - - return true; -} - -void DeInit() -{ - SPI_Firmware::DeInit(); - SPI_Powerman::DeInit(); - SPI_TSC::DeInit(); - DSi_SPI_TSC::DeInit(); + Devices[i] = nullptr; + } NDS::UnregisterEventFunc(NDS::Event_SPITransfer, 0); } -void Reset() +void SPIHost::Reset() { Cnt = 0; - SPI_Firmware::Reset(); - SPI_Powerman::Reset(); - SPI_TSC::Reset(); - if (NDS::ConsoleType == 1) DSi_SPI_TSC::Reset(); + if (Devices[SPIDevice_TSC]) + delete Devices[SPIDevice_TSC]; + + if (NDS::ConsoleType == 1) + Devices[SPIDevice_TSC] = new DSi_TSC(this); + else + Devices[SPIDevice_TSC] = new TSC(this); + + for (int i = 0; i < SPIDevice_MAX; i++) + { + Devices[i]->Reset(); + } } -void DoSavestate(Savestate* file) +void SPIHost::DoSavestate(Savestate* file) { file->Section("SPIG"); file->Var16(&Cnt); - file->Var32(&CurDevice); - SPI_Firmware::DoSavestate(file); - SPI_Powerman::DoSavestate(file); - SPI_TSC::DoSavestate(file); - if (NDS::ConsoleType == 1) DSi_SPI_TSC::DoSavestate(file); + for (int i = 0; i < SPIDevice_MAX; i++) + { + Devices[i]->DoSavestate(file); + } } -void WriteCnt(u16 val) +void SPIHost::WriteCnt(u16 val) { // turning it off should clear chipselect // TODO: confirm on hardware. libnds expects this, though. if ((Cnt & (1<<15)) && !(val & (1<<15))) { - switch (Cnt & 0x0300) + int dev = (Cnt >> 8) & 0x3; + if (dev < SPIDevice_MAX) { - case 0x0000: SPI_Powerman::Hold = 0; break; - case 0x0100: SPI_Firmware::Hold = 0; break; - case 0x0200: - if (NDS::ConsoleType == 1) - DSi_SPI_TSC::DataPos = 0; - else - SPI_TSC::DataPos = 0; - break; + Devices[dev]->Release(); } } @@ -682,7 +600,7 @@ void WriteCnt(u16 val) if (Cnt & (1<<7)) Log(LogLevel::Warn, "!! CHANGING SPICNT DURING TRANSFER: %04X\n", val); } -void TransferDone(u32 param) +void SPIHost::TransferDone(u32 param) { Cnt &= ~(1<<7); @@ -690,46 +608,40 @@ void TransferDone(u32 param) NDS::SetIRQ(1, NDS::IRQ_SPI); } -u8 ReadData() +u8 SPIHost::ReadData() { if (!(Cnt & (1<<15))) return 0; if (Cnt & (1<<7)) return 0; // checkme - switch (Cnt & 0x0300) + int dev = (Cnt >> 8) & 0x3; + if (dev < SPIDevice_MAX) { - case 0x0000: return SPI_Powerman::Read(); - case 0x0100: return SPI_Firmware::Read(); - case 0x0200: - if (NDS::ConsoleType == 1) - return DSi_SPI_TSC::Read(); - else - return SPI_TSC::Read(); - default: return 0; + return Devices[dev]->Read(); } + + return 0; } -void WriteData(u8 val) +void SPIHost::WriteData(u8 val) { if (!(Cnt & (1<<15))) return; if (Cnt & (1<<7)) return; Cnt |= (1<<7); - switch (Cnt & 0x0300) + + int dev = (Cnt >> 8) & 0x3; + if (dev < SPIDevice_MAX) { - case 0x0000: SPI_Powerman::Write(val, Cnt&(1<<11)); break; - case 0x0100: SPI_Firmware::Write(val, Cnt&(1<<11)); break; - case 0x0200: - if (NDS::ConsoleType == 1) - DSi_SPI_TSC::Write(val, Cnt&(1<<11)); - else - SPI_TSC::Write(val, Cnt&(1<<11)); - break; - default: Log(LogLevel::Warn, "SPI to unknown device %04X %02X\n", Cnt, val); break; + Devices[dev]->Write(val); + if (!(Cnt & (1<<11))) // release chipselect + Devices[dev]->Release(); + } + else + { + Log(LogLevel::Warn, "SPI to unknown device %04X %02X\n", Cnt, val); } // SPI transfers one bit per cycle -> 8 cycles per byte u32 delay = 8 * (8 << (Cnt & 0x3)); NDS::ScheduleEvent(NDS::Event_SPITransfer, false, delay, 0, 0); } - -} diff --git a/src/SPI.h b/src/SPI.h index d8152038..4578ad5f 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -28,61 +28,150 @@ #include "Savestate.h" #include "SPI_Firmware.h" -namespace SPI_Firmware +enum { + SPIDevice_PowerMan = 0, + SPIDevice_FirmwareMem, + SPIDevice_TSC, + + SPIDevice_MAX +}; u16 CRC16(const u8* data, u32 len, u32 start); -void SetupDirectBoot(bool dsi); -u32 FixFirmwareLength(u32 originalLength); +class SPIHost; -/// @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); -void RemoveFirmware(); -} - -namespace SPI_Powerman +class SPIDevice { +public: + SPIDevice(SPIHost* host) : Host(host), Hold(false), DataPos(0) {} + virtual ~SPIDevice() {} -bool GetBatteryLevelOkay(); -void SetBatteryLevelOkay(bool okay); + virtual void Reset() = 0; -} + virtual void DoSavestate(Savestate* file) = 0; -namespace SPI_TSC + virtual u8 Read() { return Data; } + virtual void Write(u8 val) = 0; + virtual void Release() { Hold = false; DataPos = 0; } + +protected: + SPIHost* Host; + + bool Hold; + u32 DataPos; + u8 Data; +}; + +class FirmwareMem : public SPIDevice { +public: + FirmwareMem(SPIHost* host); + ~FirmwareMem() override; -void SetTouchCoords(u16 x, u16 y); -void MicInputFrame(s16* data, int samples); + void Reset() override; -u8 Read(); -void Write(u8 val, u32 hold); + void DoSavestate(Savestate* file) override; -} + void SetupDirectBoot(bool dsi); -namespace SPI + const class Firmware* GetFirmware(); + bool IsLoadedFirmwareBuiltIn(); + bool InstallFirmware(class Firmware&& firmware); + bool InstallFirmware(std::unique_ptr&& firmware); + void RemoveFirmware(); + + void Write(u8 val) override; + void Release() override; + +private: + std::unique_ptr Firmware; + + u8 CurCmd; + + u8 StatusReg; + u32 Addr; + + bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset); +}; + +class PowerMan : public SPIDevice { +public: + PowerMan(SPIHost* host); + ~PowerMan() override; -extern u16 Cnt; + void Reset() override; -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); + void DoSavestate(Savestate* file) override; -void WriteCnt(u16 val); + bool GetBatteryLevelOkay(); + void SetBatteryLevelOkay(bool okay); -u8 ReadData(); -void WriteData(u8 val); + void Write(u8 val) override; -void TransferDone(u32 param); +private: + u8 Index; -} + u8 Registers[8]; + u8 RegMasks[8]; +}; + +class TSC : public SPIDevice +{ +public: + TSC(SPIHost* host); + virtual ~TSC() override; + + virtual void Reset() override; + + virtual void DoSavestate(Savestate* file) override; + + virtual void SetTouchCoords(u16 x, u16 y); + virtual void MicInputFrame(s16* data, int samples); + + virtual void Write(u8 val) override; + +protected: + u8 ControlByte; + + u16 ConvResult; + + u16 TouchX, TouchY; + + s16 MicBuffer[1024]; + int MicBufferLen; +}; + + +class SPIHost +{ +public: + SPIHost(); + ~SPIHost(); + + void Reset(); + + void DoSavestate(Savestate* file); + + FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } + PowerMan* GetPowerMan() { return (PowerMan*)Devices[SPIDevice_PowerMan]; } + TSC* GetTSC() { return (TSC*)Devices[SPIDevice_TSC]; } + + const Firmware* GetFirmware() { return GetFirmwareMem()->GetFirmware(); } + + u16 ReadCnt() { return Cnt; } + void WriteCnt(u16 val); + + u8 ReadData(); + void WriteData(u8 val); + + void TransferDone(u32 param); + +private: + u16 Cnt; + + SPIDevice* Devices[3]; +}; #endif diff --git a/src/SPI_Firmware.cpp b/src/SPI_Firmware.cpp index 44fbfe7d..943a0b32 100644 --- a/src/SPI_Firmware.cpp +++ b/src/SPI_Firmware.cpp @@ -18,6 +18,10 @@ #include "SPI_Firmware.h" #include "SPI.h" +#include "Platform.h" + +using Platform::Log; +using Platform::LogLevel; #include @@ -49,7 +53,7 @@ constexpr u8 CHANDATA[0x3C] constexpr u8 DEFAULT_UNUSED3[6] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 }; -SPI_Firmware::WifiAccessPoint::WifiAccessPoint() +Firmware::WifiAccessPoint::WifiAccessPoint() { memset(Bytes, 0, sizeof(Bytes)); Status = AccessPointStatus::NotConfigured; @@ -57,7 +61,7 @@ SPI_Firmware::WifiAccessPoint::WifiAccessPoint() UpdateChecksum(); } -SPI_Firmware::WifiAccessPoint::WifiAccessPoint(int consoletype) +Firmware::WifiAccessPoint::WifiAccessPoint(int consoletype) { memset(Bytes, 0, sizeof(Bytes)); strncpy(SSID, DEFAULT_SSID, sizeof(SSID)); @@ -67,25 +71,25 @@ SPI_Firmware::WifiAccessPoint::WifiAccessPoint(int consoletype) UpdateChecksum(); } -void SPI_Firmware::WifiAccessPoint::UpdateChecksum() +void Firmware::WifiAccessPoint::UpdateChecksum() { Checksum = CRC16(Bytes, 0xFE, 0x0000); } -SPI_Firmware::ExtendedWifiAccessPoint::ExtendedWifiAccessPoint() +Firmware::ExtendedWifiAccessPoint::ExtendedWifiAccessPoint() { Data.Base = WifiAccessPoint(); UpdateChecksum(); } -void SPI_Firmware::ExtendedWifiAccessPoint::UpdateChecksum() +void Firmware::ExtendedWifiAccessPoint::UpdateChecksum() { Data.Base.UpdateChecksum(); Data.ExtendedChecksum = CRC16(&Bytes[0x100], 0xFE, 0x0000); } -SPI_Firmware::FirmwareHeader::FirmwareHeader(int consoletype) +Firmware::FirmwareHeader::FirmwareHeader(int consoletype) { if (consoletype == 1) { @@ -143,12 +147,12 @@ SPI_Firmware::FirmwareHeader::FirmwareHeader(int consoletype) } -void SPI_Firmware::FirmwareHeader::UpdateChecksum() +void Firmware::FirmwareHeader::UpdateChecksum() { - WifiConfigChecksum = SPI_Firmware::CRC16(&Bytes[0x2C], WifiConfigLength, 0x0000); + WifiConfigChecksum = CRC16(&Bytes[0x2C], WifiConfigLength, 0x0000); } -SPI_Firmware::UserData::UserData() +Firmware::UserData::UserData() { memset(Bytes, 0, 0x74); Version = 5; @@ -160,7 +164,7 @@ SPI_Firmware::UserData::UserData() Checksum = CRC16(Bytes, 0x70, 0xFFFF); } -void SPI_Firmware::UserData::UpdateChecksum() +void Firmware::UserData::UpdateChecksum() { Checksum = CRC16(Bytes, 0x70, 0xFFFF); if (ExtendedSettings.Unknown0 == 0x01) @@ -169,7 +173,30 @@ void SPI_Firmware::UserData::UpdateChecksum() } } -SPI_Firmware::Firmware::Firmware(int consoletype) +u32 Firmware::FixFirmwareLength(u32 originalLength) +{ + if (originalLength != 0x20000 && originalLength != 0x40000 && originalLength != 0x80000) + { + Log(LogLevel::Warn, "Bad firmware size %d, ", originalLength); + + // pick the nearest power-of-two length + originalLength |= (originalLength >> 1); + originalLength |= (originalLength >> 2); + originalLength |= (originalLength >> 4); + originalLength |= (originalLength >> 8); + originalLength |= (originalLength >> 16); + originalLength++; + + // ensure it's a sane length + if (originalLength > 0x80000) originalLength = 0x80000; + else if (originalLength < 0x20000) originalLength = 0x20000; + + Log(LogLevel::Debug, "assuming %d\n", originalLength); + } + return originalLength; +} + +Firmware::Firmware(int consoletype) { FirmwareBufferLength = DEFAULT_FIRMWARE_LENGTH; FirmwareBuffer = new u8[FirmwareBufferLength]; @@ -184,16 +211,16 @@ SPI_Firmware::Firmware::Firmware(int consoletype) // user data header.UserSettingsOffset = (0x7FE00 & FirmwareMask) >> 3; - std::array& settings = *reinterpret_cast*>(UserDataPosition()); + std::array& settings = *reinterpret_cast*>(GetUserDataPosition()); settings = { - SPI_Firmware::UserData(), - SPI_Firmware::UserData(), + UserData(), + UserData(), }; // wifi access points // TODO: WFC ID?? - std::array& accesspoints = *reinterpret_cast*>(WifiAccessPointPosition()); + std::array& accesspoints = *reinterpret_cast*>(GetWifiAccessPointPosition()); accesspoints = { WifiAccessPoint(consoletype), @@ -203,7 +230,7 @@ SPI_Firmware::Firmware::Firmware(int consoletype) if (consoletype == 1) { - std::array& extendedaccesspoints = *reinterpret_cast*>(ExtendedAccessPointPosition()); + std::array& extendedaccesspoints = *reinterpret_cast*>(GetExtendedAccessPointPosition()); extendedaccesspoints = { ExtendedWifiAccessPoint(), @@ -213,7 +240,7 @@ SPI_Firmware::Firmware::Firmware(int consoletype) } } -SPI_Firmware::Firmware::Firmware(Platform::FileHandle* file) : FirmwareBuffer(nullptr), FirmwareBufferLength(0), FirmwareMask(0) +Firmware::Firmware(Platform::FileHandle* file) : FirmwareBuffer(nullptr), FirmwareBufferLength(0), FirmwareMask(0) { if (file) { @@ -239,7 +266,7 @@ SPI_Firmware::Firmware::Firmware(Platform::FileHandle* file) : FirmwareBuffer(nu } } -SPI_Firmware::Firmware::Firmware(const u8* data, u32 length) : FirmwareBuffer(nullptr), FirmwareBufferLength(FixFirmwareLength(length)) +Firmware::Firmware(const u8* data, u32 length) : FirmwareBuffer(nullptr), FirmwareBufferLength(FixFirmwareLength(length)) { if (data) { @@ -249,14 +276,14 @@ SPI_Firmware::Firmware::Firmware(const u8* data, u32 length) : FirmwareBuffer(nu } } -SPI_Firmware::Firmware::Firmware(const Firmware& other) : FirmwareBuffer(nullptr), FirmwareBufferLength(other.FirmwareBufferLength) +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 +Firmware::Firmware(Firmware&& other) noexcept { FirmwareBuffer = other.FirmwareBuffer; FirmwareBufferLength = other.FirmwareBufferLength; @@ -266,7 +293,7 @@ SPI_Firmware::Firmware::Firmware(Firmware&& other) noexcept other.FirmwareMask = 0; } -SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(const Firmware& other) +Firmware& Firmware::operator=(const Firmware& other) { if (this != &other) { @@ -280,7 +307,7 @@ SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(const Firmware& other) return *this; } -SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(Firmware&& other) noexcept +Firmware& Firmware::operator=(Firmware&& other) noexcept { if (this != &other) { @@ -296,21 +323,21 @@ SPI_Firmware::Firmware& SPI_Firmware::Firmware::operator=(Firmware&& other) noex return *this; } -SPI_Firmware::Firmware::~Firmware() +Firmware::~Firmware() { delete[] FirmwareBuffer; } -bool SPI_Firmware::Firmware::IsBootable() const +bool Firmware::IsBootable() const { return FirmwareBufferLength != DEFAULT_FIRMWARE_LENGTH && - Header().Identifier != GENERATED_FIRMWARE_IDENTIFIER + GetHeader().Identifier != GENERATED_FIRMWARE_IDENTIFIER ; } -const SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() const { - const std::array& userdata = UserData(); +const Firmware::UserData& Firmware::GetEffectiveUserData() const { + const std::array& userdata = GetUserData(); bool userdata0ChecksumOk = userdata[0].ChecksumValid(); bool userdata1ChecksumOk = userdata[1].ChecksumValid(); @@ -332,8 +359,8 @@ const SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() const } } -SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() { - std::array& userdata = UserData(); +Firmware::UserData& Firmware::GetEffectiveUserData() { + std::array& userdata = GetUserData(); bool userdata0ChecksumOk = userdata[0].ChecksumValid(); bool userdata1ChecksumOk = userdata[1].ChecksumValid(); @@ -355,25 +382,25 @@ SPI_Firmware::UserData& SPI_Firmware::Firmware::EffectiveUserData() { } } -void SPI_Firmware::Firmware::UpdateChecksums() +void Firmware::UpdateChecksums() { - Header().UpdateChecksum(); + GetHeader().UpdateChecksum(); - for (SPI_Firmware::WifiAccessPoint& ap : AccessPoints()) + for (auto& ap : GetAccessPoints()) { ap.UpdateChecksum(); } - if (Header().ConsoleType == FirmwareConsoleType::DSi) + if (GetHeader().ConsoleType == FirmwareConsoleType::DSi) { - for (SPI_Firmware::ExtendedWifiAccessPoint& eap : ExtendedAccessPoints()) + for (auto& eap : GetExtendedAccessPoints()) { eap.UpdateChecksum(); } } - for (SPI_Firmware::UserData& u : UserData()) + for (auto& u : GetUserData()) { u.UpdateChecksum(); } -} \ No newline at end of file +} diff --git a/src/SPI_Firmware.h b/src/SPI_Firmware.h index 85576047..ad66a0a4 100644 --- a/src/SPI_Firmware.h +++ b/src/SPI_Firmware.h @@ -24,10 +24,10 @@ #include "types.h" #include "Platform.h" -namespace SPI_Firmware -{ - u16 CRC16(const u8* data, u32 len, u32 start); + + + using MacAddress = std::array; using IpAddress = std::array; @@ -44,382 +44,384 @@ constexpr const char* const DEFAULT_SSID = "melonAP"; */ 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; using MacAddress = std::array; 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 RTCClockAdjust; - 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: + + 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, + }; + + /** + * @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]; + + 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 RTCClockAdjust; + 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"); + /** * Constructs a default firmware blob * filled with data necessary for booting and configuring NDS games. @@ -449,28 +451,28 @@ public: Firmware& operator=(Firmware&& other) noexcept; ~Firmware(); - [[nodiscard]] FirmwareHeader& Header() { return *reinterpret_cast(FirmwareBuffer); } - [[nodiscard]] const FirmwareHeader& Header() const { return *reinterpret_cast(FirmwareBuffer); } + [[nodiscard]] FirmwareHeader& GetHeader() { return *reinterpret_cast(FirmwareBuffer); } + [[nodiscard]] const FirmwareHeader& GetHeader() const { return *reinterpret_cast(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; } + [[nodiscard]] u32 GetWifiAccessPointOffset() const { return GetUserDataOffset() - 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]] u8* GetWifiAccessPointPosition() { return FirmwareBuffer + GetWifiAccessPointOffset(); } + [[nodiscard]] const u8* GetWifiAccessPointPosition() const { return FirmwareBuffer + GetWifiAccessPointOffset(); } - [[nodiscard]] const std::array& AccessPoints() const + [[nodiscard]] const std::array& GetAccessPoints() const { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(WifiAccessPointPosition()); + return *reinterpret_cast*>(GetWifiAccessPointPosition()); } - [[nodiscard]] std::array& AccessPoints() + [[nodiscard]] std::array& GetAccessPoints() { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(WifiAccessPointPosition()); + return *reinterpret_cast*>(GetWifiAccessPointPosition()); } /// @returns \c true if this firmware image contains bootable code. @@ -481,20 +483,20 @@ public: /// @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]] u32 GetExtendedAccessPointOffset() const { return GetUserDataOffset() + EXTENDED_WIFI_SETTINGS_OFFSET; } + [[nodiscard]] u8* GetExtendedAccessPointPosition() { return FirmwareBuffer + GetExtendedAccessPointOffset(); } + [[nodiscard]] const u8* GetExtendedAccessPointPosition() const { return FirmwareBuffer + GetExtendedAccessPointOffset(); } - [[nodiscard]] const std::array& ExtendedAccessPoints() const + [[nodiscard]] const std::array& GetExtendedAccessPoints() const { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(ExtendedAccessPointPosition()); + return *reinterpret_cast*>(GetExtendedAccessPointPosition()); } - [[nodiscard]] std::array& ExtendedAccessPoints() + [[nodiscard]] std::array& GetExtendedAccessPoints() { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(ExtendedAccessPointPosition()); + return *reinterpret_cast*>(GetExtendedAccessPointPosition()); } /// @return The pointer to the firmware buffer, @@ -508,21 +510,21 @@ public: /// @return The offset of the first user data section in the firmware. /// @see UserDataPosition - [[nodiscard]] u32 UserDataOffset() const { return Header().UserSettingsOffset << 3; } + [[nodiscard]] u32 GetUserDataOffset() const { return GetHeader().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(); } + [[nodiscard]] u8* GetUserDataPosition() { return FirmwareBuffer + GetUserDataOffset(); } + [[nodiscard]] const u8* GetUserDataPosition() const { return FirmwareBuffer + GetUserDataOffset(); } /// @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& UserData() const + [[nodiscard]] const std::array& GetUserData() const { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(UserDataPosition()); + return *reinterpret_cast*>(GetUserDataPosition()); }; /** @@ -531,10 +533,10 @@ public: * 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& UserData() + [[nodiscard]] std::array& GetUserData() { // An std::array is a wrapper around a C array, so this cast is fine. - return *reinterpret_cast*>(UserDataPosition()); + return *reinterpret_cast*>(GetUserDataPosition()); } /** @@ -543,13 +545,16 @@ public: * 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; + [[nodiscard]] const union UserData& GetEffectiveUserData() const; /** * @return Reference to whichever of the two user data sections * has the highest update counter. */ - [[nodiscard]] union UserData& EffectiveUserData(); + [[nodiscard]] union UserData& GetEffectiveUserData(); + + /// Fix the given firmware length to an acceptable length + u32 FixFirmwareLength(u32 originalLength); /// Updates the checksums of all used sections of the firmware. void UpdateChecksums(); @@ -558,6 +563,5 @@ private: u32 FirmwareBufferLength; u32 FirmwareMask; }; -} #endif //MELONDS_SPI_FIRMWARE_H diff --git a/src/Wifi.cpp b/src/Wifi.cpp index c5a67abf..7f57c4bb 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -180,7 +180,6 @@ void DeInit() void Reset() { - using namespace SPI_Firmware; memset(RAM, 0, 0x2000); memset(IO, 0, 0x1000); @@ -220,15 +219,17 @@ void Reset() } #undef BBREG_FIXED - RFVersion = GetFirmware()->Header().RFChipType; + const Firmware* fw = NDS::SPI->GetFirmware(); + + RFVersion = fw->GetHeader().RFChipType; memset(RFRegs, 0, 4*0x40); - FirmwareConsoleType console = GetFirmware()->Header().ConsoleType; - if (console == FirmwareConsoleType::DS) + Firmware::FirmwareConsoleType console = fw->GetHeader().ConsoleType; + if (console == Firmware::FirmwareConsoleType::DS) IOPORT(0x000) = 0x1440; - else if (console == FirmwareConsoleType::DSLite) + else if (console == Firmware::FirmwareConsoleType::DSLite) IOPORT(0x000) = 0xC340; - else if (NDS::ConsoleType == 1 && console == FirmwareConsoleType::DSi) + else if (NDS::ConsoleType == 1 && console == Firmware::FirmwareConsoleType::DSi) IOPORT(0x000) = 0xC340; // DSi has the modern DS-wifi variant else { diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 252594c7..951e1f54 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -580,31 +580,31 @@ 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) +void WriteFirmware(const Firmware& firmware, u32 writeoffset, u32 writelen) { if (!ROMManager::FirmwareSave) return; - if (firmware.Header().Identifier != SPI_Firmware::GENERATED_FIRMWARE_IDENTIFIER) + if (firmware.GetHeader().Identifier != 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 eapstart = firmware.GetExtendedAccessPointOffset(); + u32 eapend = eapstart + sizeof(firmware.GetExtendedAccessPoints()); - u32 apstart = firmware.WifiAccessPointOffset(); - u32 apend = apstart + sizeof(firmware.AccessPoints()); + u32 apstart = firmware.GetWifiAccessPointOffset(); + u32 apend = apstart + sizeof(firmware.GetAccessPoints()); // 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()); + const u8* buffer = firmware.GetExtendedAccessPointPosition(); + u32 length = sizeof(firmware.GetExtendedAccessPoints()) + sizeof(firmware.GetAccessPoints()); ROMManager::FirmwareSave->RequestFlush(buffer, length, writeoffset - eapstart, writelen); } } diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp index 89f74e5f..033c2fef 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp @@ -49,7 +49,7 @@ PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent), { ui->grpDSiBattery->setEnabled(false); - oldDSBatteryLevel = SPI_Powerman::GetBatteryLevelOkay(); + oldDSBatteryLevel = NDS::SPI->GetPowerMan()->GetBatteryLevelOkay(); } updateDSBatteryLevelControls(); @@ -91,7 +91,7 @@ void PowerManagementDialog::done(int r) } else { - Config::DSBatteryLevelOkay = SPI_Powerman::GetBatteryLevelOkay(); + Config::DSBatteryLevelOkay = NDS::SPI->GetPowerMan()->GetBatteryLevelOkay(); } } else @@ -103,7 +103,7 @@ void PowerManagementDialog::done(int r) } else { - SPI_Powerman::SetBatteryLevelOkay(oldDSBatteryLevel); + NDS::SPI->GetPowerMan()->SetBatteryLevelOkay(oldDSBatteryLevel); } } @@ -114,17 +114,17 @@ void PowerManagementDialog::done(int r) void PowerManagementDialog::on_rbDSBatteryLow_clicked() { - SPI_Powerman::SetBatteryLevelOkay(false); + NDS::SPI->GetPowerMan()->SetBatteryLevelOkay(false); } void PowerManagementDialog::on_rbDSBatteryOkay_clicked() { - SPI_Powerman::SetBatteryLevelOkay(true); + NDS::SPI->GetPowerMan()->SetBatteryLevelOkay(true); } void PowerManagementDialog::updateDSBatteryLevelControls() { - if (SPI_Powerman::GetBatteryLevelOkay()) + if (NDS::SPI->GetPowerMan()->GetBatteryLevelOkay()) ui->rbDSBatteryOkay->setChecked(true); else ui->rbDSBatteryLow->setChecked(true); diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 648ab676..9b4b49c0 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -588,7 +588,7 @@ void SetBatteryLevels() } else { - SPI_Powerman::SetBatteryLevelOkay(Config::DSBatteryLevelOkay); + NDS::SPI->GetPowerMan()->SetBatteryLevelOkay(Config::DSBatteryLevelOkay); } } @@ -792,10 +792,10 @@ 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, string> LoadFirmwareFromFile() +pair, string> LoadFirmwareFromFile() { string loadedpath; - unique_ptr firmware = nullptr; + unique_ptr firmware = nullptr; string firmwarepath = Config::ConsoleType == 0 ? Config::FirmwarePath : Config::DSiFirmwarePath; Log(LogLevel::Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str()); @@ -812,7 +812,7 @@ pair, string> LoadFirmwareFromFile() if (f) { - firmware = make_unique(f); + firmware = make_unique(f); if (!firmware->Buffer()) { Log(LogLevel::Warn, "Couldn't read firmware file!\n"); @@ -826,9 +826,8 @@ pair, string> LoadFirmwareFromFile() return std::make_pair(std::move(firmware), loadedpath); } -pair, string> GenerateDefaultFirmware() +pair, string> GenerateDefaultFirmware() { - using namespace SPI_Firmware; // Construct the default firmware... string settingspath; std::unique_ptr firmware = std::make_unique(Config::ConsoleType); @@ -850,27 +849,27 @@ pair, string> GenerateDefaultFirmware() // 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)); + constexpr unsigned TOTAL_WFC_SETTINGS_SIZE = 3 * (sizeof(Firmware::WifiAccessPoint) + sizeof(Firmware::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 (!FileRead(firmware->GetExtendedAccessPointPosition(), 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->GetAccessPoints() = { + Firmware::WifiAccessPoint(Config::ConsoleType), + Firmware::WifiAccessPoint(), + Firmware::WifiAccessPoint(), }; - firmware->ExtendedAccessPoints() = { - ExtendedWifiAccessPoint(), - ExtendedWifiAccessPoint(), - ExtendedWifiAccessPoint(), + firmware->GetExtendedAccessPoints() = { + Firmware::ExtendedWifiAccessPoint(), + Firmware::ExtendedWifiAccessPoint(), + Firmware::ExtendedWifiAccessPoint(), }; } @@ -884,10 +883,9 @@ pair, string> GenerateDefaultFirmware() return std::make_pair(std::move(firmware), std::move(wfcsettingspath)); } -void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) +void LoadUserSettingsFromConfig(Firmware& firmware) { - using namespace SPI_Firmware; - UserData& currentData = firmware.EffectiveUserData(); + auto& currentData = firmware.GetEffectiveUserData(); // setting up username std::string orig_username = Config::FirmwareUsername; @@ -899,10 +897,10 @@ void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) memcpy(currentData.Nickname, username.data(), usernameLength * sizeof(char16_t)); } - auto language = static_cast(Config::FirmwareLanguage); - if (language != Language::Reserved) + auto language = static_cast(Config::FirmwareLanguage); + if (language != Firmware::Language::Reserved) { // If the frontend specifies a language (rather than using the existing value)... - currentData.Settings &= ~Language::Reserved; // ..clear the existing language... + currentData.Settings &= ~Firmware::Language::Reserved; // ..clear the existing language... currentData.Settings |= language; // ...and set the new one. } @@ -937,7 +935,7 @@ void LoadUserSettingsFromConfig(SPI_Firmware::Firmware& firmware) MacAddress mac; bool rep = false; - auto& header = firmware.Header(); + auto& header = firmware.GetHeader(); memcpy(&mac, header.MacAddress.data(), sizeof(MacAddress)); @@ -1035,7 +1033,7 @@ bool InstallNAND(const u8* es_keyY) memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t)); // setting language - settings.Language = static_cast(Config::FirmwareLanguage); + settings.Language = static_cast(Config::FirmwareLanguage); // setting up color settings.FavoriteColor = Config::FirmwareFavouriteColour; @@ -1074,7 +1072,6 @@ bool InstallNAND(const u8* es_keyY) bool InstallFirmware() { - using namespace SPI_Firmware; FirmwareSave.reset(); unique_ptr firmware; string firmwarepath; @@ -1105,7 +1102,7 @@ bool InstallFirmware() FirmwareSave = std::make_unique(firmwarepath); - return InstallFirmware(std::move(firmware)); + return NDS::SPI->GetFirmwareMem()->InstallFirmware(std::move(firmware)); } bool LoadROM(QStringList filepath, bool reset) From e4f4e94694f4e4aaac5b78704fee1c39f11dd034 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 3 Nov 2023 21:20:09 +0100 Subject: [PATCH 020/157] convert RTC to OOP --- src/NDS.cpp | 23 +++--- src/NDS.h | 2 + src/RTC.cpp | 93 ++++++++-------------- src/RTC.h | 121 ++++++++++++++++++++--------- src/frontend/qt_sdl/ROMManager.cpp | 4 +- src/frontend/qt_sdl/main.cpp | 4 +- 6 files changed, 138 insertions(+), 109 deletions(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index 9b9e1e71..2e71c274 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -179,6 +179,7 @@ u16 KeyCnt[2]; u16 RCnt; SPIHost* SPI; +class RTC* RTC; bool Running; @@ -218,12 +219,12 @@ bool Init() DMAs[7] = new DMA(1, 3); SPI = new SPIHost(); + RTC = new class RTC(); if (!NDSCart::Init()) return false; if (!GBACart::Init()) return false; if (!GPU::Init()) return false; if (!SPU::Init()) return false; - if (!RTC::Init()) return false; if (!Wifi::Init()) return false; if (!DSi::Init()) return false; @@ -254,11 +255,13 @@ void DeInit() delete SPI; SPI = nullptr; + delete RTC; + RTC = nullptr; + NDSCart::DeInit(); GBACart::DeInit(); GPU::DeInit(); SPU::DeInit(); - RTC::DeInit(); Wifi::DeInit(); DSi::DeInit(); @@ -647,7 +650,7 @@ void Reset() GPU::Reset(); SPU::Reset(); SPI->Reset(); - RTC::Reset(); + RTC->Reset(); Wifi::Reset(); // TODO: move the SOUNDBIAS/degrade logic to SPU? @@ -849,7 +852,7 @@ bool DoSavestate(Savestate* file) GPU::DoSavestate(file); SPU::DoSavestate(file); SPI->DoSavestate(file); - RTC::DoSavestate(file); + RTC->DoSavestate(file); Wifi::DoSavestate(file); if (ConsoleType == 1) @@ -3866,7 +3869,7 @@ u8 ARM7IORead8(u32 addr) case 0x04000136: return (KeyInput >> 16) & 0xFF; case 0x04000137: return KeyInput >> 24; - case 0x04000138: return RTC::Read() & 0xFF; + case 0x04000138: return RTC->Read() & 0xFF; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) @@ -3957,7 +3960,7 @@ u16 ARM7IORead16(u32 addr) case 0x04000134: return RCnt; case 0x04000136: return KeyInput >> 16; - case 0x04000138: return RTC::Read(); + case 0x04000138: return RTC->Read(); case 0x04000180: return IPCSync7; case 0x04000184: @@ -4047,7 +4050,7 @@ u32 ARM7IORead32(u32 addr) case 0x04000130: return (KeyInput & 0xFFFF) | (KeyCnt[1] << 16); case 0x04000134: return RCnt | (KeyInput & 0xFFFF0000); - case 0x04000138: return RTC::Read(); + case 0x04000138: return RTC->Read(); case 0x04000180: return IPCSync7; case 0x04000184: return ARM7IORead16(addr); @@ -4139,7 +4142,7 @@ void ARM7IOWrite8(u32 addr, u8 val) RCnt = (RCnt & 0x00FF) | (val << 8); return; - case 0x04000138: RTC::Write(val, true); return; + case 0x04000138: RTC->Write(val, true); return; case 0x04000188: ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); @@ -4227,7 +4230,7 @@ void ARM7IOWrite16(u32 addr, u16 val) case 0x04000132: KeyCnt[1] = val; return; case 0x04000134: RCnt = val; return; - case 0x04000138: RTC::Write(val, false); return; + case 0x04000138: RTC->Write(val, false); return; case 0x04000180: IPCSync9 &= 0xFFF0; @@ -4395,7 +4398,7 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x04000130: KeyCnt[1] = val >> 16; return; case 0x04000134: RCnt = val & 0xFFFF; return; - case 0x04000138: RTC::Write(val & 0xFFFF, false); return; + case 0x04000138: RTC->Write(val & 0xFFFF, false); return; case 0x04000180: case 0x04000184: diff --git a/src/NDS.h b/src/NDS.h index b0126ed3..a3e74b66 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -31,6 +31,7 @@ //#define DEBUG_CHECK_DESYNC class SPIHost; +class RTC; namespace NDS { @@ -249,6 +250,7 @@ extern u32 KeyInput; extern u16 RCnt; extern SPIHost* SPI; +extern class RTC* RTC; const u32 ARM7WRAMSize = 0x10000; extern u8* ARM7WRAM; diff --git a/src/RTC.cpp b/src/RTC.cpp index 527b7f47..30eab006 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -24,52 +24,29 @@ using Platform::Log; using Platform::LogLevel; -namespace RTC -{ -/// This value represents the Nintendo DS IO register, -/// \em not the value of the system's clock. -/// The actual system time is taken directly from the host. -u16 IO; - -u8 Input; -u32 InputBit; -u32 InputPos; - -u8 Output[8]; -u32 OutputBit; -u32 OutputPos; - -u8 CurCmd; - -StateData State; - -s32 TimerError; -u32 ClockCount; void WriteDateTime(int num, u8 val); -bool Init() +RTC::RTC() { - NDS::RegisterEventFunc(NDS::Event_RTC, 0, ClockTimer); + NDS::RegisterEventFunc(NDS::Event_RTC, 0, MemberEventFunc(RTC, ClockTimer)); ResetState(); // indicate the power was off // this will be changed if a previously saved RTC state is loaded State.StatusReg1 = 0x80; - - return true; } -void DeInit() +RTC::~RTC() { NDS::UnregisterEventFunc(NDS::Event_RTC, 0); } -void Reset() +void RTC::Reset() { Input = 0; InputBit = 0; @@ -84,7 +61,7 @@ void Reset() ScheduleTimer(true); } -void DoSavestate(Savestate* file) +void RTC::DoSavestate(Savestate* file) { file->Section("RTC."); @@ -107,17 +84,17 @@ void DoSavestate(Savestate* file) } -u8 BCD(u8 val) +u8 RTC::BCD(u8 val) { return (val % 10) | ((val / 10) << 4); } -u8 FromBCD(u8 val) +u8 RTC::FromBCD(u8 val) { return (val & 0xF) + ((val >> 4) * 10); } -u8 BCDIncrement(u8 val) +u8 RTC::BCDIncrement(u8 val) { val++; if ((val & 0x0F) >= 0x0A) @@ -127,7 +104,7 @@ u8 BCDIncrement(u8 val) return val; } -u8 BCDSanitize(u8 val, u8 vmin, u8 vmax) +u8 RTC::BCDSanitize(u8 val, u8 vmin, u8 vmax) { if (val < vmin || val > vmax) val = vmin; @@ -140,12 +117,12 @@ u8 BCDSanitize(u8 val, u8 vmin, u8 vmax) } -void GetState(StateData& state) +void RTC::GetState(StateData& state) { memcpy(&state, &State, sizeof(State)); } -void SetState(StateData& state) +void RTC::SetState(StateData& state) { memcpy(&State, &state, sizeof(State)); @@ -155,7 +132,7 @@ void SetState(StateData& state) WriteDateTime(i+1, State.DateTime[i]); } -void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) +void RTC::GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) { year = FromBCD(State.DateTime[0]); year += 2000; @@ -176,7 +153,7 @@ void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& s second = FromBCD(State.DateTime[6] & 0x7F); } -void SetDateTime(int year, int month, int day, int hour, int minute, int second) +void RTC::SetDateTime(int year, int month, int day, int hour, int minute, int second) { int monthdays[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; @@ -226,7 +203,7 @@ void SetDateTime(int year, int month, int day, int hour, int minute, int second) State.StatusReg1 &= ~0x80; } -void ResetState() +void RTC::ResetState() { memset(&State, 0, sizeof(State)); State.DateTime[1] = 1; @@ -234,7 +211,7 @@ void ResetState() } -void SetIRQ(u8 irq) +void RTC::SetIRQ(u8 irq) { u8 oldstat = State.IRQFlag; State.IRQFlag |= irq; @@ -250,12 +227,12 @@ void SetIRQ(u8 irq) } } -void ClearIRQ(u8 irq) +void RTC::ClearIRQ(u8 irq) { State.IRQFlag &= ~irq; } -void ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write +void RTC::ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write { // INT1 @@ -395,7 +372,7 @@ void ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write } -u8 DaysInMonth() +u8 RTC::DaysInMonth() { u8 numdays; @@ -438,12 +415,12 @@ u8 DaysInMonth() return numdays; } -void CountYear() +void RTC::CountYear() { State.DateTime[0] = BCDIncrement(State.DateTime[0]); } -void CountMonth() +void RTC::CountMonth() { State.DateTime[1] = BCDIncrement(State.DateTime[1]); if (State.DateTime[1] > 0x12) @@ -453,7 +430,7 @@ void CountMonth() } } -void CheckEndOfMonth() +void RTC::CheckEndOfMonth() { if (State.DateTime[2] > DaysInMonth()) { @@ -462,7 +439,7 @@ void CheckEndOfMonth() } } -void CountDay() +void RTC::CountDay() { // day-of-week counter State.DateTime[3]++; @@ -474,7 +451,7 @@ void CountDay() CheckEndOfMonth(); } -void CountHour() +void RTC::CountHour() { u8 hour = BCDIncrement(State.DateTime[4] & 0x3F); u8 pm = State.DateTime[4] & 0x40; @@ -506,7 +483,7 @@ void CountHour() State.DateTime[4] = hour | pm; } -void CountMinute() +void RTC::CountMinute() { State.MinuteCount++; State.DateTime[5] = BCDIncrement(State.DateTime[5]); @@ -520,7 +497,7 @@ void CountMinute() ProcessIRQ(0); } -void CountSecond() +void RTC::CountSecond() { State.DateTime[6] = BCDIncrement(State.DateTime[6]); if (State.DateTime[6] >= 0x60) @@ -531,7 +508,7 @@ void CountSecond() } -void ScheduleTimer(bool first) +void RTC::ScheduleTimer(bool first) { if (first) TimerError = 0; @@ -544,7 +521,7 @@ void ScheduleTimer(bool first) NDS::ScheduleEvent(NDS::Event_RTC, !first, delay, 0, 0); } -void ClockTimer(u32 param) +void RTC::ClockTimer(u32 param) { ClockCount++; @@ -565,7 +542,7 @@ void ClockTimer(u32 param) } -void WriteDateTime(int num, u8 val) +void RTC::WriteDateTime(int num, u8 val) { switch (num) { @@ -619,14 +596,14 @@ void WriteDateTime(int num, u8 val) } } -void SaveDateTime() +void RTC::SaveDateTime() { int y, m, d, h, i, s; GetDateTime(y, m, d, h, i, s); Platform::WriteDateTime(y, m, d, h, i, s); } -void CmdRead() +void RTC::CmdRead() { if ((CurCmd & 0x0F) == 0x06) { @@ -704,7 +681,7 @@ void CmdRead() Log(LogLevel::Debug, "RTC: unknown read command %02X\n", CurCmd); } -void CmdWrite(u8 val) +void RTC::CmdWrite(u8 val) { if ((CurCmd & 0x0F) == 0x06) { @@ -861,7 +838,7 @@ void CmdWrite(u8 val) Log(LogLevel::Debug, "RTC: unknown write command %02X\n", CurCmd); } -void ByteIn(u8 val) +void RTC::ByteIn(u8 val) { if (InputPos == 0) { @@ -895,13 +872,13 @@ void ByteIn(u8 val) } -u16 Read() +u16 RTC::Read() { //printf("RTC READ %04X\n", IO); return IO; } -void Write(u16 val, bool byte) +void RTC::Write(u16 val, bool byte) { if (byte) val |= (IO & 0xFF00); @@ -963,5 +940,3 @@ void Write(u16 val, bool byte) else IO = (IO & 0x0001) | (val & 0xFFFE); } - -} diff --git a/src/RTC.h b/src/RTC.h index ff842711..db78f676 100644 --- a/src/RTC.h +++ b/src/RTC.h @@ -22,46 +22,95 @@ #include "types.h" #include "Savestate.h" -namespace RTC +class RTC { +public: -struct StateData -{ - u8 StatusReg1; - u8 StatusReg2; - u8 DateTime[7]; - u8 Alarm1[3]; - u8 Alarm2[3]; - u8 ClockAdjust; - u8 FreeReg; + struct StateData + { + u8 StatusReg1; + u8 StatusReg2; + u8 DateTime[7]; + u8 Alarm1[3]; + u8 Alarm2[3]; + u8 ClockAdjust; + u8 FreeReg; - u8 IRQFlag; + u8 IRQFlag; - // DSi registers - u32 MinuteCount; - u8 FOUT1; - u8 FOUT2; - u8 AlarmDate1[3]; - u8 AlarmDate2[3]; + // DSi registers + u32 MinuteCount; + u8 FOUT1; + u8 FOUT2; + u8 AlarmDate1[3]; + u8 AlarmDate2[3]; + }; + + RTC(); + ~RTC(); + + void Reset(); + + void DoSavestate(Savestate* file); + + void GetState(StateData& state); + void SetState(StateData& state); + void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second); + void SetDateTime(int year, int month, int day, int hour, int minute, int second); + + void ClockTimer(u32 param); + + u16 Read(); + void Write(u16 val, bool byte); + +private: + /// This value represents the Nintendo DS IO register, + /// \em not the value of the system's clock. + /// The actual system time is taken directly from the host. + u16 IO; + + u8 Input; + u32 InputBit; + u32 InputPos; + + u8 Output[8]; + u32 OutputBit; + u32 OutputPos; + + u8 CurCmd; + + StateData State; + + s32 TimerError; + u32 ClockCount; + + void ResetState(); + void ScheduleTimer(bool first); + + u8 BCD(u8 val); + u8 FromBCD(u8 val); + u8 BCDIncrement(u8 val); + u8 BCDSanitize(u8 val, u8 vmin, u8 vmax); + + void SetIRQ(u8 irq); + void ClearIRQ(u8 irq); + void ProcessIRQ(int type); + + u8 DaysInMonth(); + void CountYear(); + void CountMonth(); + void CheckEndOfMonth(); + void CountDay(); + void CountHour(); + void CountMinute(); + void CountSecond(); + + void WriteDateTime(int num, u8 val); + void SaveDateTime(); + + void CmdRead(); + void CmdWrite(u8 val); + void ByteIn(u8 val); }; -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); - -void GetState(StateData& state); -void SetState(StateData& state); -void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second); -void SetDateTime(int year, int month, int day, int hour, int minute, int second); -void ResetState(); - -void ScheduleTimer(bool first); -void ClockTimer(u32 param); - -u16 Read(); -void Write(u16 val, bool byte); - -} - #endif diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 9b4b49c0..19ae9543 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -597,8 +597,8 @@ void SetDateTime() QDateTime hosttime = QDateTime::currentDateTime(); QDateTime time = hosttime.addSecs(Config::RTCOffset); - RTC::SetDateTime(time.date().year(), time.date().month(), time.date().day(), - time.time().hour(), time.time().minute(), time.time().second()); + NDS::RTC->SetDateTime(time.date().year(), time.date().month(), time.date().day(), + time.time().hour(), time.time().minute(), time.time().second()); } void Reset() diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index f6704779..0945de2a 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -361,7 +361,7 @@ void EmuThread::run() RTC::StateData state; Platform::FileRead(&state, sizeof(state), 1, file); Platform::CloseFile(file); - RTC::SetState(state); + NDS::RTC->SetState(state); } char melontitle[100]; @@ -666,7 +666,7 @@ void EmuThread::run() if (file) { RTC::StateData state; - RTC::GetState(state); + NDS::RTC->GetState(state); Platform::FileWrite(&state, sizeof(state), 1, file); Platform::CloseFile(file); } From f188c2cf1a275dbe9d79683e3d66b765eb816804 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 3 Nov 2023 21:33:13 +0100 Subject: [PATCH 021/157] hopefully get the compiler to shut up --- src/DSi_NWifi.cpp | 2 +- src/SPI.cpp | 4 ++-- src/SPI.h | 2 +- src/SPI_Firmware.cpp | 2 +- src/SPI_Firmware.h | 3 +-- src/frontend/qt_sdl/ROMManager.cpp | 4 ++-- 6 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index d95def5a..76e707d9 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -163,7 +163,7 @@ void DSi_NWifi::Reset() const Firmware* fw = NDS::SPI->GetFirmware(); - MacAddress mac = fw->GetHeader().MacAddress; + MacAddress mac = fw->GetHeader().MacAddr; Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); diff --git a/src/SPI.cpp b/src/SPI.cpp index 8eb8b0e0..8432b192 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -98,7 +98,7 @@ void FirmwareMem::Reset() // disable autoboot //Firmware[userdata+0x64] &= 0xBF; - MacAddress mac = Firmware->GetHeader().MacAddress; + MacAddress mac = Firmware->GetHeader().MacAddr; 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 @@ -139,7 +139,7 @@ void FirmwareMem::SetupDirectBoot(bool dsi) if (dsi) { for (u32 i = 0; i < 6; i += 2) - DSi::ARM9Write16(0x02FFFCF4, *(u16*)&header.MacAddress[i]); // MAC address + DSi::ARM9Write16(0x02FFFCF4, *(u16*)&header.MacAddr[i]); // MAC address // checkme DSi::ARM9Write16(0x02FFFCFA, header.EnabledChannels); // enabled channels diff --git a/src/SPI.h b/src/SPI.h index 4578ad5f..2fde2269 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -85,7 +85,7 @@ public: void Release() override; private: - std::unique_ptr Firmware; + std::unique_ptr Firmware; u8 CurCmd; diff --git a/src/SPI_Firmware.cpp b/src/SPI_Firmware.cpp index 943a0b32..55071453 100644 --- a/src/SPI_Firmware.cpp +++ b/src/SPI_Firmware.cpp @@ -113,7 +113,7 @@ Firmware::FirmwareHeader::FirmwareHeader(int consoletype) WifiConfigLength = 0x138; Unused1 = 0; memcpy(&Unused3, DEFAULT_UNUSED3, sizeof(DEFAULT_UNUSED3)); - MacAddress = DEFAULT_MAC; + MacAddr = DEFAULT_MAC; EnabledChannels = 0x3FFE; memset(&Unknown2, 0xFF, sizeof(Unknown2)); RFChipType = RFChipType::Type3; diff --git a/src/SPI_Firmware.h b/src/SPI_Firmware.h index ad66a0a4..a264a6cb 100644 --- a/src/SPI_Firmware.h +++ b/src/SPI_Firmware.h @@ -45,7 +45,6 @@ constexpr const char* const DEFAULT_SSID = "melonAP"; constexpr int EXTENDED_WIFI_SETTINGS_OFFSET = -0xA00; using FirmwareIdentifier = std::array; -using MacAddress = std::array; constexpr FirmwareIdentifier GENERATED_FIRMWARE_IDENTIFIER = {'M', 'E', 'L', 'N'}; @@ -283,7 +282,7 @@ public: u8 Unused3[6]; - MacAddress MacAddress; + MacAddress MacAddr; u16 EnabledChannels; diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 19ae9543..8a54e058 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -937,7 +937,7 @@ void LoadUserSettingsFromConfig(Firmware& firmware) bool rep = false; auto& header = firmware.GetHeader(); - memcpy(&mac, header.MacAddress.data(), sizeof(MacAddress)); + memcpy(&mac, header.MacAddr.data(), sizeof(MacAddress)); MacAddress configuredMac; @@ -961,7 +961,7 @@ void LoadUserSettingsFromConfig(Firmware& firmware) if (rep) { mac[0] &= 0xFC; // ensure the MAC isn't a broadcast MAC - header.MacAddress = mac; + header.MacAddr = mac; header.UpdateChecksum(); } From ac38faef1409553c016a2a8c9a8cf74f2e5e0249 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 4 Nov 2023 00:21:46 +0100 Subject: [PATCH 022/157] update copyright years --- res/melon.rc.in | 2 +- src/ARCodeFile.cpp | 2 +- src/ARCodeFile.h | 2 +- src/AREngine.cpp | 2 +- src/AREngine.h | 2 +- src/ARM.cpp | 2 +- src/ARM.h | 2 +- src/ARMInterpreter.cpp | 2 +- src/ARMInterpreter.h | 2 +- src/ARMInterpreter_ALU.cpp | 2 +- src/ARMInterpreter_ALU.h | 2 +- src/ARMInterpreter_Branch.cpp | 2 +- src/ARMInterpreter_Branch.h | 2 +- src/ARMInterpreter_LoadStore.cpp | 2 +- src/ARMInterpreter_LoadStore.h | 2 +- src/ARMJIT.cpp | 2 +- src/ARMJIT.h | 2 +- src/ARMJIT_Memory.cpp | 2 +- src/ARMJIT_Memory.h | 2 +- src/ARMJIT_x64/ARMJIT_ALU.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Branch.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Compiler.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Compiler.h | 2 +- src/ARMJIT_x64/ARMJIT_Linkage.S | 2 +- src/ARMJIT_x64/ARMJIT_LoadStore.cpp | 2 +- src/ARM_InstrInfo.cpp | 2 +- src/ARM_InstrInfo.h | 2 +- src/ARM_InstrTable.h | 2 +- src/CP15.cpp | 2 +- src/CRC32.cpp | 2 +- src/CRC32.h | 2 +- src/DMA.cpp | 2 +- src/DMA.h | 2 +- src/DMA_Timings.h | 2 +- src/DSi.cpp | 2 +- src/DSi.h | 2 +- src/DSi_AES.cpp | 2 +- src/DSi_AES.h | 8 +- src/DSi_Camera.cpp | 2 +- src/DSi_Camera.h | 2 +- src/DSi_I2C.cpp | 2 +- src/DSi_I2C.h | 2 +- src/DSi_NAND.cpp | 2 +- src/DSi_NAND.h | 2 +- src/DSi_NDMA.cpp | 2 +- src/DSi_NDMA.h | 2 +- src/DSi_NWifi.cpp | 2 +- src/DSi_NWifi.h | 2 +- src/DSi_SD.cpp | 2 +- src/DSi_SD.h | 2 +- src/DSi_SPI_TSC.cpp | 2 +- src/DSi_SPI_TSC.h | 2 +- src/FATStorage.cpp | 2 +- src/FATStorage.h | 2 +- src/FIFO.h | 2 +- src/GBACart.cpp | 2 +- src/GBACart.h | 2 +- src/GPU.cpp | 2 +- src/GPU.h | 2 +- src/GPU2D.cpp | 2 +- src/GPU2D.h | 2 +- src/GPU2D_Soft.cpp | 2 +- src/GPU2D_Soft.h | 2 +- src/GPU3D.cpp | 2 +- src/GPU3D.h | 4 +- src/GPU3D_OpenGL.cpp | 2 +- src/GPU3D_OpenGL.h | 4 +- src/GPU3D_OpenGL_shaders.h | 2 +- src/GPU3D_Soft.cpp | 12 +- src/GPU3D_Soft.h | 10 +- src/GPU_OpenGL.cpp | 2 +- src/GPU_OpenGL.h | 4 +- src/GPU_OpenGL_shaders.h | 2 +- src/NDS.cpp | 2 +- src/NDS.h | 2 +- src/NDSCart.cpp | 3 +- src/NDSCart.h | 2 +- src/OpenGLSupport.cpp | 2 +- src/OpenGLSupport.h | 2 +- src/Platform.h | 2 +- src/ROMList.cpp | 4 +- src/ROMList.h | 2 +- src/RTC.cpp | 2 +- src/RTC.h | 2 +- src/SPI.cpp | 2 +- src/SPI.h | 2 +- src/SPU.cpp | 2 +- src/SPU.h | 2 +- src/Savestate.cpp | 4 +- src/Savestate.h | 2 +- src/Wifi.cpp | 2 +- src/Wifi.h | 2 +- src/WifiAP.cpp | 2 +- src/WifiAP.h | 2 +- src/frontend/FrontendUtil.h | 2 +- src/frontend/Util_Audio.cpp | 2 +- src/frontend/Util_Video.cpp | 6 +- src/frontend/mic_blow.h | 5202 ++++++++--------- src/frontend/qt_sdl/ArchiveUtil.cpp | 2 +- src/frontend/qt_sdl/ArchiveUtil.h | 2 +- src/frontend/qt_sdl/AudioInOut.cpp | 4 +- src/frontend/qt_sdl/AudioInOut.h | 4 +- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 10 +- src/frontend/qt_sdl/AudioSettingsDialog.h | 2 +- src/frontend/qt_sdl/CLI.cpp | 2 +- src/frontend/qt_sdl/CLI.h | 2 +- src/frontend/qt_sdl/CameraManager.cpp | 2 +- src/frontend/qt_sdl/CameraManager.h | 2 +- src/frontend/qt_sdl/CameraSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/CameraSettingsDialog.h | 2 +- src/frontend/qt_sdl/CheatsDialog.cpp | 2 +- src/frontend/qt_sdl/CheatsDialog.h | 2 +- src/frontend/qt_sdl/Config.cpp | 2 +- src/frontend/qt_sdl/Config.h | 2 +- src/frontend/qt_sdl/DateTimeDialog.cpp | 2 +- src/frontend/qt_sdl/DateTimeDialog.h | 2 +- src/frontend/qt_sdl/EmuSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/EmuSettingsDialog.h | 2 +- .../qt_sdl/FirmwareSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/FirmwareSettingsDialog.h | 2 +- src/frontend/qt_sdl/Input.cpp | 2 +- src/frontend/qt_sdl/Input.h | 2 +- .../qt_sdl/InputConfig/InputConfigDialog.cpp | 2 +- .../qt_sdl/InputConfig/InputConfigDialog.h | 2 +- src/frontend/qt_sdl/InputConfig/MapButton.h | 2 +- .../qt_sdl/InterfaceSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/InterfaceSettingsDialog.h | 2 +- src/frontend/qt_sdl/LAN_PCap.cpp | 2 +- src/frontend/qt_sdl/LAN_PCap.h | 2 +- src/frontend/qt_sdl/LAN_Socket.cpp | 2 +- src/frontend/qt_sdl/LAN_Socket.h | 2 +- src/frontend/qt_sdl/LocalMP.cpp | 2 +- src/frontend/qt_sdl/LocalMP.h | 2 +- src/frontend/qt_sdl/MPSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/MPSettingsDialog.h | 2 +- src/frontend/qt_sdl/OSD.cpp | 2 +- src/frontend/qt_sdl/OSD.h | 2 +- src/frontend/qt_sdl/OSD_shaders.h | 2 +- src/frontend/qt_sdl/PathSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/PathSettingsDialog.h | 2 +- src/frontend/qt_sdl/Platform.cpp | 2 +- .../PowerManagement/PowerManagementDialog.cpp | 2 +- .../PowerManagement/PowerManagementDialog.h | 2 +- src/frontend/qt_sdl/QPathInput.h | 2 +- src/frontend/qt_sdl/ROMInfoDialog.cpp | 2 +- src/frontend/qt_sdl/ROMInfoDialog.h | 2 +- src/frontend/qt_sdl/ROMManager.cpp | 2 +- src/frontend/qt_sdl/ROMManager.h | 2 +- src/frontend/qt_sdl/SaveManager.cpp | 2 +- src/frontend/qt_sdl/SaveManager.h | 2 +- src/frontend/qt_sdl/TitleManagerDialog.cpp | 2 +- src/frontend/qt_sdl/TitleManagerDialog.h | 2 +- src/frontend/qt_sdl/VideoSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/VideoSettingsDialog.h | 2 +- src/frontend/qt_sdl/WifiSettingsDialog.cpp | 2 +- src/frontend/qt_sdl/WifiSettingsDialog.h | 2 +- src/frontend/qt_sdl/font.h | 2 +- src/frontend/qt_sdl/main.cpp | 2 +- src/frontend/qt_sdl/main.h | 2 +- src/frontend/qt_sdl/main_shaders.h | 2 +- src/melonDLDI.h | 2 +- src/types.h | 2 +- src/version.h | 2 +- 163 files changed, 2788 insertions(+), 2789 deletions(-) diff --git a/res/melon.rc.in b/res/melon.rc.in index 3f2d8e4f..eb437b32 100644 --- a/res/melon.rc.in +++ b/res/melon.rc.in @@ -18,7 +18,7 @@ FILETYPE VFT_APP VALUE "FileVersion", "${melonDS_VERSION}" VALUE "FileDescription", "melonDS emulator" VALUE "InternalName", "SDnolem" - VALUE "LegalCopyright", "2016-2022 melonDS team" + VALUE "LegalCopyright", "2016-2023 melonDS team" VALUE "LegalTrademarks", "" VALUE "OriginalFilename", "melonDS.exe" VALUE "ProductName", "melonDS" diff --git a/src/ARCodeFile.cpp b/src/ARCodeFile.cpp index 80833e37..595df7d3 100644 --- a/src/ARCodeFile.cpp +++ b/src/ARCodeFile.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARCodeFile.h b/src/ARCodeFile.h index d0e82550..53b2f752 100644 --- a/src/ARCodeFile.h +++ b/src/ARCodeFile.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/AREngine.cpp b/src/AREngine.cpp index 62600f9e..ffed6b9c 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/AREngine.h b/src/AREngine.h index 3e6e9dd4..5426846e 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARM.cpp b/src/ARM.cpp index e24dc2ff..ea649b79 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARM.h b/src/ARM.h index 8690f313..b5992ca7 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 0451894f..7aabf4c1 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter.h b/src/ARMInterpreter.h index 9a0fc28e..38225f00 100644 --- a/src/ARMInterpreter.h +++ b/src/ARMInterpreter.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 95b6f379..9fa44db4 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_ALU.h b/src/ARMInterpreter_ALU.h index 8dcc3118..202d6b04 100644 --- a/src/ARMInterpreter_ALU.h +++ b/src/ARMInterpreter_ALU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_Branch.cpp b/src/ARMInterpreter_Branch.cpp index ab66395e..ece783c9 100644 --- a/src/ARMInterpreter_Branch.cpp +++ b/src/ARMInterpreter_Branch.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_Branch.h b/src/ARMInterpreter_Branch.h index 775e8957..3db6f408 100644 --- a/src/ARMInterpreter_Branch.h +++ b/src/ARMInterpreter_Branch.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index 81877f02..3b4b35d7 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMInterpreter_LoadStore.h b/src/ARMInterpreter_LoadStore.h index efd4f180..9016cfa9 100644 --- a/src/ARMInterpreter_LoadStore.h +++ b/src/ARMInterpreter_LoadStore.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index 77bb50dc..19b4438b 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT.h b/src/ARMJIT.h index 97c79cd9..cd97561c 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 25652cb8..a37d7f8a 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index 726b7108..6a7e13d4 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_ALU.cpp b/src/ARMJIT_x64/ARMJIT_ALU.cpp index ff307873..069dd536 100644 --- a/src/ARMJIT_x64/ARMJIT_ALU.cpp +++ b/src/ARMJIT_x64/ARMJIT_ALU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp index 281b24a2..b36f5b72 100644 --- a/src/ARMJIT_x64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp index ce098710..45a27518 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index f5817a92..680146f0 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_Linkage.S b/src/ARMJIT_x64/ARMJIT_Linkage.S index ff24e58c..023f6e7b 100644 --- a/src/ARMJIT_x64/ARMJIT_Linkage.S +++ b/src/ARMJIT_x64/ARMJIT_Linkage.S @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp index 99555fff..718f1bc7 100644 --- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARM_InstrInfo.cpp b/src/ARM_InstrInfo.cpp index a7171ba0..a5466782 100644 --- a/src/ARM_InstrInfo.cpp +++ b/src/ARM_InstrInfo.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARM_InstrInfo.h b/src/ARM_InstrInfo.h index 0104027f..56f6e623 100644 --- a/src/ARM_InstrInfo.h +++ b/src/ARM_InstrInfo.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ARM_InstrTable.h b/src/ARM_InstrTable.h index e4b29061..7cdad66d 100644 --- a/src/ARM_InstrTable.h +++ b/src/ARM_InstrTable.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/CP15.cpp b/src/CP15.cpp index 52fd5604..b8a77e24 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/CRC32.cpp b/src/CRC32.cpp index 4dec9595..8afa3ba3 100644 --- a/src/CRC32.cpp +++ b/src/CRC32.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/CRC32.h b/src/CRC32.h index 133cf51d..4a1034ca 100644 --- a/src/CRC32.h +++ b/src/CRC32.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DMA.cpp b/src/DMA.cpp index a7558ff4..b779d26e 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DMA.h b/src/DMA.h index b0e8f8a3..26884783 100644 --- a/src/DMA.h +++ b/src/DMA.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DMA_Timings.h b/src/DMA_Timings.h index 83af9ad6..faf2e9c9 100644 --- a/src/DMA_Timings.h +++ b/src/DMA_Timings.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi.cpp b/src/DSi.cpp index 0a62db2d..bdbe7f5b 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi.h b/src/DSi.h index 8822266a..06ca9798 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 67b84eca..1b65dc15 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_AES.h b/src/DSi_AES.h index 4ee9bddc..4030b339 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -33,9 +33,9 @@ __attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) #else __attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) { - for (int i = 0; i < 16; ++i) - { - ((u8*)Dst)[i] = ((u8*)Src)[15 - i]; + for (int i = 0; i < 16; ++i) + { + ((u8*)Dst)[i] = ((u8*)Src)[15 - i]; } } #endif diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 2471f8cc..5f7ae4f0 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index bf18e597..dce30a46 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index 26f3e4b8..24aaa87c 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index 8a54a2cd..9027594d 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 79458cbc..3db5621f 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 99a9ee7c..2bb3a02b 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index dba920c1..6c349fac 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h index b1ea4c96..e3da93d2 100644 --- a/src/DSi_NDMA.h +++ b/src/DSi_NDMA.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 76e707d9..6e19421d 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h index a758e961..c3e577c2 100644 --- a/src/DSi_NWifi.h +++ b/src/DSi_NWifi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 5587034b..409a4083 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_SD.h b/src/DSi_SD.h index 577572c4..0940b547 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index 13044405..7040fccf 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h index c7b9bfe6..ebac3952 100644 --- a/src/DSi_SPI_TSC.h +++ b/src/DSi_SPI_TSC.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index ec72fb60..e357470b 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/FATStorage.h b/src/FATStorage.h index 7edad13e..54adb5d4 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/FIFO.h b/src/FIFO.h index 80d9b8a0..c1cbdbf4 100644 --- a/src/FIFO.h +++ b/src/FIFO.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 020219df..295140ce 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GBACart.h b/src/GBACart.h index 1c7bfb4d..32ff6d59 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU.cpp b/src/GPU.cpp index 0cbc5996..ccfcb636 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU.h b/src/GPU.h index cec8a2dd..ea9c777d 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index d5df9929..d1f6bebd 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU2D.h b/src/GPU2D.h index 5edc0a2c..ffef57d7 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 070079a3..495b7471 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU2D_Soft.h b/src/GPU2D_Soft.h index a9fff972..e1e6eedf 100644 --- a/src/GPU2D_Soft.h +++ b/src/GPU2D_Soft.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 8cc380ad..718c6418 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU3D.h b/src/GPU3D.h index 44d422a5..1a0bfa0f 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -146,7 +146,7 @@ public: // This "Accelerated" flag currently communicates if the framebuffer should // be allocated differently and other little misc handlers. Ideally there - // are more detailed "traits" that we can ask of the Renderer3D type + // are more detailed "traits" that we can ask of the Renderer3D type const bool Accelerated; virtual void SetRenderSettings(GPU::RenderSettings& settings) = 0; diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 9648be36..2d0b15a9 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index 597f13e1..02d83037 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -150,4 +150,4 @@ private: }; -} \ No newline at end of file +} diff --git a/src/GPU3D_OpenGL_shaders.h b/src/GPU3D_OpenGL_shaders.h index 6e17c350..81313812 100644 --- a/src/GPU3D_OpenGL_shaders.h +++ b/src/GPU3D_OpenGL_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 8848a541..19154ba8 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -746,7 +746,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) std::swap(xstart, xend); std::swap(wl, wr); std::swap(zl, zr); - + // CHECKME: edge fill rules for swapped opaque shadow mask polygons if ((polyalpha < 31) || (RenderDispCnt & (3<<4))) { @@ -774,7 +774,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) rp->SlopeL.EdgeParams(&l_edgelen, &l_edgecov); rp->SlopeR.EdgeParams(&r_edgelen, &r_edgecov); - + // CHECKME: edge fill rules for unswapped opaque shadow mask polygons if ((polyalpha < 31) || (RenderDispCnt & (3<<4))) { @@ -873,7 +873,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) edge = yedge | 0x2; xlimit = xend+1; if (xlimit > 256) xlimit = 256; - + if (r_filledge) for (; x < xlimit; x++) { @@ -948,7 +948,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) s32 zl = rp->SlopeL.Interp.InterpolateZ(polygon->FinalZ[rp->CurVL], polygon->FinalZ[rp->NextVL], polygon->WBuffer); s32 zr = rp->SlopeR.Interp.InterpolateZ(polygon->FinalZ[rp->CurVR], polygon->FinalZ[rp->NextVR], polygon->WBuffer); - + // right vertical edges are pushed 1px to the left as long as either: // the left edge slope is not 0, or the span is not 0 pixels wide, and it is not at the leftmost pixel of the screen if (rp->SlopeR.Increment==0 && (rp->SlopeL.Increment!=0 || xstart != xend) && (xend != 0)) @@ -1220,7 +1220,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) if (alpha == 31) { u32 attr = polyattr | edge; - + if ((RenderDispCnt & (1<<4)) && (attr & 0xF)) { // anti-aliasing: all edges are rendered diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 948832f4..16257df9 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -232,7 +232,7 @@ private: s32 SetupDummy(s32 x0) { dx = 0; - + this->x0 = x0; this->xmin = x0; this->xmax = x0; @@ -343,7 +343,7 @@ private: else if (ret > xmax) ret = xmax; return ret; } - + template void EdgeParams_XMajor(s32* length, s32* coverage) { @@ -401,7 +401,7 @@ private: *coverage = cov; } } - + template void EdgeParams(s32* length, s32* coverage) { @@ -507,4 +507,4 @@ private: Platform::Semaphore* Sema_RenderDone; Platform::Semaphore* Sema_ScanlineCount; }; -} \ No newline at end of file +} diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp index 47e04d25..32633a7a 100644 --- a/src/GPU_OpenGL.cpp +++ b/src/GPU_OpenGL.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/GPU_OpenGL.h b/src/GPU_OpenGL.h index 90c17ae3..3841df14 100644 --- a/src/GPU_OpenGL.h +++ b/src/GPU_OpenGL.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -68,4 +68,4 @@ private: GLuint CompScreenOutputFB[2]; }; -} \ No newline at end of file +} diff --git a/src/GPU_OpenGL_shaders.h b/src/GPU_OpenGL_shaders.h index b1460cd6..9a7d6aba 100644 --- a/src/GPU_OpenGL_shaders.h +++ b/src/GPU_OpenGL_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/NDS.cpp b/src/NDS.cpp index 2e71c274..5a5ba347 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/NDS.h b/src/NDS.h index a3e74b66..b5d72276 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 33d77951..df729bd8 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -22,7 +22,6 @@ #include "NDSCart.h" #include "ARM.h" #include "CRC32.h" -#include "DSi_AES.h" #include "Platform.h" #include "ROMList.h" #include "melonDLDI.h" diff --git a/src/NDSCart.h b/src/NDSCart.h index f397225f..059fd242 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/OpenGLSupport.cpp b/src/OpenGLSupport.cpp index 5a8da116..4962b4c1 100644 --- a/src/OpenGLSupport.cpp +++ b/src/OpenGLSupport.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h index 14be01a6..ed75f386 100644 --- a/src/OpenGLSupport.h +++ b/src/OpenGLSupport.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/Platform.h b/src/Platform.h index 7fa8fbd7..de1ca8b2 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/ROMList.cpp b/src/ROMList.cpp index 725190aa..564f4a40 100644 --- a/src/ROMList.cpp +++ b/src/ROMList.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -6800,4 +6800,4 @@ const ROMListEntry ROMList[] = {0x5A5A5242, 0x04000000, 0x00000003}, }; -const size_t ROMListEntryCount = sizeof(ROMList) / sizeof(ROMListEntry); \ No newline at end of file +const size_t ROMListEntryCount = sizeof(ROMList) / sizeof(ROMListEntry); diff --git a/src/ROMList.h b/src/ROMList.h index c1bce154..11db1773 100644 --- a/src/ROMList.h +++ b/src/ROMList.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/RTC.cpp b/src/RTC.cpp index 30eab006..d451585b 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/RTC.h b/src/RTC.h index db78f676..313d2377 100644 --- a/src/RTC.h +++ b/src/RTC.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/SPI.cpp b/src/SPI.cpp index 8432b192..4b72f5c0 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/SPI.h b/src/SPI.h index 2fde2269..619a26b1 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/SPU.cpp b/src/SPU.cpp index cdfc2442..bfe66227 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/SPU.h b/src/SPU.h index 1f28c2f8..12cba611 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/Savestate.cpp b/src/Savestate.cpp index 546c16e0..77c2e621 100644 --- a/src/Savestate.cpp +++ b/src/Savestate.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -378,4 +378,4 @@ u32 Savestate::FindSection(const char* magic) const // We've reached the end of the file without finding the requested section... Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic); return NO_SECTION; -} \ No newline at end of file +} diff --git a/src/Savestate.h b/src/Savestate.h index 6707f9f4..ede3963c 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 7f57c4bb..3716b4c5 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/Wifi.h b/src/Wifi.h index b9594f45..e3570390 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index 9fef220d..c9cccea0 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/WifiAP.h b/src/WifiAP.h index e5ca1ed7..04dbfa82 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 51f8f613..375769d2 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index fc0edebc..278a5a00 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/Util_Video.cpp b/src/frontend/Util_Video.cpp index e4c49e91..e772be9a 100644 --- a/src/frontend/Util_Video.cpp +++ b/src/frontend/Util_Video.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -471,7 +471,7 @@ int GetScreenTransforms(float* out, int* kind) bool GetTouchCoords(int& x, int& y, bool clamp) { - if (HybEnable && HybScreen == 1) + if (HybEnable && HybScreen == 1) { float vx = x; float vy = y; @@ -487,7 +487,7 @@ bool GetTouchCoords(int& x, int& y, bool clamp) { x = std::clamp((int)vx, 0, 255); y = std::clamp((int)vy, 0, 191); - + return true; } if (HybPrevTouchScreen == 2) diff --git a/src/frontend/mic_blow.h b/src/frontend/mic_blow.h index 2fdd9452..20d5017c 100644 --- a/src/frontend/mic_blow.h +++ b/src/frontend/mic_blow.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -21,2606 +21,2606 @@ const u16 mic_blow[] = { - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0004, - 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0001, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0001, 0x0000, 0x0007, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0x18CE, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, - 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0003, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0001, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0007, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0004, 0x0001, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0003, 0x0001, 0x0003, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, - 0x0002, 0x0003, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x847B, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, - 0x0002, 0x0002, 0x0001, 0x0001, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0349, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0009, 0x0000, 0x0003, 0x0002, - 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x8799, 0x651E, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0xA53E, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xC6FA, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0009, - 0x0000, 0x0008, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0x0001, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0003, 0xB631, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xCBB5, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0006, 0x9D85, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x39BA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0x7D89, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, - 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, - 0x8E09, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6615, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x1CD9, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0009, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, - 0x0004, 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x4D8C, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0006, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0006, 0x0000, 0x0008, 0x0000, - 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0002, - 0x0002, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0xDB0F, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x8888, 0x0000, 0x0002, 0x0000, 0x0005, - 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x98DC, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xABF2, - 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, - 0x0001, 0x0002, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0004, 0x0000, 0xAEAB, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0x1B34, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x5D05, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0005, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x6781, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0002, 0x0002, 0x0003, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, - 0x0004, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0x0004, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, 0xFFFC, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0x0006, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, - 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x2892, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0001, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x22BC, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x312D, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, - 0x0002, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0004, - 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x4606, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xF9FF, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0001, 0x0004, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0003, 0x0002, 0x0004, 0x0002, 0x0003, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFE, 0x0003, 0x0001, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0001, - 0x0002, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xED4F, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0004, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFF9, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0005, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xEFB5, 0x0003, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, - 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0004, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFE, - 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x32F8, 0x0001, 0x0002, 0x7052, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0003, 0x0001, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0x2D60, 0x0004, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0x1DA1, 0x0004, 0x0002, 0x0003, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0007, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0x0004, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0004, 0x0001, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0xF528, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xBF77, 0x0001, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0002, 0x0004, 0x0003, 0x0002, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0x2365, 0x0000, 0x0001, 0x0002, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, - 0x0005, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, - 0x0005, 0x0000, 0x0005, 0x0002, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x9340, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0002, 0x0001, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0001, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0007, - 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, - 0x0001, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0003, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0003, - 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0x0000, 0x0003, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF8, 0x0008, 0x0000, 0x0004, 0x0001, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFF9, 0x0008, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0003, 0x0001, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0000, 0x0000, 0x0003, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0003, 0x0000, - 0x0006, 0x0000, 0x0004, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0x0003, - 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0002, 0xC55F, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0002, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0006, 0x0000, 0xACC1, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, - 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0xEE35, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0001, 0x0000, - 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0001, 0x0003, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0001, 0x0003, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, - 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0001, 0x0003, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0F4C, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, - 0xED13, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0002, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFC, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFA, 0xA5B7, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0002, 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0002, 0x0001, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x06FD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFB, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, - 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x1187, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, - 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, - 0x0000, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0xFFFC, 0xFFFF, 0xFFF7, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x0004, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0xEB31, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFC, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFB, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF7, 0xFFFF, - 0xFFF4, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFD8B, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x69E7, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0xF443, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0x52D6, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0002, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0009, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, - 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0x0007, 0x0000, 0x0004, 0x0002, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xD7B3, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0x021D, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0xFFFA, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0001, 0x0005, 0x0000, 0xA271, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x08DF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0004, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, - 0x0006, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x000A, 0x0000, 0x0009, - 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFE, 0xFFFB, 0xFFFB, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, - 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xCB6A, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0005, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, - 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xEFC1, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0006, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0xBDC8, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x5B74, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0004, 0x0002, 0x0002, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0001, 0x0002, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0002, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0006, - 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0009, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, - 0x0003, 0x0002, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0003, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x2C65, 0x0000, 0x0000, 0x0000, - 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0007, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0005, 0xFFF9, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, - 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0002, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0002, 0x0001, 0x0003, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, - 0x0002, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0009, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0007, - 0x0000, 0x0009, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x000B, 0x0000, 0x000A, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, - 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0xCBB6, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0002, 0x0003, 0x0001, - 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, 0x0009, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, - 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x6BE6, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0002, 0x1FBC, 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0x39B7, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0004, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0005, - 0x0000, 0x0007, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0005, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, - 0xFFFE, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0004, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0005, 0x0002, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, - 0xE8FD, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x5B7E, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0x1AE5, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xBD30, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x9A4C, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0005, 0x0000, 0x0006, 0x0002, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xF5EE, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0x724C, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x3F1F, 0x0001, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0004, 0x0002, 0x0002, 0x0004, 0x0002, 0x0003, - 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x8AD2, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, - 0x0007, 0x0000, 0x0007, 0x0000, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0xBEDD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0x946E, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0xC7B0, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0x851A, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0004, 0x0001, 0x0000, 0x0002, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, - 0x0005, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0x0001, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0001, 0x0004, 0x0001, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, - 0x0001, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0005, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xA54E, 0x0002, 0x0000, 0x7C4D, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0002, 0x0001, 0x0002, - 0x0003, 0x0002, 0x0003, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xADFD, 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0x95DE, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0002, 0x0002, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0004, 0x0000, 0x0005, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0005, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, - 0x0005, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xA0E0, 0x59A3, 0x4A73, 0x5D3E, - 0x7620, 0x77DB, 0x48DC, 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xBB82, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0004, 0x1D09, 0x92A8, 0x72D7, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x020F, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0001, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x63F7, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x293D, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0005, - 0x0000, 0x0009, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0xA433, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0x0807, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x098A, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x3BF1, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0x0005, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0002, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0004, 0x0001, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x4159, - 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0001, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x1905, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0x9129, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0x0000, 0x0004, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0003, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, - 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x128F, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0002, 0x0004, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0001, 0xA307, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, - 0x0006, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0xE5A4, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xC526, 0x0000, - 0x0005, 0x519E, 0xE4F3, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xA418, 0x861F, 0xE13B, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xCC64, 0x0001, 0x0000, 0x0002, 0xDE8B, 0xFFFD, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFE, 0x4AA5, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, - 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0007, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0003, 0x0002, 0x0002, 0x0001, 0x0002, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x5494, 0x57CB, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0005, - 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xD476, 0x0000, 0x0007, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0001, 0x0003, 0x0001, 0x0000, 0x60E6, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x457D, 0x0004, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0005, - 0x0000, 0x0007, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, - 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0x3004, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, - 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0xDAA3, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, - 0x0000, 0x0006, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x9C09, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0x0006, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0xFFFA, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xC23E, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x3259, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFCBD, 0x0000, 0x0001, - 0x0004, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0003, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFC, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0x0005, 0x0000, 0x0003, 0x0003, 0x0000, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0005, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0003, 0x0000, 0x0007, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0001, 0x0004, 0x0000, 0x0009, 0x0000, 0x0008, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0x0004, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xE550, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFF9, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xA807, 0x0001, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x2CF7, 0x74D1, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x0001, 0x0001, 0x0002, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x000B, 0x0000, 0x0006, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0002, 0x0003, - 0x0003, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFA, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xCF32, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, - 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, - 0x0001, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, - 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFD, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0002, 0x0001, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xE0F0, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, - 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0xBF1C, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0001, 0x20F1, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xA727, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0004, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xC285, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xD192, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0002, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0002, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x90D5, 0xFFFF, 0xFFFC, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0002, 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0001, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFF7, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0002, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0003, 0x0000, 0x0008, 0x0000, - 0x0006, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x7420, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0002, - 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0003, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x439F, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x90B5, 0x0004, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x6CD1, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x75D6, 0xFFFB, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0x7342, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0x9B49, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFC, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x5542, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0003, 0x0002, - 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0x0002, 0x0001, 0x0005, 0x0001, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0002, 0x0000, 0xE0F8, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xD0C7, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xC8CF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xF913, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0xDE55, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0002, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xF6F5, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0001, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0002, 0x0001, 0x0004, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, - 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, - 0x0004, 0x0002, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0002, 0x524D, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0007, - 0x0000, 0x0008, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0xE4B8, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0007, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, - 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0002, 0x0001, - 0x0002, 0x0001, 0x0002, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, - 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0001, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0002, 0x0002, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x1DA1, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xA67D, 0x0004, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF8, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, - 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x1ED6, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, - 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x23F1, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, - 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0003, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, - 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0004, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0x0002, 0x0001, 0x0000, 0x0004, - 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, - 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, - 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, - 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, - 0x0003, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0006, 0x0000, - 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x7FFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFC, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xD88D, 0x0003, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, - 0x0001, 0x0002, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0x0001, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFC, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, - 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0001, 0x0003, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFEBA, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, - 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, - 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0009, 0x0000, 0x0005, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, - 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xBE46, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, - 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, - 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0x7B80, 0x0000, 0xF493, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, - 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xA5C4, 0x0001, 0x0000, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, - 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0001, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, - 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xBB34, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, - 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFC, 0xFFFC, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, - 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, - 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0x0001, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, - 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, - 0x0003, 0x0002, 0x0000, 0x0003, 0x9587, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, - 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x000A, 0x0000, 0x0004, 0x0000, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFD, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0002, 0x0000, 0x0007, - 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, - 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0000, - 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFD, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0000, 0x0000, 0x0001, - 0x0003, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0002, - 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0004, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0005, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, - 0x0006, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xED3A, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFE1, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, - 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFC, - 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, - 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0003, - 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x926E, 0xFFFF, 0xFFFC, 0x8C84, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x7AD7, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x49BC, 0x0003, - 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, - 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, - 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, - 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, - 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, - 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, - 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, - 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x09CB, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0002, - 0x0002, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, - 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, - 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0009, 0x0000, 0x19CE, 0xCF98, 0xEC10, 0x74E9, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, - 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, - 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, - 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFD, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, - 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, - 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0005, 0x0000, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, - 0x0002, 0x0000, 0x0002, 0x0004, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, - 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, - 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0002, - 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x22E2, 0x0008, 0x0000, 0x0005, 0x0000, 0x0005, - 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0001, 0x0007, 0x0000, 0x0006, 0x0000, - 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0xEC7E, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x6687, 0x0004, 0x0000, 0x0004, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFA, 0xFFFF, 0xFFFB, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, - 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x000A, 0x0000, 0x0008, 0x0000, 0x0002, - 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, - 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, - 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, - 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, - 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, - 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, - 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, - 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, - 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, - 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0x0000, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, - 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0005, 0x0002, - 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, - 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, - 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0005, - 0x0000, 0x0005, 0x8A68, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, - 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, - 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0000, - 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, - 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, - 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, - 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0004, - 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, - 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, - 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, - 0x1C49, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0x7B27, 0x5A5C, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0004, + 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0001, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0001, 0x0000, 0x0007, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0x18CE, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, + 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0003, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0001, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0007, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0004, 0x0001, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0003, 0x0001, 0x0003, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, + 0x0002, 0x0003, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x847B, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, + 0x0002, 0x0002, 0x0001, 0x0001, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0349, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0009, 0x0000, 0x0003, 0x0002, + 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x8799, 0x651E, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0xA53E, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xC6FA, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0009, + 0x0000, 0x0008, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0x0001, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0003, 0xB631, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xCBB5, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0006, 0x9D85, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x39BA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0x7D89, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, + 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, + 0x8E09, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x6615, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x1CD9, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0009, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, + 0x0004, 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x4D8C, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0006, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0006, 0x0000, 0x0008, 0x0000, + 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0002, + 0x0002, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0xDB0F, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x8888, 0x0000, 0x0002, 0x0000, 0x0005, + 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x98DC, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xABF2, + 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, + 0x0001, 0x0002, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0004, 0x0000, 0xAEAB, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0x1B34, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x5D05, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0005, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x6781, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0002, 0x0002, 0x0003, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, + 0x0004, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0x0004, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, 0xFFFC, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0x0006, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, + 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x2892, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0001, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x22BC, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x312D, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, + 0x0002, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0004, + 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x4606, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xF9FF, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0001, 0x0004, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0003, 0x0002, 0x0004, 0x0002, 0x0003, 0x0002, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFE, 0x0003, 0x0001, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0001, + 0x0002, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xED4F, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0004, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFF9, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0005, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xEFB5, 0x0003, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, + 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0004, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFE, + 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x32F8, 0x0001, 0x0002, 0x7052, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0003, 0x0001, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0x2D60, 0x0004, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0x1DA1, 0x0004, 0x0002, 0x0003, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0007, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0x0004, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0004, 0x0001, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0xF528, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xBF77, 0x0001, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0002, 0x0004, 0x0003, 0x0002, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0x2365, 0x0000, 0x0001, 0x0002, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, + 0x0005, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, + 0x0005, 0x0000, 0x0005, 0x0002, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x9340, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0002, 0x0001, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0001, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0007, + 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, + 0x0001, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0003, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0003, + 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0x0000, 0x0003, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF8, 0x0008, 0x0000, 0x0004, 0x0001, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0004, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFF9, 0x0008, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0003, 0x0001, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0000, 0x0000, 0x0003, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0003, 0x0000, + 0x0006, 0x0000, 0x0004, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0x0003, + 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0002, 0xC55F, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0002, 0x0000, 0x0002, 0x0003, 0x0001, 0x0002, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0006, 0x0000, 0xACC1, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, + 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0xEE35, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0001, 0x0000, + 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0001, 0x0003, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0001, 0x0003, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, + 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0001, 0x0003, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0F4C, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, + 0xED13, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0002, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFC, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFA, 0xA5B7, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0002, 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0002, 0x0001, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x06FD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFB, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x1187, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, + 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, + 0x0000, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0xFFFC, 0xFFFF, 0xFFF7, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x0004, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0xEB31, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFC, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFB, 0x0006, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF7, 0xFFFF, + 0xFFF4, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFD8B, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x69E7, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0xF443, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0x52D6, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0002, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0009, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, + 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0x0007, 0x0000, 0x0004, 0x0002, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xD7B3, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0x021D, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0xFFFA, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0001, 0x0005, 0x0000, 0xA271, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x08DF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0004, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, + 0x0006, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x000A, 0x0000, 0x0009, + 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFE, 0xFFFB, 0xFFFB, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, + 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xCB6A, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0005, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, + 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xEFC1, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0006, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0xBDC8, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x5B74, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0004, 0x0002, 0x0002, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0001, 0x0002, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0002, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0006, + 0x0000, 0x0003, 0x0002, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0009, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, + 0x0003, 0x0002, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, 0x0003, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x2C65, 0x0000, 0x0000, 0x0000, + 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0007, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0005, 0xFFF9, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, + 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0002, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0002, 0x0001, 0x0003, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, + 0x0002, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0009, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0007, + 0x0000, 0x0009, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x000B, 0x0000, 0x000A, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, + 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0xCBB6, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0002, 0x0003, 0x0001, + 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, 0x0009, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, + 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x6BE6, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0002, 0x1FBC, 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0x39B7, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0004, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0005, + 0x0000, 0x0007, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0005, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFD, + 0xFFFE, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0004, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0005, 0x0002, 0x0004, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, + 0xE8FD, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x5B7E, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0x1AE5, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xBD30, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x9A4C, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0005, 0x0000, 0x0006, 0x0002, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xF5EE, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0x724C, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x3F1F, 0x0001, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0004, 0x0002, 0x0002, 0x0004, 0x0002, 0x0003, + 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0001, 0x0002, 0x0002, 0x0003, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x8AD2, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, + 0x0007, 0x0000, 0x0007, 0x0000, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0xBEDD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0x946E, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0xC7B0, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0x851A, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0004, 0x0001, 0x0000, 0x0002, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, + 0x0005, 0x0000, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0x0001, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0001, 0x0004, 0x0001, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, + 0x0001, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0005, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xA54E, 0x0002, 0x0000, 0x7C4D, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0002, 0x0001, 0x0002, + 0x0003, 0x0002, 0x0003, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xADFD, 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0x95DE, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0002, 0x0002, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0004, 0x0000, 0x0005, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0005, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, + 0x0005, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xA0E0, 0x59A3, 0x4A73, 0x5D3E, + 0x7620, 0x77DB, 0x48DC, 0x0006, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xBB82, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0004, 0x1D09, 0x92A8, 0x72D7, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x020F, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0001, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x63F7, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x293D, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0005, + 0x0000, 0x0009, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0xA433, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0x0807, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x098A, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x3BF1, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0x0005, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0002, 0x0001, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0004, 0x0001, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x4159, + 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0001, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x1905, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0x9129, 0x0003, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0x0000, 0x0004, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF9, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0003, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, + 0x0001, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x128F, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0x0004, 0x0000, 0x0001, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0002, 0x0002, 0x0004, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0003, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0001, 0xA307, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, + 0x0006, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0xE5A4, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xC526, 0x0000, + 0x0005, 0x519E, 0xE4F3, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xA418, 0x861F, 0xE13B, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xCC64, 0x0001, 0x0000, 0x0002, 0xDE8B, 0xFFFD, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFB, 0xFFFE, 0x4AA5, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, + 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0007, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0003, 0x0002, 0x0002, 0x0001, 0x0002, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x5494, 0x57CB, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0005, + 0x0000, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xD476, 0x0000, 0x0007, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0001, 0x0003, 0x0001, 0x0000, 0x60E6, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x457D, 0x0004, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0003, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0005, + 0x0000, 0x0007, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, + 0xFFFE, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0x3004, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, + 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0xDAA3, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0002, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, + 0x0000, 0x0006, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x9C09, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0x0006, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0xFFFA, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xC23E, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x3259, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFCBD, 0x0000, 0x0001, + 0x0004, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0003, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFC, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0x0005, 0x0000, 0x0003, 0x0003, 0x0000, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0005, 0xFFFC, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0002, 0x0002, 0x0003, 0x0000, 0x0007, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0001, 0x0004, 0x0000, 0x0009, 0x0000, 0x0008, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0x0004, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xE550, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFF9, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xA807, 0x0001, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFA, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x2CF7, 0x74D1, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0x0001, 0x0001, 0x0002, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x000B, 0x0000, 0x0006, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0002, 0x0003, + 0x0003, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFA, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xCF32, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0006, + 0x0000, 0x0008, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, + 0x0001, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0006, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, + 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0002, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFD, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0008, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0001, 0x0003, 0x0001, 0x0002, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, 0x0002, 0x0001, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0005, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xE0F0, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, + 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0xBF1C, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0001, 0x20F1, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xA727, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFD, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0004, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xC285, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xD192, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0002, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0002, 0x0001, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0002, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x90D5, 0xFFFF, 0xFFFC, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0002, 0x0003, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0001, 0x0001, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFF7, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0004, 0x0002, 0x0003, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0003, 0x0000, 0x0008, 0x0000, + 0x0006, 0x0000, 0x0002, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x7420, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0002, + 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0003, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x439F, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x90B5, 0x0004, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0006, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0001, 0x0004, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x6CD1, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x75D6, 0xFFFB, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0x7342, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0x9B49, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFC, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x5542, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0003, 0x0002, + 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0x0002, 0x0001, 0x0005, 0x0001, 0x0003, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0x0007, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0002, 0x0000, 0xE0F8, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xD0C7, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xC8CF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xF913, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0xDE55, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0002, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xF6F5, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0001, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0002, 0x0001, 0x0004, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, + 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, + 0x0004, 0x0002, 0x0002, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0002, 0x524D, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0002, 0x0003, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0001, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF9, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0007, + 0x0000, 0x0008, 0x0000, 0x0002, 0x0002, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0xE4B8, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0007, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0003, 0x0003, 0x0003, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, + 0x0006, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0002, 0x0001, + 0x0002, 0x0001, 0x0002, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, + 0xFFFE, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0003, 0x0001, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0001, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0002, 0x0002, 0x0000, 0x0002, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x1DA1, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFE, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xA67D, 0x0004, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0005, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0002, 0x0001, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFF8, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0001, 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFE, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, + 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x1ED6, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0001, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x23F1, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, + 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0001, 0x0003, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0006, 0x0000, + 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0004, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFF7, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0x0002, 0x0001, 0x0000, 0x0004, + 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, + 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0002, 0x0002, 0x0002, + 0x0002, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, + 0x0001, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0001, 0x0005, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, + 0x0003, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0006, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0006, 0x0000, + 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0003, 0x0002, 0x0002, 0x0003, 0x7FFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFC, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFF6, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0003, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xD88D, 0x0003, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, + 0x0001, 0x0002, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0x0001, 0x0003, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFC, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, + 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0001, 0x0003, 0x0004, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0001, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0004, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0001, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFEBA, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0002, 0x0002, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0002, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0001, 0x0001, + 0x0001, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0004, 0x0000, + 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0009, 0x0000, 0x0005, 0x0001, 0x0002, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, + 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0008, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xBE46, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, + 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, + 0x0003, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF7, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFD, 0x0002, 0x0002, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0x7B80, 0x0000, 0xF493, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0005, + 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0000, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xA5C4, 0x0001, 0x0000, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, + 0x0005, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0001, 0x0002, 0x0001, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, + 0x0005, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0xBB34, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, + 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFC, 0xFFFC, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFE, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0006, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, + 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0xFFFC, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFF6, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, + 0x0003, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0x0001, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, + 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0001, + 0x0003, 0x0002, 0x0000, 0x0003, 0x9587, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0005, + 0x0000, 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x000A, 0x0000, 0x0004, 0x0000, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFD, 0x0001, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0006, 0x0002, 0x0000, 0x0007, + 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0004, 0x0000, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, + 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0000, 0x0001, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0002, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0x0001, 0x0001, 0x0000, + 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x0000, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFD, 0xFFFD, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0003, 0x0000, 0x0000, 0x0000, 0x0001, + 0x0003, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0x0004, 0x0000, 0x0002, + 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0001, 0x0004, 0x0002, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0001, 0x0002, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0005, 0xFFFD, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, + 0x0006, 0x0000, 0x0005, 0x0001, 0x0002, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0xED3A, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFE1, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0000, 0x0000, 0x0002, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0003, 0x0000, 0x0004, + 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0002, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFC, + 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0001, 0x0001, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, + 0x0002, 0x0002, 0x0001, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0000, 0x0003, + 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0005, 0x0000, 0x926E, 0xFFFF, 0xFFFC, 0x8C84, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x7AD7, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x49BC, 0x0003, + 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0004, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0007, 0x0000, 0x0007, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, + 0x0008, 0x0000, 0x0007, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, + 0x0000, 0x0004, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFC, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0000, 0x0003, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, + 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0001, 0x0000, 0x0002, 0x0001, 0x0001, 0x0003, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, + 0x0002, 0x0000, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFE, 0x0002, 0x0000, + 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0001, 0x0002, 0x0001, 0x0003, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0008, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0001, 0x0003, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, + 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0003, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0000, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF8, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, + 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0004, 0x0000, 0x09CB, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0x0000, 0x0004, 0x0000, 0x0004, 0x0002, + 0x0002, 0x0003, 0x0001, 0x0001, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0006, 0x0000, 0x0006, + 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0003, + 0x0000, 0x0006, 0x0000, 0x0008, 0x0000, 0x0009, 0x0000, 0x19CE, 0xCF98, 0xEC10, 0x74E9, 0x0003, 0x0000, 0x0001, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, + 0x0005, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0002, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0005, 0x0000, 0x0008, 0x0000, + 0x0006, 0x0000, 0x0003, 0x0000, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0000, 0x0005, 0x0000, 0x0003, 0x0002, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0xFFFF, + 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFD, 0x0004, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0000, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0003, + 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, + 0x0005, 0x0000, 0x0003, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0001, 0x0005, 0x0000, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFE, 0x0003, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0000, 0x0003, 0x0000, 0x0001, + 0x0002, 0x0000, 0x0002, 0x0004, 0x0000, 0x0009, 0x0000, 0x0006, 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0002, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0001, + 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0001, 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0005, + 0x0000, 0x0004, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFE, 0x0002, 0x0000, 0x0002, + 0x0000, 0x0004, 0x0000, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x22E2, 0x0008, 0x0000, 0x0005, 0x0000, 0x0005, + 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0001, 0x0002, 0x0001, 0x0007, 0x0000, 0x0006, 0x0000, + 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0xEC7E, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0x6687, 0x0004, 0x0000, 0x0004, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0002, 0x0001, 0x0000, 0x0005, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFE, 0xFFFA, 0xFFFF, 0xFFFB, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, + 0x0003, 0x0000, 0x0005, 0x0000, 0x0006, 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x000A, 0x0000, 0x0008, 0x0000, 0x0002, + 0x0001, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0002, 0x0001, + 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0000, + 0x0002, 0x0001, 0x0000, 0x0003, 0x0000, 0x0001, 0x0002, 0x0000, 0x0003, 0x0001, 0x0002, 0x0001, 0x0001, 0x0000, 0x0000, 0x0001, + 0x0001, 0x0002, 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0005, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, + 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFB, + 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, + 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFC, 0xFFFE, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFE, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFA, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFB, 0xFFFE, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, + 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFF, + 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, + 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFE, 0x0000, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0000, 0x0003, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0000, 0x0001, 0x0000, 0x0003, 0x0000, 0x0006, + 0x0000, 0x0004, 0x0000, 0x0000, 0x0004, 0x0000, 0x0001, 0x0000, 0x0001, 0x0000, 0x0004, 0x0000, 0x0008, 0x0000, 0x0005, 0x0002, + 0x0000, 0x0004, 0x0001, 0x0001, 0x0001, 0x0001, 0x0000, 0x0002, 0x0001, 0x0000, 0x0003, 0x0001, 0x0001, 0x0002, 0x0001, 0x0001, + 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0005, 0x0000, 0x0001, 0x0001, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0003, + 0x0001, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0001, 0x0000, 0x0000, 0x0002, 0x0002, 0x0001, 0x0002, 0x0000, 0x0005, + 0x0000, 0x0005, 0x8A68, 0xFFFF, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFD, + 0xFFFF, 0x0000, 0x0006, 0x0000, 0x0005, 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0001, 0x0000, 0x0004, 0x0000, + 0x0004, 0x0000, 0x0001, 0x0001, 0x0000, 0x0006, 0x0000, 0x0007, 0x0000, 0x0000, 0x0004, 0x0000, 0x0005, 0x0001, 0x0001, 0x0000, + 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0005, 0x0000, + 0x0004, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0004, 0x0000, 0x0007, 0x0000, 0x0004, 0x0000, 0x0002, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0002, 0x0000, 0x0002, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0002, + 0x0000, 0x0003, 0x0000, 0x0000, 0x0003, 0x0000, 0x0003, 0x0000, 0x0000, 0x0004, 0x0000, 0x0006, 0xFFFB, 0xFFFF, 0xFFFC, 0xFFFF, + 0xFFFE, 0xFFFD, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFD, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, 0x0005, 0x0000, 0x0007, 0x0000, 0x0004, + 0x0000, 0x0001, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0001, 0x0002, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0004, 0x0000, + 0x0002, 0x0000, 0x0001, 0x0000, 0x0002, 0x0000, 0x0005, 0x0000, 0x0003, 0xFFFC, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFD, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0x0003, 0x0000, 0x0002, 0x0000, 0x0004, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0x0001, 0x0000, 0x0001, 0x0000, 0x0001, 0x0001, 0x0000, 0x0003, 0x0000, 0x0002, 0x0000, 0x0000, 0x0002, 0x0000, 0x0001, 0x0000, + 0xFFFF, 0xFFFD, 0xFFFF, 0xFFF9, 0xFFFF, 0xFFFA, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFA, 0x0004, 0x0000, 0x0002, 0x0001, 0x0000, 0x0001, + 0x1C49, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFE, 0xFFFE, 0xFFFF, 0xFFFB, 0xFFFF, 0xFFFB, 0xFFFF, 0x7B27, 0x5A5C, }; #endif // MIC_BLOW_H diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index b025aa8f..91b993cd 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index 761f5427..d9327c80 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/AudioInOut.cpp b/src/frontend/qt_sdl/AudioInOut.cpp index 48e8a862..120ff5fc 100644 --- a/src/frontend/qt_sdl/AudioInOut.cpp +++ b/src/frontend/qt_sdl/AudioInOut.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -383,4 +383,4 @@ void Disable() MicClose(); } -} \ No newline at end of file +} diff --git a/src/frontend/qt_sdl/AudioInOut.h b/src/frontend/qt_sdl/AudioInOut.h index 76e0cb8b..cc386251 100644 --- a/src/frontend/qt_sdl/AudioInOut.h +++ b/src/frontend/qt_sdl/AudioInOut.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -41,4 +41,4 @@ void Disable(); } -#endif \ No newline at end of file +#endif diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index ef1f5717..6b3beffa 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. @@ -77,15 +77,15 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDia const int count = SDL_GetNumAudioDevices(true); for (int i = 0; i < count; i++) { - ui->cbMic->addItem(SDL_GetAudioDeviceName(i, true)); + ui->cbMic->addItem(SDL_GetAudioDeviceName(i, true)); } if (Config::MicDevice == "" && count > 0) - { + { Config::MicDevice = SDL_GetAudioDeviceName(0, true); } - ui->cbMic->setCurrentText(QString::fromStdString(Config::MicDevice)); - + ui->cbMic->setCurrentText(QString::fromStdString(Config::MicDevice)); + grpMicMode = new QButtonGroup(this); grpMicMode->addButton(ui->rbMicNone, micInputType_Silence); grpMicMode->addButton(ui->rbMicExternal, micInputType_External); diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.h b/src/frontend/qt_sdl/AudioSettingsDialog.h index aa994414..98060b2a 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.h +++ b/src/frontend/qt_sdl/AudioSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CLI.cpp b/src/frontend/qt_sdl/CLI.cpp index 9c19d799..dce8ab67 100644 --- a/src/frontend/qt_sdl/CLI.cpp +++ b/src/frontend/qt_sdl/CLI.cpp @@ -1,5 +1,5 @@ /* - Copyright 2021-2022 melonDS team + Copyright 2021-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CLI.h b/src/frontend/qt_sdl/CLI.h index 8850fad2..4997e6a7 100644 --- a/src/frontend/qt_sdl/CLI.h +++ b/src/frontend/qt_sdl/CLI.h @@ -1,5 +1,5 @@ /* - Copyright 2021-2022 melonDS team + Copyright 2021-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp index 19cf8d4d..c158f5ab 100644 --- a/src/frontend/qt_sdl/CameraManager.cpp +++ b/src/frontend/qt_sdl/CameraManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CameraManager.h b/src/frontend/qt_sdl/CameraManager.h index 6743d19e..91e73fbe 100644 --- a/src/frontend/qt_sdl/CameraManager.h +++ b/src/frontend/qt_sdl/CameraManager.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.cpp b/src/frontend/qt_sdl/CameraSettingsDialog.cpp index 1844e0fd..1da0318f 100644 --- a/src/frontend/qt_sdl/CameraSettingsDialog.cpp +++ b/src/frontend/qt_sdl/CameraSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.h b/src/frontend/qt_sdl/CameraSettingsDialog.h index 8572ac42..a740193a 100644 --- a/src/frontend/qt_sdl/CameraSettingsDialog.h +++ b/src/frontend/qt_sdl/CameraSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CheatsDialog.cpp b/src/frontend/qt_sdl/CheatsDialog.cpp index 1da5c5e5..ceaf8032 100644 --- a/src/frontend/qt_sdl/CheatsDialog.cpp +++ b/src/frontend/qt_sdl/CheatsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/CheatsDialog.h b/src/frontend/qt_sdl/CheatsDialog.h index c5a7e13d..8af75be9 100644 --- a/src/frontend/qt_sdl/CheatsDialog.h +++ b/src/frontend/qt_sdl/CheatsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index b46c4bf3..9c6aff84 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index fba9bfb0..5e3db823 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/DateTimeDialog.cpp b/src/frontend/qt_sdl/DateTimeDialog.cpp index 2ef96881..88ae6942 100644 --- a/src/frontend/qt_sdl/DateTimeDialog.cpp +++ b/src/frontend/qt_sdl/DateTimeDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/DateTimeDialog.h b/src/frontend/qt_sdl/DateTimeDialog.h index f13dbd09..22dabcd0 100644 --- a/src/frontend/qt_sdl/DateTimeDialog.h +++ b/src/frontend/qt_sdl/DateTimeDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index 571f36a2..48b380b6 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.h b/src/frontend/qt_sdl/EmuSettingsDialog.h index 2ebfd2fc..b53d090b 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.h +++ b/src/frontend/qt_sdl/EmuSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp index ffca5676..5f76b08a 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.h b/src/frontend/qt_sdl/FirmwareSettingsDialog.h index b3695e2f..d22ce3a2 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.h +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index c1ef87c9..1d39a0fb 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Input.h b/src/frontend/qt_sdl/Input.h index 0d2292d0..04f7a1f1 100644 --- a/src/frontend/qt_sdl/Input.h +++ b/src/frontend/qt_sdl/Input.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp index b0672a04..6d651c9d 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h index 53ea8762..8d4f882a 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InputConfig/MapButton.h b/src/frontend/qt_sdl/InputConfig/MapButton.h index f90fa3f1..b35cc1ff 100644 --- a/src/frontend/qt_sdl/InputConfig/MapButton.h +++ b/src/frontend/qt_sdl/InputConfig/MapButton.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index 7c5eae61..2f7417f6 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.h b/src/frontend/qt_sdl/InterfaceSettingsDialog.h index 114aa047..5a23b6ea 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.h +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/LAN_PCap.cpp b/src/frontend/qt_sdl/LAN_PCap.cpp index f5bf4366..e1cbc11a 100644 --- a/src/frontend/qt_sdl/LAN_PCap.cpp +++ b/src/frontend/qt_sdl/LAN_PCap.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/LAN_PCap.h b/src/frontend/qt_sdl/LAN_PCap.h index 2f4663f0..fe6e0009 100644 --- a/src/frontend/qt_sdl/LAN_PCap.h +++ b/src/frontend/qt_sdl/LAN_PCap.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/LAN_Socket.cpp b/src/frontend/qt_sdl/LAN_Socket.cpp index 6753bb87..e1c86633 100644 --- a/src/frontend/qt_sdl/LAN_Socket.cpp +++ b/src/frontend/qt_sdl/LAN_Socket.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/LAN_Socket.h b/src/frontend/qt_sdl/LAN_Socket.h index f2c7ed44..9b97301d 100644 --- a/src/frontend/qt_sdl/LAN_Socket.h +++ b/src/frontend/qt_sdl/LAN_Socket.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp index 870ea8b8..56139723 100644 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ b/src/frontend/qt_sdl/LocalMP.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/LocalMP.h b/src/frontend/qt_sdl/LocalMP.h index 51dfcb93..07c6f43c 100644 --- a/src/frontend/qt_sdl/LocalMP.h +++ b/src/frontend/qt_sdl/LocalMP.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/MPSettingsDialog.cpp b/src/frontend/qt_sdl/MPSettingsDialog.cpp index e3114220..bd64dfa2 100644 --- a/src/frontend/qt_sdl/MPSettingsDialog.cpp +++ b/src/frontend/qt_sdl/MPSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/MPSettingsDialog.h b/src/frontend/qt_sdl/MPSettingsDialog.h index fe917e89..837ac8db 100644 --- a/src/frontend/qt_sdl/MPSettingsDialog.h +++ b/src/frontend/qt_sdl/MPSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp index d3becc12..02ec1587 100644 --- a/src/frontend/qt_sdl/OSD.cpp +++ b/src/frontend/qt_sdl/OSD.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/OSD.h b/src/frontend/qt_sdl/OSD.h index 907496f1..b399fb42 100644 --- a/src/frontend/qt_sdl/OSD.h +++ b/src/frontend/qt_sdl/OSD.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/OSD_shaders.h b/src/frontend/qt_sdl/OSD_shaders.h index e224fd13..1324fd9d 100644 --- a/src/frontend/qt_sdl/OSD_shaders.h +++ b/src/frontend/qt_sdl/OSD_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp index 286032e9..1f1c35cd 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.cpp +++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PathSettingsDialog.h b/src/frontend/qt_sdl/PathSettingsDialog.h index ef4fd2d0..dd64d46c 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.h +++ b/src/frontend/qt_sdl/PathSettingsDialog.h @@ -1,6 +1,6 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 951e1f54..df97590f 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp index 033c2fef..0354e8ed 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h index f335a5ea..03025864 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/QPathInput.h b/src/frontend/qt_sdl/QPathInput.h index beb618ca..fbead36a 100644 --- a/src/frontend/qt_sdl/QPathInput.h +++ b/src/frontend/qt_sdl/QPathInput.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp index 4be4cb0c..2c1acee7 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.cpp +++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ROMInfoDialog.h b/src/frontend/qt_sdl/ROMInfoDialog.h index fd036a02..39630378 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.h +++ b/src/frontend/qt_sdl/ROMInfoDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 8a54e058..0af1fccc 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 2eeed0a2..c4be23be 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/SaveManager.cpp b/src/frontend/qt_sdl/SaveManager.cpp index ee550912..78d4b0ec 100644 --- a/src/frontend/qt_sdl/SaveManager.cpp +++ b/src/frontend/qt_sdl/SaveManager.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/SaveManager.h b/src/frontend/qt_sdl/SaveManager.h index 3d85af66..6ffb4f86 100644 --- a/src/frontend/qt_sdl/SaveManager.h +++ b/src/frontend/qt_sdl/SaveManager.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index 84bd1efe..eb6b594f 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h index 5182e1da..7209f96a 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.h +++ b/src/frontend/qt_sdl/TitleManagerDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.cpp b/src/frontend/qt_sdl/VideoSettingsDialog.cpp index 95ec7d31..d5ee44c9 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.cpp +++ b/src/frontend/qt_sdl/VideoSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/VideoSettingsDialog.h b/src/frontend/qt_sdl/VideoSettingsDialog.h index 7fee5bb8..29af8e15 100644 --- a/src/frontend/qt_sdl/VideoSettingsDialog.h +++ b/src/frontend/qt_sdl/VideoSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.cpp b/src/frontend/qt_sdl/WifiSettingsDialog.cpp index 9bf265e9..d71657a3 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.cpp +++ b/src/frontend/qt_sdl/WifiSettingsDialog.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/WifiSettingsDialog.h b/src/frontend/qt_sdl/WifiSettingsDialog.h index da949243..82e1bd49 100644 --- a/src/frontend/qt_sdl/WifiSettingsDialog.h +++ b/src/frontend/qt_sdl/WifiSettingsDialog.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/font.h b/src/frontend/qt_sdl/font.h index 01e3bd29..7945c515 100644 --- a/src/frontend/qt_sdl/font.h +++ b/src/frontend/qt_sdl/font.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0945de2a..dcd52bd3 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 5832ed3b..500aec1c 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/frontend/qt_sdl/main_shaders.h b/src/frontend/qt_sdl/main_shaders.h index 37b964d5..fd9de038 100644 --- a/src/frontend/qt_sdl/main_shaders.h +++ b/src/frontend/qt_sdl/main_shaders.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/melonDLDI.h b/src/melonDLDI.h index 261f798b..1fe1e599 100644 --- a/src/melonDLDI.h +++ b/src/melonDLDI.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/types.h b/src/types.h index 47c1b9e4..38556440 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. diff --git a/src/version.h b/src/version.h index f3878206..131c610d 100644 --- a/src/version.h +++ b/src/version.h @@ -1,5 +1,5 @@ /* - Copyright 2016-2022 melonDS team + Copyright 2016-2023 melonDS team This file is part of melonDS. From 76976fef3038918ad01a9dbda4c0e5d9bebef9af Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 4 Nov 2023 14:20:58 +0100 Subject: [PATCH 023/157] convert SPU to OOP --- src/ARMJIT_Memory.cpp | 4 +- src/NDS.cpp | 55 ++++---- src/NDS.h | 4 +- src/SPI.cpp | 14 +- src/SPU.cpp | 208 ++++++++++++++--------------- src/SPU.h | 118 ++++++++++------ src/frontend/qt_sdl/AudioInOut.cpp | 7 +- src/frontend/qt_sdl/main.cpp | 8 +- 8 files changed, 216 insertions(+), 202 deletions(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index a37d7f8a..7a1781ec 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1320,7 +1320,7 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) switch (addr & 0xFF800000) { case 0x04000000: - if (addr >= 0x04000400 && addr < 0x04000520) + /*if (addr >= 0x04000400 && addr < 0x04000520) { switch (size | store) { @@ -1331,7 +1331,7 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) case 32: return (void*)SPU::Read32; case 33: return (void*)SPU::Write32; } - } + }*/ if (NDS::ConsoleType == 0) { diff --git a/src/NDS.cpp b/src/NDS.cpp index 5a5ba347..3fd8f4cc 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -178,7 +178,8 @@ u32 KeyInput; u16 KeyCnt[2]; u16 RCnt; -SPIHost* SPI; +class SPU* SPU; +class SPIHost* SPI; class RTC* RTC; bool Running; @@ -218,13 +219,13 @@ bool Init() DMAs[6] = new DMA(1, 2); DMAs[7] = new DMA(1, 3); - SPI = new SPIHost(); + SPU = new class SPU; + SPI = new class SPIHost(); RTC = new class RTC(); if (!NDSCart::Init()) return false; if (!GBACart::Init()) return false; if (!GPU::Init()) return false; - if (!SPU::Init()) return false; if (!Wifi::Init()) return false; if (!DSi::Init()) return false; @@ -240,11 +241,8 @@ void DeInit() ARMJIT::DeInit(); #endif - delete ARM9; - ARM9 = nullptr; - - delete ARM7; - ARM7 = nullptr; + delete ARM9; ARM9 = nullptr; + delete ARM7; ARM7 = nullptr; for (int i = 0; i < 8; i++) { @@ -252,16 +250,13 @@ void DeInit() DMAs[i] = nullptr; } - delete SPI; - SPI = nullptr; - - delete RTC; - RTC = nullptr; + delete SPU; SPU = nullptr; + delete SPI; SPI = nullptr; + delete RTC; RTC = nullptr; NDSCart::DeInit(); GBACart::DeInit(); GPU::DeInit(); - SPU::DeInit(); Wifi::DeInit(); DSi::DeInit(); @@ -529,7 +524,7 @@ void SetupDirectBoot(const std::string& romname) NDSCart::SPICnt = 0x8000; - SPU::SetBias(0x200); + SPU->SetBias(0x200); SetWifiWaitCnt(0x0030); } @@ -648,7 +643,7 @@ void Reset() NDSCart::Reset(); GBACart::Reset(); GPU::Reset(); - SPU::Reset(); + SPU->Reset(); SPI->Reset(); RTC->Reset(); Wifi::Reset(); @@ -656,7 +651,7 @@ void Reset() // TODO: move the SOUNDBIAS/degrade logic to SPU? // The SOUNDBIAS register does nothing on DSi - SPU::SetApplyBias(ConsoleType == 0); + SPU->SetApplyBias(ConsoleType == 0); bool degradeAudio = true; @@ -673,7 +668,7 @@ void Reset() else if (bitDepth == 2) // Always 16-bit degradeAudio = false; - SPU::SetDegrade10Bit(degradeAudio); + SPU->SetDegrade10Bit(degradeAudio); AREngine::Reset(); } @@ -722,7 +717,7 @@ void Stop(Platform::StopReason reason) Running = false; Platform::SignalStop(reason); GPU::Stop(); - SPU::Stop(); + SPU->Stop(); if (ConsoleType == 1) DSi::Stop(); @@ -850,7 +845,7 @@ bool DoSavestate(Savestate* file) if (ConsoleType == 0) GBACart::DoSavestate(file); GPU::DoSavestate(file); - SPU::DoSavestate(file); + SPU->DoSavestate(file); SPI->DoSavestate(file); RTC->DoSavestate(file); Wifi::DoSavestate(file); @@ -862,7 +857,7 @@ bool DoSavestate(Savestate* file) { GPU::SetPowerCnt(PowerControl9); - SPU::SetPowerCnt(PowerControl7 & 0x0001); + SPU->SetPowerCnt(PowerControl7 & 0x0001); Wifi::SetPowerCnt(PowerControl7 & 0x0002); } @@ -1193,7 +1188,7 @@ u32 RunFrame() ARM7Timestamp-SysTimestamp, GPU3D::Timestamp-SysTimestamp); #endif - SPU::TransferOutput(); + SPU->TransferOutput(); break; } @@ -3922,7 +3917,7 @@ u8 ARM7IORead8(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU::Read8(addr); + return SPU->Read8(addr); } if ((addr & 0xFFFFF000) != 0x04004000) @@ -4016,7 +4011,7 @@ u16 ARM7IORead16(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU::Read16(addr); + return SPU->Read16(addr); } if ((addr & 0xFFFFF000) != 0x04004000) @@ -4117,7 +4112,7 @@ u32 ARM7IORead32(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU::Read32(addr); + return SPU->Read32(addr); } if ((addr & 0xFFFFF000) != 0x04004000) @@ -4195,7 +4190,7 @@ void ARM7IOWrite8(u32 addr, u8 val) if (addr >= 0x04000400 && addr < 0x04000520) { - SPU::Write8(addr, val); + SPU->Write8(addr, val); return; } @@ -4336,7 +4331,7 @@ void ARM7IOWrite16(u32 addr, u16 val) { u16 change = PowerControl7 ^ val; PowerControl7 = val & 0x0003; - SPU::SetPowerCnt(val & 0x0001); + SPU->SetPowerCnt(val & 0x0001); Wifi::SetPowerCnt(val & 0x0002); if (change & 0x0002) UpdateWifiTimings(); } @@ -4350,7 +4345,7 @@ void ARM7IOWrite16(u32 addr, u16 val) if (addr >= 0x04000400 && addr < 0x04000520) { - SPU::Write16(addr, val); + SPU->Write16(addr, val); return; } @@ -4466,7 +4461,7 @@ void ARM7IOWrite32(u32 addr, u32 val) { u16 change = PowerControl7 ^ val; PowerControl7 = val & 0x0003; - SPU::SetPowerCnt(val & 0x0001); + SPU->SetPowerCnt(val & 0x0001); Wifi::SetPowerCnt(val & 0x0002); if (change & 0x0002) UpdateWifiTimings(); } @@ -4484,7 +4479,7 @@ void ARM7IOWrite32(u32 addr, u32 val) if (addr >= 0x04000400 && addr < 0x04000520) { - SPU::Write32(addr, val); + SPU->Write32(addr, val); return; } diff --git a/src/NDS.h b/src/NDS.h index b5d72276..cffcd5dc 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -30,6 +30,7 @@ // with this enabled, to make sure it doesn't desync //#define DEBUG_CHECK_DESYNC +class SPU; class SPIHost; class RTC; @@ -249,7 +250,8 @@ extern MemRegion SWRAM_ARM7; extern u32 KeyInput; extern u16 RCnt; -extern SPIHost* SPI; +extern class SPU* SPU; +extern class SPIHost* SPI; extern class RTC* RTC; const u32 ARM7WRAMSize = 0x10000; diff --git a/src/SPI.cpp b/src/SPI.cpp index 4b72f5c0..a95cd1c2 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -533,7 +533,11 @@ SPIHost::SPIHost() Devices[SPIDevice_FirmwareMem] = new FirmwareMem(this); Devices[SPIDevice_PowerMan] = new PowerMan(this); - Devices[SPIDevice_TSC] = nullptr; + + if (NDS::ConsoleType == 1) + Devices[SPIDevice_TSC] = new DSi_TSC(this); + else + Devices[SPIDevice_TSC] = new TSC(this); } SPIHost::~SPIHost() @@ -553,14 +557,6 @@ void SPIHost::Reset() { Cnt = 0; - if (Devices[SPIDevice_TSC]) - delete Devices[SPIDevice_TSC]; - - if (NDS::ConsoleType == 1) - Devices[SPIDevice_TSC] = new DSi_TSC(this); - else - Devices[SPIDevice_TSC] = new TSC(this); - for (int i = 0; i < SPIDevice_MAX; i++) { Devices[i]->Reset(); diff --git a/src/SPU.cpp b/src/SPU.cpp index bfe66227..89b7fb3b 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -32,12 +32,10 @@ using Platform::LogLevel; // * capture addition modes, overflow bugs // * channel hold -namespace SPU -{ -const s8 ADPCMIndexTable[8] = {-1, -1, -1, -1, 2, 4, 6, 8}; +const s8 SPUChannel::ADPCMIndexTable[8] = {-1, -1, -1, -1, 2, 4, 6, 8}; -const u16 ADPCMTable[89] = +const u16 SPUChannel::ADPCMTable[89] = { 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F, @@ -53,7 +51,7 @@ const u16 ADPCMTable[89] = 0x7FFF }; -const s16 PSGTable[8][8] = +const s16 SPUChannel::PSGTable[8][8] = { {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF}, {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, 0x7FFF, 0x7FFF}, @@ -65,76 +63,63 @@ const s16 PSGTable[8][8] = {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF} }; -// audio interpolation is an improvement upon the original hardware -// (which performs no interpolation) -int InterpType; -s16 InterpCos[0x100]; -s16 InterpCubic[0x100][4]; - -const u32 OutputBufferSize = 2*2048; -s16 OutputBackbuffer[2 * OutputBufferSize]; -u32 OutputBackbufferWritePosition; - -s16 OutputFrontBuffer[2 * OutputBufferSize]; -u32 OutputFrontBufferWritePosition; -u32 OutputFrontBufferReadPosition; - -Platform::Mutex* AudioLock; - -u16 Cnt; -u8 MasterVolume; -u16 Bias; -bool ApplyBias; -bool Degrade10Bit; - -Channel* Channels[16]; -CaptureUnit* Capture[2]; +s16 SPUChannel::InterpCos[0x100]; +s16 SPUChannel::InterpCubic[0x100][4]; +bool SPUChannel::InterpInited = false; -bool Init() +SPU::SPU() { - NDS::RegisterEventFunc(NDS::Event_SPU, 0, Mix); + NDS::RegisterEventFunc(NDS::Event_SPU, 0, MemberEventFunc(SPU, Mix)); for (int i = 0; i < 16; i++) - Channels[i] = new Channel(i); + Channels[i] = new SPUChannel(i); - Capture[0] = new CaptureUnit(0); - Capture[1] = new CaptureUnit(1); + Capture[0] = new SPUCaptureUnit(0); + Capture[1] = new SPUCaptureUnit(1); AudioLock = Platform::Mutex_Create(); - InterpType = 0; ApplyBias = true; Degrade10Bit = false; - // generate interpolation tables - // values are 1:1:14 fixed-point + memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2); - float m_pi = std::acos(-1.0f); - for (int i = 0; i < 0x100; i++) + OutputBackbufferWritePosition = 0; + OutputFrontBufferReadPosition = 0; + OutputFrontBufferWritePosition = 0; + + if (!SPUChannel::InterpInited) { - float ratio = (i * m_pi) / 255.0f; - ratio = 1.0f - std::cos(ratio); + // generate interpolation tables + // values are 1:1:14 fixed-point - InterpCos[i] = (s16)(ratio * 0x2000); + float m_pi = std::acos(-1.0f); + for (int i = 0; i < 0x100; i++) + { + float ratio = (i * m_pi) / 255.0f; + ratio = 1.0f - std::cos(ratio); + + SPUChannel::InterpCos[i] = (s16)(ratio * 0x2000); + } + + for (int i = 0; i < 0x100; i++) + { + s32 i1 = i << 6; + s32 i2 = (i * i) >> 2; + s32 i3 = (i * i * i) >> 10; + + SPUChannel::InterpCubic[i][0] = -i3 + 2*i2 - i1; + SPUChannel::InterpCubic[i][1] = i3 - 2*i2 + 0x4000; + SPUChannel::InterpCubic[i][2] = -i3 + i2 + i1; + SPUChannel::InterpCubic[i][3] = i3 - i2; + } + + SPUChannel::InterpInited = true; } - - for (int i = 0; i < 0x100; i++) - { - s32 i1 = i << 6; - s32 i2 = (i * i) >> 2; - s32 i3 = (i * i * i) >> 10; - - InterpCubic[i][0] = -i3 + 2*i2 - i1; - InterpCubic[i][1] = i3 - 2*i2 + 0x4000; - InterpCubic[i][2] = -i3 + i2 + i1; - InterpCubic[i][3] = i3 - i2; - } - - return true; } -void DeInit() +SPU::~SPU() { for (int i = 0; i < 16; i++) { @@ -153,7 +138,7 @@ void DeInit() NDS::UnregisterEventFunc(NDS::Event_SPU, 0); } -void Reset() +void SPU::Reset() { InitOutput(); @@ -170,7 +155,7 @@ void Reset() NDS::ScheduleEvent(NDS::Event_SPU, false, 1024, 0, 0); } -void Stop() +void SPU::Stop() { Platform::Mutex_Lock(AudioLock); memset(OutputFrontBuffer, 0, 2*OutputBufferSize*2); @@ -181,7 +166,7 @@ void Stop() Platform::Mutex_Unlock(AudioLock); } -void DoSavestate(Savestate* file) +void SPU::DoSavestate(Savestate* file) { file->Section("SPU."); @@ -197,43 +182,46 @@ void DoSavestate(Savestate* file) } -void SetPowerCnt(u32 val) +void SPU::SetPowerCnt(u32 val) { // TODO } -void SetInterpolation(int type) +void SPU::SetInterpolation(int type) { - InterpType = type; + for (int i = 0; i < 16; i++) + Channels[i]->InterpType = type; } -void SetBias(u16 bias) +void SPU::SetBias(u16 bias) { Bias = bias; } -void SetApplyBias(bool enable) +void SPU::SetApplyBias(bool enable) { ApplyBias = enable; } -void SetDegrade10Bit(bool enable) +void SPU::SetDegrade10Bit(bool enable) { Degrade10Bit = enable; } -Channel::Channel(u32 num) +SPUChannel::SPUChannel(u32 num) { Num = num; + + InterpType = 0; } -Channel::~Channel() +SPUChannel::~SPUChannel() { } -void Channel::Reset() +void SPUChannel::Reset() { if (NDS::ConsoleType == 1) BusRead32 = DSi::ARM7Read32; @@ -257,7 +245,7 @@ void Channel::Reset() FIFOLevel = 0; } -void Channel::DoSavestate(Savestate* file) +void SPUChannel::DoSavestate(Savestate* file) { file->Var32(&Cnt); file->Var32(&SrcAddr); @@ -289,7 +277,7 @@ void Channel::DoSavestate(Savestate* file) file->VarArray(FIFO, sizeof(FIFO)); } -void Channel::FIFO_BufferData() +void SPUChannel::FIFO_BufferData() { u32 totallen = LoopPos + Length; @@ -330,7 +318,7 @@ void Channel::FIFO_BufferData() } template -T Channel::FIFO_ReadData() +T SPUChannel::FIFO_ReadData() { T ret = *(T*)&((u8*)FIFO)[FIFOReadPos]; @@ -344,7 +332,7 @@ T Channel::FIFO_ReadData() return ret; } -void Channel::Start() +void SPUChannel::Start() { Timer = TimerReload; @@ -372,7 +360,7 @@ void Channel::Start() } } -void Channel::NextSample_PCM8() +void SPUChannel::NextSample_PCM8() { Pos++; if (Pos < 0) return; @@ -395,7 +383,7 @@ void Channel::NextSample_PCM8() CurSample = val << 8; } -void Channel::NextSample_PCM16() +void SPUChannel::NextSample_PCM16() { Pos++; if (Pos < 0) return; @@ -418,7 +406,7 @@ void Channel::NextSample_PCM16() CurSample = val; } -void Channel::NextSample_ADPCM() +void SPUChannel::NextSample_ADPCM() { Pos++; if (Pos < 8) @@ -493,13 +481,13 @@ void Channel::NextSample_ADPCM() CurSample = ADPCMVal; } -void Channel::NextSample_PSG() +void SPUChannel::NextSample_PSG() { Pos++; CurSample = PSGTable[(Cnt >> 24) & 0x7][Pos & 0x7]; } -void Channel::NextSample_Noise() +void SPUChannel::NextSample_Noise() { if (NoiseVal & 0x1) { @@ -514,7 +502,7 @@ void Channel::NextSample_Noise() } template -s32 Channel::Run() +s32 SPUChannel::Run() { if (!(Cnt & (1<<31))) return 0; @@ -586,23 +574,23 @@ s32 Channel::Run() return val; } -void Channel::PanOutput(s32 in, s32& left, s32& right) +void SPUChannel::PanOutput(s32 in, s32& left, s32& right) { left += ((s64)in * (128-Pan)) >> 10; right += ((s64)in * Pan) >> 10; } -CaptureUnit::CaptureUnit(u32 num) +SPUCaptureUnit::SPUCaptureUnit(u32 num) { Num = num; } -CaptureUnit::~CaptureUnit() +SPUCaptureUnit::~SPUCaptureUnit() { } -void CaptureUnit::Reset() +void SPUCaptureUnit::Reset() { if (NDS::ConsoleType == 1) BusWrite32 = DSi::ARM7Write32; @@ -623,7 +611,7 @@ void CaptureUnit::Reset() FIFOLevel = 0; } -void CaptureUnit::DoSavestate(Savestate* file) +void SPUCaptureUnit::DoSavestate(Savestate* file) { file->Var8(&Cnt); file->Var32(&DstAddr); @@ -640,7 +628,7 @@ void CaptureUnit::DoSavestate(Savestate* file) file->VarArray(FIFO, 4*4); } -void CaptureUnit::FIFO_FlushData() +void SPUCaptureUnit::FIFO_FlushData() { for (u32 i = 0; i < 4; i++) { @@ -660,7 +648,7 @@ void CaptureUnit::FIFO_FlushData() } template -void CaptureUnit::FIFO_WriteData(T val) +void SPUCaptureUnit::FIFO_WriteData(T val) { *(T*)&((u8*)FIFO)[FIFOWritePos] = val; @@ -672,7 +660,7 @@ void CaptureUnit::FIFO_WriteData(T val) FIFO_FlushData(); } -void CaptureUnit::Run(s32 sample) +void SPUCaptureUnit::Run(s32 sample) { Timer += 512; @@ -725,7 +713,7 @@ void CaptureUnit::Run(s32 sample) } -void Mix(u32 dummy) +void SPU::Mix(u32 dummy) { s32 left = 0, right = 0; s32 leftoutput = 0, rightoutput = 0; @@ -746,7 +734,7 @@ void Mix(u32 dummy) for (int i = 4; i < 16; i++) { - Channel* chan = Channels[i]; + SPUChannel* chan = Channels[i]; s32 channel = chan->DoRun(); chan->PanOutput(channel, left, right); @@ -871,7 +859,7 @@ void Mix(u32 dummy) NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, 0, 0); } -void TransferOutput() +void SPU::TransferOutput() { Platform::Mutex_Lock(AudioLock); for (u32 i = 0; i < OutputBackbufferWritePosition; i += 2) @@ -889,10 +877,10 @@ void TransferOutput() } } OutputBackbufferWritePosition = 0; - Platform::Mutex_Unlock(AudioLock); + Platform::Mutex_Unlock(AudioLock);; } -void TrimOutput() +void SPU::TrimOutput() { Platform::Mutex_Lock(AudioLock); const int halflimit = (OutputBufferSize / 2); @@ -904,7 +892,7 @@ void TrimOutput() Platform::Mutex_Unlock(AudioLock); } -void DrainOutput() +void SPU::DrainOutput() { Platform::Mutex_Lock(AudioLock); OutputFrontBufferWritePosition = 0; @@ -912,7 +900,7 @@ void DrainOutput() Platform::Mutex_Unlock(AudioLock); } -void InitOutput() +void SPU::InitOutput() { Platform::Mutex_Lock(AudioLock); memset(OutputBackbuffer, 0, 2*OutputBufferSize*2); @@ -922,7 +910,7 @@ void InitOutput() Platform::Mutex_Unlock(AudioLock); } -int GetOutputSize() +int SPU::GetOutputSize() { Platform::Mutex_Lock(AudioLock); @@ -938,7 +926,7 @@ int GetOutputSize() return ret; } -void Sync(bool wait) +void SPU::Sync(bool wait) { // this function is currently not used anywhere // depending on the usage context the thread safety measures could be made @@ -968,7 +956,7 @@ void Sync(bool wait) } } -int ReadOutput(s16* data, int samples) +int SPU::ReadOutput(s16* data, int samples) { Platform::Mutex_Lock(AudioLock); if (OutputFrontBufferReadPosition == OutputFrontBufferWritePosition) @@ -997,11 +985,11 @@ int ReadOutput(s16* data, int samples) } -u8 Read8(u32 addr) +u8 SPU::Read8(u32 addr) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1027,11 +1015,11 @@ u8 Read8(u32 addr) return 0; } -u16 Read16(u32 addr) +u16 SPU::Read16(u32 addr) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1054,11 +1042,11 @@ u16 Read16(u32 addr) return 0; } -u32 Read32(u32 addr) +u32 SPU::Read32(u32 addr) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1083,11 +1071,11 @@ u32 Read32(u32 addr) return 0; } -void Write8(u32 addr, u8 val) +void SPU::Write8(u32 addr, u8 val) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1124,11 +1112,11 @@ void Write8(u32 addr, u8 val) Log(LogLevel::Warn, "unknown SPU write8 %08X %02X\n", addr, val); } -void Write16(u32 addr, u16 val) +void SPU::Write16(u32 addr, u16 val) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1173,11 +1161,11 @@ void Write16(u32 addr, u16 val) Log(LogLevel::Warn, "unknown SPU write16 %08X %04X\n", addr, val); } -void Write32(u32 addr, u32 val) +void SPU::Write32(u32 addr, u32 val) { if (addr < 0x04000500) { - Channel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1220,5 +1208,3 @@ void Write32(u32 addr, u32 val) } } } - -} diff --git a/src/SPU.h b/src/SPU.h index 12cba611..407a3f62 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -20,51 +20,30 @@ #define SPU_H #include "Savestate.h" +#include "Platform.h" -namespace SPU -{ +class SPU; -bool Init(); -void DeInit(); -void Reset(); -void Stop(); - -void DoSavestate(Savestate* file); - -void SetPowerCnt(u32 val); - -// 0=none 1=linear 2=cosine 3=cubic -void SetInterpolation(int type); - -void SetBias(u16 bias); -void SetDegrade10Bit(bool enable); -void SetApplyBias(bool enable); - -void Mix(u32 dummy); - -void TrimOutput(); -void DrainOutput(); -void InitOutput(); -int GetOutputSize(); -void Sync(bool wait); -int ReadOutput(s16* data, int samples); -void TransferOutput(); - -u8 Read8(u32 addr); -u16 Read16(u32 addr); -u32 Read32(u32 addr); -void Write8(u32 addr, u8 val); -void Write16(u32 addr, u16 val); -void Write32(u32 addr, u32 val); - -class Channel +class SPUChannel { public: - Channel(u32 num); - ~Channel(); + SPUChannel(u32 num); + ~SPUChannel(); void Reset(); void DoSavestate(Savestate* file); + static const s8 ADPCMIndexTable[8]; + static const u16 ADPCMTable[89]; + static const s16 PSGTable[8][8]; + + static s16 InterpCos[0x100]; + static s16 InterpCubic[0x100][4]; + static bool InterpInited; + + // audio interpolation is an improvement upon the original hardware + // (which performs no interpolation) + int InterpType; + u32 Num; u32 Cnt; @@ -164,11 +143,11 @@ private: u32 (*BusRead32)(u32 addr); }; -class CaptureUnit +class SPUCaptureUnit { public: - CaptureUnit(u32 num); - ~CaptureUnit(); + SPUCaptureUnit(u32 num); + ~SPUCaptureUnit(); void Reset(); void DoSavestate(Savestate* file); @@ -221,6 +200,61 @@ private: void (*BusWrite32)(u32 addr, u32 val); }; -} +class SPU +{ +public: + SPU(); + ~SPU(); + void Reset(); + void DoSavestate(Savestate* file); + + void Stop(); + + void SetPowerCnt(u32 val); + + // 0=none 1=linear 2=cosine 3=cubic + void SetInterpolation(int type); + + void SetBias(u16 bias); + void SetDegrade10Bit(bool enable); + void SetApplyBias(bool enable); + + void Mix(u32 dummy); + + void TrimOutput(); + void DrainOutput(); + void InitOutput(); + int GetOutputSize(); + void Sync(bool wait); + int ReadOutput(s16* data, int samples); + void TransferOutput(); + + u8 Read8(u32 addr); + u16 Read16(u32 addr); + u32 Read32(u32 addr); + void Write8(u32 addr, u8 val); + void Write16(u32 addr, u16 val); + void Write32(u32 addr, u32 val); + +private: + static const u32 OutputBufferSize = 2*2048; + s16 OutputBackbuffer[2 * OutputBufferSize]; + u32 OutputBackbufferWritePosition; + + s16 OutputFrontBuffer[2 * OutputBufferSize]; + u32 OutputFrontBufferWritePosition; + u32 OutputFrontBufferReadPosition; + + Platform::Mutex* AudioLock; + + u16 Cnt; + u8 MasterVolume; + u16 Bias; + bool ApplyBias; + bool Degrade10Bit; + + SPUChannel* Channels[16]; + SPUCaptureUnit* Capture[2]; +}; #endif // SPU_H diff --git a/src/frontend/qt_sdl/AudioInOut.cpp b/src/frontend/qt_sdl/AudioInOut.cpp index 120ff5fc..7faac9f3 100644 --- a/src/frontend/qt_sdl/AudioInOut.cpp +++ b/src/frontend/qt_sdl/AudioInOut.cpp @@ -22,6 +22,7 @@ #include "FrontendUtil.h" #include "Config.h" +#include "NDS.h" #include "SPU.h" #include "Platform.h" #include "Input.h" @@ -54,7 +55,7 @@ void AudioCallback(void* data, Uint8* stream, int len) int num_in; SDL_LockMutex(audioSyncLock); - num_in = SPU::ReadOutput(buf_in, len_in); + num_in = NDS::SPU->ReadOutput(buf_in, len_in); SDL_CondSignal(audioSync); SDL_UnlockMutex(audioSyncLock); @@ -352,7 +353,7 @@ void AudioSync() if (audioDevice) { SDL_LockMutex(audioSyncLock); - while (SPU::GetOutputSize() > 1024) + while (NDS::SPU->GetOutputSize() > 1024) { int ret = SDL_CondWaitTimeout(audioSync, audioSyncLock, 500); if (ret == SDL_MUTEX_TIMEDOUT) break; @@ -365,7 +366,7 @@ void UpdateSettings() { MicClose(); - SPU::SetInterpolation(Config::AudioInterp); + NDS::SPU->SetInterpolation(Config::AudioInterp); SetupMicInputData(); MicOpen(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index dcd52bd3..700caea3 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -342,7 +342,7 @@ void EmuThread::run() GPU::InitRenderer(videoRenderer); GPU::SetRenderSettings(videoRenderer, videoSettings); - SPU::SetInterpolation(Config::AudioInterp); + NDS::SPU->SetInterpolation(Config::AudioInterp); Input::Init(); @@ -2942,12 +2942,12 @@ void MainWindow::onPathSettingsFinished(int res) void MainWindow::onUpdateAudioSettings() { - SPU::SetInterpolation(Config::AudioInterp); + NDS::SPU->SetInterpolation(Config::AudioInterp); if (Config::AudioBitDepth == 0) - SPU::SetDegrade10Bit(NDS::ConsoleType == 0); + NDS::SPU->SetDegrade10Bit(NDS::ConsoleType == 0); else - SPU::SetDegrade10Bit(Config::AudioBitDepth == 1); + NDS::SPU->SetDegrade10Bit(Config::AudioBitDepth == 1); } void MainWindow::onAudioSettingsFinished(int res) From 2bd09eafebbc98e2e2b9fbb023b31551ccd8cf12 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 4 Nov 2023 17:00:12 +0100 Subject: [PATCH 024/157] convert Wifi and WifiAP --- src/ARMJIT_Memory.cpp | 9 +- src/NDS.cpp | 29 ++-- src/NDS.h | 2 + src/Wifi.cpp | 197 +++++++-------------- src/Wifi.h | 396 ++++++++++++++++++++++++++---------------- src/WifiAP.cpp | 66 ++----- src/WifiAP.h | 44 +++-- 7 files changed, 376 insertions(+), 367 deletions(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 7a1781ec..b1673abc 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1212,7 +1212,7 @@ int ClassifyAddress7(u32 addr) } } -void WifiWrite32(u32 addr, u32 val) +/*void WifiWrite32(u32 addr, u32 val) { Wifi::Write(addr, val & 0xFFFF); Wifi::Write(addr + 2, val >> 16); @@ -1221,7 +1221,7 @@ void WifiWrite32(u32 addr, u32 val) u32 WifiRead32(u32 addr) { return (u32)Wifi::Read(addr) | ((u32)Wifi::Read(addr + 2) << 16); -} +}*/ template void VRAMWrite(u32 addr, T val) @@ -1358,7 +1358,8 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) } } break; - case 0x04800000: + // TODO: the wifi funcs also ought to check POWCNT + /*case 0x04800000: if (addr < 0x04810000 && size >= 16) { switch (size | store) @@ -1369,7 +1370,7 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) case 33: return (void*)WifiWrite32; } } - break; + break;*/ case 0x06000000: case 0x06800000: switch (size | store) diff --git a/src/NDS.cpp b/src/NDS.cpp index 3fd8f4cc..2fdbf63b 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -181,6 +181,7 @@ u16 RCnt; class SPU* SPU; class SPIHost* SPI; class RTC* RTC; +class Wifi* Wifi; bool Running; @@ -222,11 +223,11 @@ bool Init() SPU = new class SPU; SPI = new class SPIHost(); RTC = new class RTC(); + Wifi = new class Wifi(); if (!NDSCart::Init()) return false; if (!GBACart::Init()) return false; if (!GPU::Init()) return false; - if (!Wifi::Init()) return false; if (!DSi::Init()) return false; @@ -253,11 +254,11 @@ void DeInit() delete SPU; SPU = nullptr; delete SPI; SPI = nullptr; delete RTC; RTC = nullptr; + delete Wifi; Wifi = nullptr; NDSCart::DeInit(); GBACart::DeInit(); GPU::DeInit(); - Wifi::DeInit(); DSi::DeInit(); @@ -646,7 +647,7 @@ void Reset() SPU->Reset(); SPI->Reset(); RTC->Reset(); - Wifi::Reset(); + Wifi->Reset(); // TODO: move the SOUNDBIAS/degrade logic to SPU? @@ -848,7 +849,7 @@ bool DoSavestate(Savestate* file) SPU->DoSavestate(file); SPI->DoSavestate(file); RTC->DoSavestate(file); - Wifi::DoSavestate(file); + Wifi->DoSavestate(file); if (ConsoleType == 1) DSi::DoSavestate(file); @@ -858,7 +859,7 @@ bool DoSavestate(Savestate* file) GPU::SetPowerCnt(PowerControl9); SPU->SetPowerCnt(PowerControl7 & 0x0001); - Wifi::SetPowerCnt(PowerControl7 & 0x0002); + Wifi->SetPowerCnt(PowerControl7 & 0x0002); } #ifdef JIT_ENABLED @@ -2542,8 +2543,8 @@ u8 ARM7Read8(u32 addr) if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - if (addr & 0x1) return Wifi::Read(addr-1) >> 8; - return Wifi::Read(addr) & 0xFF; + if (addr & 0x1) return Wifi->Read(addr-1) >> 8; + return Wifi->Read(addr) & 0xFF; } break; @@ -2609,7 +2610,7 @@ u16 ARM7Read16(u32 addr) if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - return Wifi::Read(addr); + return Wifi->Read(addr); } break; @@ -2675,7 +2676,7 @@ u32 ARM7Read32(u32 addr) if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - return Wifi::Read(addr) | (Wifi::Read(addr+2) << 16); + return Wifi->Read(addr) | (Wifi->Read(addr+2) << 16); } break; @@ -2818,7 +2819,7 @@ void ARM7Write16(u32 addr, u16 val) if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return; - Wifi::Write(addr, val); + Wifi->Write(addr, val); return; } break; @@ -2898,8 +2899,8 @@ void ARM7Write32(u32 addr, u32 val) if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return; - Wifi::Write(addr, val & 0xFFFF); - Wifi::Write(addr+2, val >> 16); + Wifi->Write(addr, val & 0xFFFF); + Wifi->Write(addr+2, val >> 16); return; } break; @@ -4332,7 +4333,7 @@ void ARM7IOWrite16(u32 addr, u16 val) u16 change = PowerControl7 ^ val; PowerControl7 = val & 0x0003; SPU->SetPowerCnt(val & 0x0001); - Wifi::SetPowerCnt(val & 0x0002); + Wifi->SetPowerCnt(val & 0x0002); if (change & 0x0002) UpdateWifiTimings(); } return; @@ -4462,7 +4463,7 @@ void ARM7IOWrite32(u32 addr, u32 val) u16 change = PowerControl7 ^ val; PowerControl7 = val & 0x0003; SPU->SetPowerCnt(val & 0x0001); - Wifi::SetPowerCnt(val & 0x0002); + Wifi->SetPowerCnt(val & 0x0002); if (change & 0x0002) UpdateWifiTimings(); } return; diff --git a/src/NDS.h b/src/NDS.h index cffcd5dc..4b556c64 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -33,6 +33,7 @@ class SPU; class SPIHost; class RTC; +class Wifi; namespace NDS { @@ -253,6 +254,7 @@ extern u16 RCnt; extern class SPU* SPU; extern class SPIHost* SPI; extern class RTC* RTC; +extern class Wifi* Wifi; const u32 ARM7WRAMSize = 0x10000; extern u8* ARM7WRAM; diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 3716b4c5..1486b409 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -27,91 +27,19 @@ using Platform::Log; using Platform::LogLevel; -namespace Wifi -{ //#define WIFI_LOG printf #define WIFI_LOG(...) {} #define PRINT_MAC(pf, mac) Log(LogLevel::Debug, "%s: %02X:%02X:%02X:%02X:%02X:%02X\n", pf, (mac)[0], (mac)[1], (mac)[2], (mac)[3], (mac)[4], (mac)[5]); -u8 RAM[0x2000]; -u16 IO[0x1000>>1]; - #define IOPORT(x) IO[(x)>>1] #define IOPORT8(x) ((u8*)IO)[x] // destination MACs for MP frames -const u8 MPCmdMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x00}; -const u8 MPReplyMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x10}; -const u8 MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03}; - -const int kTimerInterval = 8; -const u32 kTimeCheckMask = ~(kTimerInterval - 1); - -bool Enabled; -bool PowerOn; - -s32 TimerError; - -u16 Random; - -// general, always-on microsecond counter -u64 USTimestamp; - -u64 USCounter; -u64 USCompare; -bool BlockBeaconIRQ14; - -u32 CmdCounter; - -u8 BBRegs[0x100]; -u8 BBRegsRO[0x100]; - -u8 RFVersion; -u32 RFRegs[0x40]; - -struct TXSlot -{ - bool Valid; - u16 Addr; - u16 Length; - u8 Rate; - u8 CurPhase; - int CurPhaseTime; - u32 HalfwordTimeMask; -}; - -TXSlot TXSlots[6]; -u8 TXBuffer[0x2000]; - -u8 RXBuffer[2048]; -u32 RXBufferPtr; -int RXTime; -u32 RXHalfwordTimeMask; - -u32 ComStatus; // 0=waiting for packets 1=receiving 2=sending -u32 TXCurSlot; -u32 RXCounter; - -int MPReplyTimer; -u16 MPClientMask, MPClientFail; - -u8 MPClientReplies[15*1024]; - -u16 MPLastSeqno; - -bool MPInited; -bool LANInited; - -int USUntilPowerOn; -bool ForcePowerOn; - -// MULTIPLAYER SYNC APPARATUS -bool IsMP; -bool IsMPClient; -u64 NextSync; // for clients: timestamp for next sync point -u64 RXTimestamp; +const u8 Wifi::MPCmdMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x00}; +const u8 Wifi::MPReplyMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x10}; +const u8 Wifi::MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03}; // multiplayer host TX sequence: // 1. preamble @@ -148,9 +76,20 @@ u64 RXTimestamp; // * TX errors (if applicable) -bool Init() +bool MACEqual(u8* a, const u8* b) { - NDS::RegisterEventFunc(NDS::Event_Wifi, 0, USTimer); + return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]); +} + +bool MACIsBroadcast(u8* a) +{ + return (*(u32*)&a[0] == 0xFFFFFFFF) && (*(u16*)&a[4] == 0xFFFF); +} + + +Wifi::Wifi() +{ + NDS::RegisterEventFunc(NDS::Event_Wifi, 0, MemberEventFunc(Wifi, USTimer)); //MPInited = false; //LANInited = false; @@ -161,24 +100,22 @@ bool Init() Platform::LAN_Init(); LANInited = true; - WifiAP::Init(); - - return true; + WifiAP = new class WifiAP(this); } -void DeInit() +Wifi::~Wifi() { if (MPInited) Platform::MP_DeInit(); if (LANInited) Platform::LAN_DeInit(); - WifiAP::DeInit(); + delete WifiAP; WifiAP = nullptr; NDS::UnregisterEventFunc(NDS::Event_Wifi, 0); } -void Reset() +void Wifi::Reset() { memset(RAM, 0, 0x2000); memset(IO, 0, 0x1000); @@ -277,10 +214,10 @@ void Reset() NextSync = 0; RXTimestamp = 0; - WifiAP::Reset(); + WifiAP->Reset(); } -void DoSavestate(Savestate* file) +void Wifi::DoSavestate(Savestate* file) { file->Section("WIFI"); @@ -355,7 +292,7 @@ void DoSavestate(Savestate* file) } -void ScheduleTimer(bool first) +void Wifi::ScheduleTimer(bool first) { if (first) TimerError = 0; @@ -367,7 +304,7 @@ void ScheduleTimer(bool first) NDS::ScheduleEvent(NDS::Event_Wifi, !first, delay, 0, 0); } -void UpdatePowerOn() +void Wifi::UpdatePowerOn() { bool on = Enabled; @@ -405,17 +342,14 @@ void UpdatePowerOn() } } -void SetPowerCnt(u32 val) +void Wifi::SetPowerCnt(u32 val) { Enabled = val & (1<<1); UpdatePowerOn(); } -void PowerDown(); -void StartTX_Beacon(); - -void SetIRQ(u32 irq) +void Wifi::SetIRQ(u32 irq) { u32 oldflags = IOPORT(W_IF) & IOPORT(W_IE); @@ -426,7 +360,7 @@ void SetIRQ(u32 irq) NDS::SetIRQ(1, NDS::IRQ_Wifi); } -void SetIRQ13() +void Wifi::SetIRQ13() { SetIRQ(13); @@ -440,7 +374,7 @@ void SetIRQ13() } } -void SetIRQ14(int source) // 0=USCOMPARE 1=BEACONCOUNT 2=forced +void Wifi::SetIRQ14(int source) // 0=USCOMPARE 1=BEACONCOUNT 2=forced { if (source != 2) IOPORT(W_BeaconCount1) = IOPORT(W_BeaconInterval); @@ -469,7 +403,7 @@ void SetIRQ14(int source) // 0=USCOMPARE 1=BEACONCOUNT 2=forced IOPORT(W_ListenCount)--; } -void SetIRQ15() +void Wifi::SetIRQ15() { SetIRQ(15); @@ -481,7 +415,7 @@ void SetIRQ15() } -void SetStatus(u32 status) +void Wifi::SetStatus(u32 status) { // TODO, eventually: states 2/4/7 u16 rfpins[10] = {0x04, 0x84, 0, 0x46, 0, 0x84, 0x87, 0, 0x46, 0x04}; @@ -490,7 +424,7 @@ void SetStatus(u32 status) } -void PowerDown() +void Wifi::PowerDown() { IOPORT(W_TXReqRead) &= ~0x000F; IOPORT(W_PowerState) |= 0x0200; @@ -504,20 +438,14 @@ void PowerDown() } -bool MACEqual(const u8* a, const u8* b) -{ - return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]); -} - - -int PreambleLen(int rate) +int Wifi::PreambleLen(int rate) { if (rate == 1) return 192; if (IOPORT(W_Preamble) & 0x0004) return 96; return 192; } -u32 NumClients(u16 bitmask) +u32 Wifi::NumClients(u16 bitmask) { u32 ret = 0; for (int i = 1; i < 16; i++) @@ -527,14 +455,14 @@ u32 NumClients(u16 bitmask) return ret; } -void IncrementTXCount(TXSlot* slot) +void Wifi::IncrementTXCount(TXSlot* slot) { u8 cnt = RAM[slot->Addr + 0x4]; if (cnt < 0xFF) cnt++; *(u16*)&RAM[slot->Addr + 0x4] = cnt; } -void ReportMPReplyErrors(u16 clientfail) +void Wifi::ReportMPReplyErrors(u16 clientfail) { // TODO: do these trigger any IRQ? @@ -547,7 +475,7 @@ void ReportMPReplyErrors(u16 clientfail) } } -void TXSendFrame(TXSlot* slot, int num) +void Wifi::TXSendFrame(TXSlot* slot, int num) { u32 noseqno = 0; @@ -597,7 +525,7 @@ void TXSendFrame(TXSlot* slot, int num) case 2: case 3: Platform::MP_SendPacket(TXBuffer, 12+len, USTimestamp); - if (!IsMP) WifiAP::SendPacket(TXBuffer, 12+len); + if (!IsMP) WifiAP->SendPacket(TXBuffer, 12+len); break; case 1: @@ -617,7 +545,7 @@ void TXSendFrame(TXSlot* slot, int num) } } -void StartTX_LocN(int nslot, int loc) +void Wifi::StartTX_LocN(int nslot, int loc) { TXSlot* slot = &TXSlots[nslot]; @@ -637,7 +565,7 @@ void StartTX_LocN(int nslot, int loc) slot->CurPhaseTime = PreambleLen(slot->Rate); } -void StartTX_Cmd() +void Wifi::StartTX_Cmd() { TXSlot* slot = &TXSlots[1]; @@ -672,7 +600,7 @@ void StartTX_Cmd() } } -void StartTX_Beacon() +void Wifi::StartTX_Beacon() { TXSlot* slot = &TXSlots[4]; @@ -691,7 +619,7 @@ void StartTX_Beacon() IOPORT(W_TXBusy) |= 0x0010; } -void FireTX() +void Wifi::FireTX() { if (!(IOPORT(W_RXCnt) & 0x8000)) return; @@ -736,7 +664,7 @@ void FireTX() } } -void SendMPDefaultReply() +void Wifi::SendMPDefaultReply() { u8 reply[12 + 28]; @@ -766,7 +694,7 @@ void SendMPDefaultReply() WIFI_LOG("wifi: sent %d/40 bytes of MP default reply\n", txlen); } -void SendMPReply(u16 clienttime, u16 clientmask) +void Wifi::SendMPReply(u16 clienttime, u16 clientmask) { TXSlot* slot = &TXSlots[5]; @@ -827,7 +755,7 @@ void SendMPReply(u16 clienttime, u16 clientmask) IOPORT(W_TXBusy) |= 0x0080; } -void SendMPAck(u16 cmdcount, u16 clientfail) +void Wifi::SendMPAck(u16 cmdcount, u16 clientfail) { u8 ack[12 + 32]; @@ -873,10 +801,7 @@ void SendMPAck(u16 cmdcount, u16 clientfail) WIFI_LOG("wifi: sent %d/44 bytes of MP ack, %d %d\n", txlen, ComStatus, RXTime); } -bool CheckRX(int type); -void MPClientReplyRX(int client); - -bool ProcessTX(TXSlot* slot, int num) +bool Wifi::ProcessTX(TXSlot* slot, int num) { slot->CurPhaseTime -= kTimerInterval; if (slot->CurPhaseTime > 0) @@ -1137,7 +1062,7 @@ bool ProcessTX(TXSlot* slot, int num) } -inline void IncrementRXAddr(u16& addr, u16 inc = 2) +inline void Wifi::IncrementRXAddr(u16& addr, u16 inc) { for (u32 i = 0; i < inc; i += 2) { @@ -1148,7 +1073,7 @@ inline void IncrementRXAddr(u16& addr, u16 inc = 2) } } -void StartRX() +void Wifi::StartRX() { u16 framelen = *(u16*)&RXBuffer[8]; RXTime = framelen; @@ -1176,7 +1101,7 @@ void StartRX() ComStatus |= 1; } -void FinishRX() +void Wifi::FinishRX() { ComStatus &= ~0x1; RXCounter = 0; @@ -1451,7 +1376,7 @@ void FinishRX() } } -void MPClientReplyRX(int client) +void Wifi::MPClientReplyRX(int client) { if (IOPORT(W_PowerState) & 0x0300) return; @@ -1492,7 +1417,7 @@ void MPClientReplyRX(int client) StartRX(); } -bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames +bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames { if (IOPORT(W_PowerState) & 0x0300) return false; @@ -1517,7 +1442,7 @@ bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames { rxlen = Platform::MP_RecvPacket(RXBuffer, ×tamp); if ((rxlen <= 0) && (!IsMP)) - rxlen = WifiAP::RecvPacket(RXBuffer); + rxlen = WifiAP->RecvPacket(RXBuffer); } else { @@ -1632,7 +1557,7 @@ bool CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames } -void MSTimer() +void Wifi::MSTimer() { if (IOPORT(W_USCompareCnt)) { @@ -1656,7 +1581,7 @@ void MSTimer() } } -void USTimer(u32 param) +void Wifi::USTimer(u32 param) { USTimestamp += kTimerInterval; @@ -1676,7 +1601,7 @@ void USTimer(u32 param) } if (!(USTimestamp & 0x3FF & kTimeCheckMask)) - WifiAP::MSTimer(); + WifiAP->MSTimer(); bool switchOffPowerSaving = false; if (USUntilPowerOn < 0) @@ -1839,7 +1764,7 @@ void USTimer(u32 param) } -void RFTransfer_Type2() +void Wifi::RFTransfer_Type2() { u32 id = (IOPORT(W_RFData2) >> 2) & 0x1F; @@ -1856,7 +1781,7 @@ void RFTransfer_Type2() } } -void RFTransfer_Type3() +void Wifi::RFTransfer_Type3() { u32 id = (IOPORT(W_RFData1) >> 8) & 0x3F; @@ -1873,7 +1798,7 @@ void RFTransfer_Type3() } -u16 Read(u32 addr) +u16 Wifi::Read(u32 addr) { if (addr >= 0x04810000) return 0; @@ -1976,7 +1901,7 @@ u16 Read(u32 addr) return IOPORT(addr&0xFFF); } -void Write(u32 addr, u16 val) +void Wifi::Write(u32 addr, u16 val) { if (addr >= 0x04810000) return; @@ -2331,14 +2256,12 @@ void Write(u32 addr, u16 val) } -u8* GetMAC() +u8* Wifi::GetMAC() { return (u8*)&IOPORT(W_MACAddr0); } -u8* GetBSSID() +u8* Wifi::GetBSSID() { return (u8*)&IOPORT(W_BSSID0); } - -} diff --git a/src/Wifi.h b/src/Wifi.h index e3570390..5a4f839e 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -21,178 +21,268 @@ #include "Savestate.h" -namespace Wifi +class WifiAP; + +class Wifi { +public: -enum -{ - W_ID = 0x000, + enum + { + W_ID = 0x000, - W_ModeReset = 0x004, - W_ModeWEP = 0x006, - W_TXStatCnt = 0x008, - W_IF = 0x010, - W_IE = 0x012, + W_ModeReset = 0x004, + W_ModeWEP = 0x006, + W_TXStatCnt = 0x008, + W_IF = 0x010, + W_IE = 0x012, - W_MACAddr0 = 0x018, - W_MACAddr1 = 0x01A, - W_MACAddr2 = 0x01C, - W_BSSID0 = 0x020, - W_BSSID1 = 0x022, - W_BSSID2 = 0x024, - W_AIDLow = 0x028, - W_AIDFull = 0x02A, + W_MACAddr0 = 0x018, + W_MACAddr1 = 0x01A, + W_MACAddr2 = 0x01C, + W_BSSID0 = 0x020, + W_BSSID1 = 0x022, + W_BSSID2 = 0x024, + W_AIDLow = 0x028, + W_AIDFull = 0x02A, - W_TXRetryLimit = 0x02C, - W_RXCnt = 0x030, - W_WEPCnt = 0x032, + W_TXRetryLimit = 0x02C, + W_RXCnt = 0x030, + W_WEPCnt = 0x032, - W_PowerUS = 0x036, - W_PowerTX = 0x038, - W_PowerState = 0x03C, - W_PowerForce = 0x040, - W_PowerUnk = 0x48, + W_PowerUS = 0x036, + W_PowerTX = 0x038, + W_PowerState = 0x03C, + W_PowerForce = 0x040, + W_PowerUnk = 0x48, - W_Random = 0x044, + W_Random = 0x044, - W_RXBufBegin = 0x050, - W_RXBufEnd = 0x052, - W_RXBufWriteCursor = 0x054, - W_RXBufWriteAddr = 0x056, - W_RXBufReadAddr = 0x058, - W_RXBufReadCursor = 0x05A, - W_RXBufCount = 0x05C, - W_RXBufDataRead = 0x060, - W_RXBufGapAddr = 0x062, - W_RXBufGapSize = 0x064, + W_RXBufBegin = 0x050, + W_RXBufEnd = 0x052, + W_RXBufWriteCursor = 0x054, + W_RXBufWriteAddr = 0x056, + W_RXBufReadAddr = 0x058, + W_RXBufReadCursor = 0x05A, + W_RXBufCount = 0x05C, + W_RXBufDataRead = 0x060, + W_RXBufGapAddr = 0x062, + W_RXBufGapSize = 0x064, - W_TXBufWriteAddr = 0x068, - W_TXBufCount = 0x06C, - W_TXBufDataWrite = 0x070, - W_TXBufGapAddr = 0x074, - W_TXBufGapSize = 0x076, + W_TXBufWriteAddr = 0x068, + W_TXBufCount = 0x06C, + W_TXBufDataWrite = 0x070, + W_TXBufGapAddr = 0x074, + W_TXBufGapSize = 0x076, - W_TXSlotBeacon = 0x080, - W_TXBeaconTIM = 0x084, - W_ListenCount = 0x088, - W_BeaconInterval = 0x08C, - W_ListenInterval = 0x08E, - W_TXSlotCmd = 0x090, - W_TXSlotReply1 = 0x094, - W_TXSlotReply2 = 0x098, - W_TXSlotLoc1 = 0x0A0, - W_TXSlotLoc2 = 0x0A4, - W_TXSlotLoc3 = 0x0A8, - W_TXReqReset = 0x0AC, - W_TXReqSet = 0x0AE, - W_TXReqRead = 0x0B0, - W_TXSlotReset = 0x0B4, - W_TXBusy = 0x0B6, - W_TXStat = 0x0B8, - W_Preamble = 0x0BC, - W_CmdTotalTime = 0x0C0, - W_CmdReplyTime = 0x0C4, - W_RXFilter = 0x0D0, - W_RXLenCrop = 0x0DA, - W_RXFilter2 = 0x0E0, + W_TXSlotBeacon = 0x080, + W_TXBeaconTIM = 0x084, + W_ListenCount = 0x088, + W_BeaconInterval = 0x08C, + W_ListenInterval = 0x08E, + W_TXSlotCmd = 0x090, + W_TXSlotReply1 = 0x094, + W_TXSlotReply2 = 0x098, + W_TXSlotLoc1 = 0x0A0, + W_TXSlotLoc2 = 0x0A4, + W_TXSlotLoc3 = 0x0A8, + W_TXReqReset = 0x0AC, + W_TXReqSet = 0x0AE, + W_TXReqRead = 0x0B0, + W_TXSlotReset = 0x0B4, + W_TXBusy = 0x0B6, + W_TXStat = 0x0B8, + W_Preamble = 0x0BC, + W_CmdTotalTime = 0x0C0, + W_CmdReplyTime = 0x0C4, + W_RXFilter = 0x0D0, + W_RXLenCrop = 0x0DA, + W_RXFilter2 = 0x0E0, - W_USCountCnt = 0x0E8, - W_USCompareCnt = 0x0EA, - W_CmdCountCnt = 0x0EE, + W_USCountCnt = 0x0E8, + W_USCompareCnt = 0x0EA, + W_CmdCountCnt = 0x0EE, - W_USCount0 = 0x0F8, - W_USCount1 = 0x0FA, - W_USCount2 = 0x0FC, - W_USCount3 = 0x0FE, - W_USCompare0 = 0x0F0, - W_USCompare1 = 0x0F2, - W_USCompare2 = 0x0F4, - W_USCompare3 = 0x0F6, + W_USCount0 = 0x0F8, + W_USCount1 = 0x0FA, + W_USCount2 = 0x0FC, + W_USCount3 = 0x0FE, + W_USCompare0 = 0x0F0, + W_USCompare1 = 0x0F2, + W_USCompare2 = 0x0F4, + W_USCompare3 = 0x0F6, - W_ContentFree = 0x10C, - W_PreBeacon = 0x110, - W_CmdCount = 0x118, - W_BeaconCount1 = 0x11C, - W_BeaconCount2 = 0x134, + W_ContentFree = 0x10C, + W_PreBeacon = 0x110, + W_CmdCount = 0x118, + W_BeaconCount1 = 0x11C, + W_BeaconCount2 = 0x134, - W_BBCnt = 0x158, - W_BBWrite = 0x15A, - W_BBRead = 0x15C, - W_BBBusy = 0x15E, - W_BBMode = 0x160, - W_BBPower = 0x168, + W_BBCnt = 0x158, + W_BBWrite = 0x15A, + W_BBRead = 0x15C, + W_BBBusy = 0x15E, + W_BBMode = 0x160, + W_BBPower = 0x168, - W_RFData2 = 0x17C, - W_RFData1 = 0x17E, - W_RFBusy = 0x180, - W_RFCnt = 0x184, + W_RFData2 = 0x17C, + W_RFData1 = 0x17E, + W_RFBusy = 0x180, + W_RFCnt = 0x184, - W_TXHeaderCnt = 0x194, - W_RFPins = 0x19C, + W_TXHeaderCnt = 0x194, + W_RFPins = 0x19C, - W_RXStatIncIF = 0x1A8, - W_RXStatIncIE = 0x1AA, - W_RXStatHalfIF = 0x1AC, - W_RXStatHalfIE = 0x1AE, - W_TXErrorCount = 0x1C0, - W_RXCount = 0x1C4, + W_RXStatIncIF = 0x1A8, + W_RXStatIncIE = 0x1AA, + W_RXStatHalfIF = 0x1AC, + W_RXStatHalfIE = 0x1AE, + W_TXErrorCount = 0x1C0, + W_RXCount = 0x1C4, - W_CMDStat0 = 0x1D0, - W_CMDStat1 = 0x1D2, - W_CMDStat2 = 0x1D4, - W_CMDStat3 = 0x1D6, - W_CMDStat4 = 0x1D8, - W_CMDStat5 = 0x1DA, - W_CMDStat6 = 0x1DC, - W_CMDStat7 = 0x1DE, + W_CMDStat0 = 0x1D0, + W_CMDStat1 = 0x1D2, + W_CMDStat2 = 0x1D4, + W_CMDStat3 = 0x1D6, + W_CMDStat4 = 0x1D8, + W_CMDStat5 = 0x1DA, + W_CMDStat6 = 0x1DC, + W_CMDStat7 = 0x1DE, - W_TXSeqNo = 0x210, - W_RFStatus = 0x214, - W_IFSet = 0x21C, - W_RXTXAddr = 0x268, + W_TXSeqNo = 0x210, + W_RFStatus = 0x214, + W_IFSet = 0x21C, + W_RXTXAddr = 0x268, + }; + + Wifi(); + ~Wifi(); + void Reset(); + void DoSavestate(Savestate* file); + + void SetPowerCnt(u32 val); + + void USTimer(u32 param); + + u16 Read(u32 addr); + void Write(u32 addr, u16 val); + + u8* GetMAC(); + u8* GetBSSID(); + +private: + u8 RAM[0x2000]; + u16 IO[0x1000>>1]; + + static const u8 MPCmdMAC[6]; + static const u8 MPReplyMAC[6]; + static const u8 MPAckMAC[6]; + + static const int kTimerInterval = 8; + static const u32 kTimeCheckMask = ~(kTimerInterval - 1); + + bool Enabled; + bool PowerOn; + + s32 TimerError; + + u16 Random; + + // general, always-on microsecond counter + u64 USTimestamp; + + u64 USCounter; + u64 USCompare; + bool BlockBeaconIRQ14; + + u32 CmdCounter; + + u8 BBRegs[0x100]; + u8 BBRegsRO[0x100]; + + u8 RFVersion; + u32 RFRegs[0x40]; + + struct TXSlot + { + bool Valid; + u16 Addr; + u16 Length; + u8 Rate; + u8 CurPhase; + int CurPhaseTime; + u32 HalfwordTimeMask; + }; + + TXSlot TXSlots[6]; + u8 TXBuffer[0x2000]; + + u8 RXBuffer[2048]; + u32 RXBufferPtr; + int RXTime; + u32 RXHalfwordTimeMask; + + u32 ComStatus; // 0=waiting for packets 1=receiving 2=sending + u32 TXCurSlot; + u32 RXCounter; + + int MPReplyTimer; + u16 MPClientMask, MPClientFail; + + u8 MPClientReplies[15*1024]; + + u16 MPLastSeqno; + + bool MPInited; + bool LANInited; + + int USUntilPowerOn; + bool ForcePowerOn; + + // MULTIPLAYER SYNC APPARATUS + bool IsMP; + bool IsMPClient; + u64 NextSync; // for clients: timestamp for next sync point + u64 RXTimestamp; + + class WifiAP* WifiAP; + + void ScheduleTimer(bool first); + void UpdatePowerOn(); + + void SetIRQ(u32 irq); + void SetIRQ13(); + void SetIRQ14(int source); + void SetIRQ15(); + + void SetStatus(u32 status); + void PowerDown(); + + int PreambleLen(int rate); + u32 NumClients(u16 bitmask); + void IncrementTXCount(TXSlot* slot); + void ReportMPReplyErrors(u16 clientfail); + + void TXSendFrame(TXSlot* slot, int num); + void StartTX_LocN(int nslot, int loc); + void StartTX_Cmd(); + void StartTX_Beacon(); + void FireTX(); + void SendMPDefaultReply(); + void SendMPReply(u16 clienttime, u16 clientmask); + void SendMPAck(u16 cmdcount, u16 clientfail); + bool ProcessTX(TXSlot* slot, int num); + + void IncrementRXAddr(u16& addr, u16 inc = 2); + void StartRX(); + void FinishRX(); + void MPClientReplyRX(int client); + bool CheckRX(int type); + + void MSTimer(); + + void RFTransfer_Type2(); + void RFTransfer_Type3(); }; -enum -{ - Event_RXCheck = 0, - Event_IRQ15, - Event_MSTimer, - Event_RFWakeup, - Event_RX, - Event_TX, - Event_MPClientSync, - Event_RF, - Event_BB, - - Event_MAX -}; - -struct SchedEvent -{ - void (*Func)(u32 param); - u64 Timestamp; - u32 Param; -}; - - -extern bool MPInited; - - -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); - -void SetPowerCnt(u32 val); - -void USTimer(u32 param); - -u16 Read(u32 addr); -void Write(u32 addr, u16 val); - -u8* GetMAC(); -u8* GetBSSID(); - -} - #endif diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index c9cccea0..9107a812 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -30,10 +30,9 @@ using Platform::Log; using Platform::LogLevel; -namespace WifiAP -{ -const u8 APMac[6] = {AP_MAC}; +const char* WifiAP::APName = "melonAP"; +const u8 WifiAP::APMac[6] = {0x00, 0xF0, 0x77, 0x77, 0x77, 0x77}; #define PWRITE_8(p, v) *p++ = v; #define PWRITE_16(p, v) *(u16*)p = v; p += 2; @@ -65,33 +64,19 @@ const u8 APMac[6] = {AP_MAC}; #define PALIGN_4(p, base) while (PLEN(p,base) & 0x3) *p++ = 0xFF; -u64 USCounter; - -u16 SeqNo; - -bool BeaconDue; - -u8 PacketBuffer[2048]; -int PacketLen; -int RXNum; - -u8 LANBuffer[2048]; - -// this is a lazy AP, we only keep track of one client -// 0=disconnected 1=authenticated 2=associated -int ClientStatus; +bool MACEqual(u8* a, const u8* b); +bool MACIsBroadcast(u8* a); -bool Init() -{ - return true; -} - -void DeInit() +WifiAP::WifiAP(class Wifi* wifi) : Wifi(wifi) { } -void Reset() +WifiAP::~WifiAP() +{ +} + +void WifiAP::Reset() { // random starting point for the counter USCounter = 0x428888000ULL; @@ -107,18 +92,7 @@ void Reset() } -bool MACEqual(u8* a, u8* b) -{ - return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]); -} - -bool MACIsBroadcast(u8* a) -{ - return (*(u32*)&a[0] == 0xFFFFFFFF) && (*(u16*)&a[4] == 0xFFFF); -} - - -void MSTimer() +void WifiAP::MSTimer() { USCounter += 0x400; @@ -131,7 +105,7 @@ void MSTimer() } -int HandleManagementFrame(u8* data, int len) +int WifiAP::HandleManagementFrame(u8* data, int len) { // TODO: perfect this // noting that frames sent pre-auth/assoc don't have a proper BSSID @@ -199,8 +173,8 @@ int HandleManagementFrame(u8* data, int len) PWRITE_16(p, 0x0021); // capability PWRITE_8(p, 0x01); PWRITE_8(p, 0x02); PWRITE_8(p, 0x82); PWRITE_8(p, 0x84); // rates PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, 0x06); // current channel - PWRITE_8(p, 0x00); PWRITE_8(p, strlen(AP_NAME)); - memcpy(p, AP_NAME, strlen(AP_NAME)); p += strlen(AP_NAME); + PWRITE_8(p, 0x00); PWRITE_8(p, strlen(APName)); + memcpy(p, APName, strlen(APName)); p += strlen(APName); PacketLen = PLEN(p, base); RXNum = 1; @@ -282,7 +256,7 @@ int HandleManagementFrame(u8* data, int len) } -int SendPacket(u8* data, int len) +int WifiAP::SendPacket(u8* data, int len) { data += 12; @@ -330,7 +304,7 @@ int SendPacket(u8* data, int len) return 0; } -int RecvPacket(u8* data) +int WifiAP::RecvPacket(u8* data) { if (BeaconDue) { @@ -353,8 +327,8 @@ int RecvPacket(u8* data) PWRITE_8(p, 0x01); PWRITE_8(p, 0x02); PWRITE_8(p, 0x82); PWRITE_8(p, 0x84); // rates PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, 0x06); // current channel PWRITE_8(p, 0x05); PWRITE_8(p, 0x04); PWRITE_8(p, 0); PWRITE_8(p, 0); PWRITE_8(p, 0); PWRITE_8(p, 0); // TIM - PWRITE_8(p, 0x00); PWRITE_8(p, strlen(AP_NAME)); - memcpy(p, AP_NAME, strlen(AP_NAME)); p += strlen(AP_NAME); + PWRITE_8(p, 0x00); PWRITE_8(p, strlen(APName)); + memcpy(p, APName, strlen(APName)); p += strlen(APName); PALIGN_4(p, base); PWRITE_32(p, 0xDEADBEEF); // checksum. doesn't matter for now @@ -394,7 +368,7 @@ int RecvPacket(u8* data) // check destination MAC if (!MACIsBroadcast(&LANBuffer[0])) { - if (!MACEqual(&LANBuffer[0], Wifi::GetMAC())) + if (!MACEqual(&LANBuffer[0], Wifi->GetMAC())) return 0; } @@ -427,5 +401,3 @@ int RecvPacket(u8* data) return 0; } - -} diff --git a/src/WifiAP.h b/src/WifiAP.h index 04dbfa82..717e1c6c 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -21,24 +21,44 @@ #include "types.h" -namespace WifiAP +class Wifi; + +class WifiAP { +public: + WifiAP(Wifi* wifi); + ~WifiAP(); + void Reset(); -#define AP_MAC 0x00, 0xF0, 0x77, 0x77, 0x77, 0x77 -#define AP_NAME "melonAP" + static const char* APName; + static const u8 APMac[6]; -extern const u8 APMac[6]; + void MSTimer(); -bool Init(); -void DeInit(); -void Reset(); + // packet format: 12-byte TX header + original 802.11 frame + int SendPacket(u8* data, int len); + int RecvPacket(u8* data); -void MSTimer(); +private: + class Wifi* Wifi; -// packet format: 12-byte TX header + original 802.11 frame -int SendPacket(u8* data, int len); -int RecvPacket(u8* data); + u64 USCounter; -} + u16 SeqNo; + + bool BeaconDue; + + u8 PacketBuffer[2048]; + int PacketLen; + int RXNum; + + u8 LANBuffer[2048]; + + // this is a lazy AP, we only keep track of one client + // 0=disconnected 1=authenticated 2=associated + int ClientStatus; + + int HandleManagementFrame(u8* data, int len); +}; #endif From 8f1b0d4a052db068a029cd5784e52da7ecc9813e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 4 Nov 2023 17:28:16 +0100 Subject: [PATCH 025/157] convert AREngine --- src/AREngine.cpp | 37 ++++-------------------------- src/AREngine.h | 28 +++++++++++++++------- src/ARM.cpp | 2 +- src/NDS.cpp | 8 ++++--- src/NDS.h | 4 ++++ src/frontend/qt_sdl/ROMManager.cpp | 6 ++--- 6 files changed, 38 insertions(+), 47 deletions(-) diff --git a/src/AREngine.cpp b/src/AREngine.cpp index ffed6b9c..c8888ac6 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -26,32 +26,17 @@ using Platform::Log; using Platform::LogLevel; -namespace AREngine -{ -// AR code file - frontend is responsible for managing this -ARCodeFile* CodeFile; - -u8 (*BusRead8)(u32 addr); -u16 (*BusRead16)(u32 addr); -u32 (*BusRead32)(u32 addr); -void (*BusWrite8)(u32 addr, u8 val); -void (*BusWrite16)(u32 addr, u16 val); -void (*BusWrite32)(u32 addr, u32 val); - - -bool Init() +AREngine::AREngine() { CodeFile = nullptr; - - return true; } -void DeInit() +AREngine::~AREngine() { } -void Reset() +void AREngine::Reset() { if (NDS::ConsoleType == 1) { @@ -74,16 +59,6 @@ void Reset() } -ARCodeFile* GetCodeFile() -{ - return CodeFile; -} - -void SetCodeFile(ARCodeFile* file) -{ - CodeFile = file; -} - #define case16(x) \ case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \ @@ -91,7 +66,7 @@ void SetCodeFile(ARCodeFile* file) case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \ case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F) -void RunCheat(ARCode& arcode) +void AREngine::RunCheat(ARCode& arcode) { u32* code = &arcode.Code[0]; @@ -437,7 +412,7 @@ void RunCheat(ARCode& arcode) } } -void RunCheats() +void AREngine::RunCheats() { if (!CodeFile) return; @@ -454,5 +429,3 @@ void RunCheats() } } } - -} diff --git a/src/AREngine.h b/src/AREngine.h index 5426846e..cd6d4a97 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -21,18 +21,30 @@ #include "ARCodeFile.h" -namespace AREngine +class AREngine { +public: + AREngine(); + ~AREngine(); + void Reset(); -bool Init(); -void DeInit(); -void Reset(); + ARCodeFile* GetCodeFile() { return CodeFile; } + void SetCodeFile(ARCodeFile* file) { CodeFile = file; } -ARCodeFile* GetCodeFile(); -void SetCodeFile(ARCodeFile* file); + void RunCheats(); -void RunCheats(); +private: + ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this -} + // TEMPORARY + u8 (*BusRead8)(u32 addr); + u16 (*BusRead16)(u32 addr); + u32 (*BusRead32)(u32 addr); + void (*BusWrite8)(u32 addr, u8 val); + void (*BusWrite16)(u32 addr, u16 val); + void (*BusWrite32)(u32 addr, u32 val); + + void RunCheat(ARCode& arcode); +}; #endif // ARENGINE_H diff --git a/src/ARM.cpp b/src/ARM.cpp index ea649b79..4f2f8928 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -590,7 +590,7 @@ void ARM::TriggerIRQ() if (Num == 1) { if ((NDS::IF[1] & NDS::IE[1]) & (1<RunCheats(); } } diff --git a/src/NDS.cpp b/src/NDS.cpp index 2fdbf63b..05a77217 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -183,6 +183,8 @@ class SPIHost* SPI; class RTC* RTC; class Wifi* Wifi; +class AREngine* AREngine; + bool Running; bool RunningGame; @@ -231,7 +233,7 @@ bool Init() if (!DSi::Init()) return false; - if (!AREngine::Init()) return false; + AREngine = new class AREngine(); return true; } @@ -262,7 +264,7 @@ void DeInit() DSi::DeInit(); - AREngine::DeInit(); + delete AREngine; AREngine = nullptr; UnregisterEventFunc(Event_Div, 0); UnregisterEventFunc(Event_Sqrt, 0); @@ -671,7 +673,7 @@ void Reset() SPU->SetDegrade10Bit(degradeAudio); - AREngine::Reset(); + AREngine->Reset(); } void Start() diff --git a/src/NDS.h b/src/NDS.h index 4b556c64..660df8d4 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -35,6 +35,8 @@ class SPIHost; class RTC; class Wifi; +class AREngine; + namespace NDS { @@ -256,6 +258,8 @@ extern class SPIHost* SPI; extern class RTC* RTC; extern class Wifi* Wifi; +extern class AREngine* AREngine; + const u32 ARM7WRAMSize = 0x10000; extern u8* ARM7WRAM; diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 0af1fccc..a0355f66 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -462,7 +462,7 @@ void UnloadCheats() { delete CheatFile; CheatFile = nullptr; - AREngine::SetCodeFile(nullptr); + NDS::AREngine->SetCodeFile(nullptr); } } @@ -475,7 +475,7 @@ void LoadCheats() // TODO: check for error (malformed cheat file, ...) CheatFile = new ARCodeFile(filename); - AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); + NDS::AREngine->SetCodeFile(CheatsOn ? CheatFile : nullptr); } void LoadBIOSFiles() @@ -570,7 +570,7 @@ void EnableCheats(bool enable) { CheatsOn = enable; if (CheatFile) - AREngine::SetCodeFile(CheatsOn ? CheatFile : nullptr); + NDS::AREngine->SetCodeFile(CheatsOn ? CheatFile : nullptr); } ARCodeFile* GetCheatFile() From 7837c169a15db672451a3f80ac17e3e7291d95c5 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 4 Nov 2023 17:46:52 +0100 Subject: [PATCH 026/157] convert AES --- src/DSi.cpp | 62 +++++++++++++------------ src/DSi.h | 4 ++ src/DSi_AES.cpp | 117 ++++++++++++++++------------------------------- src/DSi_AES.h | 78 ++++++++++++++++++++++--------- src/DSi_NDMA.cpp | 8 ++-- 5 files changed, 136 insertions(+), 133 deletions(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index bdbe7f5b..9ae7d93f 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -79,6 +79,8 @@ std::unique_ptr NANDImage; DSi_SDHost* SDMMC; DSi_SDHost* SDIO; +DSi_AES* AES; + // FIXME: these currently have no effect (and aren't stored in a savestate) // ... not that they matter all that much u8 GPIO_Data; @@ -102,7 +104,6 @@ bool Init() if (!DSi_I2C::Init()) return false; if (!DSi_CamModule::Init()) return false; - if (!DSi_AES::Init()) return false; if (!DSi_DSP::Init()) return false; NDMAs[0] = new DSi_NDMA(0, 0); @@ -117,6 +118,8 @@ bool Init() SDMMC = new DSi_SDHost(0); SDIO = new DSi_SDHost(1); + AES = new DSi_AES(); + return true; } @@ -134,7 +137,6 @@ void DeInit() DSi_I2C::DeInit(); DSi_CamModule::DeInit(); - DSi_AES::DeInit(); DSi_DSP::DeInit(); for (int i = 0; i < 8; i++) @@ -143,10 +145,10 @@ void DeInit() NDMAs[i] = nullptr; } - delete SDMMC; - SDMMC = nullptr; - delete SDIO; - SDIO = nullptr; + delete SDMMC; SDMMC = nullptr; + delete SDIO; SDIO = nullptr; + + delete AES; AES = nullptr; NANDImage = nullptr; // The NANDImage is cleaned up (and its underlying file closed) @@ -176,7 +178,7 @@ void Reset() SDMMC->Reset(); SDIO->Reset(); - DSi_AES::Reset(); + AES->Reset(); if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) { @@ -282,7 +284,7 @@ void DoSavestate(Savestate* file) for (int i = 0; i < 8; i++) NDMAs[i]->DoSavestate(file); - DSi_AES::DoSavestate(file); + AES->DoSavestate(file); DSi_CamModule::DoSavestate(file); DSi_DSP::DoSavestate(file); DSi_I2C::DoSavestate(file); @@ -706,7 +708,7 @@ void SoftReset() SDMMC->Reset(); SDIO->Reset(); - DSi_AES::Reset(); + AES->Reset(); if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) { @@ -2839,8 +2841,8 @@ u32 ARM7IORead32(u32 addr) case 0x0400416C: return NDMAs[7]->FillData; case 0x04004170: return NDMAs[7]->Cnt; - case 0x04004400: return DSi_AES::ReadCnt(); - case 0x0400440C: return DSi_AES::ReadOutputFIFO(); + case 0x04004400: return AES->ReadCnt(); + case 0x0400440C: return AES->ReadOutputFIFO(); case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF; case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32; @@ -2929,7 +2931,7 @@ void ARM7IOWrite8(u32 addr, u8 val) u32 shift = (addr&3)*8; addr -= 0x04004420; addr &= ~3; - DSi_AES::WriteIV(addr, (u32)val << shift, 0xFF << shift); + AES->WriteIV(addr, (u32)val << shift, 0xFF << shift); return; } if (addr >= 0x04004430 && addr < 0x04004440) @@ -2937,7 +2939,7 @@ void ARM7IOWrite8(u32 addr, u8 val) u32 shift = (addr&3)*8; addr -= 0x04004430; addr &= ~3; - DSi_AES::WriteMAC(addr, (u32)val << shift, 0xFF << shift); + AES->WriteMAC(addr, (u32)val << shift, 0xFF << shift); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -2951,9 +2953,9 @@ void ARM7IOWrite8(u32 addr, u8 val) switch (addr >> 4) { - case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; - case 1: DSi_AES::WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; - case 2: DSi_AES::WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 0: AES->WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 1: AES->WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 2: AES->WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; } } @@ -2999,7 +3001,7 @@ void ARM7IOWrite16(u32 addr, u16 val) return; case 0x04004406: - DSi_AES::WriteBlkCnt(val<<16); + AES->WriteBlkCnt(val<<16); return; case 0x4004700: @@ -3024,7 +3026,7 @@ void ARM7IOWrite16(u32 addr, u16 val) u32 shift = (addr&1)*16; addr -= 0x04004420; addr &= ~1; - DSi_AES::WriteIV(addr, (u32)val << shift, 0xFFFF << shift); + AES->WriteIV(addr, (u32)val << shift, 0xFFFF << shift); return; } if (addr >= 0x04004430 && addr < 0x04004440) @@ -3032,7 +3034,7 @@ void ARM7IOWrite16(u32 addr, u16 val) u32 shift = (addr&1)*16; addr -= 0x04004430; addr &= ~1; - DSi_AES::WriteMAC(addr, (u32)val << shift, 0xFFFF << shift); + AES->WriteMAC(addr, (u32)val << shift, 0xFFFF << shift); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -3046,9 +3048,9 @@ void ARM7IOWrite16(u32 addr, u16 val) switch (addr >> 4) { - case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; - case 1: DSi_AES::WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; - case 2: DSi_AES::WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 0: AES->WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 1: AES->WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 2: AES->WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; } } @@ -3146,9 +3148,9 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x0400416C: NDMAs[7]->FillData = val; return; case 0x04004170: NDMAs[7]->WriteCnt(val); return; - case 0x04004400: DSi_AES::WriteCnt(val); return; - case 0x04004404: DSi_AES::WriteBlkCnt(val); return; - case 0x04004408: DSi_AES::WriteInputFIFO(val); return; + case 0x04004400: AES->WriteCnt(val); return; + case 0x04004404: AES->WriteBlkCnt(val); return; + case 0x04004408: AES->WriteInputFIFO(val); return; case 0x4004700: Log(LogLevel::Debug, "32-Bit SNDExCnt write? %08X %08X\n", val, NDS::ARM7->R[15]); @@ -3159,13 +3161,13 @@ void ARM7IOWrite32(u32 addr, u32 val) if (addr >= 0x04004420 && addr < 0x04004430) { addr -= 0x04004420; - DSi_AES::WriteIV(addr, val, 0xFFFFFFFF); + AES->WriteIV(addr, val, 0xFFFFFFFF); return; } if (addr >= 0x04004430 && addr < 0x04004440) { addr -= 0x04004430; - DSi_AES::WriteMAC(addr, val, 0xFFFFFFFF); + AES->WriteMAC(addr, val, 0xFFFFFFFF); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -3176,9 +3178,9 @@ void ARM7IOWrite32(u32 addr, u32 val) switch (addr >> 4) { - case 0: DSi_AES::WriteKeyNormal(n, addr&0xF, val, 0xFFFFFFFF); return; - case 1: DSi_AES::WriteKeyX(n, addr&0xF, val, 0xFFFFFFFF); return; - case 2: DSi_AES::WriteKeyY(n, addr&0xF, val, 0xFFFFFFFF); return; + case 0: AES->WriteKeyNormal(n, addr&0xF, val, 0xFFFFFFFF); return; + case 1: AES->WriteKeyX(n, addr&0xF, val, 0xFFFFFFFF); return; + case 2: AES->WriteKeyY(n, addr&0xF, val, 0xFFFFFFFF); return; } } diff --git a/src/DSi.h b/src/DSi.h index 06ca9798..aa507f55 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -22,6 +22,8 @@ #include "NDS.h" #include "DSi_SD.h" +class DSi_AES; + namespace DSi_NAND { class NANDImage; @@ -56,6 +58,8 @@ extern u32 NWRAMStart[2][3]; extern u32 NWRAMEnd[2][3]; extern u32 NWRAMMask[2][3]; +extern DSi_AES* AES; + bool Init(); void DeInit(); void Reset(); diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 1b65dc15..b1d89bd1 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -21,63 +21,11 @@ #include "DSi.h" #include "DSi_NAND.h" #include "DSi_AES.h" -#include "FIFO.h" -#include "tiny-AES-c/aes.hpp" #include "Platform.h" using Platform::Log; using Platform::LogLevel; -namespace DSi_AES -{ - -u32 Cnt; - -u32 BlkCnt; -u32 RemExtra; -u32 RemBlocks; - -bool OutputFlush; - -u32 InputDMASize, OutputDMASize; -u32 AESMode; - -FIFO InputFIFO; -FIFO OutputFIFO; - -u8 IV[16]; - -u8 MAC[16]; - -u8 KeyNormal[4][16]; -u8 KeyX[4][16]; -u8 KeyY[4][16]; - -u8 CurKey[16]; -u8 CurMAC[16]; - -// output MAC for CCM encrypt -u8 OutputMAC[16]; -bool OutputMACDue; - -AES_ctx Ctx; - -void ROL16(u8* val, u32 n) -{ - u32 n_coarse = n >> 3; - u32 n_fine = n & 7; - u8 tmp[16]; - - for (u32 i = 0; i < 16; i++) - { - tmp[i] = val[(i - n_coarse) & 0xF]; - } - - for (u32 i = 0; i < 16; i++) - { - val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine)); - } -} #define _printhex(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); printf("\n"); } #define _printhex2(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[z]); } @@ -86,19 +34,17 @@ void ROL16(u8* val, u32 n) #define _printhex2R(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); } -bool Init() +DSi_AES::DSi_AES() { const u8 zero[16] = {0}; AES_init_ctx_iv(&Ctx, zero, zero); - - return true; } -void DeInit() +DSi_AES::~DSi_AES() { } -void Reset() +void DSi_AES::Reset() { Cnt = 0; @@ -152,7 +98,7 @@ void Reset() *(u32*)&KeyY[3][8] = 0x202DDD1D; } -void DoSavestate(Savestate* file) +void DSi_AES::DoSavestate(Savestate* file) { file->Section("AESi"); @@ -190,7 +136,7 @@ void DoSavestate(Savestate* file) } -void ProcessBlock_CCM_Extra() +void DSi_AES::ProcessBlock_CCM_Extra() { u8 data[16]; u8 data_rev[16]; @@ -206,7 +152,7 @@ void ProcessBlock_CCM_Extra() AES_ECB_encrypt(&Ctx, CurMAC); } -void ProcessBlock_CCM_Decrypt() +void DSi_AES::ProcessBlock_CCM_Decrypt() { u8 data[16]; u8 data_rev[16]; @@ -234,7 +180,7 @@ void ProcessBlock_CCM_Decrypt() OutputFIFO.Write(*(u32*)&data[12]); } -void ProcessBlock_CCM_Encrypt() +void DSi_AES::ProcessBlock_CCM_Encrypt() { u8 data[16]; u8 data_rev[16]; @@ -262,7 +208,7 @@ void ProcessBlock_CCM_Encrypt() OutputFIFO.Write(*(u32*)&data[12]); } -void ProcessBlock_CTR() +void DSi_AES::ProcessBlock_CTR() { u8 data[16]; u8 data_rev[16]; @@ -287,7 +233,7 @@ void ProcessBlock_CTR() } -u32 ReadCnt() +u32 DSi_AES::ReadCnt() { u32 ret = Cnt; @@ -297,7 +243,7 @@ u32 ReadCnt() return ret; } -void WriteCnt(u32 val) +void DSi_AES::WriteCnt(u32 val) { u32 oldcnt = Cnt; Cnt = val & 0xFC1FF000; @@ -380,12 +326,12 @@ void WriteCnt(u32 val) // val, AESMode, (val >> 26) & 0x3, InputDMASize, OutputDMASize, RemBlocks, BlkCnt); } -void WriteBlkCnt(u32 val) +void DSi_AES::WriteBlkCnt(u32 val) { BlkCnt = val; } -u32 ReadOutputFIFO() +u32 DSi_AES::ReadOutputFIFO() { if (OutputFIFO.IsEmpty()) Log(LogLevel::Warn, "!!! AES OUTPUT FIFO EMPTY\n"); @@ -416,7 +362,7 @@ u32 ReadOutputFIFO() return ret; } -void WriteInputFIFO(u32 val) +void DSi_AES::WriteInputFIFO(u32 val) { // TODO: add some delay to processing @@ -429,7 +375,7 @@ void WriteInputFIFO(u32 val) Update(); } -void CheckInputDMA() +void DSi_AES::CheckInputDMA() { if (RemBlocks == 0 && RemExtra == 0) return; @@ -442,7 +388,7 @@ void CheckInputDMA() Update(); } -void CheckOutputDMA() +void DSi_AES::CheckOutputDMA() { if (OutputFIFO.Level() >= OutputDMASize) { @@ -451,7 +397,7 @@ void CheckOutputDMA() } } -void Update() +void DSi_AES::Update() { if (RemExtra > 0) { @@ -539,7 +485,7 @@ void Update() } -void WriteIV(u32 offset, u32 val, u32 mask) +void DSi_AES::WriteIV(u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&IV[offset]; @@ -548,7 +494,7 @@ void WriteIV(u32 offset, u32 val, u32 mask) //printf("AES: IV: "); _printhex(IV, 16); } -void WriteMAC(u32 offset, u32 val, u32 mask) +void DSi_AES::WriteMAC(u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&MAC[offset]; @@ -557,7 +503,24 @@ void WriteMAC(u32 offset, u32 val, u32 mask) //printf("AES: MAC: "); _printhex(MAC, 16); } -void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey) +void DSi_AES::ROL16(u8* val, u32 n) +{ + u32 n_coarse = n >> 3; + u32 n_fine = n & 7; + u8 tmp[16]; + + for (u32 i = 0; i < 16; i++) + { + tmp[i] = val[(i - n_coarse) & 0xF]; + } + + for (u32 i = 0; i < 16; i++) + { + val[i] = (tmp[i] << n_fine) | (tmp[(i - 1) & 0xF] >> (8-n_fine)); + } +} + +void DSi_AES::DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey) { const u8 key_const[16] = {0xFF, 0xFE, 0xFB, 0x4E, 0x29, 0x59, 0x02, 0x58, 0x2A, 0x68, 0x0F, 0x5F, 0x1A, 0x4F, 0x3E, 0x79}; u8 tmp[16]; @@ -578,7 +541,7 @@ void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey) memcpy(normalkey, tmp, 16); } -void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask) +void DSi_AES::WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&KeyNormal[slot][offset]; @@ -587,7 +550,7 @@ void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask) //printf("KeyNormal(%d): ", slot); _printhex(KeyNormal[slot], 16); } -void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask) +void DSi_AES::WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&KeyX[slot][offset]; @@ -596,7 +559,7 @@ void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask) //printf("KeyX(%d): ", slot); _printhex(KeyX[slot], 16); } -void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask) +void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask) { u32 old = *(u32*)&KeyY[slot][offset]; @@ -609,5 +572,3 @@ void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask) DeriveNormalKey(KeyX[slot], KeyY[slot], KeyNormal[slot]); } } - -} diff --git a/src/DSi_AES.h b/src/DSi_AES.h index 4030b339..1f1c6bf9 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -21,6 +21,8 @@ #include "types.h" #include "Savestate.h" +#include "FIFO.h" +#include "tiny-AES-c/aes.hpp" #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" @@ -41,35 +43,69 @@ __attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) #endif #pragma GCC diagnostic pop -namespace DSi_AES +class DSi_AES { +public: + DSi_AES(); + ~DSi_AES(); + void Reset(); + void DoSavestate(Savestate* file); -extern u32 Cnt; + u32 ReadCnt(); + void WriteCnt(u32 val); + void WriteBlkCnt(u32 val); -bool Init(); -void DeInit(); -void Reset(); + u32 ReadOutputFIFO(); + void WriteInputFIFO(u32 val); + void CheckInputDMA(); + void CheckOutputDMA(); + void Update(); -void DoSavestate(Savestate* file); + void WriteIV(u32 offset, u32 val, u32 mask); + void WriteMAC(u32 offset, u32 val, u32 mask); + void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask); + void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask); + void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask); -u32 ReadCnt(); -void WriteCnt(u32 val); -void WriteBlkCnt(u32 val); + static void ROL16(u8* val, u32 n); + static void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey); -u32 ReadOutputFIFO(); -void WriteInputFIFO(u32 val); -void CheckInputDMA(); -void CheckOutputDMA(); -void Update(); +private: + u32 Cnt; -void WriteIV(u32 offset, u32 val, u32 mask); -void WriteMAC(u32 offset, u32 val, u32 mask); -void WriteKeyNormal(u32 slot, u32 offset, u32 val, u32 mask); -void WriteKeyX(u32 slot, u32 offset, u32 val, u32 mask); -void WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask); + u32 BlkCnt; + u32 RemExtra; + u32 RemBlocks; -void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey); + bool OutputFlush; -} + u32 InputDMASize, OutputDMASize; + u32 AESMode; + + FIFO InputFIFO; + FIFO OutputFIFO; + + u8 IV[16]; + + u8 MAC[16]; + + u8 KeyNormal[4][16]; + u8 KeyX[4][16]; + u8 KeyY[4][16]; + + u8 CurKey[16]; + u8 CurMAC[16]; + + // output MAC for CCM encrypt + u8 OutputMAC[16]; + bool OutputMACDue; + + AES_ctx Ctx; + + void ProcessBlock_CCM_Extra(); + void ProcessBlock_CCM_Decrypt(); + void ProcessBlock_CCM_Encrypt(); + void ProcessBlock_CTR(); +}; #endif // DSI_AES_H diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index 6c349fac..f3c3745e 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -347,8 +347,8 @@ void DSi_NDMA::Run7() Running = 0; NDS::ResumeCPU(1, 1<<(Num+4)); - DSi_AES::CheckInputDMA(); - DSi_AES::CheckOutputDMA(); + DSi::AES->CheckInputDMA(); + DSi::AES->CheckOutputDMA(); } return; @@ -372,6 +372,6 @@ void DSi_NDMA::Run7() InProgress = false; NDS::ResumeCPU(1, 1<<(Num+4)); - DSi_AES::CheckInputDMA(); - DSi_AES::CheckOutputDMA(); + DSi::AES->CheckInputDMA(); + DSi::AES->CheckOutputDMA(); } From 54ebf1b1b2bc3e23e24e55ce163d2071780ab562 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 4 Nov 2023 19:42:36 +0100 Subject: [PATCH 027/157] convert DSi I2C and camera --- src/DSi.cpp | 42 ++-- src/DSi.h | 4 + src/DSi_Camera.cpp | 124 ++++------ src/DSi_Camera.h | 82 ++++--- src/DSi_I2C.cpp | 215 ++++++++--------- src/DSi_I2C.h | 225 ++++++++++++------ src/NDS.cpp | 4 +- src/SPI.h | 10 - src/frontend/qt_sdl/AudioSettingsDialog.cpp | 7 +- .../PowerManagement/PowerManagementDialog.cpp | 31 +-- src/frontend/qt_sdl/ROMManager.cpp | 4 +- src/frontend/qt_sdl/main.cpp | 19 +- 12 files changed, 400 insertions(+), 367 deletions(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index 9ae7d93f..1aba7d99 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -79,6 +79,8 @@ std::unique_ptr NANDImage; DSi_SDHost* SDMMC; DSi_SDHost* SDIO; +DSi_I2CHost* I2C; +DSi_CamModule* CamModule; DSi_AES* AES; // FIXME: these currently have no effect (and aren't stored in a savestate) @@ -102,8 +104,6 @@ bool Init() NWRAM_C = new u8[NWRAMSize]; #endif - if (!DSi_I2C::Init()) return false; - if (!DSi_CamModule::Init()) return false; if (!DSi_DSP::Init()) return false; NDMAs[0] = new DSi_NDMA(0, 0); @@ -118,6 +118,8 @@ bool Init() SDMMC = new DSi_SDHost(0); SDIO = new DSi_SDHost(1); + I2C = new DSi_I2CHost(); + CamModule = new DSi_CamModule(); AES = new DSi_AES(); return true; @@ -135,8 +137,6 @@ void DeInit() NWRAM_C = nullptr; #endif - DSi_I2C::DeInit(); - DSi_CamModule::DeInit(); DSi_DSP::DeInit(); for (int i = 0; i < 8; i++) @@ -148,6 +148,8 @@ void DeInit() delete SDMMC; SDMMC = nullptr; delete SDIO; SDIO = nullptr; + delete I2C; I2C = nullptr; + delete CamModule; CamModule = nullptr; delete AES; AES = nullptr; NANDImage = nullptr; @@ -166,8 +168,8 @@ void Reset() NDMACnt[0] = 0; NDMACnt[1] = 0; for (int i = 0; i < 8; i++) NDMAs[i]->Reset(); - DSi_I2C::Reset(); - DSi_CamModule::Reset(); + I2C->Reset(); + CamModule->Reset(); DSi_DSP::Reset(); SDMMC->CloseHandles(); @@ -210,7 +212,7 @@ void Reset() void Stop() { - DSi_CamModule::Stop(); + CamModule->Stop(); } void DoSavestate(Savestate* file) @@ -285,9 +287,9 @@ void DoSavestate(Savestate* file) NDMAs[i]->DoSavestate(file); AES->DoSavestate(file); - DSi_CamModule::DoSavestate(file); + CamModule->DoSavestate(file); DSi_DSP::DoSavestate(file); - DSi_I2C::DoSavestate(file); + I2C->DoSavestate(file); SDMMC->DoSavestate(file); SDIO->DoSavestate(file); } @@ -581,7 +583,7 @@ void SetupDirectBoot() ARM9Write32(0x02FFFC00, cartid); ARM9Write16(0x02FFFC40, 0x0001); // boot indicator - ARM9Write8(0x02FFFDFA, DSi_BPTWL::GetBootFlag() | 0x80); + ARM9Write8(0x02FFFDFA, I2C->GetBPTWL()->GetBootFlag() | 0x80); ARM9Write8(0x02FFFDFB, 0x01); } @@ -2302,7 +2304,7 @@ u8 ARM9IORead8(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return DSi_CamModule::Read8(addr); + return CamModule->Read8(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) @@ -2337,7 +2339,7 @@ u16 ARM9IORead16(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return DSi_CamModule::Read16(addr); + return CamModule->Read16(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) @@ -2402,7 +2404,7 @@ u32 ARM9IORead32(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return DSi_CamModule::Read32(addr); + return CamModule->Read32(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) @@ -2472,7 +2474,7 @@ void ARM9IOWrite8(u32 addr, u8 val) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return DSi_CamModule::Write8(addr, val); + return CamModule->Write8(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) @@ -2532,7 +2534,7 @@ void ARM9IOWrite16(u32 addr, u16 val) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return DSi_CamModule::Write16(addr, val); + return CamModule->Write16(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) @@ -2682,7 +2684,7 @@ void ARM9IOWrite32(u32 addr, u32 val) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return DSi_CamModule::Write32(addr, val); + return CamModule->Write32(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) @@ -2715,8 +2717,8 @@ u8 ARM7IORead8(u32 addr) CASE_READ8_32BIT(0x0400405C, MBK[1][7]) CASE_READ8_32BIT(0x04004060, MBK[1][8]) - case 0x04004500: return DSi_I2C::ReadData(); - case 0x04004501: return DSi_I2C::Cnt; + case 0x04004500: return I2C->ReadData(); + case 0x04004501: return I2C->ReadCnt(); case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF; case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF; @@ -2899,8 +2901,8 @@ void ARM7IOWrite8(u32 addr, u8 val) return; } - case 0x04004500: DSi_I2C::WriteData(val); return; - case 0x04004501: DSi_I2C::WriteCnt(val); return; + case 0x04004500: I2C->WriteData(val); return; + case 0x04004501: I2C->WriteCnt(val); return; case 0x4004700: DSi_DSP::WriteSNDExCnt((u16)val | (DSi_DSP::SNDExCnt & 0xFF00)); diff --git a/src/DSi.h b/src/DSi.h index aa507f55..0e772f63 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -22,6 +22,8 @@ #include "NDS.h" #include "DSi_SD.h" +class DSi_I2CHost; +class DSi_CamModule; class DSi_AES; namespace DSi_NAND @@ -58,6 +60,8 @@ extern u32 NWRAMStart[2][3]; extern u32 NWRAMEnd[2][3]; extern u32 NWRAMMask[2][3]; +extern DSi_I2CHost* I2C; +extern DSi_CamModule* CamModule; extern DSi_AES* AES; bool Init(); diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 5f7ae4f0..74916293 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -25,48 +25,27 @@ using Platform::Log; using Platform::LogLevel; -namespace DSi_CamModule -{ - -Camera* Camera0; // 78 / facing outside -Camera* Camera1; // 7A / selfie cam - -u16 ModuleCnt; -u16 Cnt; - -u32 CropStart, CropEnd; - -// pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are -u32 DataBuffer[512]; -u32 BufferReadPos, BufferWritePos; -u32 BufferNumLines; -Camera* CurCamera; // note on camera data/etc intervals // on hardware those are likely affected by several factors // namely, how long cameras take to process frames // camera IRQ is fired at roughly 15FPS with default config -const u32 kIRQInterval = 1120000; // ~30 FPS -const u32 kTransferStart = 60000; +const u32 DSi_CamModule::kIRQInterval = 1120000; // ~30 FPS +const u32 DSi_CamModule::kTransferStart = 60000; -bool Init() +DSi_CamModule::DSi_CamModule() { - NDS::RegisterEventFunc(NDS::Event_DSi_CamIRQ, 0, IRQ); - NDS::RegisterEventFunc(NDS::Event_DSi_CamTransfer, 0, TransferScanline); + NDS::RegisterEventFunc(NDS::Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ)); + NDS::RegisterEventFunc(NDS::Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline)); - Camera0 = new Camera(0); - Camera1 = new Camera(1); - - return true; + Camera0 = DSi::I2C->GetOuterCamera(); + Camera1 = DSi::I2C->GetInnerCamera(); } -void DeInit() +DSi_CamModule::~DSi_CamModule() { - delete Camera0; - delete Camera1; - Camera0 = nullptr; Camera1 = nullptr; @@ -74,11 +53,8 @@ void DeInit() NDS::UnregisterEventFunc(NDS::Event_DSi_CamTransfer, 0); } -void Reset() +void DSi_CamModule::Reset() { - Camera0->Reset(); - Camera1->Reset(); - ModuleCnt = 0; // CHECKME Cnt = 0; @@ -94,13 +70,13 @@ void Reset() NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, false, kIRQInterval, 0, 0); } -void Stop() +void DSi_CamModule::Stop() { Camera0->Stop(); Camera1->Stop(); } -void DoSavestate(Savestate* file) +void DSi_CamModule::DoSavestate(Savestate* file) { file->Section("CAMi"); @@ -110,15 +86,12 @@ void DoSavestate(Savestate* file) /*file->VarArray(FrameBuffer, sizeof(FrameBuffer)); file->Var32(&TransferPos); file->Var32(&FrameLength);*/ - - Camera0->DoSavestate(file); - Camera1->DoSavestate(file); } -void IRQ(u32 param) +void DSi_CamModule::IRQ(u32 param) { - Camera* activecam = nullptr; + DSi_Camera* activecam = nullptr; // TODO: cameras don't have any priority! // activating both together will jumble the image data together @@ -145,7 +118,7 @@ void IRQ(u32 param) NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, 0, 0); } -void TransferScanline(u32 line) +void DSi_CamModule::TransferScanline(u32 line) { u32* dstbuf = &DataBuffer[BufferWritePos]; int maxlen = 512 - BufferWritePos; @@ -252,7 +225,7 @@ void TransferScanline(u32 line) } -u8 Read8(u32 addr) +u8 DSi_CamModule::Read8(u32 addr) { // @@ -260,7 +233,7 @@ u8 Read8(u32 addr) return 0; } -u16 Read16(u32 addr) +u16 DSi_CamModule::Read16(u32 addr) { switch (addr) { @@ -272,7 +245,7 @@ u16 Read16(u32 addr) return 0; } -u32 Read32(u32 addr) +u32 DSi_CamModule::Read32(u32 addr) { switch (addr) { @@ -298,14 +271,14 @@ u32 Read32(u32 addr) return 0; } -void Write8(u32 addr, u8 val) +void DSi_CamModule::Write8(u32 addr, u8 val) { // Log(LogLevel::Debug, "unknown DSi cam write8 %08X %02X\n", addr, val); } -void Write16(u32 addr, u16 val) +void DSi_CamModule::Write16(u32 addr, u16 val) { switch (addr) { @@ -384,7 +357,7 @@ void Write16(u32 addr, u16 val) Log(LogLevel::Debug, "unknown DSi cam write16 %08X %04X\n", addr, val); } -void Write32(u32 addr, u32 val) +void DSi_CamModule::Write32(u32 addr, u32 val) { switch (addr) { @@ -403,16 +376,15 @@ void Write32(u32 addr, u32 val) -Camera::Camera(u32 num) -{ - Num = num; -} - -Camera::~Camera() +DSi_Camera::DSi_Camera(DSi_I2CHost* host, u32 num) : DSi_I2CDevice(host), Num(num) { } -void Camera::DoSavestate(Savestate* file) +DSi_Camera::~DSi_Camera() +{ +} + +void DSi_Camera::DoSavestate(Savestate* file) { char magic[5] = "CAMx"; magic[3] = '0' + Num; @@ -433,7 +405,7 @@ void Camera::DoSavestate(Savestate* file) file->VarArray(MCURegs, 0x8000); } -void Camera::Reset() +void DSi_Camera::Reset() { Platform::Camera_Stop(Num); @@ -458,12 +430,12 @@ void Camera::Reset() memset(FrameBuffer, 0, (640*480/2)*sizeof(u32)); } -void Camera::Stop() +void DSi_Camera::Stop() { Platform::Camera_Stop(Num); } -bool Camera::IsActivated() +bool DSi_Camera::IsActivated() { if (StandbyCnt & (1<<14)) return false; // standby if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled @@ -472,7 +444,7 @@ bool Camera::IsActivated() } -void Camera::StartTransfer() +void DSi_Camera::StartTransfer() { TransferY = 0; @@ -502,12 +474,12 @@ void Camera::StartTransfer() Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true); } -bool Camera::TransferDone() +bool DSi_Camera::TransferDone() { return TransferY >= FrameHeight; } -int Camera::TransferScanline(u32* buffer, int maxlen) +int DSi_Camera::TransferScanline(u32* buffer, int maxlen) { if (TransferY >= FrameHeight) return 0; @@ -560,12 +532,12 @@ int Camera::TransferScanline(u32* buffer, int maxlen) } -void Camera::I2C_Start() +void DSi_Camera::Acquire() { DataPos = 0; } -u8 Camera::I2C_Read(bool last) +u8 DSi_Camera::Read(bool last) { u8 ret; @@ -586,7 +558,7 @@ u8 Camera::I2C_Read(bool last) return ret; } -void Camera::I2C_Write(u8 val, bool last) +void DSi_Camera::Write(u8 val, bool last) { if (DataPos < 2) { @@ -615,7 +587,7 @@ void Camera::I2C_Write(u8 val, bool last) else DataPos++; } -u16 Camera::I2C_ReadReg(u16 addr) +u16 DSi_Camera::I2C_ReadReg(u16 addr) { switch (addr) { @@ -651,7 +623,7 @@ u16 Camera::I2C_ReadReg(u16 addr) return 0; } -void Camera::I2C_WriteReg(u16 addr, u16 val) +void DSi_Camera::I2C_WriteReg(u16 addr, u16 val) { switch (addr) { @@ -720,14 +692,14 @@ void Camera::I2C_WriteReg(u16 addr, u16 val) // TODO: not sure at all what is the accessible range // or if there is any overlap in the address range -u8 Camera::MCU_Read(u16 addr) +u8 DSi_Camera::MCU_Read(u16 addr) { addr &= 0x7FFF; return MCURegs[addr]; } -void Camera::MCU_Write(u16 addr, u8 val) +void DSi_Camera::MCU_Write(u16 addr, u8 val) { addr &= 0x7FFF; @@ -749,7 +721,7 @@ void Camera::MCU_Write(u16 addr, u8 val) } -void Camera::InputFrame(u32* data, int width, int height, bool rgb) +void DSi_Camera::InputFrame(u32* data, int width, int height, bool rgb) { // TODO: double-buffering? @@ -820,19 +792,3 @@ void Camera::InputFrame(u32* data, int width, int height, bool rgb) } } } - -} - - - - - - - - - - - - - - diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index dce30a46..c3165b3f 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -21,38 +21,15 @@ #include "types.h" #include "Savestate.h" +#include "DSi_I2C.h" -namespace DSi_CamModule -{ +class DSi_CamModule; -class Camera; - -extern Camera* Camera0; -extern Camera* Camera1; - -bool Init(); -void DeInit(); -void Reset(); -void Stop(); - -void DoSavestate(Savestate* file); - -void IRQ(u32 param); - -void TransferScanline(u32 line); - -u8 Read8(u32 addr); -u16 Read16(u32 addr); -u32 Read32(u32 addr); -void Write8(u32 addr, u8 val); -void Write16(u32 addr, u16 val); -void Write32(u32 addr, u32 val); - -class Camera +class DSi_Camera : public DSi_I2CDevice { public: - Camera(u32 num); - ~Camera(); + DSi_Camera(DSi_I2CHost* host, u32 num); + ~DSi_Camera(); void DoSavestate(Savestate* file); @@ -66,9 +43,9 @@ public: // lengths in words int TransferScanline(u32* buffer, int maxlen); - void I2C_Start(); - u8 I2C_Read(bool last); - void I2C_Write(u8 val, bool last); + void Acquire(); + u8 Read(bool last); + void Write(u8 val, bool last); void InputFrame(u32* data, int width, int height, bool rgb); @@ -101,6 +78,47 @@ private: u32 FrameBuffer[640*480/2]; // YUYV framebuffer, two pixels per word }; -} + +class DSi_CamModule +{ +public: + DSi_CamModule(); + ~DSi_CamModule(); + void Reset(); + void Stop(); + void DoSavestate(Savestate* file); + + DSi_Camera* GetOuterCamera() { return Camera0; } + DSi_Camera* GetInnerCamera() { return Camera1; } + + void IRQ(u32 param); + + void TransferScanline(u32 line); + + u8 Read8(u32 addr); + u16 Read16(u32 addr); + u32 Read32(u32 addr); + void Write8(u32 addr, u8 val); + void Write16(u32 addr, u16 val); + void Write32(u32 addr, u32 val); + +private: + DSi_Camera* Camera0; // 78 / facing outside + DSi_Camera* Camera1; // 7A / selfie cam + + u16 ModuleCnt; + u16 Cnt; + + u32 CropStart, CropEnd; + + // pixel data buffer holds a maximum of 512 words, regardless of how long scanlines are + u32 DataBuffer[512]; + u32 BufferReadPos, BufferWritePos; + u32 BufferNumLines; + DSi_Camera* CurCamera; + + static const u32 kIRQInterval; + static const u32 kTransferStart; +}; #endif // DSI_CAMERA_H diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index 24aaa87c..8549ecb2 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -29,18 +29,16 @@ using Platform::Log; using Platform::LogLevel; -namespace DSi_BPTWL -{ // TODO: These are purely approximations -const double PowerButtonShutdownTime = 0.5; -const double PowerButtonForcedShutdownTime = 5.0; -const double VolumeSwitchRepeatStart = 0.5; -const double VolumeSwitchRepeatRate = 1.0 / 6; +const double DSi_BPTWL::PowerButtonShutdownTime = 0.5; +const double DSi_BPTWL::PowerButtonForcedShutdownTime = 5.0; +const double DSi_BPTWL::VolumeSwitchRepeatStart = 0.5; +const double DSi_BPTWL::VolumeSwitchRepeatRate = 1.0 / 6; // Could not find a pattern or a decent formula for these, // regardless, they're only 64 bytes in size -const u8 VolumeDownTable[32] = +const u8 DSi_BPTWL::VolumeDownTable[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x09, 0x0A, @@ -48,7 +46,7 @@ const u8 VolumeDownTable[32] = 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, }; -const u8 VolumeUpTable[32] = +const u8 DSi_BPTWL::VolumeUpTable[32] = { 0x02, 0x03, 0x06, 0x07, 0x08, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, @@ -56,27 +54,16 @@ const u8 VolumeUpTable[32] = 0x1D, 0x1E, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, }; -double PowerButtonTime = 0.0; -bool PowerButtonDownFlag = false; -bool PowerButtonShutdownFlag = false; -double VolumeSwitchTime = 0.0; -double VolumeSwitchRepeatTime = 0.0; -bool VolumeSwitchDownFlag = false; -u32 VolumeSwitchKeysDown = 0; -u8 Registers[0x100]; -u32 CurPos; - -bool Init() -{ - return true; -} - -void DeInit() +DSi_BPTWL::DSi_BPTWL(DSi_I2CHost* host) : DSi_I2CDevice(host) { } -void Reset() +DSi_BPTWL::~DSi_BPTWL() +{ +} + +void DSi_BPTWL::Reset() { CurPos = -1; memset(Registers, 0x5A, 0x100); @@ -115,10 +102,11 @@ void Reset() VolumeSwitchTime = 0.0; VolumeSwitchRepeatTime = 0.0; VolumeSwitchKeysDown = 0; + VolumeSwitchDownFlag = false; } -void DoSavestate(Savestate* file) +void DSi_BPTWL::DoSavestate(Savestate* file) { file->Section("I2BP"); @@ -127,21 +115,21 @@ void DoSavestate(Savestate* file) } // TODO: Needs more investigation on the other bits -inline bool GetIRQMode() +inline bool DSi_BPTWL::GetIRQMode() { return Registers[0x12] & 0x01; } -u8 GetBootFlag() { return Registers[0x70]; } +u8 DSi_BPTWL::GetBootFlag() { return Registers[0x70]; } -bool GetBatteryCharging() { return Registers[0x20] >> 7; } -void SetBatteryCharging(bool charging) +bool DSi_BPTWL::GetBatteryCharging() { return Registers[0x20] >> 7; } +void DSi_BPTWL::SetBatteryCharging(bool charging) { Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F)); } -u8 GetBatteryLevel() { return Registers[0x20] & 0xF; } -void SetBatteryLevel(u8 batteryLevel) +u8 DSi_BPTWL::GetBatteryLevel() { return Registers[0x20] & 0xF; } +void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel) { Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F)); //SPI_Powerman::SetBatteryLevelOkay(batteryLevel > batteryLevel_Low ? true : false); @@ -153,20 +141,20 @@ void SetBatteryLevel(u8 batteryLevel) } -u8 GetVolumeLevel() { return Registers[0x40]; } -void SetVolumeLevel(u8 volume) +u8 DSi_BPTWL::GetVolumeLevel() { return Registers[0x40]; } +void DSi_BPTWL::SetVolumeLevel(u8 volume) { Registers[0x40] = volume & 0x1F; } -u8 GetBacklightLevel() { return Registers[0x41]; } -void SetBacklightLevel(u8 backlight) +u8 DSi_BPTWL::GetBacklightLevel() { return Registers[0x41]; } +void DSi_BPTWL::SetBacklightLevel(u8 backlight) { Registers[0x41] = backlight > 4 ? 4 : backlight; } -void ResetButtonState() +void DSi_BPTWL::ResetButtonState() { PowerButtonTime = 0.0; PowerButtonDownFlag = false; @@ -178,7 +166,7 @@ void ResetButtonState() VolumeSwitchRepeatTime = 0.0; } -void DoHardwareReset(bool direct) +void DSi_BPTWL::DoHardwareReset(bool direct) { ResetButtonState(); @@ -196,14 +184,14 @@ void DoHardwareReset(bool direct) NDS::ARM7->Halt(4); } -void DoShutdown() +void DSi_BPTWL::DoShutdown() { ResetButtonState(); NDS::Stop(Platform::StopReason::PowerOff); } -void SetPowerButtonHeld(double time) +void DSi_BPTWL::SetPowerButtonHeld(double time) { if (!PowerButtonDownFlag) { @@ -230,7 +218,7 @@ void SetPowerButtonHeld(double time) } } -void SetPowerButtonReleased(double time) +void DSi_BPTWL::SetPowerButtonReleased(double time) { double elapsed = time - PowerButtonTime; if (elapsed >= 0 && elapsed < PowerButtonShutdownTime) @@ -243,12 +231,12 @@ void SetPowerButtonReleased(double time) PowerButtonShutdownFlag = false; } -void SetVolumeSwitchHeld(u32 key) +void DSi_BPTWL::SetVolumeSwitchHeld(u32 key) { VolumeSwitchKeysDown |= (1 << key); } -void SetVolumeSwitchReleased(u32 key) +void DSi_BPTWL::SetVolumeSwitchReleased(u32 key) { VolumeSwitchKeysDown &= ~(1 << key); VolumeSwitchDownFlag = false; @@ -256,7 +244,7 @@ void SetVolumeSwitchReleased(u32 key) VolumeSwitchRepeatTime = 0.0; } -inline bool CheckVolumeSwitchKeysValid() +inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid() { bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up); bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down); @@ -264,7 +252,7 @@ inline bool CheckVolumeSwitchKeysValid() return up != down; } -s32 ProcessVolumeSwitchInput(double time) +s32 DSi_BPTWL::ProcessVolumeSwitchInput(double time) { if (!CheckVolumeSwitchKeysValid()) return -1; @@ -303,7 +291,7 @@ s32 ProcessVolumeSwitchInput(double time) } -void DoPowerButtonPress() +void DSi_BPTWL::DoPowerButtonPress() { // Set button pressed IRQ SetIRQ(IRQ_PowerButtonPressed); @@ -311,7 +299,7 @@ void DoPowerButtonPress() // There is no default hardware behavior for pressing the power button } -void DoPowerButtonReset() +void DSi_BPTWL::DoPowerButtonReset() { // Reset via IRQ, handled by software SetIRQ(IRQ_PowerButtonReset); @@ -324,7 +312,7 @@ void DoPowerButtonReset() } } -void DoPowerButtonShutdown() +void DSi_BPTWL::DoPowerButtonShutdown() { // Shutdown via IRQ, handled by software if (!PowerButtonShutdownFlag) @@ -346,12 +334,12 @@ void DoPowerButtonShutdown() // down the power button, the DSi will still shut down } -void DoPowerButtonForceShutdown() +void DSi_BPTWL::DoPowerButtonForceShutdown() { DoShutdown(); } -void DoVolumeSwitchPress(u32 key) +void DSi_BPTWL::DoVolumeSwitchPress(u32 key) { u8 volume = Registers[0x40]; @@ -373,7 +361,7 @@ void DoVolumeSwitchPress(u32 key) SetIRQ(IRQ_VolumeSwitchPressed); } -void SetIRQ(u8 irqFlag) +void DSi_BPTWL::SetIRQ(u8 irqFlag) { Registers[0x10] |= irqFlag & IRQ_ValidMask; @@ -383,12 +371,12 @@ void SetIRQ(u8 irqFlag) } } -void Start() +void DSi_BPTWL::Acquire() { //printf("BPTWL: start\n"); } -u8 Read(bool last) +u8 DSi_BPTWL::Read(bool last) { //printf("BPTWL: read %02X -> %02X @ %08X\n", CurPos, Registers[CurPos], NDS::GetPC(1)); u8 ret = Registers[CurPos]; @@ -409,7 +397,7 @@ u8 Read(bool last) return ret; } -void Write(u8 val, bool last) +void DSi_BPTWL::Write(u8 val, bool last) { if (last) { @@ -460,51 +448,69 @@ void Write(u8 val, bool last) CurPos++; // CHECKME } + +DSi_I2CHost::DSi_I2CHost() +{ + BPTWL = new DSi_BPTWL(this); + Camera0 = new DSi_Camera(this, 0); + Camera1 = new DSi_Camera(this, 1); } - -namespace DSi_I2C +DSi_I2CHost::~DSi_I2CHost() { - -u8 Cnt; -u8 Data; - -u32 Device; - -bool Init() -{ - if (!DSi_BPTWL::Init()) return false; - - return true; + delete BPTWL; BPTWL = nullptr; + delete Camera0; Camera0 = nullptr; + delete Camera1; Camera1 = nullptr; } -void DeInit() -{ - DSi_BPTWL::DeInit(); -} - -void Reset() +void DSi_I2CHost::Reset() { Cnt = 0; Data = 0; - Device = -1; + CurDeviceID = 0; + CurDevice = nullptr; - DSi_BPTWL::Reset(); + BPTWL->Reset(); + Camera0->Reset(); + Camera1->Reset(); } -void DoSavestate(Savestate* file) +void DSi_I2CHost::DoSavestate(Savestate* file) { file->Section("I2Ci"); file->Var8(&Cnt); file->Var8(&Data); - file->Var32(&Device); + file->Var8(&CurDeviceID); - DSi_BPTWL::DoSavestate(file); + if (!file->Saving) + { + GetCurDevice(); + } + + BPTWL->DoSavestate(file); + Camera0->DoSavestate(file); + Camera1->DoSavestate(file); } -void WriteCnt(u8 val) +void DSi_I2CHost::GetCurDevice() +{ + switch (CurDeviceID) + { + case 0x4A: CurDevice = BPTWL; break; + case 0x78: CurDevice = Camera0; break; + case 0x7A: CurDevice = Camera1; break; + case 0xA0: + case 0xE0: CurDevice = nullptr; break; + default: + Log(LogLevel::Warn, "I2C: unknown device %02X\n", CurDeviceID); + CurDevice = nullptr; + break; + } +} + +void DSi_I2CHost::WriteCnt(u8 val) { //printf("I2C: write CNT %02X, %02X, %08X\n", val, Data, NDS::GetPC(1)); @@ -522,17 +528,13 @@ void WriteCnt(u8 val) // read val &= 0xF7; - switch (Device) + if (CurDevice) + { + Data = CurDevice->Read(islast); + } + else { - case 0x4A: Data = DSi_BPTWL::Read(islast); break; - case 0x78: Data = DSi_CamModule::Camera0->I2C_Read(islast); break; - case 0x7A: Data = DSi_CamModule::Camera1->I2C_Read(islast); break; - case 0xA0: - case 0xE0: Data = 0xFF; break; - default: - Log(LogLevel::Warn, "I2C: read on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, 0, islast); Data = 0xFF; - break; } //printf("I2C read, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast); @@ -545,37 +547,30 @@ void WriteCnt(u8 val) if (val & (1<<1)) { - Device = Data & 0xFE; + CurDeviceID = Data & 0xFE; //printf("I2C: %s start, device=%02X\n", (Data&0x01)?"read":"write", Device); - switch (Device) + GetCurDevice(); + if (CurDevice) + { + CurDevice->Acquire(); + } + else { - case 0x4A: DSi_BPTWL::Start(); break; - case 0x78: DSi_CamModule::Camera0->I2C_Start(); break; - case 0x7A: DSi_CamModule::Camera1->I2C_Start(); break; - case 0xA0: - case 0xE0: ack = false; break; - default: - Log(LogLevel::Warn, "I2C: %s start on unknown device %02X\n", (Data&0x01)?"read":"write", Device); ack = false; - break; } } else { //printf("I2C write, device=%02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast); - switch (Device) + if (CurDevice) + { + CurDevice->Write(Data, islast); + } + else { - case 0x4A: DSi_BPTWL::Write(Data, islast); break; - case 0x78: DSi_CamModule::Camera0->I2C_Write(Data, islast); break; - case 0x7A: DSi_CamModule::Camera1->I2C_Write(Data, islast); break; - case 0xA0: - case 0xE0: ack = false; break; - default: - Log(LogLevel::Warn, "I2C: write on unknown device %02X, cnt=%02X, data=%02X, last=%d\n", Device, val, Data, islast); ack = false; - break; } } @@ -588,14 +583,12 @@ void WriteCnt(u8 val) Cnt = val; } -u8 ReadData() +u8 DSi_I2CHost::ReadData() { return Data; } -void WriteData(u8 val) +void DSi_I2CHost::WriteData(u8 val) { Data = val; } - -} diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index 9027594d..b55dd0bf 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -22,95 +22,162 @@ #include "types.h" #include "Savestate.h" -namespace DSi_BPTWL +class DSi_I2CHost; +class DSi_Camera; + +class DSi_I2CDevice { +public: + DSi_I2CDevice(DSi_I2CHost* host) : Host(host) {} + virtual ~DSi_I2CDevice() {} + virtual void Reset() = 0; + virtual void DoSavestate(Savestate* file) = 0; -u8 GetBootFlag(); + virtual void Acquire() = 0; + virtual u8 Read(bool last) = 0; + virtual void Write(u8 val, bool last) = 0; -bool GetBatteryCharging(); -void SetBatteryCharging(bool charging); - -enum -{ - batteryLevel_Critical = 0x0, - batteryLevel_AlmostEmpty = 0x1, - batteryLevel_Low = 0x3, - batteryLevel_Half = 0x7, - batteryLevel_ThreeQuarters = 0xB, - batteryLevel_Full = 0xF +protected: + DSi_I2CHost* Host; }; -u8 GetBatteryLevel(); -void SetBatteryLevel(u8 batteryLevel); - -// 0-31 -u8 GetVolumeLevel(); -void SetVolumeLevel(u8 volume); - -// 0-4 -u8 GetBacklightLevel(); -void SetBacklightLevel(u8 backlight); - -void DoHardwareReset(bool direct); -void DoShutdown(); - -enum +class DSi_BPTWL : public DSi_I2CDevice { - volumeKey_Up, - volumeKey_Down, +public: + + enum + { + batteryLevel_Critical = 0x0, + batteryLevel_AlmostEmpty = 0x1, + batteryLevel_Low = 0x3, + batteryLevel_Half = 0x7, + batteryLevel_ThreeQuarters = 0xB, + batteryLevel_Full = 0xF + }; + + enum + { + volumeKey_Up, + volumeKey_Down, + }; + + enum + { + IRQ_PowerButtonReset = 0x01, // Triggered after releasing the power button quickly + IRQ_PowerButtonShutdown = 0x02, // Triggered after holding the power button for less than a second + IRQ_PowerButtonPressed = 0x08, // Triggered after pressing the power button + IRQ_BatteryEmpty = 0x10, // + IRQ_BatteryLow = 0x20, // Triggered when the battery level reaches 1 + IRQ_VolumeSwitchPressed = 0x40, // Triggered once when the volume sliders are first pressed and repeatedly when held down + /* + Bit 2 (0x04) could be set when holding the power button for more than 5 seconds? (forced power off) + It is unknown whether it is set as the console powers off immediately. + Bit 7 (0x80) is unused? + Both bits are never used by the official ARM7 libraries, but could have some undocumented hardware functionality (?). + */ + IRQ_ValidMask = 0x7B, + }; + + DSi_BPTWL(DSi_I2CHost* host); + ~DSi_BPTWL() override; + void Reset() override; + void DoSavestate(Savestate* file) override; + + u8 GetBootFlag(); + + bool GetBatteryCharging(); + void SetBatteryCharging(bool charging); + + u8 GetBatteryLevel(); + void SetBatteryLevel(u8 batteryLevel); + + // 0-31 + u8 GetVolumeLevel(); + void SetVolumeLevel(u8 volume); + + // 0-4 + u8 GetBacklightLevel(); + void SetBacklightLevel(u8 backlight); + + void DoHardwareReset(bool direct); + void DoShutdown(); + + // Used by hotkeys + void SetPowerButtonHeld(double time); + void SetPowerButtonReleased(double time); + void SetVolumeSwitchHeld(u32 key); + void SetVolumeSwitchReleased(u32 key); + s32 ProcessVolumeSwitchInput(double time); + + void DoPowerButtonPress(); + void DoPowerButtonReset(); + void DoPowerButtonShutdown(); + void DoPowerButtonForceShutdown(); + void DoVolumeSwitchPress(u32 key); + + void SetIRQ(u8 irqFlag); + + void Acquire() override; + u8 Read(bool last) override; + void Write(u8 val, bool last) override; + +private: + static const double PowerButtonShutdownTime; + static const double PowerButtonForcedShutdownTime; + static const double VolumeSwitchRepeatStart; + static const double VolumeSwitchRepeatRate; + + static const u8 VolumeDownTable[32]; + static const u8 VolumeUpTable[32]; + + double PowerButtonTime; + bool PowerButtonDownFlag; + bool PowerButtonShutdownFlag; + double VolumeSwitchTime; + double VolumeSwitchRepeatTime; + bool VolumeSwitchDownFlag ; + u32 VolumeSwitchKeysDown; + + u8 Registers[0x100]; + u32 CurPos; + + bool GetIRQMode(); + + void ResetButtonState(); + bool CheckVolumeSwitchKeysValid(); }; -// Used by hotkeys -void SetPowerButtonHeld(double time); -void SetPowerButtonReleased(double time); -void SetVolumeSwitchHeld(u32 key); -void SetVolumeSwitchReleased(u32 key); -s32 ProcessVolumeSwitchInput(double time); -void DoPowerButtonPress(); -void DoPowerButtonReset(); -void DoPowerButtonShutdown(); -void DoPowerButtonForceShutdown(); -void DoVolumeSwitchPress(u32 key); - -enum +class DSi_I2CHost { - IRQ_PowerButtonReset = 0x01, // Triggered after releasing the power button quickly - IRQ_PowerButtonShutdown = 0x02, // Triggered after holding the power button for less than a second - IRQ_PowerButtonPressed = 0x08, // Triggered after pressing the power button - IRQ_BatteryEmpty = 0x10, // - IRQ_BatteryLow = 0x20, // Triggered when the battery level reaches 1 - IRQ_VolumeSwitchPressed = 0x40, // Triggered once when the volume sliders are first pressed and repeatedly when held down - /* - Bit 2 (0x04) could be set when holding the power button for more than 5 seconds? (forced power off) - It is unknown whether it is set as the console powers off immediately. - Bit 7 (0x80) is unused? - Both bits are never used by the official ARM7 libraries, but could have some undocumented hardware functionality (?). - */ - IRQ_ValidMask = 0x7B, +public: + DSi_I2CHost(); + ~DSi_I2CHost(); + void Reset(); + void DoSavestate(Savestate* file); + + DSi_BPTWL* GetBPTWL() { return BPTWL; } + DSi_Camera* GetOuterCamera() { return Camera0; } + DSi_Camera* GetInnerCamera() { return Camera1; } + + u8 ReadCnt() { return Cnt; } + void WriteCnt(u8 val); + + u8 ReadData(); + void WriteData(u8 val); + +private: + u8 Cnt; + u8 Data; + + DSi_BPTWL* BPTWL; // 4A / BPTWL IC + DSi_Camera* Camera0; // 78 / facing outside + DSi_Camera* Camera1; // 7A / selfie cam + + u8 CurDeviceID; + DSi_I2CDevice* CurDevice; + + void GetCurDevice(); }; -void SetIRQ(u8 irqFlag); - -} - -namespace DSi_I2C -{ - -extern u8 Cnt; - -bool Init(); -void DeInit(); -void Reset(); -void DoSavestate(Savestate* file); - -void WriteCnt(u8 val); - -u8 ReadData(); -void WriteData(u8 val); - -//void TransferDone(u32 param); - -} - #endif // DSI_I2C_H diff --git a/src/NDS.cpp b/src/NDS.cpp index 05a77217..8f459a06 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1365,8 +1365,8 @@ void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) { switch (cam) { - case 0: return DSi_CamModule::Camera0->InputFrame(data, width, height, rgb); - case 1: return DSi_CamModule::Camera1->InputFrame(data, width, height, rgb); + case 0: return DSi::CamModule->GetOuterCamera()->InputFrame(data, width, height, rgb); + case 1: return DSi::CamModule->GetInnerCamera()->InputFrame(data, width, height, rgb); } } } diff --git a/src/SPI.h b/src/SPI.h index 619a26b1..bf665d90 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -46,9 +46,7 @@ class SPIDevice public: SPIDevice(SPIHost* host) : Host(host), Hold(false), DataPos(0) {} virtual ~SPIDevice() {} - virtual void Reset() = 0; - virtual void DoSavestate(Savestate* file) = 0; virtual u8 Read() { return Data; } @@ -68,9 +66,7 @@ class FirmwareMem : public SPIDevice public: FirmwareMem(SPIHost* host); ~FirmwareMem() override; - void Reset() override; - void DoSavestate(Savestate* file) override; void SetupDirectBoot(bool dsi); @@ -100,9 +96,7 @@ class PowerMan : public SPIDevice public: PowerMan(SPIHost* host); ~PowerMan() override; - void Reset() override; - void DoSavestate(Savestate* file) override; bool GetBatteryLevelOkay(); @@ -122,9 +116,7 @@ class TSC : public SPIDevice public: TSC(SPIHost* host); virtual ~TSC() override; - virtual void Reset() override; - virtual void DoSavestate(Savestate* file) override; virtual void SetTouchCoords(u16 x, u16 y); @@ -149,9 +141,7 @@ class SPIHost public: SPIHost(); ~SPIHost(); - void Reset(); - void DoSavestate(Savestate* file); FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index 6b3beffa..386519bf 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -24,6 +24,7 @@ #include "Platform.h" #include "Config.h" #include "NDS.h" +#include "DSi.h" #include "DSi_I2C.h" #include "AudioSettingsDialog.h" @@ -126,7 +127,7 @@ void AudioSettingsDialog::onSyncVolumeLevel() if (Config::DSiVolumeSync && NDS::ConsoleType == 1) { bool state = ui->slVolume->blockSignals(true); - ui->slVolume->setValue(DSi_BPTWL::GetVolumeLevel()); + ui->slVolume->setValue(DSi::I2C->GetBPTWL()->GetVolumeLevel()); ui->slVolume->blockSignals(state); } } @@ -181,7 +182,7 @@ void AudioSettingsDialog::on_slVolume_valueChanged(int val) { if (Config::DSiVolumeSync && NDS::ConsoleType == 1) { - DSi_BPTWL::SetVolumeLevel(val); + DSi::I2C->GetBPTWL()->SetVolumeLevel(val); return; } @@ -196,7 +197,7 @@ void AudioSettingsDialog::on_chkSyncDSiVolume_clicked(bool checked) if (Config::DSiVolumeSync && NDS::ConsoleType == 1) { ui->slVolume->setMaximum(31); - ui->slVolume->setValue(DSi_BPTWL::GetVolumeLevel()); + ui->slVolume->setValue(DSi::I2C->GetBPTWL()->GetVolumeLevel()); ui->slVolume->setPageStep(4); ui->slVolume->setTickPosition(QSlider::TicksBelow); } diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp index 0354e8ed..1d69988b 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp @@ -22,6 +22,7 @@ #include "SPI.h" #include "DSi_I2C.h" #include "NDS.h" +#include "DSi.h" #include "Config.h" #include "Platform.h" @@ -42,8 +43,8 @@ PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent), { ui->grpDSBattery->setEnabled(false); - oldDSiBatteryLevel = DSi_BPTWL::GetBatteryLevel(); - oldDSiBatteryCharging = DSi_BPTWL::GetBatteryCharging(); + oldDSiBatteryLevel = DSi::I2C->GetBPTWL()->GetBatteryLevel(); + oldDSiBatteryCharging = DSi::I2C->GetBPTWL()->GetBatteryCharging(); } else { @@ -54,9 +55,9 @@ PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent), updateDSBatteryLevelControls(); - ui->cbDSiBatteryCharging->setChecked(DSi_BPTWL::GetBatteryCharging()); + ui->cbDSiBatteryCharging->setChecked(DSi::I2C->GetBPTWL()->GetBatteryCharging()); int dsiBatterySliderPos; - switch (DSi_BPTWL::GetBatteryLevel()) + switch (DSi::I2C->GetBPTWL()->GetBatteryLevel()) { case DSi_BPTWL::batteryLevel_AlmostEmpty: dsiBatterySliderPos = 0; break; case DSi_BPTWL::batteryLevel_Low: dsiBatterySliderPos = 1; break; @@ -86,8 +87,8 @@ void PowerManagementDialog::done(int r) { if (NDS::ConsoleType == 1) { - Config::DSiBatteryLevel = DSi_BPTWL::GetBatteryLevel(); - Config::DSiBatteryCharging = DSi_BPTWL::GetBatteryCharging(); + Config::DSiBatteryLevel = DSi::I2C->GetBPTWL()->GetBatteryLevel(); + Config::DSiBatteryCharging = DSi::I2C->GetBPTWL()->GetBatteryCharging(); } else { @@ -98,8 +99,8 @@ void PowerManagementDialog::done(int r) { if (NDS::ConsoleType == 1) { - DSi_BPTWL::SetBatteryLevel(oldDSiBatteryLevel); - DSi_BPTWL::SetBatteryCharging(oldDSiBatteryCharging); + DSi::I2C->GetBPTWL()->SetBatteryLevel(oldDSiBatteryLevel); + DSi::I2C->GetBPTWL()->SetBatteryCharging(oldDSiBatteryCharging); } else { @@ -132,7 +133,7 @@ void PowerManagementDialog::updateDSBatteryLevelControls() void PowerManagementDialog::on_cbDSiBatteryCharging_toggled() { - DSi_BPTWL::SetBatteryCharging(ui->cbDSiBatteryCharging->isChecked()); + DSi::I2C->GetBPTWL()->SetBatteryCharging(ui->cbDSiBatteryCharging->isChecked()); } void PowerManagementDialog::on_sliderDSiBatteryLevel_valueChanged(int value) @@ -142,13 +143,13 @@ void PowerManagementDialog::on_sliderDSiBatteryLevel_valueChanged(int value) u8 newBatteryLevel; switch (value) { - case 0: newBatteryLevel = DSi_BPTWL::batteryLevel_AlmostEmpty; break; - case 1: newBatteryLevel = DSi_BPTWL::batteryLevel_Low; break; - case 2: newBatteryLevel = DSi_BPTWL::batteryLevel_Half; break; - case 3: newBatteryLevel = DSi_BPTWL::batteryLevel_ThreeQuarters; break; - case 4: newBatteryLevel = DSi_BPTWL::batteryLevel_Full; break; + case 0: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_AlmostEmpty; break; + case 1: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_Low; break; + case 2: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_Half; break; + case 3: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_ThreeQuarters; break; + case 4: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_Full; break; } - DSi_BPTWL::SetBatteryLevel(newBatteryLevel); + DSi::I2C->GetBPTWL()->SetBatteryLevel(newBatteryLevel); updateDSBatteryLevelControls(); } diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index a0355f66..6b4ddeee 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -583,8 +583,8 @@ void SetBatteryLevels() { if (NDS::ConsoleType == 1) { - DSi_BPTWL::SetBatteryLevel(Config::DSiBatteryLevel); - DSi_BPTWL::SetBatteryCharging(Config::DSiBatteryCharging); + DSi::I2C->GetBPTWL()->SetBatteryLevel(Config::DSiBatteryLevel); + DSi::I2C->GetBPTWL()->SetBatteryCharging(Config::DSiBatteryCharging); } else { diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 700caea3..e96bb4bd 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -90,8 +90,9 @@ #include "Platform.h" #include "LocalMP.h" #include "Config.h" -#include "DSi_I2C.h" #include "RTC.h" +#include "DSi.h" +#include "DSi_I2C.h" #include "Savestate.h" @@ -409,33 +410,33 @@ void EmuThread::run() // Handle power button if (Input::HotkeyDown(HK_PowerButton)) { - DSi_BPTWL::SetPowerButtonHeld(currentTime); + DSi::I2C->GetBPTWL()->SetPowerButtonHeld(currentTime); } else if (Input::HotkeyReleased(HK_PowerButton)) { - DSi_BPTWL::SetPowerButtonReleased(currentTime); + DSi::I2C->GetBPTWL()->SetPowerButtonReleased(currentTime); } // Handle volume buttons if (Input::HotkeyDown(HK_VolumeUp)) { - DSi_BPTWL::SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); + DSi::I2C->GetBPTWL()->SetVolumeSwitchHeld(DSi::I2C->GetBPTWL()->volumeKey_Up); } else if (Input::HotkeyReleased(HK_VolumeUp)) { - DSi_BPTWL::SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); + DSi::I2C->GetBPTWL()->SetVolumeSwitchReleased(DSi::I2C->GetBPTWL()->volumeKey_Up); } if (Input::HotkeyDown(HK_VolumeDown)) { - DSi_BPTWL::SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); + DSi::I2C->GetBPTWL()->SetVolumeSwitchHeld(DSi::I2C->GetBPTWL()->volumeKey_Down); } else if (Input::HotkeyReleased(HK_VolumeDown)) { - DSi_BPTWL::SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); + DSi::I2C->GetBPTWL()->SetVolumeSwitchReleased(DSi::I2C->GetBPTWL()->volumeKey_Down); } - DSi_BPTWL::ProcessVolumeSwitchInput(currentTime); + DSi::I2C->GetBPTWL()->ProcessVolumeSwitchInput(currentTime); } if (EmuRunning == emuStatus_Running || EmuRunning == emuStatus_FrameStep) @@ -562,7 +563,7 @@ void EmuThread::run() if (Config::DSiVolumeSync && NDS::ConsoleType == 1) { - u8 volumeLevel = DSi_BPTWL::GetVolumeLevel(); + u8 volumeLevel = DSi::I2C->GetBPTWL()->GetVolumeLevel(); if (volumeLevel != dsiVolumeLevel) { dsiVolumeLevel = volumeLevel; From 11c22f077d4ca79a53f0e1769e587fff1c2daf27 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 5 Nov 2023 11:58:50 +0100 Subject: [PATCH 028/157] convert DSP --- src/DSi.cpp | 55 +++++++++++----------- src/DSi.h | 2 + src/DSi_DSP.cpp | 119 ++++++++++++++++++++---------------------------- src/DSi_DSP.h | 96 +++++++++++++++++++++++++------------- 4 files changed, 143 insertions(+), 129 deletions(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index 1aba7d99..8ea26c5f 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -82,6 +82,7 @@ DSi_SDHost* SDIO; DSi_I2CHost* I2C; DSi_CamModule* CamModule; DSi_AES* AES; +DSi_DSP* DSP; // FIXME: these currently have no effect (and aren't stored in a savestate) // ... not that they matter all that much @@ -104,8 +105,6 @@ bool Init() NWRAM_C = new u8[NWRAMSize]; #endif - if (!DSi_DSP::Init()) return false; - NDMAs[0] = new DSi_NDMA(0, 0); NDMAs[1] = new DSi_NDMA(0, 1); NDMAs[2] = new DSi_NDMA(0, 2); @@ -121,6 +120,7 @@ bool Init() I2C = new DSi_I2CHost(); CamModule = new DSi_CamModule(); AES = new DSi_AES(); + DSP = new DSi_DSP(); return true; } @@ -137,8 +137,6 @@ void DeInit() NWRAM_C = nullptr; #endif - DSi_DSP::DeInit(); - for (int i = 0; i < 8; i++) { delete NDMAs[i]; @@ -151,6 +149,7 @@ void DeInit() delete I2C; I2C = nullptr; delete CamModule; CamModule = nullptr; delete AES; AES = nullptr; + delete DSP; DSP = nullptr; NANDImage = nullptr; // The NANDImage is cleaned up (and its underlying file closed) @@ -170,7 +169,7 @@ void Reset() I2C->Reset(); CamModule->Reset(); - DSi_DSP::Reset(); + DSP->Reset(); SDMMC->CloseHandles(); SDIO->CloseHandles(); @@ -197,7 +196,7 @@ void Reset() SCFG_MC = 0x0010 | (~((u32)(NDSCart::Cart != nullptr))&1);//0x0011; SCFG_RST = 0; - DSi_DSP::SetRstLine(false); + DSP->SetRstLine(false); GPIO_Data = 0xff; // these actually initialize to high after reset GPIO_Dir = 0x80; // enable sound out, all others input @@ -239,7 +238,7 @@ void DoSavestate(Savestate* file) { Set_SCFG_Clock9(SCFG_Clock9); Set_SCFG_MC(SCFG_MC); - DSi_DSP::SetRstLine(SCFG_RST & 0x0001); + DSP->SetRstLine(SCFG_RST & 0x0001); MBK[0][8] = 0; MBK[1][8] = 0; @@ -288,7 +287,7 @@ void DoSavestate(Savestate* file) AES->DoSavestate(file); CamModule->DoSavestate(file); - DSi_DSP::DoSavestate(file); + DSP->DoSavestate(file); I2C->DoSavestate(file); SDMMC->DoSavestate(file); SDIO->DoSavestate(file); @@ -700,7 +699,7 @@ void SoftReset() // TODO: does the DSP get reset? NWRAM doesn't, so I'm assuming no // *HOWEVER*, the bootrom (which does get rerun) does remap NWRAM, and thus // the DSP most likely gets reset - DSi_DSP::Reset(); + DSP->Reset(); SDMMC->CloseHandles(); SDIO->CloseHandles(); @@ -727,7 +726,7 @@ void SoftReset() SCFG_MC = 0x0010;//0x0011; // TODO: is this actually reset? SCFG_RST = 0; - DSi_DSP::SetRstLine(false); + DSP->SetRstLine(false); // LCD init flag @@ -2310,7 +2309,7 @@ u8 ARM9IORead8(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSi_DSP::Read8(addr); + return DSP->Read8(addr); } return NDS::ARM9IORead8(addr); @@ -2345,7 +2344,7 @@ u16 ARM9IORead16(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSi_DSP::Read16(addr); + return DSP->Read16(addr); } return NDS::ARM9IORead16(addr); @@ -2410,7 +2409,7 @@ u32 ARM9IORead32(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSi_DSP::Read32(addr); + return DSP->Read32(addr); } return NDS::ARM9IORead32(addr); @@ -2434,7 +2433,7 @@ void ARM9IOWrite8(u32 addr, u8 val) if (!(SCFG_EXT[0] & (1 << 31))) /* no access to SCFG Registers if disabled*/ return; SCFG_RST = (SCFG_RST & 0xFF00) | val; - DSi_DSP::SetRstLine(val & 1); + DSP->SetRstLine(val & 1); return; case 0x04004040: @@ -2480,7 +2479,7 @@ void ARM9IOWrite8(u32 addr, u8 val) if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSi_DSP::Write8(addr, val); + return DSP->Write8(addr, val); } return NDS::ARM9IOWrite8(addr, val); @@ -2500,7 +2499,7 @@ void ARM9IOWrite16(u32 addr, u16 val) if (!(SCFG_EXT[0] & (1 << 31))) /* no access to SCFG Registers if disabled*/ return; SCFG_RST = val; - DSi_DSP::SetRstLine(val & 1); + DSP->SetRstLine(val & 1); return; case 0x04004040: @@ -2540,7 +2539,7 @@ void ARM9IOWrite16(u32 addr, u16 val) if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSi_DSP::Write16(addr, val); + return DSP->Write16(addr, val); } return NDS::ARM9IOWrite16(addr, val); @@ -2555,7 +2554,7 @@ void ARM9IOWrite32(u32 addr, u32 val) return; Set_SCFG_Clock9(val & 0xFFFF); SCFG_RST = val >> 16; - DSi_DSP::SetRstLine((val >> 16) & 1); + DSP->SetRstLine((val >> 16) & 1); break; case 0x04004008: @@ -2690,7 +2689,7 @@ void ARM9IOWrite32(u32 addr, u32 val) if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSi_DSP::Write32(addr, val); + return DSP->Write32(addr, val); } return NDS::ARM9IOWrite32(addr, val); @@ -2730,8 +2729,8 @@ u8 ARM7IORead8(u32 addr) case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56; case 0x04004D08: return 0; - case 0x4004700: return DSi_DSP::SNDExCnt; - case 0x4004701: return DSi_DSP::SNDExCnt >> 8; + case 0x4004700: return DSP->ReadSNDExCnt() & 0xFF; + case 0x4004701: return DSP->ReadSNDExCnt() >> 8; case 0x04004C00: return GPIO_Data; case 0x04004C01: return GPIO_Dir; @@ -2773,7 +2772,7 @@ u16 ARM7IORead16(u32 addr) case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48; case 0x04004D08: return 0; - case 0x4004700: return DSi_DSP::SNDExCnt; + case 0x4004700: return DSP->ReadSNDExCnt(); case 0x04004C00: return GPIO_Data | ((u16)GPIO_Dir << 8); case 0x04004C02: return GPIO_IEdgeSel | ((u16)GPIO_IE << 8); @@ -2852,7 +2851,7 @@ u32 ARM7IORead32(u32 addr) case 0x4004700: Log(LogLevel::Debug, "32-Bit SNDExCnt read? %08X\n", NDS::ARM7->R[15]); - return DSi_DSP::SNDExCnt; + return DSP->ReadSNDExCnt(); } if (addr >= 0x04004800 && addr < 0x04004A00) @@ -2905,10 +2904,10 @@ void ARM7IOWrite8(u32 addr, u8 val) case 0x04004501: I2C->WriteCnt(val); return; case 0x4004700: - DSi_DSP::WriteSNDExCnt((u16)val | (DSi_DSP::SNDExCnt & 0xFF00)); + DSP->WriteSNDExCnt((u16)val, 0xFF); return; case 0x4004701: - DSi_DSP::WriteSNDExCnt(((u16)val << 8) | (DSi_DSP::SNDExCnt & 0x00FF)); + DSP->WriteSNDExCnt(((u16)val << 8), 0xFF00); return; case 0x04004C00: @@ -3007,7 +3006,7 @@ void ARM7IOWrite16(u32 addr, u16 val) return; case 0x4004700: - DSi_DSP::WriteSNDExCnt(val); + DSP->WriteSNDExCnt(val, 0xFFFF); return; case 0x04004C00: @@ -3156,7 +3155,7 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x4004700: Log(LogLevel::Debug, "32-Bit SNDExCnt write? %08X %08X\n", val, NDS::ARM7->R[15]); - DSi_DSP::WriteSNDExCnt(val); + DSP->WriteSNDExCnt(val, 0xFFFF); return; } @@ -3204,7 +3203,7 @@ void ARM7IOWrite32(u32 addr, u32 val) if (addr >= 0x04004300 && addr <= 0x04004400) { - DSi_DSP::Write32(addr, val); + DSP->Write32(addr, val); return; } diff --git a/src/DSi.h b/src/DSi.h index 0e772f63..66a4e808 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -25,6 +25,7 @@ class DSi_I2CHost; class DSi_CamModule; class DSi_AES; +class DSi_DSP; namespace DSi_NAND { @@ -63,6 +64,7 @@ extern u32 NWRAMMask[2][3]; extern DSi_I2CHost* I2C; extern DSi_CamModule* CamModule; extern DSi_AES* AES; +extern DSi_DSP* DSP; bool Init(); void DeInit(); diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index d4747635..82a52aeb 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -27,34 +27,12 @@ using Platform::Log; using Platform::LogLevel; -namespace DSi_DSP -{ -// not sure whether to not rather put it somewhere else -u16 SNDExCnt; - -Teakra::Teakra* TeakraCore; - -bool SCFG_RST; - -u16 DSP_PADR; -u16 DSP_PCFG; -u16 DSP_PSTS; -u16 DSP_PSEM; -u16 DSP_PMASK; -u16 DSP_PCLEAR; -u16 DSP_CMD[3]; -u16 DSP_REP[3]; - -u64 DSPTimestamp; - -FIFO PDATAReadFifo/*, *PDATAWriteFifo*/; -int PDataDMALen = 0; - -constexpr u32 DataMemoryOffset = 0x20000; // from Teakra memory_interface.h +const u32 DSi_DSP::DataMemoryOffset = 0x20000; // from Teakra memory_interface.h // NOTE: ^ IS IN DSP WORDS, NOT IN BYTES! -u16 GetPSTS() + +u16 DSi_DSP::GetPSTS() { u16 r = DSP_PSTS & (1<<9); // this is the only sticky bit //r &= ~((1<<2)|(1<<7)); // we support instant resets and wrfifo xfers @@ -73,26 +51,26 @@ u16 GetPSTS() return r; } -void IrqRep0() +void DSi_DSP::IrqRep0() { if (DSP_PCFG & (1<< 9)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); } -void IrqRep1() +void DSi_DSP::IrqRep1() { if (DSP_PCFG & (1<<10)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); } -void IrqRep2() +void DSi_DSP::IrqRep2() { if (DSP_PCFG & (1<<11)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); } -void IrqSem() +void DSi_DSP::IrqSem() { DSP_PSTS |= 1<<9; // apparently these are always fired? NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); } -u16 DSPRead16(u32 addr) +u16 DSi_DSP::DSPRead16(u32 addr) { if (!(addr & 0x40000)) { @@ -106,7 +84,7 @@ u16 DSPRead16(u32 addr) } } -void DSPWrite16(u32 addr, u16 val) +void DSi_DSP::DSPWrite16(u32 addr, u16 val) { // TODO: does the rule for overlapping NWRAM slots also apply to the DSP side? @@ -122,29 +100,32 @@ void DSPWrite16(u32 addr, u16 val) } } -void AudioCb(std::array frame) +void DSi_DSP::AudioCb(std::array frame) { // TODO } -bool Init() +DSi_DSP::DSi_DSP() { - NDS::RegisterEventFunc(NDS::Event_DSi_DSP, 0, DSPCatchUpU32); + NDS::RegisterEventFunc(NDS::Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32)); TeakraCore = new Teakra::Teakra(); SCFG_RST = false; - if (!TeakraCore) return false; + // ???? + //if (!TeakraCore) return false; - TeakraCore->SetRecvDataHandler(0, IrqRep0); - TeakraCore->SetRecvDataHandler(1, IrqRep1); - TeakraCore->SetRecvDataHandler(2, IrqRep2); + using namespace std::placeholders; - TeakraCore->SetSemaphoreHandler(IrqSem); + TeakraCore->SetRecvDataHandler(0, std::bind(&DSi_DSP::IrqRep0, this)); + TeakraCore->SetRecvDataHandler(1, std::bind(&DSi_DSP::IrqRep1, this)); + TeakraCore->SetRecvDataHandler(2, std::bind(&DSi_DSP::IrqRep2, this)); + + TeakraCore->SetSemaphoreHandler(std::bind(&DSi_DSP::IrqSem, this)); Teakra::SharedMemoryCallback smcb; - smcb.read16 = DSPRead16; - smcb.write16 = DSPWrite16; + smcb.read16 = std::bind(&DSi_DSP::DSPRead16, this, _1); + smcb.write16 = std::bind(&DSi_DSP::DSPWrite16, this, _1, _2); TeakraCore->SetSharedMemoryCallback(smcb); // these happen instantaneously and without too much regard for bus aribtration @@ -158,14 +139,13 @@ bool Init() cb.write32 = DSi::ARM9Write32; TeakraCore->SetAHBMCallback(cb); - TeakraCore->SetAudioCallback(AudioCb); + TeakraCore->SetAudioCallback(std::bind(&DSi_DSP::AudioCb, this, _1)); //PDATAReadFifo = new FIFO(16); //PDATAWriteFifo = new FIFO(16); - - return true; } -void DeInit() + +DSi_DSP::~DSi_DSP() { //if (PDATAWriteFifo) delete PDATAWriteFifo; if (TeakraCore) delete TeakraCore; @@ -177,7 +157,7 @@ void DeInit() NDS::UnregisterEventFunc(NDS::Event_DSi_DSP, 0); } -void Reset() +void DSi_DSP::Reset() { DSPTimestamp = 0; @@ -200,28 +180,28 @@ void Reset() SNDExCnt = 0; } -bool IsRstReleased() +bool DSi_DSP::IsRstReleased() { return SCFG_RST; } -void SetRstLine(bool release) +void DSi_DSP::SetRstLine(bool release) { SCFG_RST = release; Reset(); DSPTimestamp = NDS::ARM9Timestamp; // only start now! } -inline bool IsDSPCoreEnabled() +inline bool DSi_DSP::IsDSPCoreEnabled() { return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0))); } -inline bool IsDSPIOEnabled() +inline bool DSi_DSP::IsDSPIOEnabled() { return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST; } -bool DSPCatchUp() +bool DSi_DSP::DSPCatchUp() { //asm volatile("int3"); if (!IsDSPCoreEnabled()) @@ -249,9 +229,9 @@ bool DSPCatchUp() return true; } -void DSPCatchUpU32(u32 _) { DSPCatchUp(); } +void DSi_DSP::DSPCatchUpU32(u32 _) { DSPCatchUp(); } -void PDataDMAWrite(u16 wrval) +void DSi_DSP::PDataDMAWrite(u16 wrval) { u32 addr = DSP_PADR; @@ -293,7 +273,7 @@ void PDataDMAWrite(u16 wrval) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); // wrfifo empty } // TODO: FIFO interrupts! (rd full, nonempty) -u16 PDataDMARead() +u16 DSi_DSP::PDataDMARead() { u16 r = 0; u32 addr = DSP_PADR; @@ -331,7 +311,7 @@ u16 PDataDMARead() return r; } -void PDataDMAFetch() +void DSi_DSP::PDataDMAFetch() { if (!PDataDMALen) return; @@ -339,7 +319,7 @@ void PDataDMAFetch() if (PDataDMALen > 0) --PDataDMALen; } -void PDataDMAStart() +void DSi_DSP::PDataDMAStart() { switch ((DSP_PSTS & (3<<2)) >> 2) { @@ -358,13 +338,13 @@ void PDataDMAStart() NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); } -void PDataDMACancel() +void DSi_DSP::PDataDMACancel() { PDataDMALen = 0; PDATAReadFifo.Clear(); } -u16 PDataDMAReadMMIO() +u16 DSi_DSP::PDataDMAReadMMIO() { u16 ret; @@ -395,7 +375,7 @@ u16 PDataDMAReadMMIO() return ret; } -u8 Read8(u32 addr) +u8 DSi_DSP::Read8(u32 addr) { //if (!IsDSPIOEnabled()) return 0; DSPCatchUp(); @@ -422,7 +402,7 @@ u8 Read8(u32 addr) return 0; } -u16 Read16(u32 addr) +u16 DSi_DSP::Read16(u32 addr) { //printf("DSP READ16 %d %08X %08X\n", IsDSPCoreEnabled(), addr, NDS::GetPC(0)); //if (!IsDSPIOEnabled()) return 0; @@ -465,14 +445,14 @@ u16 Read16(u32 addr) return 0; } -u32 Read32(u32 addr) +u32 DSi_DSP::Read32(u32 addr) { addr &= 0x3C; return Read16(addr); // *shrug* (doesn't do anything unintended due to the // 4byte spacing between regs while they're all 16bit) } -void Write8(u32 addr, u8 val) +void DSi_DSP::Write8(u32 addr, u8 val) { //if (!IsDSPIOEnabled()) return; DSPCatchUp(); @@ -493,7 +473,7 @@ void Write8(u32 addr, u8 val) // no REPx writes } } -void Write16(u32 addr, u16 val) +void DSi_DSP::Write16(u32 addr, u16 val) { Log(LogLevel::Debug,"DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, NDS::GetPC(0)); //if (!IsDSPIOEnabled()) return; @@ -548,14 +528,16 @@ void Write16(u32 addr, u16 val) } } -void Write32(u32 addr, u32 val) +void DSi_DSP::Write32(u32 addr, u32 val) { addr &= 0x3C; Write16(addr, val & 0xFFFF); } -void WriteSNDExCnt(u16 val) +void DSi_DSP::WriteSNDExCnt(u16 val, u16 mask) { + val = (val & mask) | (SNDExCnt & ~mask); + // it can be written even in NDS mode // mic frequency can only be changed if it was disabled @@ -569,7 +551,7 @@ void WriteSNDExCnt(u16 val) SNDExCnt = val & 0xE00F; } -void Run(u32 cycles) +void DSi_DSP::Run(u32 cycles) { if (!IsDSPCoreEnabled()) { @@ -586,7 +568,7 @@ void Run(u32 cycles) 16384/*from citra (TeakraSlice)*/, 0, 0); } -void DoSavestate(Savestate* file) +void DSi_DSP::DoSavestate(Savestate* file) { file->Section("DSPi"); @@ -611,6 +593,3 @@ void DoSavestate(Savestate* file) // TODO: save the Teakra state!!! } - -} - diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h index e88b5b56..56850c0e 100644 --- a/src/DSi_DSP.h +++ b/src/DSi_DSP.h @@ -25,50 +25,84 @@ // TODO: for actual sound output // * audio callbacks -namespace DSi_DSP +namespace Teakra { class Teakra; } + +class DSi_DSP { +public: + DSi_DSP(); + ~DSi_DSP(); + void Reset(); + void DoSavestate(Savestate* file); -extern u16 SNDExCnt; + void DSPCatchUpU32(u32 _); -extern u16 DSP_PDATA; -extern u16 DSP_PADR; -extern u16 DSP_PCFG; -extern u16 DSP_PSTS; -extern u16 DSP_PSEM; -extern u16 DSP_PMASK; -extern u16 DSP_PCLEAR; -extern u16 DSP_SEM; -extern u16 DSP_CMD[3]; -extern u16 DSP_REP[3]; + // SCFG_RST bit0 + bool IsRstReleased(); + void SetRstLine(bool release); -bool Init(); -void DeInit(); -void Reset(); + // DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT) + u8 Read8(u32 addr); + void Write8(u32 addr, u8 val); -void DoSavestate(Savestate* file); + u16 Read16(u32 addr); + void Write16(u32 addr, u16 val); -void DSPCatchUpU32(u32 _); + u32 Read32(u32 addr); + void Write32(u32 addr, u32 val); -// SCFG_RST bit0 -bool IsRstReleased(); -void SetRstLine(bool release); + u16 ReadSNDExCnt() { return SNDExCnt; } + void WriteSNDExCnt(u16 val, u16 mask); -// DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT) -u8 Read8(u32 addr); -void Write8(u32 addr, u8 val); + // NOTE: checks SCFG_CLK9 + void Run(u32 cycles); -u16 Read16(u32 addr); -void Write16(u32 addr, u16 val); + void IrqRep0(); + void IrqRep1(); + void IrqRep2(); + void IrqSem(); + u16 DSPRead16(u32 addr); + void DSPWrite16(u32 addr, u16 val); + void AudioCb(std::array frame); -u32 Read32(u32 addr); -void Write32(u32 addr, u32 val); +private: + // not sure whether to not rather put it somewhere else + u16 SNDExCnt; -void WriteSNDExCnt(u16 val); + Teakra::Teakra* TeakraCore; -// NOTE: checks SCFG_CLK9 -void Run(u32 cycles); + bool SCFG_RST; -} + u16 DSP_PADR; + u16 DSP_PCFG; + u16 DSP_PSTS; + u16 DSP_PSEM; + u16 DSP_PMASK; + u16 DSP_PCLEAR; + u16 DSP_CMD[3]; + u16 DSP_REP[3]; + + u64 DSPTimestamp; + + FIFO PDATAReadFifo/*, *PDATAWriteFifo*/; + int PDataDMALen; + + static const u32 DataMemoryOffset; + + u16 GetPSTS(); + + inline bool IsDSPCoreEnabled(); + inline bool IsDSPIOEnabled(); + + bool DSPCatchUp(); + + void PDataDMAWrite(u16 wrval); + u16 PDataDMARead(); + void PDataDMAFetch(); + void PDataDMAStart(); + void PDataDMACancel(); + u16 PDataDMAReadMMIO(); +}; #endif // DSI_DSP_H From 0aff9471c5a0f9f437e76cd9a7dd42312fc7968f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 5 Nov 2023 15:38:22 +0100 Subject: [PATCH 029/157] fuck every aspect of this --- src/NDSCart.cpp | 4 ++-- src/WifiAP.cpp | 4 ++-- src/WifiAP.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index df729bd8..4c6efddf 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1741,8 +1741,6 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen) return cart; } -// Why a move function? Because the Cart object is polymorphic, -// and cloning polymorphic objects without knowing the underlying type is annoying. bool InsertROM(std::unique_ptr&& cart) { if (!cart) { @@ -1753,6 +1751,8 @@ bool InsertROM(std::unique_ptr&& cart) if (Cart) EjectCart(); + // Why a move function? Because the Cart object is polymorphic, + // and cloning polymorphic objects without knowing the underlying type is annoying. Cart = std::move(cart); Cart->Reset(); diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index 9107a812..50100c50 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -68,7 +68,7 @@ bool MACEqual(u8* a, const u8* b); bool MACIsBroadcast(u8* a); -WifiAP::WifiAP(class Wifi* wifi) : Wifi(wifi) +WifiAP::WifiAP(Wifi* client) : Client(client) { } @@ -368,7 +368,7 @@ int WifiAP::RecvPacket(u8* data) // check destination MAC if (!MACIsBroadcast(&LANBuffer[0])) { - if (!MACEqual(&LANBuffer[0], Wifi->GetMAC())) + if (!MACEqual(&LANBuffer[0], Client->GetMAC())) return 0; } diff --git a/src/WifiAP.h b/src/WifiAP.h index 717e1c6c..34f71e7c 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -26,7 +26,7 @@ class Wifi; class WifiAP { public: - WifiAP(Wifi* wifi); + WifiAP(Wifi* client); ~WifiAP(); void Reset(); @@ -40,7 +40,7 @@ public: int RecvPacket(u8* data); private: - class Wifi* Wifi; + Wifi* Client; u64 USCounter; From df571078cfafcd0b98f3b84b84e44c97ac8ff7cf Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Sun, 5 Nov 2023 15:40:48 +0000 Subject: [PATCH 030/157] CameraManager: wait for camera to be loaded In QCamera in Qt 5, the camera is required to have been loaded before querying its settings and resolutions. Doing so without loading being finished would result in the returned list being empty. See https://doc.qt.io/qt-5.15/qcamera.html#supportedViewfinderSettings Add a QEventLoop that waits for the state to change from Loading to Loaded before supportedViewfinderSettings() is called to ensure that valid information is returned. (Fixes my camera being blank in preview, same issue also presumed to occur when camera is needed in game, fix tested on a USB camera on a Linux system and Qt 5.) --- src/frontend/qt_sdl/CameraManager.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp index c158f5ab..7f4db5d4 100644 --- a/src/frontend/qt_sdl/CameraManager.cpp +++ b/src/frontend/qt_sdl/CameraManager.cpp @@ -16,6 +16,8 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include + #include "CameraManager.h" #include "Config.h" @@ -256,6 +258,12 @@ void CameraManager::init() if (camDevice) { camDevice->load(); + if (camDevice->status() == QCamera::LoadingStatus) + { + QEventLoop loop; + connect(camDevice, &QCamera::statusChanged, &loop, &QEventLoop::quit); + loop.exec(); + } const QList supported = camDevice->supportedViewfinderSettings(); bool good = false; From 0e4d0823617240345cc8ac5f3703636d041c2df3 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Sun, 5 Nov 2023 20:21:16 +0000 Subject: [PATCH 031/157] ROMManager: initialise filedata to nullptr If a user manages to open a file as a ROM that is greater than 1 GiB, it will cause a segmentation fault (a crash) in LoadROM due to a delete being called on an uninitialised pointer, which is undefined behaviour. Initialise filedata to nullptr to prevent this, as deleting a null pointer is defined as a no-op. --- src/frontend/qt_sdl/ROMManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 6b4ddeee..6b2b0b06 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -1109,7 +1109,7 @@ bool LoadROM(QStringList filepath, bool reset) { if (filepath.empty()) return false; - u8* filedata; + u8* filedata = nullptr; u32 filelen; std::string basepath; From 2b3bba512eed4df6a477b7abdf548e13e4fe0078 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Mon, 6 Nov 2023 20:17:06 +0000 Subject: [PATCH 032/157] Fix some memory leaks Free some objects that were allocated with new but not deleted, and in one case, do not set a pointer to nullptr before deleting, as this results in a memory leak due to memory allocated not being freed. --- src/frontend/qt_sdl/CameraManager.cpp | 1 + src/frontend/qt_sdl/LocalMP.cpp | 2 +- src/frontend/qt_sdl/main.cpp | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp index 7f4db5d4..cc532ff1 100644 --- a/src/frontend/qt_sdl/CameraManager.cpp +++ b/src/frontend/qt_sdl/CameraManager.cpp @@ -146,6 +146,7 @@ CameraManager::~CameraManager() // save settings here? delete[] frameBuffer; + delete[] tempFrameBuffer; } void CameraManager::init() diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp index 56139723..4b3a6a64 100644 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ b/src/frontend/qt_sdl/LocalMP.cpp @@ -311,8 +311,8 @@ void DeInit() MPQueue->detach(); } - MPQueue = nullptr; delete MPQueue; + MPQueue = nullptr; } void SetRecvTimeout(int timeout) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index e96bb4bd..f033e668 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -832,6 +832,7 @@ ScreenHandler::ScreenHandler(QWidget* widget) ScreenHandler::~ScreenHandler() { mouseTimer->stop(); + delete mouseTimer; } void ScreenHandler::screenSetupLayout(int w, int h) @@ -1872,6 +1873,8 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) MainWindow::~MainWindow() { + delete[] actScreenAspectTop; + delete[] actScreenAspectBot; } void MainWindow::closeEvent(QCloseEvent* event) @@ -3376,6 +3379,8 @@ int main(int argc, char** argv) int ret = melon.exec(); + delete options; + emuThread->emuStop(); emuThread->wait(); delete emuThread; From 8fa9705079d330dbbfec5d9a877b5ff9cd790f3e Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Mon, 6 Nov 2023 21:27:09 +0000 Subject: [PATCH 033/157] ArchiveUtil: use signed return type instead of unsigned The ExtractFileFromArchive function can sometimes return -1 on error, however the function's return type was specified as u32, which would mean that it would instead be represented as the maximum value. Change the function's return type to the signed s32 instead, and correct uses. --- src/frontend/qt_sdl/ArchiveUtil.cpp | 2 +- src/frontend/qt_sdl/ArchiveUtil.h | 2 +- src/frontend/qt_sdl/ROMManager.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index 91b993cd..6f4b1348 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -119,7 +119,7 @@ QVector ExtractFileFromArchive(QString path, QString wantedFile, QByteA } -u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize) +s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize) { struct archive *a = archive_read_new(); struct archive_entry *entry; diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index d9327c80..14ff9968 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -36,7 +36,7 @@ namespace Archive { QVector ListArchive(QString path); -u32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize); +s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize); //QVector ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer); //u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata); diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 6b2b0b06..0bb6b7fe 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -1362,7 +1362,7 @@ bool LoadGBAROM(QStringList filepath) { // file inside archive - u32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen); + s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen); if (lenread < 0) return false; if (!filedata) return false; if (lenread != filelen) From 24a33e505e2b4757dd6bbdd3afc10fba67b304f4 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 7 Nov 2023 10:53:01 +0100 Subject: [PATCH 034/157] Also exclude .note.GNU-stack section on Windows arm64 --- src/ARMJIT_A64/ARMJIT_Linkage.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ARMJIT_A64/ARMJIT_Linkage.S b/src/ARMJIT_A64/ARMJIT_Linkage.S index ad1a3f0f..b73905bd 100644 --- a/src/ARMJIT_A64/ARMJIT_Linkage.S +++ b/src/ARMJIT_A64/ARMJIT_Linkage.S @@ -95,7 +95,7 @@ ARM_RestoreContext: br x18 -#ifndef __APPLE__ +#if !defined(__APPLE__) && !defined(__WIN32__) .section .note.GNU-stack,"",@progbits #endif From b4ff911fa357dd4a3572ff55373f8ddcd3f6f5ad Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Tue, 7 Nov 2023 15:22:25 -0500 Subject: [PATCH 035/157] Fix regression caused by change to front face polygon culling (#1820) * fix regression with facing view Only the check for a polygon being counter-clockwise is supposed to be <= * only use dot < 0 for 'cull front face' polygons this is the fix. --- src/GPU3D.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 718c6418..55d6de73 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -1070,7 +1070,7 @@ void SubmitPolygon() bool facingview = (dot <= 0); - if (facingview) + if (dot < 0) { if (!(CurPolygonAttr & (1<<7))) { From 8b47178add1e78da5ff96b335ecac73f34f4a8e9 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Nov 2023 16:21:21 -0500 Subject: [PATCH 036/157] Move GBACart-related global state into objects (#1870) - RAII will now do the heavy lifting - Mark some methods as const or noexcept - Once the `NDS` object is finalized, most of these `assert`s can go away --- src/GBACart.cpp | 64 ++++------------- src/GBACart.h | 94 ++++++++++++------------ src/NDS.cpp | 136 +++++++++++++++++++++-------------- src/NDS.h | 4 +- src/frontend/qt_sdl/main.cpp | 6 +- 5 files changed, 151 insertions(+), 153 deletions(-) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 295140ce..f20feea7 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -42,11 +42,6 @@ const char SOLAR_SENSOR_GAMECODES[10][5] = "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) }; -std::unique_ptr Cart; - -u16 OpenBusDecay; - - CartCommon::CartCommon() { } @@ -77,7 +72,7 @@ int CartCommon::SetInput(int num, bool pressed) return -1; } -u16 CartCommon::ROMRead(u32 addr) +u16 CartCommon::ROMRead(u32 addr) const { return 0; } @@ -243,7 +238,7 @@ void CartGame::LoadSave(const u8* savedata, u32 savelen) Platform::WriteGBASave(savedata, len, 0, len); } -u16 CartGame::ROMRead(u32 addr) +u16 CartGame::ROMRead(u32 addr) const { addr &= 0x01FFFFFF; @@ -639,7 +634,7 @@ void CartRAMExpansion::DoSavestate(Savestate* file) file->Var16(&RAMEnable); } -u16 CartRAMExpansion::ROMRead(u32 addr) +u16 CartRAMExpansion::ROMRead(u32 addr) const { addr &= 0x01FFFFFF; @@ -696,25 +691,12 @@ void CartRAMExpansion::ROMWrite(u32 addr, u16 val) } } - -bool Init() -{ - Cart = nullptr; - - return true; -} - -void DeInit() -{ - Cart = nullptr; -} - -void Reset() +void GBACartSlot::Reset() noexcept { if (Cart) Cart->Reset(); } -void DoSavestate(Savestate* file) +void GBACartSlot::DoSavestate(Savestate* file) noexcept { file->Section("GBAC"); // Game Boy Advance Cartridge @@ -816,7 +798,7 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen) return cart; } -bool InsertROM(std::unique_ptr&& cart) +bool GBACartSlot::InsertROM(std::unique_ptr&& cart) noexcept { if (!cart) { Log(LogLevel::Error, "Failed to insert invalid GBA cart; existing cart (if any) was not ejected.\n"); @@ -844,14 +826,14 @@ bool InsertROM(std::unique_ptr&& cart) return true; } -bool LoadROM(const u8* romdata, u32 romlen) +bool GBACartSlot::LoadROM(const u8* romdata, u32 romlen) noexcept { std::unique_ptr data = ParseROM(romdata, romlen); return InsertROM(std::move(data)); } -void LoadSave(const u8* savedata, u32 savelen) +void GBACartSlot::LoadSave(const u8* savedata, u32 savelen) noexcept { if (Cart) { @@ -862,7 +844,7 @@ void LoadSave(const u8* savedata, u32 savelen) } } -void LoadAddon(int type) +void GBACartSlot::LoadAddon(int type) noexcept { switch (type) { @@ -876,13 +858,13 @@ void LoadAddon(int type) } } -void EjectCart() +void GBACartSlot::EjectCart() noexcept { Cart = nullptr; } -int SetInput(int num, bool pressed) +int GBACartSlot::SetInput(int num, bool pressed) noexcept { if (Cart) return Cart->SetInput(num, pressed); @@ -890,44 +872,28 @@ int SetInput(int num, bool pressed) } -void SetOpenBusDecay(u16 val) -{ - OpenBusDecay = val; -} - - -u16 ROMRead(u32 addr) +u16 GBACartSlot::ROMRead(u32 addr) const noexcept { if (Cart) return Cart->ROMRead(addr); return ((addr >> 1) & 0xFFFF) | OpenBusDecay; } -void ROMWrite(u32 addr, u16 val) +void GBACartSlot::ROMWrite(u32 addr, u16 val) noexcept { if (Cart) Cart->ROMWrite(addr, val); } -u8 SRAMRead(u32 addr) +u8 GBACartSlot::SRAMRead(u32 addr) noexcept { if (Cart) return Cart->SRAMRead(addr); return 0xFF; } -void SRAMWrite(u32 addr, u8 val) +void GBACartSlot::SRAMWrite(u32 addr, u8 val) noexcept { if (Cart) Cart->SRAMWrite(addr, val); } -u8* GetSaveMemory() -{ - return Cart ? Cart->GetSaveMemory() : nullptr; -} - -u32 GetSaveMemoryLength() -{ - return Cart ? Cart->GetSaveMemoryLength() : 0; -} - } diff --git a/src/GBACart.h b/src/GBACart.h index 32ff6d59..7447f6f4 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -53,7 +53,7 @@ public: virtual int SetInput(int num, bool pressed); - virtual u16 ROMRead(u32 addr); + virtual u16 ROMRead(u32 addr) const; virtual void ROMWrite(u32 addr, u16 val); virtual u8 SRAMRead(u32 addr); @@ -83,7 +83,7 @@ public: virtual void SetupSave(u32 type) override; virtual void LoadSave(const u8* savedata, u32 savelen) override; - virtual u16 ROMRead(u32 addr) override; + virtual u16 ROMRead(u32 addr) const override; virtual void ROMWrite(u32 addr, u16 val) override; virtual u8 SRAMRead(u32 addr) override; @@ -180,7 +180,7 @@ public: void DoSavestate(Savestate* file) override; - u16 ROMRead(u32 addr) override; + u16 ROMRead(u32 addr) const override; void ROMWrite(u32 addr, u16 val) override; private: @@ -195,13 +195,52 @@ enum Input_SolarSensorUp, }; -extern std::unique_ptr Cart; +class GBACartSlot +{ +public: + GBACartSlot() noexcept = default; + ~GBACartSlot() noexcept = default; + void Reset() noexcept; + void DoSavestate(Savestate* file) noexcept; + /// Applies the GBACartData to the emulator state and unloads an existing ROM if any. + /// Upon successful insertion, \c cart will be nullptr and the global GBACart state + /// (\c CartROM, CartInserted, etc.) will be updated. + bool InsertROM(std::unique_ptr&& cart) noexcept; + bool LoadROM(const u8* romdata, u32 romlen) noexcept; + void LoadSave(const u8* savedata, u32 savelen) noexcept; -bool Init(); -void DeInit(); -void Reset(); + void LoadAddon(int type) noexcept; -void DoSavestate(Savestate* file); + void EjectCart() noexcept; + + // TODO: make more flexible, support nonbinary inputs + int SetInput(int num, bool pressed) noexcept; + + void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; } + + u16 ROMRead(u32 addr) const noexcept; + void ROMWrite(u32 addr, u16 val) noexcept; + + u8 SRAMRead(u32 addr) noexcept; + void SRAMWrite(u32 addr, u8 val) noexcept; + + /// This function is intended to allow frontends to save and load SRAM + /// without using melonDS APIs. + /// Modifying the emulated SRAM for any other reason is strongly discouraged. + /// The returned pointer may be invalidated if the emulator is reset, + /// or when a new game is loaded. + /// Consequently, don't store the returned pointer for any longer than necessary. + /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. + [[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + + /// @returns The length of the buffer returned by ::GetSaveMemory() + /// if a cart is loaded and supports SRAM, otherwise zero. + [[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } +private: + std::unique_ptr Cart = nullptr; + u16 OpenBusDecay = 0; +}; /// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass /// that can be inserted into the emulator or used to extract information about the cart beforehand. @@ -213,45 +252,6 @@ void DoSavestate(Savestate* file); /// or \c nullptr if the ROM data couldn't be parsed. std::unique_ptr ParseROM(const u8* romdata, u32 romlen); -/// Applies the GBACartData to the emulator state and unloads an existing ROM if any. -/// Upon successful insertion, \c cart will be nullptr and the global GBACart state -/// (\c CartROM, CartInserted, etc.) will be updated. -bool InsertROM(std::unique_ptr&& cart); -bool LoadROM(const u8* romdata, u32 romlen); -void LoadSave(const u8* savedata, u32 savelen); - -void LoadAddon(int type); - -void EjectCart(); - -//bool LoadROM(const char* path, const char* sram); -//bool LoadROM(const u8* romdata, u32 filelength, const char *sram); -//void RelocateSave(const char* path, bool write); - -// TODO: make more flexible, support nonbinary inputs -int SetInput(int num, bool pressed); - -void SetOpenBusDecay(u16 val); - -u16 ROMRead(u32 addr); -void ROMWrite(u32 addr, u16 val); - -u8 SRAMRead(u32 addr); -void SRAMWrite(u32 addr, u8 val); - -/// This function is intended to allow frontends to save and load SRAM -/// without using melonDS APIs. -/// Modifying the emulated SRAM for any other reason is strongly discouraged. -/// The returned pointer may be invalidated if the emulator is reset, -/// or when a new game is loaded. -/// Consequently, don't store the returned pointer for any longer than necessary. -/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. -u8* GetSaveMemory(); - -/// @returns The length of the buffer returned by ::GetSaveMemory() -/// if a cart is loaded and supports SRAM, otherwise zero. -u32 GetSaveMemoryLength(); - } #endif // GBACART_H diff --git a/src/NDS.cpp b/src/NDS.cpp index 8f459a06..8eca7154 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -16,6 +16,7 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include #include #include #include @@ -182,7 +183,7 @@ class SPU* SPU; class SPIHost* SPI; class RTC* RTC; class Wifi* Wifi; - +std::unique_ptr GBACartSlot; class AREngine* AREngine; bool Running; @@ -226,9 +227,9 @@ bool Init() SPI = new class SPIHost(); RTC = new class RTC(); Wifi = new class Wifi(); + GBACartSlot = std::make_unique(); if (!NDSCart::Init()) return false; - if (!GBACart::Init()) return false; if (!GPU::Init()) return false; if (!DSi::Init()) return false; @@ -259,7 +260,7 @@ void DeInit() delete Wifi; Wifi = nullptr; NDSCart::DeInit(); - GBACart::DeInit(); + GBACartSlot = nullptr; GPU::DeInit(); DSi::DeInit(); @@ -644,7 +645,7 @@ void Reset() RCnt = 0; NDSCart::Reset(); - GBACart::Reset(); + GBACartSlot->Reset(); GPU::Reset(); SPU->Reset(); SPI->Reset(); @@ -846,7 +847,7 @@ bool DoSavestate(Savestate* file) NDSCart::DoSavestate(file); if (ConsoleType == 0) - GBACart::DoSavestate(file); + GBACartSlot->DoSavestate(file); GPU::DoSavestate(file); SPU->DoSavestate(file); SPI->DoSavestate(file); @@ -911,23 +912,28 @@ bool CartInserted() bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) { - if (!GBACart::LoadROM(romdata, romlen)) + if (!GBACartSlot) + return false; + + if (!GBACartSlot->LoadROM(romdata, romlen)) return false; if (savedata && savelen) - GBACart::LoadSave(savedata, savelen); + GBACartSlot->LoadSave(savedata, savelen); return true; } void LoadGBAAddon(int type) { - GBACart::LoadAddon(type); + if (GBACartSlot) + GBACartSlot->LoadAddon(type); } void EjectGBACart() { - GBACart::EjectCart(); + if (GBACartSlot) + GBACartSlot->EjectCart(); } void LoadBIOS() @@ -1493,7 +1499,8 @@ void SetGBASlotTimings() // for example, the Cartridge Construction Kit relies on this to determine that // the GBA slot is empty - GBACart::SetOpenBusDecay(openbus[(curcnt>>2) & 0x3]); + assert(GBACartSlot != nullptr); + GBACartSlot->SetOpenBusDecay(openbus[(curcnt>>2) & 0x3]); } @@ -2161,12 +2168,14 @@ u8 ARM9Read8(u32 addr) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled - if (addr & 0x1) return GBACart::ROMRead(addr-1) >> 8; - return GBACart::ROMRead(addr) & 0xFF; + assert(GBACartSlot != nullptr); + if (addr & 0x1) return GBACartSlot->ROMRead(addr-1) >> 8; + return GBACartSlot->ROMRead(addr) & 0xFF; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr); + assert(GBACartSlot != nullptr); + return GBACartSlot->SRAMRead(addr); } Log(LogLevel::Debug, "unknown arm9 read8 %08X\n", addr); @@ -2221,12 +2230,14 @@ u16 ARM9Read16(u32 addr) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled - return GBACart::ROMRead(addr); + assert(GBACartSlot != nullptr); + return GBACartSlot->ROMRead(addr); case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr) | - (GBACart::SRAMRead(addr+1) << 8); + assert(GBACartSlot != nullptr); + return GBACartSlot->SRAMRead(addr) | + (GBACartSlot->SRAMRead(addr+1) << 8); } //if (addr) Log(LogLevel::Warn, "unknown arm9 read16 %08X %08X\n", addr, ARM9->R[15]); @@ -2281,15 +2292,17 @@ u32 ARM9Read32(u32 addr) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled - return GBACart::ROMRead(addr) | - (GBACart::ROMRead(addr+2) << 16); + assert(GBACartSlot != nullptr); + return GBACartSlot->ROMRead(addr) | + (GBACartSlot->ROMRead(addr+2) << 16); case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr) | - (GBACart::SRAMRead(addr+1) << 8) | - (GBACart::SRAMRead(addr+2) << 16) | - (GBACart::SRAMRead(addr+3) << 24); + assert(GBACartSlot != nullptr); + return GBACartSlot->SRAMRead(addr) | + (GBACartSlot->SRAMRead(addr+1) << 8) | + (GBACartSlot->SRAMRead(addr+2) << 16) | + (GBACartSlot->SRAMRead(addr+3) << 24); } //Log(LogLevel::Warn, "unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9->R[15], ARM9->R[12]); @@ -2332,7 +2345,8 @@ void ARM9Write8(u32 addr, u8 val) case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val); + assert(GBACartSlot != nullptr); + GBACartSlot->SRAMWrite(addr, val); return; } @@ -2392,13 +2406,15 @@ void ARM9Write16(u32 addr, u16 val) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::ROMWrite(addr, val); + assert(GBACartSlot != nullptr); + GBACartSlot->ROMWrite(addr, val); return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val & 0xFF); - GBACart::SRAMWrite(addr+1, val >> 8); + assert(GBACartSlot != nullptr); + GBACartSlot->SRAMWrite(addr, val & 0xFF); + GBACartSlot->SRAMWrite(addr+1, val >> 8); return; } @@ -2458,16 +2474,18 @@ void ARM9Write32(u32 addr, u32 val) case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::ROMWrite(addr, val & 0xFFFF); - GBACart::ROMWrite(addr+2, val >> 16); + assert(GBACartSlot != nullptr); + GBACartSlot->ROMWrite(addr, val & 0xFFFF); + GBACartSlot->ROMWrite(addr+2, val >> 16); return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val & 0xFF); - GBACart::SRAMWrite(addr+1, (val >> 8) & 0xFF); - GBACart::SRAMWrite(addr+2, (val >> 16) & 0xFF); - GBACart::SRAMWrite(addr+3, val >> 24); + assert(GBACartSlot != nullptr); + GBACartSlot->SRAMWrite(addr, val & 0xFF); + GBACartSlot->SRAMWrite(addr+1, (val >> 8) & 0xFF); + GBACartSlot->SRAMWrite(addr+2, (val >> 16) & 0xFF); + GBACartSlot->SRAMWrite(addr+3, val >> 24); return; } @@ -2559,13 +2577,15 @@ u8 ARM7Read8(u32 addr) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled - if (addr & 0x1) return GBACart::ROMRead(addr-1) >> 8; - return GBACart::ROMRead(addr) & 0xFF; + assert(GBACartSlot != nullptr); + if (addr & 0x1) return GBACartSlot->ROMRead(addr-1) >> 8; + return GBACartSlot->ROMRead(addr) & 0xFF; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr); + assert(GBACartSlot != nullptr); + return GBACartSlot->SRAMRead(addr); } Log(LogLevel::Debug, "unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7->R[15], ARM7->R[0], ARM7->R[1]); @@ -2625,13 +2645,15 @@ u16 ARM7Read16(u32 addr) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled - return GBACart::ROMRead(addr); + assert(GBACartSlot != nullptr); + return GBACartSlot->ROMRead(addr); case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr) | - (GBACart::SRAMRead(addr+1) << 8); + assert(GBACartSlot != nullptr); + return GBACartSlot->SRAMRead(addr) | + (GBACartSlot->SRAMRead(addr+1) << 8); } Log(LogLevel::Debug, "unknown arm7 read16 %08X %08X\n", addr, ARM7->R[15]); @@ -2691,16 +2713,18 @@ u32 ARM7Read32(u32 addr) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled - return GBACart::ROMRead(addr) | - (GBACart::ROMRead(addr+2) << 16); + assert(GBACartSlot != nullptr); + return GBACartSlot->ROMRead(addr) | + (GBACartSlot->ROMRead(addr+2) << 16); case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled - return GBACart::SRAMRead(addr) | - (GBACart::SRAMRead(addr+1) << 8) | - (GBACart::SRAMRead(addr+2) << 16) | - (GBACart::SRAMRead(addr+3) << 24); + assert(GBACartSlot != nullptr); + return GBACartSlot->SRAMRead(addr) | + (GBACartSlot->SRAMRead(addr+1) << 8) | + (GBACartSlot->SRAMRead(addr+2) << 16) | + (GBACartSlot->SRAMRead(addr+3) << 24); } //Log(LogLevel::Warn, "unknown arm7 read32 %08X | %08X\n", addr, ARM7->R[15]); @@ -2765,7 +2789,8 @@ void ARM7Write8(u32 addr, u8 val) case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val); + assert(GBACartSlot != nullptr); + GBACartSlot->SRAMWrite(addr, val); return; } @@ -2839,14 +2864,15 @@ void ARM7Write16(u32 addr, u16 val) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::ROMWrite(addr, val); + assert(GBACartSlot != nullptr); + GBACartSlot->ROMWrite(addr, val); return; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val & 0xFF); - GBACart::SRAMWrite(addr+1, val >> 8); + GBACartSlot->SRAMWrite(addr, val & 0xFF); + GBACartSlot->SRAMWrite(addr+1, val >> 8); return; } @@ -2920,17 +2946,19 @@ void ARM7Write32(u32 addr, u32 val) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::ROMWrite(addr, val & 0xFFFF); - GBACart::ROMWrite(addr+2, val >> 16); + assert(GBACartSlot != nullptr); + GBACartSlot->ROMWrite(addr, val & 0xFFFF); + GBACartSlot->ROMWrite(addr+2, val >> 16); return; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACart::SRAMWrite(addr, val & 0xFF); - GBACart::SRAMWrite(addr+1, (val >> 8) & 0xFF); - GBACart::SRAMWrite(addr+2, (val >> 16) & 0xFF); - GBACart::SRAMWrite(addr+3, val >> 24); + assert(GBACartSlot != nullptr); + GBACartSlot->SRAMWrite(addr, val & 0xFF); + GBACartSlot->SRAMWrite(addr+1, (val >> 8) & 0xFF); + GBACartSlot->SRAMWrite(addr+2, (val >> 16) & 0xFF); + GBACartSlot->SRAMWrite(addr+3, val >> 24); return; } diff --git a/src/NDS.h b/src/NDS.h index 660df8d4..18054370 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -20,11 +20,13 @@ #define NDS_H #include +#include #include #include "Platform.h" #include "Savestate.h" #include "types.h" +#include "GBACart.h" // when touching the main loop/timing code, pls test a lot of shit // with this enabled, to make sure it doesn't desync @@ -257,7 +259,7 @@ extern class SPU* SPU; extern class SPIHost* SPI; extern class RTC* RTC; extern class Wifi* Wifi; - +extern std::unique_ptr GBACartSlot; extern class AREngine* AREngine; const u32 ARM7WRAMSize = 0x10000; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index f033e668..a4e56d44 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -384,7 +384,8 @@ void EmuThread::run() if (Input::HotkeyPressed(HK_SolarSensorDecrease)) { - int level = GBACart::SetInput(GBACart::Input_SolarSensorDown, true); + assert(NDS::GBACartSlot != nullptr); + int level = NDS::GBACartSlot->SetInput(GBACart::Input_SolarSensorDown, true); if (level != -1) { char msg[64]; @@ -394,7 +395,8 @@ void EmuThread::run() } if (Input::HotkeyPressed(HK_SolarSensorIncrease)) { - int level = GBACart::SetInput(GBACart::Input_SolarSensorUp, true); + assert(NDS::GBACartSlot != nullptr); + int level = NDS::GBACartSlot->SetInput(GBACart::Input_SolarSensorUp, true); if (level != -1) { char msg[64]; From 3d3e4240a05167c716437488be8cbb1dba4e8ec5 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 8 Nov 2023 16:21:30 -0500 Subject: [PATCH 037/157] Make AREngine::RunCheat public (#1872) - I use it directly in melonDS DS to apply single cheats without using ARCodeFile - Before the AREngine refactor I could just redeclare the function in my code - Now I can't --- src/AREngine.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/AREngine.h b/src/AREngine.h index cd6d4a97..8557eb77 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -32,7 +32,7 @@ public: void SetCodeFile(ARCodeFile* file) { CodeFile = file; } void RunCheats(); - + void RunCheat(ARCode& arcode); private: ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this @@ -43,8 +43,6 @@ private: void (*BusWrite8)(u32 addr, u8 val); void (*BusWrite16)(u32 addr, u16 val); void (*BusWrite32)(u32 addr, u32 val); - - void RunCheat(ARCode& arcode); }; #endif // ARENGINE_H From 88072a02c523e26390af6bd726608b3e567f996f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 9 Nov 2023 12:57:16 -0500 Subject: [PATCH 038/157] Move NDSCart-related global state into objects (#1871) * Move NDSCart-related global state into objects - RAII will now do the heavy lifting - Mark some methods as const or noexcept * Move GBACart-related global state into objects (#1870) - RAII will now do the heavy lifting - Mark some methods as const or noexcept - Once the `NDS` object is finalized, most of these `assert`s can go away * Make AREngine::RunCheat public (#1872) - I use it directly in melonDS DS to apply single cheats without using ARCodeFile - Before the AREngine refactor I could just redeclare the function in my code - Now I can't --- src/ARMJIT_Memory.cpp | 7 +- src/DSi.cpp | 16 +- src/NDS.cpp | 288 +++++++++++++------------- src/NDS.h | 2 + src/NDSCart.cpp | 151 +++++--------- src/NDSCart.h | 178 +++++++++------- src/frontend/qt_sdl/ROMInfoDialog.cpp | 4 +- 7 files changed, 324 insertions(+), 322 deletions(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index b1673abc..095fb301 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1248,6 +1248,11 @@ T VRAMRead(u32 addr) } } +u32 NDSCartSlot_ReadROMData() +{ // TODO: Add a NDS* parameter, when NDS* is eventually implemented + return NDS::NDSCartSlot->ReadROMData(); +} + void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) { if (cpu->Num == 0) @@ -1256,7 +1261,7 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) { case 0x04000000: if (!store && size == 32 && addr == 0x04100010 && NDS::ExMemCnt[0] & (1<<11)) - return (void*)NDSCart::ReadROMData; + return (void*)NDSCartSlot_ReadROMData; /* unfortunately we can't map GPU2D this way diff --git a/src/DSi.cpp b/src/DSi.cpp index 8ea26c5f..bf7748e2 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -193,7 +193,7 @@ void Reset() SCFG_Clock7 = 0x0187; SCFG_EXT[0] = 0x8307F100; SCFG_EXT[1] = 0x93FFFB06; - SCFG_MC = 0x0010 | (~((u32)(NDSCart::Cart != nullptr))&1);//0x0011; + SCFG_MC = 0x0010 | (~((u32)(NDS::NDSCartSlot->GetCart() != nullptr))&1);//0x0011; SCFG_RST = 0; DSP->SetRstLine(false); @@ -310,13 +310,13 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv) if ((offset == 0) || (size == 0)) return; - const NDSHeader& header = NDSCart::Cart->GetHeader(); + const NDSHeader& header = NDS::NDSCartSlot->GetCart()->GetHeader(); if ((header.DSiCryptoFlags & (1<<4)) || (header.AppFlags & (1<<7))) { // dev key - const u8* cartrom = NDSCart::Cart->GetROM(); + const u8* cartrom = NDS::NDSCartSlot->GetCart()->GetROM(); memcpy(key, &cartrom[0], 16); } else @@ -403,9 +403,9 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv) void SetupDirectBoot() { bool dsmode = false; - NDSHeader& header = NDSCart::Cart->GetHeader(); - const u8* cartrom = NDSCart::Cart->GetROM(); - u32 cartid = NDSCart::Cart->ID(); + NDSHeader& header = NDS::NDSCartSlot->GetCart()->GetHeader(); + const u8* cartrom = NDS::NDSCartSlot->GetCart()->GetROM(); + u32 cartid = NDS::NDSCartSlot->GetCart()->ID(); DSi_TSC* tsc = (DSi_TSC*)NDS::SPI->GetTSC(); // TODO: add controls for forcing DS or DSi mode? @@ -594,7 +594,7 @@ void SetupDirectBoot() if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000) { u8 securearea[0x800]; - NDSCart::DecryptSecureArea(securearea); + NDS::NDSCartSlot->DecryptSecureArea(securearea); for (u32 i = 0; i < 0x800; i+=4) { @@ -1289,7 +1289,7 @@ void Set_SCFG_MC(u32 val) if ((oldslotstatus == 0x0) && ((SCFG_MC & 0xC) == 0x4)) { - NDSCart::ResetCart(); + NDS::NDSCartSlot->ResetCart(); } } diff --git a/src/NDS.cpp b/src/NDS.cpp index 8eca7154..79359290 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -183,6 +183,7 @@ class SPU* SPU; class SPIHost* SPI; class RTC* RTC; class Wifi* Wifi; +std::unique_ptr NDSCartSlot; std::unique_ptr GBACartSlot; class AREngine* AREngine; @@ -227,9 +228,8 @@ bool Init() SPI = new class SPIHost(); RTC = new class RTC(); Wifi = new class Wifi(); + NDSCartSlot = std::make_unique(); GBACartSlot = std::make_unique(); - - if (!NDSCart::Init()) return false; if (!GPU::Init()) return false; if (!DSi::Init()) return false; @@ -259,7 +259,7 @@ void DeInit() delete RTC; RTC = nullptr; delete Wifi; Wifi = nullptr; - NDSCart::DeInit(); + NDSCartSlot = nullptr; GBACartSlot = nullptr; GPU::DeInit(); @@ -405,7 +405,7 @@ bool NeedsDirectBoot() void SetupDirectBoot(const std::string& romname) { - const NDSHeader& header = NDSCart::Cart->GetHeader(); + const NDSHeader& header = NDSCartSlot->GetCart()->GetHeader(); if (ConsoleType == 1) { @@ -413,8 +413,8 @@ void SetupDirectBoot(const std::string& romname) } else { - u32 cartid = NDSCart::Cart->ID(); - const u8* cartrom = NDSCart::Cart->GetROM(); + u32 cartid = NDSCartSlot->GetCart()->ID(); + const u8* cartrom = NDSCartSlot->GetCart()->GetROM(); MapSharedWRAM(3); // setup main RAM data @@ -447,7 +447,7 @@ void SetupDirectBoot(const std::string& romname) if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000) { u8 securearea[0x800]; - NDSCart::DecryptSecureArea(securearea); + NDSCartSlot->DecryptSecureArea(securearea); for (u32 i = 0; i < 0x800; i+=4) { @@ -500,7 +500,7 @@ void SetupDirectBoot(const std::string& romname) ARM9->CP15Write(0x911, 0x00000020); } - NDSCart::SetupDirectBoot(romname); + NDSCartSlot->SetupDirectBoot(romname); ARM9->R[12] = header.ARM9EntryAddress; ARM9->R[13] = 0x03002F7C; @@ -526,7 +526,7 @@ void SetupDirectBoot(const std::string& romname) // checkme RCnt = 0x8000; - NDSCart::SPICnt = 0x8000; + NDSCartSlot->SetSPICnt(0x8000); SPU->SetBias(0x200); @@ -644,7 +644,7 @@ void Reset() KeyCnt[1] = 0; RCnt = 0; - NDSCart::Reset(); + NDSCartSlot->Reset(); GBACartSlot->Reset(); GPU::Reset(); SPU->Reset(); @@ -845,7 +845,7 @@ bool DoSavestate(Savestate* file) ARM9->DoSavestate(file); ARM7->DoSavestate(file); - NDSCart::DoSavestate(file); + NDSCartSlot->DoSavestate(file); if (ConsoleType == 0) GBACartSlot->DoSavestate(file); GPU::DoSavestate(file); @@ -885,11 +885,11 @@ void SetConsoleType(int type) bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) { - if (!NDSCart::LoadROM(romdata, romlen)) + if (!NDSCartSlot->LoadROM(romdata, romlen)) return false; if (savedata && savelen) - NDSCart::LoadSave(savedata, savelen); + NDSCartSlot->LoadSave(savedata, savelen); return true; } @@ -897,17 +897,17 @@ bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) void LoadSave(const u8* savedata, u32 savelen) { if (savedata && savelen) - NDSCart::LoadSave(savedata, savelen); + NDSCartSlot->LoadSave(savedata, savelen); } void EjectCart() { - NDSCart::EjectCart(); + NDSCartSlot->EjectCart(); } bool CartInserted() { - return NDSCart::Cart != nullptr; + return NDSCartSlot->GetCart() != nullptr; } bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) @@ -1741,9 +1741,9 @@ void MonitorARM9Jump(u32 addr) // checkme: can the entrypoint addr be THUMB? // also TODO: make it work in DSi mode - if ((!RunningGame) && NDSCart::Cart) + if ((!RunningGame) && NDSCartSlot->GetCart()) { - const NDSHeader& header = NDSCart::Cart->GetHeader(); + const NDSHeader& header = NDSCartSlot->GetCart()->GetHeader(); if (addr == header.ARM9EntryAddress) { Log(LogLevel::Info, "Game is now booting\n"); @@ -3035,40 +3035,40 @@ u8 ARM9IORead8(u32 addr) case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ReadSPIData(); + return NDSCartSlot->ReadSPIData(); return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[0]; + return NDSCartSlot->GetROMCommand(0); return 0; case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[1]; + return NDSCartSlot->GetROMCommand(1); return 0; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[2]; + return NDSCartSlot->GetROMCommand(2); return 0; case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[3]; + return NDSCartSlot->GetROMCommand(3); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[4]; + return NDSCartSlot->GetROMCommand(4); return 0; case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[5]; + return NDSCartSlot->GetROMCommand(5); return 0; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[6]; + return NDSCartSlot->GetROMCommand(6); return 0; case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[7]; + return NDSCartSlot->GetROMCommand(7); return 0; case 0x04000208: return IME[0]; @@ -3182,32 +3182,32 @@ u16 ARM9IORead16(u32 addr) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::SPICnt; + return NDSCartSlot->GetSPICnt(); return 0; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ReadSPIData(); + return NDSCartSlot->ReadSPIData(); return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8); + return NDSCartSlot->GetROMCommand(0) | + (NDSCartSlot->GetROMCommand(1) << 8); return 0; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[2] | - (NDSCart::ROMCommand[3] << 8); + return NDSCartSlot->GetROMCommand(2) | + (NDSCartSlot->GetROMCommand(3) << 8); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8); + return NDSCartSlot->GetROMCommand(4) | + (NDSCartSlot->GetROMCommand(5) << 8); return 0; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[6] | - (NDSCart::ROMCommand[7] << 8); + return NDSCartSlot->GetROMCommand(6) | + (NDSCartSlot->GetROMCommand(7) << 8); return 0; case 0x04000204: return ExMemCnt[0]; @@ -3316,26 +3316,26 @@ u32 ARM9IORead32(u32 addr) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16); + return NDSCartSlot->GetSPICnt() | (NDSCartSlot->ReadSPIData() << 16); return 0; case 0x040001A4: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCnt; + return NDSCartSlot->GetROMCnt(); return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8) | - (NDSCart::ROMCommand[2] << 16) | - (NDSCart::ROMCommand[3] << 24); + return NDSCartSlot->GetROMCommand(0) | + (NDSCartSlot->GetROMCommand(1) << 8) | + (NDSCartSlot->GetROMCommand(2) << 16) | + (NDSCartSlot->GetROMCommand(3) << 24); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8) | - (NDSCart::ROMCommand[6] << 16) | - (NDSCart::ROMCommand[7] << 24); + return NDSCartSlot->GetROMCommand(4) | + (NDSCartSlot->GetROMCommand(5) << 8) | + (NDSCartSlot->GetROMCommand(6) << 16) | + (NDSCartSlot->GetROMCommand(7) << 24); return 0; case 0x04000208: return IME[0]; @@ -3386,7 +3386,7 @@ u32 ARM9IORead32(u32 addr) return IPCFIFO7.Peek(); case 0x04100010: - if (!(ExMemCnt[0] & (1<<11))) return NDSCart::ReadROMData(); + if (!(ExMemCnt[0] & (1<<11))) return NDSCartSlot->ReadROMData(); return 0; case 0x04004000: @@ -3441,25 +3441,25 @@ void ARM9IOWrite8(u32 addr, u8 val) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0xFF00) | val); + NDSCartSlot->WriteSPICnt((NDSCartSlot->GetSPICnt() & 0xFF00) | val); return; case 0x040001A1: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0x00FF) | (val << 8)); + NDSCartSlot->WriteSPICnt((NDSCartSlot->GetSPICnt() & 0x00FF) | (val << 8)); return; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPIData(val); + NDSCartSlot->WriteSPIData(val); return; - case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[0] = val; return; - case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[1] = val; return; - case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[2] = val; return; - case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[3] = val; return; - case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[4] = val; return; - case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[5] = val; return; - case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[6] = val; return; - case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) NDSCart::ROMCommand[7] = val; return; + case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(0, val); return; + case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(1, val); return; + case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(2, val); return; + case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(3, val); return; + case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(4, val); return; + case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(5, val); return; + case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(6, val); return; + case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(7, val); return; case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return; @@ -3574,39 +3574,39 @@ void ARM9IOWrite16(u32 addr, u16 val) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPICnt(val); + NDSCartSlot->WriteSPICnt(val); return; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteSPIData(val & 0xFF); + NDSCartSlot->WriteSPIData(val & 0xFF); return; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = val >> 8; + NDSCartSlot->SetROMCommand(0, val & 0xFF); + NDSCartSlot->SetROMCommand(1, val >> 8); } return; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[2] = val & 0xFF; - NDSCart::ROMCommand[3] = val >> 8; + NDSCartSlot->SetROMCommand(2, val & 0xFF); + NDSCartSlot->SetROMCommand(3, val >> 8); } return; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = val >> 8; + NDSCartSlot->SetROMCommand(4, val & 0xFF); + NDSCartSlot->SetROMCommand(5, val >> 8); } return; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[6] = val & 0xFF; - NDSCart::ROMCommand[7] = val >> 8; + NDSCartSlot->SetROMCommand(6, val & 0xFF); + NDSCartSlot->SetROMCommand(7, val >> 8); } return; @@ -3760,31 +3760,31 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::WriteSPICnt(val & 0xFFFF); - NDSCart::WriteSPIData((val >> 16) & 0xFF); + NDSCartSlot->WriteSPICnt(val & 0xFFFF); + NDSCartSlot->WriteSPIData((val >> 16) & 0xFF); } return; case 0x040001A4: if (!(ExMemCnt[0] & (1<<11))) - NDSCart::WriteROMCnt(val); + NDSCartSlot->WriteROMCnt(val); return; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[2] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[3] = val >> 24; + NDSCartSlot->SetROMCommand(0, val & 0xFF); + NDSCartSlot->SetROMCommand(1, (val >> 8) & 0xFF); + NDSCartSlot->SetROMCommand(2, (val >> 16) & 0xFF); + NDSCartSlot->SetROMCommand(3, val >> 24); } return; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) { - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[6] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[7] = val >> 24; + NDSCartSlot->SetROMCommand(4, val & 0xFF); + NDSCartSlot->SetROMCommand(5, (val >> 8) & 0xFF); + NDSCartSlot->SetROMCommand(6, (val >> 16) & 0xFF); + NDSCartSlot->SetROMCommand(7, val >> 24); } return; @@ -3830,7 +3830,7 @@ void ARM9IOWrite32(u32 addr, u32 val) return; case 0x04100010: - if (!(ExMemCnt[0] & (1<<11))) NDSCart::WriteROMData(val); + if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->WriteROMData(val); return; // NO$GBA debug register "String Out (raw)" @@ -3899,40 +3899,40 @@ u8 ARM7IORead8(u32 addr) case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ReadSPIData(); + return NDSCartSlot->ReadSPIData(); return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[0]; + return NDSCartSlot->GetROMCommand(0); return 0; case 0x040001A9: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[1]; + return NDSCartSlot->GetROMCommand(1); return 0; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[2]; + return NDSCartSlot->GetROMCommand(2); return 0; case 0x040001AB: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[3]; + return NDSCartSlot->GetROMCommand(3); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[4]; + return NDSCartSlot->GetROMCommand(4); return 0; case 0x040001AD: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[5]; + return NDSCartSlot->GetROMCommand(5); return 0; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[6]; + return NDSCartSlot->GetROMCommand(6); return 0; case 0x040001AF: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[7]; + return NDSCartSlot->GetROMCommand(7); return 0; case 0x040001C2: return SPI->ReadData(); @@ -3999,28 +3999,28 @@ u16 ARM7IORead16(u32 addr) return val; } - case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCart::SPICnt; return 0; - case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCart::ReadSPIData(); return 0; + case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot->GetSPICnt(); return 0; + case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot->ReadSPIData(); return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8); + return NDSCartSlot->GetROMCommand(0) | + (NDSCartSlot->GetROMCommand(1) << 8); return 0; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[2] | - (NDSCart::ROMCommand[3] << 8); + return NDSCartSlot->GetROMCommand(2) | + (NDSCartSlot->GetROMCommand(3) << 8); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8); + return NDSCartSlot->GetROMCommand(4) | + (NDSCartSlot->GetROMCommand(5) << 8); return 0; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[6] | - (NDSCart::ROMCommand[7] << 8); + return NDSCartSlot->GetROMCommand(6) | + (NDSCartSlot->GetROMCommand(7) << 8); return 0; case 0x040001C0: return SPI->ReadCnt(); @@ -4083,26 +4083,26 @@ u32 ARM7IORead32(u32 addr) case 0x040001A0: if (ExMemCnt[0] & (1<<11)) - return NDSCart::SPICnt | (NDSCart::ReadSPIData() << 16); + return NDSCartSlot->GetSPICnt() | (NDSCartSlot->ReadSPIData() << 16); return 0; case 0x040001A4: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCnt; + return NDSCartSlot->GetROMCnt(); return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[0] | - (NDSCart::ROMCommand[1] << 8) | - (NDSCart::ROMCommand[2] << 16) | - (NDSCart::ROMCommand[3] << 24); + return NDSCartSlot->GetROMCommand(0) | + (NDSCartSlot->GetROMCommand(1) << 8) | + (NDSCartSlot->GetROMCommand(2) << 16) | + (NDSCartSlot->GetROMCommand(3) << 24); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCart::ROMCommand[4] | - (NDSCart::ROMCommand[5] << 8) | - (NDSCart::ROMCommand[6] << 16) | - (NDSCart::ROMCommand[7] << 24); + return NDSCartSlot->GetROMCommand(4) | + (NDSCartSlot->GetROMCommand(5) << 8) | + (NDSCartSlot->GetROMCommand(6) << 16) | + (NDSCartSlot->GetROMCommand(7) << 24); return 0; case 0x040001C0: @@ -4137,7 +4137,7 @@ u32 ARM7IORead32(u32 addr) return IPCFIFO9.Peek(); case 0x04100010: - if (ExMemCnt[0] & (1<<11)) return NDSCart::ReadROMData(); + if (ExMemCnt[0] & (1<<11)) return NDSCartSlot->ReadROMData(); return 0; } @@ -4177,26 +4177,26 @@ void ARM7IOWrite8(u32 addr, u8 val) case 0x040001A0: if (ExMemCnt[0] & (1<<11)) { - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0xFF00) | val); + NDSCartSlot->WriteSPICnt((NDSCartSlot->GetSPICnt() & 0xFF00) | val); } return; case 0x040001A1: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteSPICnt((NDSCart::SPICnt & 0x00FF) | (val << 8)); + NDSCartSlot->WriteSPICnt((NDSCartSlot->GetSPICnt() & 0x00FF) | (val << 8)); return; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteSPIData(val); + NDSCartSlot->WriteSPIData(val); return; - case 0x040001A8: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[0] = val; return; - case 0x040001A9: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[1] = val; return; - case 0x040001AA: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[2] = val; return; - case 0x040001AB: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[3] = val; return; - case 0x040001AC: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[4] = val; return; - case 0x040001AD: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[5] = val; return; - case 0x040001AE: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[6] = val; return; - case 0x040001AF: if (ExMemCnt[0] & (1<<11)) NDSCart::ROMCommand[7] = val; return; + case 0x040001A8: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(0, val); return; + case 0x040001A9: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(1, val); return; + case 0x040001AA: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(2, val); return; + case 0x040001AB: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(3, val); return; + case 0x040001AC: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(4, val); return; + case 0x040001AD: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(5, val); return; + case 0x040001AE: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(6, val); return; + case 0x040001AF: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(7, val); return; case 0x040001C2: SPI->WriteData(val); @@ -4287,39 +4287,39 @@ void ARM7IOWrite16(u32 addr, u16 val) case 0x040001A0: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteSPICnt(val); + NDSCartSlot->WriteSPICnt(val); return; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteSPIData(val & 0xFF); + NDSCartSlot->WriteSPIData(val & 0xFF); return; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = val >> 8; + NDSCartSlot->SetROMCommand(0, val & 0xFF); + NDSCartSlot->SetROMCommand(1, val >> 8); } return; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[2] = val & 0xFF; - NDSCart::ROMCommand[3] = val >> 8; + NDSCartSlot->SetROMCommand(2, val & 0xFF); + NDSCartSlot->SetROMCommand(3, val >> 8); } return; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = val >> 8; + NDSCartSlot->SetROMCommand(4, val & 0xFF); + NDSCartSlot->SetROMCommand(5, val >> 8); } return; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[6] = val & 0xFF; - NDSCart::ROMCommand[7] = val >> 8; + NDSCartSlot->SetROMCommand(6, val & 0xFF); + NDSCartSlot->SetROMCommand(7, val >> 8); } return; @@ -4448,31 +4448,31 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x040001A0: if (ExMemCnt[0] & (1<<11)) { - NDSCart::WriteSPICnt(val & 0xFFFF); - NDSCart::WriteSPIData((val >> 16) & 0xFF); + NDSCartSlot->WriteSPICnt(val & 0xFFFF); + NDSCartSlot->WriteSPIData((val >> 16) & 0xFF); } return; case 0x040001A4: if (ExMemCnt[0] & (1<<11)) - NDSCart::WriteROMCnt(val); + NDSCartSlot->WriteROMCnt(val); return; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[0] = val & 0xFF; - NDSCart::ROMCommand[1] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[2] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[3] = val >> 24; + NDSCartSlot->SetROMCommand(0, val & 0xFF); + NDSCartSlot->SetROMCommand(1, (val >> 8) & 0xFF); + NDSCartSlot->SetROMCommand(2, (val >> 16) & 0xFF); + NDSCartSlot->SetROMCommand(3, val >> 24); } return; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) { - NDSCart::ROMCommand[4] = val & 0xFF; - NDSCart::ROMCommand[5] = (val >> 8) & 0xFF; - NDSCart::ROMCommand[6] = (val >> 16) & 0xFF; - NDSCart::ROMCommand[7] = val >> 24; + NDSCartSlot->SetROMCommand(4, val & 0xFF); + NDSCartSlot->SetROMCommand(5, (val >> 8) & 0xFF); + NDSCartSlot->SetROMCommand(6, (val >> 16) & 0xFF); + NDSCartSlot->SetROMCommand(7, val >> 24); } return; @@ -4504,7 +4504,7 @@ void ARM7IOWrite32(u32 addr, u32 val) return; case 0x04100010: - if (ExMemCnt[0] & (1<<11)) NDSCart::WriteROMData(val); + if (ExMemCnt[0] & (1<<11)) NDSCartSlot->WriteROMData(val); return; } diff --git a/src/NDS.h b/src/NDS.h index 18054370..127510dd 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -26,6 +26,7 @@ #include "Platform.h" #include "Savestate.h" #include "types.h" +#include "NDSCart.h" #include "GBACart.h" // when touching the main loop/timing code, pls test a lot of shit @@ -259,6 +260,7 @@ extern class SPU* SPU; extern class SPIHost* SPI; extern class RTC* RTC; extern class Wifi* Wifi; +extern std::unique_ptr NDSCartSlot; extern std::unique_ptr GBACartSlot; extern class AREngine* AREngine; diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 4c6efddf..bf2b7913 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -41,36 +41,12 @@ enum // SRAM TODO: emulate write delays??? -u16 SPICnt; -u32 ROMCnt; - -u8 SPIData; -u32 SPIDataPos; -bool SPIHold; - -u8 ROMCommand[8]; -u32 ROMData; - -u8 TransferData[0x4000]; -u32 TransferPos; -u32 TransferLen; -u32 TransferDir; -u8 TransferCmd[8]; - -std::unique_ptr Cart; - -u32 Key1_KeyBuf[0x412]; - -u64 Key2_X; -u64 Key2_Y; - - u32 ByteSwap(u32 val) { return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); } -void Key1_Encrypt(u32* data) +void NDSCartSlot::Key1_Encrypt(u32* data) noexcept { u32 y = data[0]; u32 x = data[1]; @@ -91,7 +67,7 @@ void Key1_Encrypt(u32* data) data[1] = y ^ Key1_KeyBuf[0x11]; } -void Key1_Decrypt(u32* data) +void NDSCartSlot::Key1_Decrypt(u32* data) noexcept { u32 y = data[0]; u32 x = data[1]; @@ -112,7 +88,7 @@ void Key1_Decrypt(u32* data) data[1] = y ^ Key1_KeyBuf[0x0]; } -void Key1_ApplyKeycode(u32* keycode, u32 mod) +void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept { Key1_Encrypt(&keycode[1]); Key1_Encrypt(&keycode[0]); @@ -131,7 +107,7 @@ void Key1_ApplyKeycode(u32* keycode, u32 mod) } } -void Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) +void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) noexcept { if (externalBios) { @@ -146,19 +122,19 @@ void Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) } else { - memcpy(Key1_KeyBuf, bios + (dsi ? 0xC6D0 : 0x0030), 0x1048); + memcpy(Key1_KeyBuf.data(), bios + (dsi ? 0xC6D0 : 0x0030), sizeof(Key1_KeyBuf)); Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf from memory\n"); } } else { // well - memset(Key1_KeyBuf, 0, 0x1048); + memset(Key1_KeyBuf.data(), 0, sizeof(Key1_KeyBuf)); Platform::Log(LogLevel::Debug, "NDSCart: Initialized Key1_KeyBuf to zero\n"); } } -void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) +void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) noexcept { Key1_LoadKeyBuf(dsi, Platform::GetConfigBool(Platform::ExternalBIOSEnable), bios, biosLength); @@ -174,7 +150,7 @@ void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 bi } -void Key2_Encrypt(u8* data, u32 len) +void NDSCartSlot::Key2_Encrypt(u8* data, u32 len) noexcept { for (u32 i = 0; i < len; i++) { @@ -260,7 +236,7 @@ void CartCommon::LoadSave(const u8* savedata, u32 savelen) { } -int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) +int CartCommon::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { if (CmdEncMode == 0) { @@ -289,7 +265,7 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) case 0x3C: CmdEncMode = 1; - Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); DSiMode = false; return 0; @@ -297,7 +273,7 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) if (IsDSi) { CmdEncMode = 1; - Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS)); + cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS)); DSiMode = true; } return 0; @@ -314,7 +290,7 @@ int CartCommon::ROMCommandStart(u8* cmd, u8* data, u32 len) u8 cmddec[8]; *(u32*)&cmddec[0] = ByteSwap(*(u32*)&cmd[4]); *(u32*)&cmddec[4] = ByteSwap(*(u32*)&cmd[0]); - Key1_Decrypt((u32*)cmddec); + cartslot.Key1_Decrypt((u32*)cmddec); u32 tmp = ByteSwap(*(u32*)&cmddec[4]); *(u32*)&cmddec[4] = ByteSwap(*(u32*)&cmddec[0]); *(u32*)&cmddec[0] = tmp; @@ -518,9 +494,9 @@ void CartRetail::LoadSave(const u8* savedata, u32 savelen) Platform::WriteNDSSave(savedata, len, 0, len); } -int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len) +int CartRetail::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len); + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cartslot, cmd, data, len); switch (cmd[0]) { @@ -541,7 +517,7 @@ int CartRetail::ROMCommandStart(u8* cmd, u8* data, u32 len) return 0; default: - return CartCommon::ROMCommandStart(cmd, data, len); + return CartCommon::ROMCommandStart(cartslot, cmd, data, len); } } @@ -922,9 +898,9 @@ void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen) BuildSRAMID(); } -int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len) +int CartRetailNAND::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len); + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cartslot, cmd, data, len); switch (cmd[0]) { @@ -1054,7 +1030,7 @@ int CartRetailNAND::ROMCommandStart(u8* cmd, u8* data, u32 len) return 0; default: - return CartRetail::ROMCommandStart(cmd, data, len); + return CartRetail::ROMCommandStart(cartslot, cmd, data, len); } } @@ -1272,9 +1248,9 @@ void CartHomebrew::DoSavestate(Savestate* file) CartCommon::DoSavestate(file); } -int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len) +int CartHomebrew::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cmd, data, len); + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cartslot, cmd, data, len); switch (cmd[0]) { @@ -1305,7 +1281,7 @@ int CartHomebrew::ROMCommandStart(u8* cmd, u8* data, u32 len) return 1; default: - return CartCommon::ROMCommandStart(cmd, data, len); + return CartCommon::ROMCommandStart(cartslot, cmd, data, len); } } @@ -1471,32 +1447,29 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) -bool Init() +NDSCartSlot::NDSCartSlot() noexcept { - NDS::RegisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_PrepareData, ROMPrepareData); - NDS::RegisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_End, ROMEndTransfer); - NDS::RegisterEventFunc(NDS::Event_ROMSPITransfer, 0, SPITransferDone); - - Cart = nullptr; - - return true; + NDS::RegisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData)); + NDS::RegisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer)); + NDS::RegisterEventFunc(NDS::Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone)); + // All fields are default-constructed because they're listed as such in the class declaration } -void DeInit() +NDSCartSlot::~NDSCartSlot() noexcept { - Cart = nullptr; - NDS::UnregisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_PrepareData); NDS::UnregisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_End); NDS::UnregisterEventFunc(NDS::Event_ROMSPITransfer, 0); + + // Cart is cleaned up automatically because it's a unique_ptr } -void Reset() +void NDSCartSlot::Reset() noexcept { ResetCart(); } -void DoSavestate(Savestate* file) +void NDSCartSlot::DoSavestate(Savestate* file) noexcept { file->Section("NDSC"); @@ -1507,14 +1480,14 @@ void DoSavestate(Savestate* file) file->Var32(&SPIDataPos); file->Bool32(&SPIHold); - file->VarArray(ROMCommand, 8); + file->VarArray(ROMCommand.data(), sizeof(ROMCommand)); file->Var32(&ROMData); - file->VarArray(TransferData, 0x4000); + file->VarArray(TransferData.data(), sizeof(TransferData)); file->Var32(&TransferPos); file->Var32(&TransferLen); file->Var32(&TransferDir); - file->VarArray(TransferCmd, 8); + file->VarArray(TransferCmd.data(), sizeof(TransferCmd)); // cart inserted/len/ROM/etc should be already populated // savestate should be loaded after the right game is loaded @@ -1589,7 +1562,7 @@ bool ReadROMParams(u32 gamecode, ROMListEntry* params) } -void DecryptSecureArea(u8* out) +void NDSCartSlot::DecryptSecureArea(u8* out) noexcept { const NDSHeader& header = Cart->GetHeader(); const u8* cartrom = Cart->GetROM(); @@ -1741,7 +1714,7 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen) return cart; } -bool InsertROM(std::unique_ptr&& cart) +bool NDSCartSlot::InsertROM(std::unique_ptr&& cart) noexcept { if (!cart) { Log(LogLevel::Error, "Failed to insert invalid cart; existing cart (if any) was not ejected.\n"); @@ -1793,36 +1766,26 @@ bool InsertROM(std::unique_ptr&& cart) return true; } -bool LoadROM(const u8* romdata, u32 romlen) +bool NDSCartSlot::LoadROM(const u8* romdata, u32 romlen) noexcept { std::unique_ptr cart = ParseROM(romdata, romlen); return InsertROM(std::move(cart)); } -void LoadSave(const u8* savedata, u32 savelen) +void NDSCartSlot::LoadSave(const u8* savedata, u32 savelen) noexcept { if (Cart) Cart->LoadSave(savedata, savelen); } -void SetupDirectBoot(const std::string& romname) +void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept { if (Cart) Cart->SetupDirectBoot(romname); } -u8* GetSaveMemory() -{ - return Cart ? Cart->GetSaveMemory() : nullptr; -} - -u32 GetSaveMemoryLength() -{ - return Cart ? Cart->GetSaveMemoryLength() : 0; -} - -void EjectCart() +void NDSCartSlot::EjectCart() noexcept { if (!Cart) return; @@ -1837,7 +1800,7 @@ void EjectCart() // CHECKME: does an eject imply anything for the ROM/SPI transfer registers? } -void ResetCart() +void NDSCartSlot::ResetCart() noexcept { // CHECKME: what if there is a transfer in progress? @@ -1848,24 +1811,24 @@ void ResetCart() SPIDataPos = 0; SPIHold = false; - memset(ROMCommand, 0, 8); + memset(ROMCommand.data(), 0, sizeof(ROMCommand)); ROMData = 0; Key2_X = 0; Key2_Y = 0; - memset(TransferData, 0, 0x4000); + memset(TransferData.data(), 0, sizeof(TransferData)); TransferPos = 0; TransferLen = 0; TransferDir = 0; - memset(TransferCmd, 0, 8); + memset(TransferCmd.data(), 0, sizeof(TransferCmd)); TransferCmd[0] = 0xFF; if (Cart) Cart->Reset(); } -void ROMEndTransfer(u32 param) +void NDSCartSlot::ROMEndTransfer(u32 param) noexcept { ROMCnt &= ~(1<<31); @@ -1873,10 +1836,10 @@ void ROMEndTransfer(u32 param) NDS::SetIRQ((NDS::ExMemCnt[0]>>11)&0x1, NDS::IRQ_CartXferDone); if (Cart) - Cart->ROMCommandFinish(TransferCmd, TransferData, TransferLen); + Cart->ROMCommandFinish(TransferCmd.data(), TransferData.data(), TransferLen); } -void ROMPrepareData(u32 param) +void NDSCartSlot::ROMPrepareData(u32 param) noexcept { if (TransferDir == 0) { @@ -1896,7 +1859,7 @@ void ROMPrepareData(u32 param) NDS::CheckDMAs(0, 0x05); } -void WriteROMCnt(u32 val) +void NDSCartSlot::WriteROMCnt(u32 val) noexcept { u32 xferstart = (val & ~ROMCnt) & (1<<31); ROMCnt = (val & 0xFF7F7FFF) | (ROMCnt & 0x20800000); @@ -1941,7 +1904,7 @@ void WriteROMCnt(u32 val) *(u32*)&TransferCmd[0] = *(u32*)&ROMCommand[0]; *(u32*)&TransferCmd[4] = *(u32*)&ROMCommand[4]; - memset(TransferData, 0xFF, TransferLen); + memset(TransferData.data(), 0xFF, TransferLen); /*printf("ROM COMMAND %04X %08X %02X%02X%02X%02X%02X%02X%02X%02X SIZE %04X\n", SPICnt, ROMCnt, @@ -1954,7 +1917,7 @@ void WriteROMCnt(u32 val) TransferDir = 0; if (Cart) - TransferDir = Cart->ROMCommandStart(TransferCmd, TransferData, TransferLen); + TransferDir = Cart->ROMCommandStart(*this, TransferCmd.data(), TransferData.data(), TransferLen); if ((datasize > 0) && (((ROMCnt >> 30) & 0x1) != TransferDir)) Log(LogLevel::Debug, "NDSCART: !! BAD TRANSFER DIRECTION FOR CMD %02X, DIR=%d, ROMCNT=%08X\n", ROMCommand[0], TransferDir, ROMCnt); @@ -1985,7 +1948,7 @@ void WriteROMCnt(u32 val) NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*(cmddelay+4), ROMTransfer_PrepareData, 0); } -void AdvanceROMTransfer() +void NDSCartSlot::AdvanceROMTransfer() noexcept { ROMCnt &= ~(1<<23); @@ -2005,7 +1968,7 @@ void AdvanceROMTransfer() ROMEndTransfer(0); } -u32 ReadROMData() +u32 NDSCartSlot::ReadROMData() noexcept { if (ROMCnt & (1<<30)) return 0; @@ -2017,7 +1980,7 @@ u32 ReadROMData() return ROMData; } -void WriteROMData(u32 val) +void NDSCartSlot::WriteROMData(u32 val) noexcept { if (!(ROMCnt & (1<<30))) return; @@ -2038,7 +2001,7 @@ void WriteROMData(u32 val) } -void WriteSPICnt(u16 val) +void NDSCartSlot::WriteSPICnt(u16 val) noexcept { if ((SPICnt & 0x2040) == 0x2040 && (val & 0x2000) == 0x0000) { @@ -2055,12 +2018,12 @@ void WriteSPICnt(u16 val) Log(LogLevel::Debug, "!! CHANGING AUXSPICNT DURING TRANSFER: %04X\n", val); } -void SPITransferDone(u32 param) +void NDSCartSlot::SPITransferDone(u32 param) noexcept { SPICnt &= ~(1<<7); } -u8 ReadSPIData() +u8 NDSCartSlot::ReadSPIData() const noexcept { if (!(SPICnt & (1<<15))) return 0; if (!(SPICnt & (1<<13))) return 0; @@ -2069,7 +2032,7 @@ u8 ReadSPIData() return SPIData; } -void WriteSPIData(u8 val) +void NDSCartSlot::WriteSPIData(u8 val) noexcept { if (!(SPICnt & (1<<15))) return; if (!(SPICnt & (1<<13))) return; diff --git a/src/NDSCart.h b/src/NDSCart.h index 059fd242..1b172518 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -41,6 +41,8 @@ enum CartType Homebrew = 0x201, }; +class NDSCartSlot; + // CartCommon -- base code shared by all cart types class CartCommon { @@ -59,7 +61,7 @@ public: virtual void SetupSave(u32 type); virtual void LoadSave(const u8* savedata, u32 savelen); - virtual int ROMCommandStart(u8* cmd, u8* data, u32 len); + virtual int ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len); virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len); virtual u8 SPIWrite(u8 val, u32 pos, bool last); @@ -112,7 +114,7 @@ public: virtual void SetupSave(u32 type) override; virtual void LoadSave(const u8* savedata, u32 savelen) override; - virtual int ROMCommandStart(u8* cmd, u8* data, u32 len) override; + virtual int ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; virtual u8 SPIWrite(u8 val, u32 pos, bool last) override; @@ -151,7 +153,7 @@ public: void LoadSave(const u8* savedata, u32 savelen) override; - int ROMCommandStart(u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; u8 SPIWrite(u8 val, u32 pos, bool last) override; @@ -216,7 +218,7 @@ public: void DoSavestate(Savestate* file) override; - int ROMCommandStart(u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; private: @@ -228,21 +230,110 @@ private: bool ReadOnly; }; -extern u16 SPICnt; -extern u32 ROMCnt; +class NDSCartSlot +{ +public: + NDSCartSlot() noexcept; + ~NDSCartSlot() noexcept; + void Reset() noexcept; + void ResetCart() noexcept; + void DoSavestate(Savestate* file) noexcept; + void DecryptSecureArea(u8* out) noexcept; -extern u8 ROMCommand[8]; + /// Loads a Nintendo DS cart object into the cart slot. + /// The cart slot takes ownership of the cart object and its underlying resources + /// and re-encrypts the ROM's secure area if necessary. + /// If a cartridge is already inserted, it is first ejected + /// and its state is discarded. + /// If the provided cart is not valid, + /// then the currently-loaded ROM will not be ejected. + /// + /// @param cart Movable reference to the cart. + /// @returns \c true if the cart was successfully loaded, + /// \c false otherwise. + /// @post If the cart was successfully loaded, + /// then \c cart will be \c nullptr + /// and \c Cart will contain the object that \c cart previously pointed to. + /// Otherwise, \c cart and \c Cart will be both be unchanged. + bool InsertROM(std::unique_ptr&& cart) noexcept; -/// The currently loaded NDS cart. -extern std::unique_ptr Cart; + /// Parses a ROM image and loads it into the emulator. + /// This function is equivalent to calling ::ParseROM() and ::InsertROM() in sequence. + /// @param romdata Pointer to the ROM image. + /// The cart emulator maintains its own copy of this data, + /// so the caller is free to discard this data after calling this function. + /// @param romlen The length of the ROM image, in bytes. + /// @returns \c true if the ROM image was successfully loaded, + /// \c false if not. + bool LoadROM(const u8* romdata, u32 romlen) noexcept; + void LoadSave(const u8* savedata, u32 savelen) noexcept; + void SetupDirectBoot(const std::string& romname) noexcept; -bool Init(); -void DeInit(); -void Reset(); + /// This function is intended to allow frontends to save and load SRAM + /// without using melonDS APIs. + /// Modifying the emulated SRAM for any other reason is strongly discouraged. + /// The returned pointer may be invalidated if the emulator is reset, + /// or when a new game is loaded. + /// Consequently, don't store the returned pointer for any longer than necessary. + /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. + [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + [[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } -void DoSavestate(Savestate* file); + /// @returns The length of the buffer returned by ::GetSaveMemory() + /// if a cart is loaded and supports SRAM, otherwise zero. + [[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } + void EjectCart() noexcept; + u32 ReadROMData() noexcept; + void WriteROMData(u32 val) noexcept; + void WriteSPICnt(u16 val) noexcept; + void WriteROMCnt(u32 val) noexcept; + [[nodiscard]] u8 ReadSPIData() const noexcept; + void WriteSPIData(u8 val) noexcept; -void DecryptSecureArea(u8* out); + [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); } + [[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); } + + [[nodiscard]] u8 GetROMCommand(u8 index) const noexcept { return ROMCommand[index]; } + void SetROMCommand(u8 index, u8 val) noexcept { ROMCommand[index] = val; } + + [[nodiscard]] u32 GetROMCnt() const noexcept { return ROMCnt; } + [[nodiscard]] u16 GetSPICnt() const noexcept { return SPICnt; } + void SetSPICnt(u16 val) noexcept { SPICnt = val; } +private: + friend class CartCommon; + u16 SPICnt {}; + u32 ROMCnt {}; + std::array ROMCommand {}; + u8 SPIData; + u32 SPIDataPos; + bool SPIHold; + + u32 ROMData; + + std::array TransferData; + u32 TransferPos; + u32 TransferLen; + u32 TransferDir; + std::array TransferCmd; + + std::unique_ptr Cart; + + std::array Key1_KeyBuf; + + u64 Key2_X; + u64 Key2_Y; + + void Key1_Encrypt(u32* data) noexcept; + void Key1_Decrypt(u32* data) noexcept; + void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept; + void Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) noexcept; + void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) noexcept; + void Key2_Encrypt(u8* data, u32 len) noexcept; + void ROMEndTransfer(u32 param) noexcept; + void ROMPrepareData(u32 param) noexcept; + void AdvanceROMTransfer() noexcept; + void SPITransferDone(u32 param) noexcept; +}; /// Parses the given ROM data and constructs a \c NDSCart::CartCommon subclass /// that can be inserted into the emulator or used to extract information about the cart beforehand. @@ -253,65 +344,6 @@ void DecryptSecureArea(u8* out); /// @returns A \c NDSCart::CartCommon object representing the parsed ROM, /// or \c nullptr if the ROM data couldn't be parsed. std::unique_ptr ParseROM(const u8* romdata, u32 romlen); - -/// Loads a Nintendo DS cart object into the emulator. -/// The emulator takes ownership of the cart object and its underlying resources -/// and re-encrypts the ROM's secure area if necessary. -/// If a cartridge is already inserted, it is first ejected -/// and its state is discarded. -/// If the provided cart is not valid, -/// then the currently-loaded ROM will not be ejected. -/// -/// @param cart Movable reference to the cart. -/// @returns \c true if the cart was successfully loaded, -/// \c false otherwise. -/// @post If the cart was successfully loaded, -/// then \c cart will be \c nullptr -/// and \c Cart will contain the object that \c cart previously pointed to. -/// Otherwise, \c cart and \c Cart will be both be unchanged. -bool InsertROM(std::unique_ptr&& cart); - -/// Parses a ROM image and loads it into the emulator. -/// This function is equivalent to calling ::ParseROM() and ::InsertROM() in sequence. -/// @param romdata Pointer to the ROM image. -/// The cart emulator maintains its own copy of this data, -/// so the caller is free to discard this data after calling this function. -/// @param romlen The length of the ROM image, in bytes. -/// @returns \c true if the ROM image was successfully loaded, -/// \c false if not. -bool LoadROM(const u8* romdata, u32 romlen); -void LoadSave(const u8* savedata, u32 savelen); -void SetupDirectBoot(const std::string& romname); - -/// This function is intended to allow frontends to save and load SRAM -/// without using melonDS APIs. -/// Modifying the emulated SRAM for any other reason is strongly discouraged. -/// The returned pointer may be invalidated if the emulator is reset, -/// or when a new game is loaded. -/// Consequently, don't store the returned pointer for any longer than necessary. -/// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. -u8* GetSaveMemory(); - -/// @returns The length of the buffer returned by ::GetSaveMemory() -/// if a cart is loaded and supports SRAM, otherwise zero. -u32 GetSaveMemoryLength(); - -void EjectCart(); - -void ResetCart(); - -void WriteROMCnt(u32 val); -u32 ReadROMData(); -void WriteROMData(u32 val); - -void WriteSPICnt(u16 val); -u8 ReadSPIData(); -void WriteSPIData(u8 val); - -void ROMPrepareData(u32 param); -void ROMEndTransfer(u32 param); -void SPITransferDone(u32 param); - } #endif diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp index 2c1acee7..b381803b 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.cpp +++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp @@ -43,8 +43,8 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - const NDSBanner* banner = NDSCart::Cart->Banner(); - const NDSHeader& header = NDSCart::Cart->GetHeader(); + const NDSBanner* banner = NDS::NDSCartSlot->GetCart()->Banner(); + const NDSHeader& header = NDS::NDSCartSlot->GetCart()->GetHeader(); u32 iconData[32 * 32]; ROMManager::ROMIcon(banner->Icon, banner->Palette, iconData); iconImage = QImage(reinterpret_cast(iconData), 32, 32, QImage::Format_ARGB32).copy(); From 4558be0d8eb79d276c89392b9410e6edb649db95 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Thu, 9 Nov 2023 15:54:51 -0500 Subject: [PATCH 039/157] Refactor the GPU to be object-oriented (#1873) * Refactor GPU3D to be an object - Who has two thumbs and is the sworn enemy of global state? This guy! * Refactor GPU itself to be an object - Wow, it's used in a lot of places - Also introduce a new `Melon` namespace for a few classes - I expect other classes will be moved into `Melon` over time * Change signature of Renderer3D::SetRenderSettings - Make it noexcept, and its argument const * Remove some stray whitespace --- src/ARM.cpp | 11 +- src/ARM.h | 13 +- src/ARMJIT_Memory.cpp | 86 ++- src/DMA.cpp | 9 +- src/DMA.h | 8 +- src/DSi.cpp | 34 +- src/DSi_NDMA.cpp | 6 +- src/DSi_NDMA.h | 8 +- src/GPU.cpp | 386 ++++-------- src/GPU.h | 1079 ++++++++++++++++++---------------- src/GPU2D.cpp | 49 +- src/GPU2D.h | 11 +- src/GPU2D_Soft.cpp | 119 ++-- src/GPU2D_Soft.h | 8 +- src/GPU3D.cpp | 282 ++------- src/GPU3D.h | 276 +++++++-- src/GPU3D_OpenGL.cpp | 86 +-- src/GPU3D_OpenGL.h | 13 +- src/GPU3D_Soft.cpp | 106 ++-- src/GPU3D_Soft.h | 11 +- src/GPU_OpenGL.cpp | 24 +- src/GPU_OpenGL.h | 12 +- src/NDS.cpp | 329 +++++------ src/NDS.h | 7 + src/frontend/qt_sdl/main.cpp | 30 +- 25 files changed, 1513 insertions(+), 1490 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index 4f2f8928..18d50fe2 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -109,10 +109,11 @@ u32 ARM::ConditionTable[16] = }; -ARM::ARM(u32 num) +ARM::ARM(u32 num, Melon::GPU& gpu) : #ifdef GDBSTUB_ENABLED - : GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)) + GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)), #endif + GPU(gpu) { // well uh Num = num; @@ -133,7 +134,7 @@ ARM::~ARM() // dorp } -ARMv5::ARMv5() : ARM(0) +ARMv5::ARMv5(Melon::GPU& gpu) : ARM(0, gpu) { #ifndef JIT_ENABLED DTCM = new u8[DTCMPhysicalSize]; @@ -142,7 +143,7 @@ ARMv5::ARMv5() : ARM(0) PU_Map = PU_PrivMap; } -ARMv4::ARMv4() : ARM(1) +ARMv4::ARMv4(Melon::GPU& gpu) : ARM(1, gpu) { // } @@ -1144,7 +1145,7 @@ void ARM::WriteMem(u32 addr, int size, u32 v) void ARM::ResetGdb() { NDS::Reset(); - GPU::StartFrame(); // need this to properly kick off the scheduler & frame output + GPU.StartFrame(); // need this to properly kick off the scheduler & frame output } int ARM::RemoteCmd(const u8* cmd, size_t len) { diff --git a/src/ARM.h b/src/ARM.h index b5992ca7..d2d0ea90 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -42,13 +42,18 @@ enum const u32 ITCMPhysicalSize = 0x8000; const u32 DTCMPhysicalSize = 0x4000; +namespace Melon +{ +class GPU; +} + class ARM #ifdef GDBSTUB_ENABLED : public Gdb::StubCallbacks #endif { public: - ARM(u32 num); + ARM(u32 num, Melon::GPU& gpu); virtual ~ARM(); // destroy shit virtual void Reset(); @@ -209,12 +214,14 @@ protected: void GdbCheckA(); void GdbCheckB(); void GdbCheckC(); +private: + Melon::GPU& GPU; }; class ARMv5 : public ARM { public: - ARMv5(); + ARMv5(Melon::GPU& gpu); ~ARMv5(); void Reset() override; @@ -358,7 +365,7 @@ public: class ARMv4 : public ARM { public: - ARMv4(); + ARMv4(Melon::GPU& gpu); void Reset() override; diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 095fb301..3591a25d 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1228,11 +1228,11 @@ void VRAMWrite(u32 addr, T val) { switch (addr & 0x00E00000) { - case 0x00000000: GPU::WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU::WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU::WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU::WriteVRAM_BOBJ(addr, val); return; - default: GPU::WriteVRAM_LCDC(addr, val); return; + case 0x00000000: NDS::GPU->WriteVRAM_ABG(addr, val); return; + case 0x00200000: NDS::GPU->WriteVRAM_BBG(addr, val); return; + case 0x00400000: NDS::GPU->WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: NDS::GPU->WriteVRAM_BOBJ(addr, val); return; + default: NDS::GPU->WriteVRAM_LCDC(addr, val); return; } } template @@ -1240,14 +1240,56 @@ T VRAMRead(u32 addr) { switch (addr & 0x00E00000) { - case 0x00000000: return GPU::ReadVRAM_ABG(addr); - case 0x00200000: return GPU::ReadVRAM_BBG(addr); - case 0x00400000: return GPU::ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU::ReadVRAM_BOBJ(addr); - default: return GPU::ReadVRAM_LCDC(addr); + case 0x00000000: return NDS::GPU->ReadVRAM_ABG(addr); + case 0x00200000: return NDS::GPU->ReadVRAM_BBG(addr); + case 0x00400000: return NDS::GPU->ReadVRAM_AOBJ(addr); + case 0x00600000: return NDS::GPU->ReadVRAM_BOBJ(addr); + default: return NDS::GPU->ReadVRAM_LCDC(addr); } } +static u8 GPU3D_Read8(u32 addr) noexcept +{ + return NDS::GPU->GPU3D.Read8(addr); +} + +static u16 GPU3D_Read16(u32 addr) noexcept +{ + return NDS::GPU->GPU3D.Read16(addr); +} + +static u32 GPU3D_Read32(u32 addr) noexcept +{ + return NDS::GPU->GPU3D.Read32(addr); +} + +static void GPU3D_Write8(u32 addr, u8 val) noexcept +{ + NDS::GPU->GPU3D.Write8(addr, val); +} + +static void GPU3D_Write16(u32 addr, u16 val) noexcept +{ + NDS::GPU->GPU3D.Write16(addr, val); +} + +static void GPU3D_Write32(u32 addr, u32 val) noexcept +{ + NDS::GPU->GPU3D.Write32(addr, val); +} + +template +static T GPU_ReadVRAM_ARM7(u32 addr) noexcept +{ + return NDS::GPU->ReadVRAM_ARM7(addr); +} + +template +static void GPU_WriteVRAM_ARM7(u32 addr, T val) noexcept +{ + NDS::GPU->WriteVRAM_ARM7(addr, val); +} + u32 NDSCartSlot_ReadROMData() { // TODO: Add a NDS* parameter, when NDS* is eventually implemented return NDS::NDSCartSlot->ReadROMData(); @@ -1273,12 +1315,12 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) { switch (size | store) { - case 8: return (void*)GPU3D::Read8; - case 9: return (void*)GPU3D::Write8; - case 16: return (void*)GPU3D::Read16; - case 17: return (void*)GPU3D::Write16; - case 32: return (void*)GPU3D::Read32; - case 33: return (void*)GPU3D::Write32; + case 8: return (void*)GPU3D_Read8; + case 9: return (void*)GPU3D_Write8; + case 16: return (void*)GPU3D_Read16; + case 17: return (void*)GPU3D_Write16; + case 32: return (void*)GPU3D_Read32; + case 33: return (void*)GPU3D_Write32; } } @@ -1380,12 +1422,12 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) case 0x06800000: switch (size | store) { - case 8: return (void*)GPU::ReadVRAM_ARM7; - case 9: return (void*)GPU::WriteVRAM_ARM7; - case 16: return (void*)GPU::ReadVRAM_ARM7; - case 17: return (void*)GPU::WriteVRAM_ARM7; - case 32: return (void*)GPU::ReadVRAM_ARM7; - case 33: return (void*)GPU::WriteVRAM_ARM7; + case 8: return (void*)GPU_ReadVRAM_ARM7; + case 9: return (void*)GPU_WriteVRAM_ARM7; + case 16: return (void*)GPU_ReadVRAM_ARM7; + case 17: return (void*)GPU_WriteVRAM_ARM7; + case 32: return (void*)GPU_ReadVRAM_ARM7; + case 33: return (void*)GPU_WriteVRAM_ARM7; } } } diff --git a/src/DMA.cpp b/src/DMA.cpp index b779d26e..91f23454 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -47,9 +47,10 @@ using Platform::LogLevel; // TODO: timings are nonseq when address is fixed/decrementing -DMA::DMA(u32 cpu, u32 num) : +DMA::DMA(u32 cpu, u32 num, Melon::GPU& gpu) : CPU(cpu), - Num(num) + Num(num), + GPU(gpu) { if (cpu == 0) CountMask = 0x001FFFFF; @@ -142,7 +143,7 @@ void DMA::WriteCnt(u32 val) if ((StartMode & 0x7) == 0) Start(); else if (StartMode == 0x07) - GPU3D::CheckFIFODMA(); + GPU.GPU3D.CheckFIFODMA(); if (StartMode==0x06 || StartMode==0x13) Log(LogLevel::Warn, "UNIMPLEMENTED ARM%d DMA%d START MODE %02X, %08X->%08X\n", CPU?7:9, Num, StartMode, SrcAddr, DstAddr); @@ -609,7 +610,7 @@ void DMA::Run9() NDS::ResumeCPU(0, 1<DispStat[0] |= (1<<6); + NDS::GPU->DispStat[1] |= (1<<6); } void Stop() @@ -730,8 +730,8 @@ void SoftReset() // LCD init flag - GPU::DispStat[0] |= (1<<6); - GPU::DispStat[1] |= (1<<6); + NDS::GPU->DispStat[0] |= (1<<6); + NDS::GPU->DispStat[1] |= (1<<6); } bool LoadNAND() @@ -1528,11 +1528,11 @@ void ARM9Write8(u32 addr, u8 val) #endif switch (addr & 0x00E00000) { - case 0x00000000: GPU::WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU::WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU::WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU::WriteVRAM_BOBJ(addr, val); return; - default: GPU::WriteVRAM_LCDC(addr, val); return; + case 0x00000000: NDS::GPU->WriteVRAM_ABG(addr, val); return; + case 0x00200000: NDS::GPU->WriteVRAM_BBG(addr, val); return; + case 0x00400000: NDS::GPU->WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: NDS::GPU->WriteVRAM_BOBJ(addr, val); return; + default: NDS::GPU->WriteVRAM_LCDC(addr, val); return; } case 0x08000000: diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index f3c3745e..0afb2010 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -26,7 +26,7 @@ using Platform::Log; using Platform::LogLevel; -DSi_NDMA::DSi_NDMA(u32 cpu, u32 num) +DSi_NDMA::DSi_NDMA(u32 cpu, u32 num, Melon::GPU& gpu) : GPU(gpu) { CPU = cpu; Num = num; @@ -125,7 +125,7 @@ void DSi_NDMA::WriteCnt(u32 val) if ((StartMode & 0x1F) == 0x10) Start(); else if (StartMode == 0x0A) - GPU3D::CheckFIFODMA(); + GPU.GPU3D.CheckFIFODMA(); // TODO: unsupported start modes: // * timers (00-03) @@ -259,7 +259,7 @@ void DSi_NDMA::Run9() NDS::ResumeCPU(0, 1<<(Num+4)); if (StartMode == 0x0A) - GPU3D::CheckFIFODMA(); + GPU.GPU3D.CheckFIFODMA(); } return; diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h index e3da93d2..732bc9d2 100644 --- a/src/DSi_NDMA.h +++ b/src/DSi_NDMA.h @@ -22,10 +22,15 @@ #include "types.h" #include "Savestate.h" +namespace Melon +{ +class GPU; +} + class DSi_NDMA { public: - DSi_NDMA(u32 cpu, u32 num); + DSi_NDMA(u32 cpu, u32 num, Melon::GPU& gpu); ~DSi_NDMA(); void Reset(); @@ -73,6 +78,7 @@ public: u32 Cnt; private: + Melon::GPU& GPU; u32 CPU, Num; u32 StartMode; diff --git a/src/GPU.cpp b/src/GPU.cpp index ccfcb636..987068d9 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -25,17 +25,18 @@ #endif #include "GPU2D_Soft.h" +#include "GPU3D_Soft.h" +#include "GPU3D_OpenGL.h" using Platform::Log; using Platform::LogLevel; -namespace GPU -{ - #define LINE_CYCLES (355*6) #define HBLANK_CYCLES (48+(256*6)) #define FRAME_CYCLES (LINE_CYCLES * 263) +namespace Melon +{ enum { LCD_StartHBlank = 0, @@ -43,62 +44,6 @@ enum LCD_FinishFrame, }; -u16 VCount; -u32 NextVCount; -u16 TotalScanlines; - -bool RunFIFO; - -u16 DispStat[2], VMatch[2]; - -u8 Palette[2*1024]; -u8 OAM[2*1024]; - -u8 VRAM_A[128*1024]; -u8 VRAM_B[128*1024]; -u8 VRAM_C[128*1024]; -u8 VRAM_D[128*1024]; -u8 VRAM_E[ 64*1024]; -u8 VRAM_F[ 16*1024]; -u8 VRAM_G[ 16*1024]; -u8 VRAM_H[ 32*1024]; -u8 VRAM_I[ 16*1024]; -u8* const VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I}; -u32 const VRAMMask[9] = {0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF}; - -u8 VRAMCNT[9]; -u8 VRAMSTAT; - -u32 VRAMMap_LCDC; - -u32 VRAMMap_ABG[0x20]; -u32 VRAMMap_AOBJ[0x10]; -u32 VRAMMap_BBG[0x8]; -u32 VRAMMap_BOBJ[0x8]; - -u32 VRAMMap_ABGExtPal[4]; -u32 VRAMMap_AOBJExtPal; -u32 VRAMMap_BBGExtPal[4]; -u32 VRAMMap_BOBJExtPal; - -u32 VRAMMap_Texture[4]; -u32 VRAMMap_TexPal[8]; - -u32 VRAMMap_ARM7[2]; - -u8* VRAMPtr_ABG[0x20]; -u8* VRAMPtr_AOBJ[0x10]; -u8* VRAMPtr_BBG[0x8]; -u8* VRAMPtr_BOBJ[0x8]; - -int FrontBuffer; -u32* Framebuffer[2][2]; -int Renderer = 0; - -GPU2D::Unit GPU2D_A(0); -GPU2D::Unit GPU2D_B(1); - -std::unique_ptr GPU2D_Renderer = {}; /* VRAM invalidation tracking @@ -121,64 +66,24 @@ std::unique_ptr GPU2D_Renderer = {}; VRAMDirty need to be reset for the respective VRAM bank. */ -VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG; -VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ; -VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG; -VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ; - -VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal; -VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal; -VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal; -VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal; - -VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture; -VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal; - -NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9]; - -u8 VRAMFlat_ABG[512*1024]; -u8 VRAMFlat_BBG[128*1024]; -u8 VRAMFlat_AOBJ[256*1024]; -u8 VRAMFlat_BOBJ[128*1024]; - -u8 VRAMFlat_ABGExtPal[32*1024]; -u8 VRAMFlat_BBGExtPal[32*1024]; -u8 VRAMFlat_AOBJExtPal[8*1024]; -u8 VRAMFlat_BOBJExtPal[8*1024]; - -u8 VRAMFlat_Texture[512*1024]; -u8 VRAMFlat_TexPal[128*1024]; - -u32 OAMDirty; -u32 PaletteDirty; - -#ifdef OGLRENDERER_ENABLED -std::unique_ptr CurGLCompositor = {}; -#endif - -bool Init() +GPU::GPU() noexcept : GPU2D_A(0, *this), GPU2D_B(1, *this) { - NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartHBlank, StartHBlank); - NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartScanline, StartScanline); - NDS::RegisterEventFunc(NDS::Event_LCD, LCD_FinishFrame, FinishFrame); - NDS::RegisterEventFunc(NDS::Event_DisplayFIFO, 0, DisplayFIFO); + NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); + NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); + NDS::RegisterEventFunc(NDS::Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); + NDS::RegisterEventFunc(NDS::Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); - GPU2D_Renderer = std::make_unique(); - if (!GPU3D::Init()) return false; + GPU2D_Renderer = std::make_unique(*this); FrontBuffer = 0; Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL; Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL; Renderer = 0; - - return true; } -void DeInit() +GPU::~GPU() noexcept { - GPU2D_Renderer.reset(); - GPU3D::DeInit(); - + // All unique_ptr fields are automatically cleaned up if (Framebuffer[0][0]) delete[] Framebuffer[0][0]; if (Framebuffer[0][1]) delete[] Framebuffer[0][1]; if (Framebuffer[1][0]) delete[] Framebuffer[1][0]; @@ -189,17 +94,13 @@ void DeInit() Framebuffer[1][0] = nullptr; Framebuffer[1][1] = nullptr; -#ifdef OGLRENDERER_ENABLED - CurGLCompositor = nullptr; -#endif - NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_StartHBlank); NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_StartScanline); NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_FinishFrame); NDS::UnregisterEventFunc(NDS::Event_DisplayFIFO, 0); } -void ResetVRAMCache() +void GPU::ResetVRAMCache() noexcept { for (int i = 0; i < 9; i++) VRAMDirty[i] = NonStupidBitField<128*1024/VRAMDirtyGranularity>(); @@ -227,7 +128,7 @@ void ResetVRAMCache() memset(VRAMFlat_TexPal, 0, sizeof(VRAMFlat_TexPal)); } -void Reset() +void GPU::Reset() noexcept { VCount = 0; NextVCount = -1; @@ -278,7 +179,7 @@ void Reset() memset(VRAMPtr_BOBJ, 0, sizeof(VRAMPtr_BOBJ)); size_t fbsize; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; @@ -296,7 +197,7 @@ void Reset() GPU2D_A.Reset(); GPU2D_B.Reset(); - GPU3D::Reset(); + GPU3D.Reset(); int backbuf = FrontBuffer ? 0 : 1; GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]); @@ -309,10 +210,10 @@ void Reset() PaletteDirty = 0xF; } -void Stop() +void GPU::Stop() noexcept { int fbsize; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; @@ -325,12 +226,12 @@ void Stop() #ifdef OGLRENDERER_ENABLED // This needs a better way to know that we're // using the OpenGL renderer specifically - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) CurGLCompositor->Stop(); #endif } -void DoSavestate(Savestate* file) +void GPU::DoSavestate(Savestate* file) noexcept { file->Section("GPUG"); @@ -391,12 +292,12 @@ void DoSavestate(Savestate* file) GPU2D_A.DoSavestate(file); GPU2D_B.DoSavestate(file); - GPU3D::DoSavestate(file); + GPU3D.DoSavestate(file); ResetVRAMCache(); } -void AssignFramebuffers() +void GPU::AssignFramebuffers() noexcept { int backbuf = FrontBuffer ? 0 : 1; if (NDS::PowerControl9 & (1<<15)) @@ -409,41 +310,41 @@ void AssignFramebuffers() } } -void InitRenderer(int renderer) +void GPU::InitRenderer(int renderer) noexcept { #ifdef OGLRENDERER_ENABLED if (renderer == 1) { - CurGLCompositor = GLCompositor::New(); + CurGLCompositor = GLCompositor::New(*this); // Create opengl renderer if (!CurGLCompositor) { // Fallback on software renderer renderer = 0; - GPU3D::CurrentRenderer = std::make_unique(); + GPU3D.SetCurrentRenderer(std::make_unique(*this)); } - GPU3D::CurrentRenderer = GPU3D::GLRenderer::New(); - if (!GPU3D::CurrentRenderer) + GPU3D.SetCurrentRenderer(GPU3D::GLRenderer::New(*this)); + if (!GPU3D.GetCurrentRenderer()) { // Fallback on software renderer CurGLCompositor.reset(); renderer = 0; - GPU3D::CurrentRenderer = std::make_unique(); + GPU3D.SetCurrentRenderer(std::make_unique(*this)); } } else #endif { - GPU3D::CurrentRenderer = std::make_unique(); + GPU3D.SetCurrentRenderer(std::make_unique(*this)); } Renderer = renderer; } -void DeInitRenderer() +void GPU::DeInitRenderer() noexcept { // Delete the 3D renderer, if it exists - GPU3D::CurrentRenderer.reset(); + GPU3D.SetCurrentRenderer(nullptr); #ifdef OGLRENDERER_ENABLED // Delete the compositor, if one exists @@ -451,22 +352,22 @@ void DeInitRenderer() #endif } -void ResetRenderer() +void GPU::ResetRenderer() noexcept { if (Renderer == 0) { - GPU3D::CurrentRenderer->Reset(); + GPU3D.GetCurrentRenderer()->Reset(); } #ifdef OGLRENDERER_ENABLED else { CurGLCompositor->Reset(); - GPU3D::CurrentRenderer->Reset(); + GPU3D.GetCurrentRenderer()->Reset(); } #endif } -void SetRenderSettings(int renderer, RenderSettings& settings) +void GPU::SetRenderSettings(int renderer, RenderSettings& settings) noexcept { if (renderer != Renderer) { @@ -475,7 +376,7 @@ void SetRenderSettings(int renderer, RenderSettings& settings) } int fbsize; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; @@ -499,13 +400,13 @@ void SetRenderSettings(int renderer, RenderSettings& settings) if (Renderer == 0) { - GPU3D::CurrentRenderer->SetRenderSettings(settings); + GPU3D.GetCurrentRenderer()->SetRenderSettings(settings); } #ifdef OGLRENDERER_ENABLED else { CurGLCompositor->SetRenderSettings(settings); - GPU3D::CurrentRenderer->SetRenderSettings(settings); + GPU3D.GetCurrentRenderer()->SetRenderSettings(settings); } #endif } @@ -541,7 +442,14 @@ void SetRenderSettings(int renderer, RenderSettings& settings) // when reading: values are read from each bank and ORed together // when writing: value is written to each bank -u8* GetUniqueBankPtr(u32 mask, u32 offset) +u8* GPU::GetUniqueBankPtr(u32 mask, u32 offset) noexcept +{ + if (!mask || (mask & (mask - 1)) != 0) return NULL; + int num = __builtin_ctz(mask); + return &VRAM[num][offset & VRAMMask[num]]; +} + +const u8* GPU::GetUniqueBankPtr(u32 mask, u32 offset) const noexcept { if (!mask || (mask & (mask - 1)) != 0) return NULL; int num = __builtin_ctz(mask); @@ -556,7 +464,7 @@ u8* GetUniqueBankPtr(u32 mask, u32 offset) #define UNMAP_RANGE_PTR(map, base, n) \ for (int i = 0; i < n; i++) { VRAMMap_##map[(base)+i] &= ~bankmask; VRAMPtr_##map[(base)+i] = GetUniqueBankPtr(VRAMMap_##map[(base)+i], ((base)+i)<<14); } -void MapVRAM_AB(u32 bank, u8 cnt) +void GPU::MapVRAM_AB(u32 bank, u8 cnt) noexcept { cnt &= 0x9B; @@ -616,7 +524,7 @@ void MapVRAM_AB(u32 bank, u8 cnt) } } -void MapVRAM_CD(u32 bank, u8 cnt) +void GPU::MapVRAM_CD(u32 bank, u8 cnt) noexcept { cnt &= 0x9F; @@ -705,7 +613,7 @@ void MapVRAM_CD(u32 bank, u8 cnt) } } -void MapVRAM_E(u32 bank, u8 cnt) +void GPU::MapVRAM_E(u32 bank, u8 cnt) noexcept { cnt &= 0x87; @@ -769,7 +677,7 @@ void MapVRAM_E(u32 bank, u8 cnt) } } -void MapVRAM_FG(u32 bank, u8 cnt) +void GPU::MapVRAM_FG(u32 bank, u8 cnt) noexcept { cnt &= 0x9F; @@ -869,7 +777,7 @@ void MapVRAM_FG(u32 bank, u8 cnt) } } -void MapVRAM_H(u32 bank, u8 cnt) +void GPU::MapVRAM_H(u32 bank, u8 cnt) noexcept { cnt &= 0x83; @@ -931,7 +839,7 @@ void MapVRAM_H(u32 bank, u8 cnt) } } -void MapVRAM_I(u32 bank, u8 cnt) +void GPU::MapVRAM_I(u32 bank, u8 cnt) noexcept { cnt &= 0x83; @@ -1002,7 +910,7 @@ void MapVRAM_I(u32 bank, u8 cnt) } -void SetPowerCnt(u32 val) +void GPU::SetPowerCnt(u32 val) noexcept { // POWCNT1 effects: // * bit0: asplodes hardware??? not tested. @@ -1016,13 +924,13 @@ void SetPowerCnt(u32 val) GPU2D_A.SetEnabled(val & (1<<1)); GPU2D_B.SetEnabled(val & (1<<9)); - GPU3D::SetEnabled(val & (1<<3), val & (1<<2)); + GPU3D.SetEnabled(val & (1<<3), val & (1<<2)); AssignFramebuffers(); } -void DisplayFIFO(u32 x) +void GPU::DisplayFIFO(u32 x) noexcept { // sample the FIFO // as this starts 16 cycles (~3 pixels) before display start, @@ -1045,7 +953,7 @@ void DisplayFIFO(u32 x) GPU2D_A.SampleFIFO(253, 3); // sample the remaining pixels } -void StartFrame() +void GPU::StartFrame() noexcept { // only run the display FIFO if needed: // * if it is used for display or capture @@ -1056,7 +964,7 @@ void StartFrame() StartScanline(0); } -void StartHBlank(u32 line) +void GPU::StartHBlank(u32 line) noexcept { DispStat[0] |= (1<<1); DispStat[1] |= (1<<1); @@ -1082,7 +990,7 @@ void StartHBlank(u32 line) } else if (VCount == 215) { - GPU3D::VCount215(); + GPU3D.VCount215(); } else if (VCount == 262) { @@ -1099,25 +1007,25 @@ void StartHBlank(u32 line) NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_FinishFrame, line+1); } -void FinishFrame(u32 lines) +void GPU::FinishFrame(u32 lines) noexcept { FrontBuffer = FrontBuffer ? 0 : 1; AssignFramebuffers(); TotalScanlines = lines; - if (GPU3D::AbortFrame) + if (GPU3D.AbortFrame) { - GPU3D::RestartFrame(); - GPU3D::AbortFrame = false; + GPU3D.RestartFrame(); + GPU3D.AbortFrame = false; } } -void BlankFrame() +void GPU::BlankFrame() noexcept { int backbuf = FrontBuffer ? 0 : 1; int fbsize; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) fbsize = (256*3 + 1) * 192; else fbsize = 256 * 192; @@ -1131,7 +1039,7 @@ void BlankFrame() TotalScanlines = 263; } -void StartScanline(u32 line) +void GPU::StartScanline(u32 line) noexcept { if (line == 0) VCount = 0; @@ -1201,7 +1109,7 @@ void StartScanline(u32 line) // texture memory anyway and only update it before the start //of the next frame. // So we can give the rasteriser a bit more headroom - GPU3D::VCount144(); + GPU3D.VCount144(); // VBlank DispStat[0] |= (1<<0); @@ -1217,11 +1125,11 @@ void StartScanline(u32 line) GPU2D_A.VBlank(); GPU2D_B.VBlank(); - GPU3D::VBlank(); + GPU3D.VBlank(); #ifdef OGLRENDERER_ENABLED // Need a better way to identify the openGL renderer in particular - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU3D.IsRendererAccelerated()) CurGLCompositor->RenderFrame(); #endif } @@ -1231,7 +1139,7 @@ void StartScanline(u32 line) } -void SetDispStat(u32 cpu, u16 val) +void GPU::SetDispStat(u32 cpu, u16 val) noexcept { val &= 0xFFB8; DispStat[cpu] &= 0x0047; @@ -1240,7 +1148,7 @@ void SetDispStat(u32 cpu, u16 val) VMatch[cpu] = (val >> 8) | ((val & 0x80) << 1); } -void SetVCount(u16 val) +void GPU::SetVCount(u16 val) noexcept { // VCount write is delayed until the next scanline @@ -1248,12 +1156,12 @@ void SetVCount(u16 val) // 3D engine seems to give up on the current frame in that situation, repeating the last two scanlines // TODO: also check the various DMA types that can be involved - GPU3D::AbortFrame |= NextVCount != val; + GPU3D.AbortFrame |= NextVCount != val; NextVCount = val; } template -NonStupidBitField VRAMTrackingSet::DeriveState(u32* currentMappings) +NonStupidBitField VRAMTrackingSet::DeriveState(u32* currentMappings, GPU& gpu) { NonStupidBitField result; u16 banksToBeZeroed = 0; @@ -1282,20 +1190,20 @@ NonStupidBitField VRAMTrackingSet> 14)]; + u32 dirty = ((u32*)gpu.VRAMDirty[num].Data)[i & (gpu.VRAMMask[num] >> 14)]; result.Data[i / 2] |= (u64)dirty << ((i&1)*32); } else if (MappingGranularity == 8*1024) { - u16 dirty = ((u16*)VRAMDirty[num].Data)[i & (VRAMMask[num] >> 13)]; + u16 dirty = ((u16*)gpu.VRAMDirty[num].Data)[i & (gpu.VRAMMask[num] >> 13)]; result.Data[i / 4] |= (u64)dirty << ((i&3)*16); } else if (MappingGranularity == 128*1024) { - result.Data[i * 4 + 0] |= VRAMDirty[num].Data[0]; - result.Data[i * 4 + 1] |= VRAMDirty[num].Data[1]; - result.Data[i * 4 + 2] |= VRAMDirty[num].Data[2]; - result.Data[i * 4 + 3] |= VRAMDirty[num].Data[3]; + result.Data[i * 4 + 0] |= gpu.VRAMDirty[num].Data[0]; + result.Data[i * 4 + 1] |= gpu.VRAMDirty[num].Data[1]; + result.Data[i * 4 + 2] |= gpu.VRAMDirty[num].Data[2]; + result.Data[i * 4 + 3] |= gpu.VRAMDirty[num].Data[3]; } else { @@ -1310,137 +1218,63 @@ NonStupidBitField VRAMTrackingSet VRAMTrackingSet<32*1024, 8*1024>::DeriveState(u32*); -template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(u32*); -template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(u32*); -template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(u32*); -template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(u32*); -template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(u32*); +template NonStupidBitField<32*1024/VRAMDirtyGranularity> VRAMTrackingSet<32*1024, 8*1024>::DeriveState(u32*, GPU& gpu); +template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(u32*, GPU& gpu); +template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(u32*, GPU& gpu); +template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(u32*, GPU& gpu); +template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(u32*, GPU& gpu); +template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(u32*, GPU& gpu); -template -inline bool CopyLinearVRAM(u8* flat, u32* mappings, NonStupidBitField& dirty, u64 (*slowAccess)(u32 addr)) + + +bool GPU::MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept { - const u32 VRAMBitsPerMapping = MappingGranularity / VRAMDirtyGranularity; - - bool change = false; - - typename NonStupidBitField::Iterator it = dirty.Begin(); - while (it != dirty.End()) - { - u32 offset = *it * VRAMDirtyGranularity; - u8* dst = flat + offset; - u8* fastAccess = GetUniqueBankPtr(mappings[*it / VRAMBitsPerMapping], offset); - if (fastAccess) - { - memcpy(dst, fastAccess, VRAMDirtyGranularity); - } - else - { - for (u32 i = 0; i < VRAMDirtyGranularity; i += 8) - *(u64*)&dst[i] = slowAccess(offset + i); - } - change = true; - it++; - } - return change; + return CopyLinearVRAM<128*1024>(VRAMFlat_Texture, VRAMMap_Texture, dirty, &GPU::ReadVRAM_Texture); } - -bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) +bool GPU::MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<128*1024>(VRAMFlat_Texture, VRAMMap_Texture, dirty, ReadVRAM_Texture); + return CopyLinearVRAM<16*1024>(VRAMFlat_TexPal, VRAMMap_TexPal, dirty, &GPU::ReadVRAM_TexPal); } -bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) + +bool GPU::MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_TexPal, VRAMMap_TexPal, dirty, ReadVRAM_TexPal); + return CopyLinearVRAM<16*1024>(VRAMFlat_ABG, VRAMMap_ABG, dirty, &GPU::ReadVRAM_ABG); } - -bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) +bool GPU::MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_ABG, VRAMMap_ABG, dirty, ReadVRAM_ABG); + return CopyLinearVRAM<16*1024>(VRAMFlat_BBG, VRAMMap_BBG, dirty, &GPU::ReadVRAM_BBG); } -bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) + +bool GPU::MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_BBG, VRAMMap_BBG, dirty, ReadVRAM_BBG); + return CopyLinearVRAM<16*1024>(VRAMFlat_AOBJ, VRAMMap_AOBJ, dirty, &GPU::ReadVRAM_AOBJ); } - -bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty) +bool GPU::MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_AOBJ, VRAMMap_AOBJ, dirty, ReadVRAM_AOBJ); + return CopyLinearVRAM<16*1024>(VRAMFlat_BOBJ, VRAMMap_BOBJ, dirty, &GPU::ReadVRAM_BOBJ); } -bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) + +bool GPU::MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept { - return CopyLinearVRAM<16*1024>(VRAMFlat_BOBJ, VRAMMap_BOBJ, dirty, ReadVRAM_BOBJ); + return CopyLinearVRAM<8*1024>(VRAMFlat_ABGExtPal, VRAMMap_ABGExtPal, dirty, &GPU::ReadVRAM_ABGExtPal); } - -template -T ReadVRAM_ABGExtPal(u32 addr) +bool GPU::MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept { - u32 mask = VRAMMap_ABGExtPal[(addr >> 13) & 0x3]; - - T ret = 0; - if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0x7FFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; - if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; - - return ret; + return CopyLinearVRAM<8*1024>(VRAMFlat_BBGExtPal, VRAMMap_BBGExtPal, dirty, &GPU::ReadVRAM_BBGExtPal); } -template -T ReadVRAM_BBGExtPal(u32 addr) +bool GPU::MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept { - u32 mask = VRAMMap_BBGExtPal[(addr >> 13) & 0x3]; - - T ret = 0; - if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF]; - - return ret; + return CopyLinearVRAM<8*1024>(VRAMFlat_AOBJExtPal, &VRAMMap_AOBJExtPal, dirty, &GPU::ReadVRAM_AOBJExtPal); } - -template -T ReadVRAM_AOBJExtPal(u32 addr) +bool GPU::MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept { - u32 mask = VRAMMap_AOBJExtPal; - - T ret = 0; - if (mask & (1<<4)) ret |= *(T*)&VRAM_F[addr & 0x1FFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_G[addr & 0x1FFF]; - - return ret; + return CopyLinearVRAM<8*1024>(VRAMFlat_BOBJExtPal, &VRAMMap_BOBJExtPal, dirty, &GPU::ReadVRAM_BOBJExtPal); } - -template -T ReadVRAM_BOBJExtPal(u32 addr) -{ - u32 mask = VRAMMap_BOBJExtPal; - - T ret = 0; - if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x1FFF]; - - return ret; -} - -bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) -{ - return CopyLinearVRAM<8*1024>(VRAMFlat_ABGExtPal, VRAMMap_ABGExtPal, dirty, ReadVRAM_ABGExtPal); -} -bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) -{ - return CopyLinearVRAM<8*1024>(VRAMFlat_BBGExtPal, VRAMMap_BBGExtPal, dirty, ReadVRAM_BBGExtPal); -} - -bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) -{ - return CopyLinearVRAM<8*1024>(VRAMFlat_AOBJExtPal, &VRAMMap_AOBJExtPal, dirty, ReadVRAM_AOBJExtPal); -} -bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) -{ - return CopyLinearVRAM<8*1024>(VRAMFlat_BOBJExtPal, &VRAMMap_BOBJExtPal, dirty, ReadVRAM_BOBJExtPal); -} - } diff --git a/src/GPU.h b/src/GPU.h index ea9c777d..17164ab7 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -22,67 +22,23 @@ #include #include "GPU2D.h" +#include "GPU3D.h" #include "NonStupidBitfield.h" #ifdef OGLRENDERER_ENABLED #include "GPU_OpenGL.h" #endif -namespace GPU + +namespace GPU3D { +class GPU3D; +} -extern u16 VCount; -extern u16 TotalScanlines; - -extern u16 DispStat[2]; - -extern u8 VRAMCNT[9]; -extern u8 VRAMSTAT; - -extern u8 Palette[2*1024]; -extern u8 OAM[2*1024]; - -extern u8 VRAM_A[128*1024]; -extern u8 VRAM_B[128*1024]; -extern u8 VRAM_C[128*1024]; -extern u8 VRAM_D[128*1024]; -extern u8 VRAM_E[ 64*1024]; -extern u8 VRAM_F[ 16*1024]; -extern u8 VRAM_G[ 16*1024]; -extern u8 VRAM_H[ 32*1024]; -extern u8 VRAM_I[ 16*1024]; - -extern u8* const VRAM[9]; - -extern u32 VRAMMap_LCDC; -extern u32 VRAMMap_ABG[0x20]; -extern u32 VRAMMap_AOBJ[0x10]; -extern u32 VRAMMap_BBG[0x8]; -extern u32 VRAMMap_BOBJ[0x8]; -extern u32 VRAMMap_ABGExtPal[4]; -extern u32 VRAMMap_AOBJExtPal; -extern u32 VRAMMap_BBGExtPal[4]; -extern u32 VRAMMap_BOBJExtPal; -extern u32 VRAMMap_Texture[4]; -extern u32 VRAMMap_TexPal[8]; -extern u32 VRAMMap_ARM7[2]; - -extern u8* VRAMPtr_ABG[0x20]; -extern u8* VRAMPtr_AOBJ[0x10]; -extern u8* VRAMPtr_BBG[0x8]; -extern u8* VRAMPtr_BOBJ[0x8]; - -extern int FrontBuffer; -extern u32* Framebuffer[2][2]; - -extern GPU2D::Unit GPU2D_A; -extern GPU2D::Unit GPU2D_B; - -extern int Renderer; - -const u32 VRAMDirtyGranularity = 512; - -extern NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9]; +namespace Melon +{ +static constexpr u32 VRAMDirtyGranularity = 512; +class GPU; template struct VRAMTrackingSet @@ -100,60 +56,9 @@ struct VRAMTrackingSet Mapping[i] = 0x8000; } } - NonStupidBitField DeriveState(u32* currentMappings); + NonStupidBitField DeriveState(u32* currentMappings, GPU& gpu); }; -extern VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG; -extern VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ; -extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG; -extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ; - -extern VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal; -extern VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal; -extern VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal; -extern VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal; - -extern VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture; -extern VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal; - -extern u8 VRAMFlat_ABG[512*1024]; -extern u8 VRAMFlat_BBG[128*1024]; -extern u8 VRAMFlat_AOBJ[256*1024]; -extern u8 VRAMFlat_BOBJ[128*1024]; - -extern u8 VRAMFlat_ABGExtPal[32*1024]; -extern u8 VRAMFlat_BBGExtPal[32*1024]; - -extern u8 VRAMFlat_AOBJExtPal[8*1024]; -extern u8 VRAMFlat_BOBJExtPal[8*1024]; - -extern u8 VRAMFlat_Texture[512*1024]; -extern u8 VRAMFlat_TexPal[128*1024]; - -bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty); - -bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty); - -bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty); - -bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty); - -bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty); -bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty); - -void SyncDirtyFlags(); - -extern u32 OAMDirty; -extern u32 PaletteDirty; - -#ifdef OGLRENDERER_ENABLED -extern std::unique_ptr CurGLCompositor; -#endif - struct RenderSettings { bool Soft_Threaded; @@ -162,462 +67,646 @@ struct RenderSettings bool GL_BetterPolygons; }; - -bool Init(); -void DeInit(); -void Reset(); -void Stop(); - -void DoSavestate(Savestate* file); - -void InitRenderer(int renderer); -void DeInitRenderer(); -void ResetRenderer(); - -void SetRenderSettings(int renderer, RenderSettings& settings); - - -u8* GetUniqueBankPtr(u32 mask, u32 offset); - -void MapVRAM_AB(u32 bank, u8 cnt); -void MapVRAM_CD(u32 bank, u8 cnt); -void MapVRAM_E(u32 bank, u8 cnt); -void MapVRAM_FG(u32 bank, u8 cnt); -void MapVRAM_H(u32 bank, u8 cnt); -void MapVRAM_I(u32 bank, u8 cnt); - - -template -T ReadVRAM_LCDC(u32 addr) +class GPU { - int bank; +public: + GPU() noexcept; + ~GPU() noexcept; + void Reset() noexcept; + void Stop() noexcept; - switch (addr & 0xFF8FC000) + void DoSavestate(Savestate* file) noexcept; + + [[deprecated("Set the renderer directly instead of using an integer code")]] void InitRenderer(int renderer) noexcept; + void DeInitRenderer() noexcept; + void ResetRenderer() noexcept; + + void SetRenderSettings(int renderer, RenderSettings& settings) noexcept; + + u8* GetUniqueBankPtr(u32 mask, u32 offset) noexcept; + const u8* GetUniqueBankPtr(u32 mask, u32 offset) const noexcept; + + void MapVRAM_AB(u32 bank, u8 cnt) noexcept; + void MapVRAM_CD(u32 bank, u8 cnt) noexcept; + void MapVRAM_E(u32 bank, u8 cnt) noexcept; + void MapVRAM_FG(u32 bank, u8 cnt) noexcept; + void MapVRAM_H(u32 bank, u8 cnt) noexcept; + void MapVRAM_I(u32 bank, u8 cnt) noexcept; + + template + T ReadVRAM_LCDC(u32 addr) const noexcept { - case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000: - case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000: - bank = 0; - addr &= 0x1FFFF; - break; + int bank; - case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000: - case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000: - bank = 1; - addr &= 0x1FFFF; - break; + switch (addr & 0xFF8FC000) + { + case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000: + case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000: + bank = 0; + addr &= 0x1FFFF; + break; - case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000: - case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000: - bank = 2; - addr &= 0x1FFFF; - break; + case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000: + case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000: + bank = 1; + addr &= 0x1FFFF; + break; - case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000: - case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000: - bank = 3; - addr &= 0x1FFFF; - break; + case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000: + case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000: + bank = 2; + addr &= 0x1FFFF; + break; - case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000: - bank = 4; - addr &= 0xFFFF; - break; + case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000: + case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000: + bank = 3; + addr &= 0x1FFFF; + break; - case 0x06890000: - bank = 5; - addr &= 0x3FFF; - break; + case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000: + bank = 4; + addr &= 0xFFFF; + break; - case 0x06894000: - bank = 6; - addr &= 0x3FFF; - break; + case 0x06890000: + bank = 5; + addr &= 0x3FFF; + break; - case 0x06898000: - case 0x0689C000: - bank = 7; - addr &= 0x7FFF; - break; + case 0x06894000: + bank = 6; + addr &= 0x3FFF; + break; - case 0x068A0000: - bank = 8; - addr &= 0x3FFF; - break; + case 0x06898000: + case 0x0689C000: + bank = 7; + addr &= 0x7FFF; + break; - default: return 0; + case 0x068A0000: + bank = 8; + addr &= 0x3FFF; + break; + + default: return 0; + } + + if (VRAMMap_LCDC & (1< -void WriteVRAM_LCDC(u32 addr, T val) -{ - int bank; - - switch (addr & 0xFF8FC000) + template + void WriteVRAM_LCDC(u32 addr, T val) { - case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000: - case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000: - bank = 0; - addr &= 0x1FFFF; - break; + int bank; - case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000: - case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000: - bank = 1; - addr &= 0x1FFFF; - break; + switch (addr & 0xFF8FC000) + { + case 0x06800000: case 0x06804000: case 0x06808000: case 0x0680C000: + case 0x06810000: case 0x06814000: case 0x06818000: case 0x0681C000: + bank = 0; + addr &= 0x1FFFF; + break; - case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000: - case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000: - bank = 2; - addr &= 0x1FFFF; - break; + case 0x06820000: case 0x06824000: case 0x06828000: case 0x0682C000: + case 0x06830000: case 0x06834000: case 0x06838000: case 0x0683C000: + bank = 1; + addr &= 0x1FFFF; + break; - case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000: - case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000: - bank = 3; - addr &= 0x1FFFF; - break; + case 0x06840000: case 0x06844000: case 0x06848000: case 0x0684C000: + case 0x06850000: case 0x06854000: case 0x06858000: case 0x0685C000: + bank = 2; + addr &= 0x1FFFF; + break; - case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000: - bank = 4; - addr &= 0xFFFF; - break; + case 0x06860000: case 0x06864000: case 0x06868000: case 0x0686C000: + case 0x06870000: case 0x06874000: case 0x06878000: case 0x0687C000: + bank = 3; + addr &= 0x1FFFF; + break; - case 0x06890000: - bank = 5; - addr &= 0x3FFF; - break; + case 0x06880000: case 0x06884000: case 0x06888000: case 0x0688C000: + bank = 4; + addr &= 0xFFFF; + break; - case 0x06894000: - bank = 6; - addr &= 0x3FFF; - break; + case 0x06890000: + bank = 5; + addr &= 0x3FFF; + break; - case 0x06898000: - case 0x0689C000: - bank = 7; - addr &= 0x7FFF; - break; + case 0x06894000: + bank = 6; + addr &= 0x3FFF; + break; - case 0x068A0000: - bank = 8; - addr &= 0x3FFF; - break; + case 0x06898000: + case 0x0689C000: + bank = 7; + addr &= 0x7FFF; + break; - default: return; + case 0x068A0000: + bank = 8; + addr &= 0x3FFF; + break; + + default: return; + } + + if (VRAMMap_LCDC & (1< + T ReadVRAM_ABG(u32 addr) const noexcept { - *(T*)&VRAM[bank][addr] = val; - VRAMDirty[bank][addr / VRAMDirtyGranularity] = true; + u8* ptr = VRAMPtr_ABG[(addr >> 14) & 0x1F]; + if (ptr) return *(T*)&ptr[addr & 0x3FFF]; + + T ret = 0; + u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F]; + + if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; + if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; + if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; + if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; + if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; + if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; + + return ret; } -} - -template -T ReadVRAM_ABG(u32 addr) -{ - u8* ptr = VRAMPtr_ABG[(addr >> 14) & 0x1F]; - if (ptr) return *(T*)&ptr[addr & 0x3FFF]; - - T ret = 0; - u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F]; - - if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; - if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; - if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; - if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; - if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; - if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; - - return ret; -} - -template -void WriteVRAM_ABG(u32 addr, T val) -{ - u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F]; - - if (mask & (1<<0)) + template + void WriteVRAM_ABG(u32 addr, T val) { - VRAMDirty[0][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_A[addr & 0x1FFFF] = val; + u32 mask = VRAMMap_ABG[(addr >> 14) & 0x1F]; + + if (mask & (1<<0)) + { + VRAMDirty[0][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_A[addr & 0x1FFFF] = val; + } + if (mask & (1<<1)) + { + VRAMDirty[1][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_B[addr & 0x1FFFF] = val; + } + if (mask & (1<<2)) + { + VRAMDirty[2][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_C[addr & 0x1FFFF] = val; + } + if (mask & (1<<3)) + { + VRAMDirty[3][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_D[addr & 0x1FFFF] = val; + } + if (mask & (1<<4)) + { + VRAMDirty[4][(addr & 0xFFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_E[addr & 0xFFFF] = val; + } + if (mask & (1<<5)) + { + VRAMDirty[5][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_F[addr & 0x3FFF] = val; + } + if (mask & (1<<6)) + { + VRAMDirty[6][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_G[addr & 0x3FFF] = val; + } } - if (mask & (1<<1)) + + + template + T ReadVRAM_AOBJ(u32 addr) const noexcept { - VRAMDirty[1][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_B[addr & 0x1FFFF] = val; + u8* ptr = VRAMPtr_AOBJ[(addr >> 14) & 0xF]; + if (ptr) return *(T*)&ptr[addr & 0x3FFF]; + + T ret = 0; + u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF]; + + if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; + if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; + if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; + if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; + + return ret; } - if (mask & (1<<2)) + + template + void WriteVRAM_AOBJ(u32 addr, T val) { - VRAMDirty[2][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_C[addr & 0x1FFFF] = val; + u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF]; + + if (mask & (1<<0)) + { + VRAMDirty[0][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_A[addr & 0x1FFFF] = val; + } + if (mask & (1<<1)) + { + VRAMDirty[1][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_B[addr & 0x1FFFF] = val; + } + if (mask & (1<<4)) + { + VRAMDirty[4][(addr & 0xFFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_E[addr & 0xFFFF] = val; + } + if (mask & (1<<5)) + { + VRAMDirty[5][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_F[addr & 0x3FFF] = val; + } + if (mask & (1<<6)) + { + VRAMDirty[6][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_G[addr & 0x3FFF] = val; + } } - if (mask & (1<<3)) + + + template + T ReadVRAM_BBG(u32 addr) const noexcept { - VRAMDirty[3][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_D[addr & 0x1FFFF] = val; + u8* ptr = VRAMPtr_BBG[(addr >> 14) & 0x7]; + if (ptr) return *(T*)&ptr[addr & 0x3FFF]; + + T ret = 0; + u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7]; + + if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; + if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF]; + if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF]; + + return ret; } - if (mask & (1<<4)) + + template + void WriteVRAM_BBG(u32 addr, T val) { - VRAMDirty[4][(addr & 0xFFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_E[addr & 0xFFFF] = val; + u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7]; + + if (mask & (1<<2)) + { + VRAMDirty[2][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_C[addr & 0x1FFFF] = val; + } + if (mask & (1<<7)) + { + VRAMDirty[7][(addr & 0x7FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_H[addr & 0x7FFF] = val; + } + if (mask & (1<<8)) + { + VRAMDirty[8][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_I[addr & 0x3FFF] = val; + } } - if (mask & (1<<5)) + + + template + T ReadVRAM_BOBJ(u32 addr) const noexcept { - VRAMDirty[5][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_F[addr & 0x3FFF] = val; + u8* ptr = VRAMPtr_BOBJ[(addr >> 14) & 0x7]; + if (ptr) return *(T*)&ptr[addr & 0x3FFF]; + + T ret = 0; + u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7]; + + if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; + if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF]; + + return ret; } - if (mask & (1<<6)) + + template + void WriteVRAM_BOBJ(u32 addr, T val) { - VRAMDirty[6][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_G[addr & 0x3FFF] = val; + u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7]; + + if (mask & (1<<3)) + { + VRAMDirty[3][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_D[addr & 0x1FFFF] = val; + } + if (mask & (1<<8)) + { + VRAMDirty[8][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; + *(T*)&VRAM_I[addr & 0x3FFF] = val; + } } -} - -template -T ReadVRAM_AOBJ(u32 addr) -{ - u8* ptr = VRAMPtr_AOBJ[(addr >> 14) & 0xF]; - if (ptr) return *(T*)&ptr[addr & 0x3FFF]; - - T ret = 0; - u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF]; - - if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; - if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; - if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; - if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; - - return ret; -} - -template -void WriteVRAM_AOBJ(u32 addr, T val) -{ - u32 mask = VRAMMap_AOBJ[(addr >> 14) & 0xF]; - - if (mask & (1<<0)) + template + T ReadVRAM_ARM7(u32 addr) const noexcept { - VRAMDirty[0][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_A[addr & 0x1FFFF] = val; + T ret = 0; + u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1]; + + if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; + if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; + + return ret; } - if (mask & (1<<1)) + + template + void WriteVRAM_ARM7(u32 addr, T val) { - VRAMDirty[1][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_B[addr & 0x1FFFF] = val; + u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1]; + + if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val; + if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val; } - if (mask & (1<<4)) + + + template + T ReadVRAM_BG(u32 addr) const noexcept { - VRAMDirty[4][(addr & 0xFFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_E[addr & 0xFFFF] = val; + if ((addr & 0xFFE00000) == 0x06000000) + return ReadVRAM_ABG(addr); + else + return ReadVRAM_BBG(addr); } - if (mask & (1<<5)) + + template + T ReadVRAM_OBJ(u32 addr) const noexcept { - VRAMDirty[5][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_F[addr & 0x3FFF] = val; + if ((addr & 0xFFE00000) == 0x06400000) + return ReadVRAM_AOBJ(addr); + else + return ReadVRAM_BOBJ(addr); } - if (mask & (1<<6)) + + + template + T ReadVRAM_Texture(u32 addr) const noexcept { - VRAMDirty[6][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_G[addr & 0x3FFF] = val; + T ret = 0; + u32 mask = VRAMMap_Texture[(addr >> 17) & 0x3]; + + if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; + if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; + if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; + if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; + + return ret; } -} - -template -T ReadVRAM_BBG(u32 addr) -{ - u8* ptr = VRAMPtr_BBG[(addr >> 14) & 0x7]; - if (ptr) return *(T*)&ptr[addr & 0x3FFF]; - - T ret = 0; - u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7]; - - if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; - if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF]; - if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF]; - - return ret; -} - -template -void WriteVRAM_BBG(u32 addr, T val) -{ - u32 mask = VRAMMap_BBG[(addr >> 14) & 0x7]; - - if (mask & (1<<2)) + template + T ReadVRAM_TexPal(u32 addr) const noexcept { - VRAMDirty[2][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_C[addr & 0x1FFFF] = val; + T ret = 0; + u32 mask = VRAMMap_TexPal[(addr >> 14) & 0x7]; + + if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; + if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; + + return ret; } - if (mask & (1<<7)) + + template + T ReadPalette(u32 addr) const noexcept { - VRAMDirty[7][(addr & 0x7FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_H[addr & 0x7FFF] = val; + return *(T*)&Palette[addr & 0x7FF]; } - if (mask & (1<<8)) + + template + void WritePalette(u32 addr, T val) { - VRAMDirty[8][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_I[addr & 0x3FFF] = val; + addr &= 0x7FF; + + *(T*)&Palette[addr] = val; + PaletteDirty |= 1 << (addr / VRAMDirtyGranularity); } -} - -template -T ReadVRAM_BOBJ(u32 addr) -{ - u8* ptr = VRAMPtr_BOBJ[(addr >> 14) & 0x7]; - if (ptr) return *(T*)&ptr[addr & 0x3FFF]; - - T ret = 0; - u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7]; - - if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; - if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x3FFF]; - - return ret; -} - -template -void WriteVRAM_BOBJ(u32 addr, T val) -{ - u32 mask = VRAMMap_BOBJ[(addr >> 14) & 0x7]; - - if (mask & (1<<3)) + template + T ReadOAM(u32 addr) const noexcept { - VRAMDirty[3][(addr & 0x1FFFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_D[addr & 0x1FFFF] = val; + return *(T*)&OAM[addr & 0x7FF]; } - if (mask & (1<<8)) + + template + void WriteOAM(u32 addr, T val) { - VRAMDirty[8][(addr & 0x3FFF) / VRAMDirtyGranularity] = true; - *(T*)&VRAM_I[addr & 0x3FFF] = val; + addr &= 0x7FF; + + *(T*)&OAM[addr] = val; + OAMDirty |= 1 << (addr / 1024); } + + void SetPowerCnt(u32 val) noexcept; + + void StartFrame() noexcept; + void FinishFrame(u32 lines) noexcept; + void BlankFrame() noexcept; + void StartScanline(u32 line) noexcept; + void StartHBlank(u32 line) noexcept; + + void DisplayFIFO(u32 x) noexcept; + + void SetDispStat(u32 cpu, u16 val) noexcept; + + void SetVCount(u16 val) noexcept; + bool MakeVRAMFlat_ABGCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_BBGCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept; + + bool MakeVRAMFlat_AOBJCoherent(NonStupidBitField<256*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_BOBJCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept; + + bool MakeVRAMFlat_ABGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_BBGExtPalCoherent(NonStupidBitField<32*1024/VRAMDirtyGranularity>& dirty) noexcept; + + bool MakeVRAMFlat_AOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_BOBJExtPalCoherent(NonStupidBitField<8*1024/VRAMDirtyGranularity>& dirty) noexcept; + + bool MakeVRAMFlat_TextureCoherent(NonStupidBitField<512*1024/VRAMDirtyGranularity>& dirty) noexcept; + bool MakeVRAMFlat_TexPalCoherent(NonStupidBitField<128*1024/VRAMDirtyGranularity>& dirty) noexcept; + + void SyncDirtyFlags() noexcept; + + u16 VCount = 0; + u16 TotalScanlines = 0; + u16 DispStat[2] {}; + u8 VRAMCNT[9] {}; + u8 VRAMSTAT = 0; + + u8 Palette[2*1024] {}; + u8 OAM[2*1024] {}; + + u8 VRAM_A[128*1024] {}; + u8 VRAM_B[128*1024] {}; + u8 VRAM_C[128*1024] {}; + u8 VRAM_D[128*1024] {}; + u8 VRAM_E[ 64*1024] {}; + u8 VRAM_F[ 16*1024] {}; + u8 VRAM_G[ 16*1024] {}; + u8 VRAM_H[ 32*1024] {}; + u8 VRAM_I[ 16*1024] {}; + + u8* const VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I}; + u32 const VRAMMask[9] = {0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF}; + + u32 VRAMMap_LCDC = 0; + u32 VRAMMap_ABG[0x20] {}; + u32 VRAMMap_AOBJ[0x10] {}; + u32 VRAMMap_BBG[0x8] {}; + u32 VRAMMap_BOBJ[0x8] {}; + u32 VRAMMap_ABGExtPal[4] {}; + u32 VRAMMap_AOBJExtPal {}; + u32 VRAMMap_BBGExtPal[4] {}; + u32 VRAMMap_BOBJExtPal {}; + u32 VRAMMap_Texture[4] {}; + u32 VRAMMap_TexPal[8] {}; + u32 VRAMMap_ARM7[2] {}; + + u8* VRAMPtr_ABG[0x20] {}; + u8* VRAMPtr_AOBJ[0x10] {}; + u8* VRAMPtr_BBG[0x8] {}; + u8* VRAMPtr_BOBJ[0x8] {}; + + int FrontBuffer = 0; + u32* Framebuffer[2][2] {}; + + GPU2D::Unit GPU2D_A; + GPU2D::Unit GPU2D_B; + GPU3D::GPU3D GPU3D {}; + + NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9] {}; + VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG {}; + VRAMTrackingSet<256*1024, 16*1024> VRAMDirty_AOBJ {}; + VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BBG {}; + VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_BOBJ {}; + + VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_ABGExtPal {}; + VRAMTrackingSet<32*1024, 8*1024> VRAMDirty_BBGExtPal {}; + VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_AOBJExtPal {}; + VRAMTrackingSet<8*1024, 8*1024> VRAMDirty_BOBJExtPal {}; + + VRAMTrackingSet<512*1024, 128*1024> VRAMDirty_Texture {}; + VRAMTrackingSet<128*1024, 16*1024> VRAMDirty_TexPal {}; + + u8 VRAMFlat_ABG[512*1024] {}; + u8 VRAMFlat_BBG[128*1024] {}; + u8 VRAMFlat_AOBJ[256*1024] {}; + u8 VRAMFlat_BOBJ[128*1024] {}; + + u8 VRAMFlat_ABGExtPal[32*1024] {}; + u8 VRAMFlat_BBGExtPal[32*1024] {}; + + u8 VRAMFlat_AOBJExtPal[8*1024] {}; + u8 VRAMFlat_BOBJExtPal[8*1024] {}; + + u8 VRAMFlat_Texture[512*1024] {}; + u8 VRAMFlat_TexPal[128*1024] {}; + + int Renderer = 0; +#ifdef OGLRENDERER_ENABLED + std::unique_ptr CurGLCompositor = nullptr; +#endif +private: + void ResetVRAMCache() noexcept; + void AssignFramebuffers() noexcept; + template + T ReadVRAM_ABGExtPal(u32 addr) const noexcept + { + u32 mask = VRAMMap_ABGExtPal[(addr >> 13) & 0x3]; + + T ret = 0; + if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0x7FFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; + if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; + + return ret; + } + + template + T ReadVRAM_BBGExtPal(u32 addr) const noexcept + { + u32 mask = VRAMMap_BBGExtPal[(addr >> 13) & 0x3]; + + T ret = 0; + if (mask & (1<<7)) ret |= *(T*)&VRAM_H[addr & 0x7FFF]; + + return ret; + } + + template + T ReadVRAM_AOBJExtPal(u32 addr) const noexcept + { + u32 mask = VRAMMap_AOBJExtPal; + + T ret = 0; + if (mask & (1<<4)) ret |= *(T*)&VRAM_F[addr & 0x1FFF]; + if (mask & (1<<5)) ret |= *(T*)&VRAM_G[addr & 0x1FFF]; + + return ret; + } + + template + T ReadVRAM_BOBJExtPal(u32 addr) const noexcept + { + u32 mask = VRAMMap_BOBJExtPal; + + T ret = 0; + if (mask & (1<<8)) ret |= *(T*)&VRAM_I[addr & 0x1FFF]; + + return ret; + } + + template + constexpr bool CopyLinearVRAM(u8* flat, const u32* mappings, NonStupidBitField& dirty, u64 (GPU::* const slowAccess)(u32) const noexcept) noexcept + { + const u32 VRAMBitsPerMapping = MappingGranularity / VRAMDirtyGranularity; + + bool change = false; + + typename NonStupidBitField::Iterator it = dirty.Begin(); + while (it != dirty.End()) + { + u32 offset = *it * VRAMDirtyGranularity; + u8* dst = flat + offset; + u8* fastAccess = GetUniqueBankPtr(mappings[*it / VRAMBitsPerMapping], offset); + if (fastAccess) + { + memcpy(dst, fastAccess, VRAMDirtyGranularity); + } + else + { + for (u32 i = 0; i < VRAMDirtyGranularity; i += 8) + *(u64*)&dst[i] = (this->*slowAccess)(offset + i); + } + change = true; + it++; + } + return change; + } + + u32 NextVCount = 0; + + bool RunFIFO = false; + + u16 VMatch[2] {}; + + std::unique_ptr GPU2D_Renderer = nullptr; + + u32 OAMDirty = 0; + u32 PaletteDirty = 0; +}; } -template -T ReadVRAM_ARM7(u32 addr) -{ - T ret = 0; - u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1]; - - if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; - if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; - - return ret; -} - -template -void WriteVRAM_ARM7(u32 addr, T val) -{ - u32 mask = VRAMMap_ARM7[(addr >> 17) & 0x1]; - - if (mask & (1<<2)) *(T*)&VRAM_C[addr & 0x1FFFF] = val; - if (mask & (1<<3)) *(T*)&VRAM_D[addr & 0x1FFFF] = val; -} - - -template -T ReadVRAM_BG(u32 addr) -{ - if ((addr & 0xFFE00000) == 0x06000000) - return ReadVRAM_ABG(addr); - else - return ReadVRAM_BBG(addr); -} - -template -T ReadVRAM_OBJ(u32 addr) -{ - if ((addr & 0xFFE00000) == 0x06400000) - return ReadVRAM_AOBJ(addr); - else - return ReadVRAM_BOBJ(addr); -} - - -template -T ReadVRAM_Texture(u32 addr) -{ - T ret = 0; - u32 mask = VRAMMap_Texture[(addr >> 17) & 0x3]; - - if (mask & (1<<0)) ret |= *(T*)&VRAM_A[addr & 0x1FFFF]; - if (mask & (1<<1)) ret |= *(T*)&VRAM_B[addr & 0x1FFFF]; - if (mask & (1<<2)) ret |= *(T*)&VRAM_C[addr & 0x1FFFF]; - if (mask & (1<<3)) ret |= *(T*)&VRAM_D[addr & 0x1FFFF]; - - return ret; -} - -template -T ReadVRAM_TexPal(u32 addr) -{ - T ret = 0; - u32 mask = VRAMMap_TexPal[(addr >> 14) & 0x7]; - - if (mask & (1<<4)) ret |= *(T*)&VRAM_E[addr & 0xFFFF]; - if (mask & (1<<5)) ret |= *(T*)&VRAM_F[addr & 0x3FFF]; - if (mask & (1<<6)) ret |= *(T*)&VRAM_G[addr & 0x3FFF]; - - return ret; -} - -template -T ReadPalette(u32 addr) -{ - return *(T*)&Palette[addr & 0x7FF]; -} - -template -void WritePalette(u32 addr, T val) -{ - addr &= 0x7FF; - - *(T*)&Palette[addr] = val; - PaletteDirty |= 1 << (addr / VRAMDirtyGranularity); -} - -template -T ReadOAM(u32 addr) -{ - return *(T*)&OAM[addr & 0x7FF]; -} - -template -void WriteOAM(u32 addr, T val) -{ - addr &= 0x7FF; - - *(T*)&OAM[addr] = val; - OAMDirty |= 1 << (addr / 1024); -} - -void SetPowerCnt(u32 val); - -void StartFrame(); -void FinishFrame(u32 lines); -void BlankFrame(); -void StartScanline(u32 line); -void StartHBlank(u32 line); - -void DisplayFIFO(u32 x); - -void SetDispStat(u32 cpu, u16 val); - -void SetVCount(u16 val); -} - -#include "GPU3D.h" - #endif diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index d1f6bebd..e892116c 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -85,9 +85,8 @@ using Platform::LogLevel; namespace GPU2D { -Unit::Unit(u32 num) +Unit::Unit(u32 num, Melon::GPU& gpu) : Num(num), GPU(gpu) { - Num = num; } void Unit::Reset() @@ -287,10 +286,10 @@ void Unit::Write8(u32 addr, u8 val) return; case 0x10: - if (!Num) GPU3D::SetRenderXPos((GPU3D::RenderXPos & 0xFF00) | val); + if (!Num) GPU.GPU3D.SetRenderXPos((GPU.GPU3D.GetRenderXPos() & 0xFF00) | val); break; case 0x11: - if (!Num) GPU3D::SetRenderXPos((GPU3D::RenderXPos & 0x00FF) | (val << 8)); + if (!Num) GPU.GPU3D.SetRenderXPos((GPU.GPU3D.GetRenderXPos() & 0x00FF) | (val << 8)); break; } @@ -383,7 +382,7 @@ void Unit::Write16(u32 addr, u16 val) return; case 0x010: - if (!Num) GPU3D::SetRenderXPos(val); + if (!Num) GPU.GPU3D.SetRenderXPos(val); break; case 0x068: @@ -422,21 +421,21 @@ void Unit::Write16(u32 addr, u16 val) case 0x026: BGRotD[0] = val; return; case 0x028: BGXRef[0] = (BGXRef[0] & 0xFFFF0000) | val; - if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0]; + if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0]; return; case 0x02A: if (val & 0x0800) val |= 0xF000; BGXRef[0] = (BGXRef[0] & 0xFFFF) | (val << 16); - if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0]; + if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0]; return; case 0x02C: BGYRef[0] = (BGYRef[0] & 0xFFFF0000) | val; - if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0]; + if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0]; return; case 0x02E: if (val & 0x0800) val |= 0xF000; BGYRef[0] = (BGYRef[0] & 0xFFFF) | (val << 16); - if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0]; + if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0]; return; case 0x030: BGRotA[1] = val; return; @@ -445,21 +444,21 @@ void Unit::Write16(u32 addr, u16 val) case 0x036: BGRotD[1] = val; return; case 0x038: BGXRef[1] = (BGXRef[1] & 0xFFFF0000) | val; - if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1]; + if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1]; return; case 0x03A: if (val & 0x0800) val |= 0xF000; BGXRef[1] = (BGXRef[1] & 0xFFFF) | (val << 16); - if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1]; + if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1]; return; case 0x03C: BGYRef[1] = (BGYRef[1] & 0xFFFF0000) | val; - if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1]; + if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1]; return; case 0x03E: if (val & 0x0800) val |= 0xF000; BGYRef[1] = (BGYRef[1] & 0xFFFF) | (val << 16); - if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1]; + if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1]; return; case 0x040: @@ -541,23 +540,23 @@ void Unit::Write32(u32 addr, u32 val) case 0x028: if (val & 0x08000000) val |= 0xF0000000; BGXRef[0] = val; - if (GPU::VCount < 192) BGXRefInternal[0] = BGXRef[0]; + if (GPU.VCount < 192) BGXRefInternal[0] = BGXRef[0]; return; case 0x02C: if (val & 0x08000000) val |= 0xF0000000; BGYRef[0] = val; - if (GPU::VCount < 192) BGYRefInternal[0] = BGYRef[0]; + if (GPU.VCount < 192) BGYRefInternal[0] = BGYRef[0]; return; case 0x038: if (val & 0x08000000) val |= 0xF0000000; BGXRef[1] = val; - if (GPU::VCount < 192) BGXRefInternal[1] = BGXRef[1]; + if (GPU.VCount < 192) BGXRefInternal[1] = BGXRef[1]; return; case 0x03C: if (val & 0x08000000) val |= 0xF0000000; BGYRef[1] = val; - if (GPU::VCount < 192) BGYRefInternal[1] = BGYRef[1]; + if (GPU.VCount < 192) BGYRefInternal[1] = BGYRef[1]; return; } } @@ -628,15 +627,15 @@ u16* Unit::GetBGExtPal(u32 slot, u32 pal) const u32 PaletteSize = 256 * 2; const u32 SlotSize = PaletteSize * 16; return (u16*)&(Num == 0 - ? GPU::VRAMFlat_ABGExtPal - : GPU::VRAMFlat_BBGExtPal)[slot * SlotSize + pal * PaletteSize]; + ? GPU.VRAMFlat_ABGExtPal + : GPU.VRAMFlat_BBGExtPal)[slot * SlotSize + pal * PaletteSize]; } u16* Unit::GetOBJExtPal() { return Num == 0 - ? (u16*)GPU::VRAMFlat_AOBJExtPal - : (u16*)GPU::VRAMFlat_BOBJExtPal; + ? (u16*)GPU.VRAMFlat_AOBJExtPal + : (u16*)GPU.VRAMFlat_BOBJExtPal; } void Unit::CheckWindows(u32 line) @@ -698,12 +697,12 @@ void Unit::GetBGVRAM(u8*& data, u32& mask) { if (Num == 0) { - data = GPU::VRAMFlat_ABG; + data = GPU.VRAMFlat_ABG; mask = 0x7FFFF; } else { - data = GPU::VRAMFlat_BBG; + data = GPU.VRAMFlat_BBG; mask = 0x1FFFF; } } @@ -712,12 +711,12 @@ void Unit::GetOBJVRAM(u8*& data, u32& mask) { if (Num == 0) { - data = GPU::VRAMFlat_AOBJ; + data = GPU.VRAMFlat_AOBJ; mask = 0x3FFFF; } else { - data = GPU::VRAMFlat_BOBJ; + data = GPU.VRAMFlat_BOBJ; mask = 0x1FFFF; } } diff --git a/src/GPU2D.h b/src/GPU2D.h index ffef57d7..ad052bf1 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -22,13 +22,20 @@ #include "types.h" #include "Savestate.h" +namespace Melon +{ +class GPU; +} + namespace GPU2D { class Unit { public: - Unit(u32 num); + // take a reference to the GPU so we can access its state + // and ensure that it's not null + Unit(u32 num, Melon::GPU& gpu); Unit(const Unit&) = delete; Unit& operator=(const Unit&) = delete; @@ -116,6 +123,8 @@ public: u32 CaptureCnt; u16 MasterBrightness; +private: + Melon::GPU& GPU; }; class Renderer2D diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 495b7471..7447bcf9 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -18,12 +18,13 @@ #include "GPU2D_Soft.h" #include "GPU.h" +#include "GPU3D_OpenGL.h" namespace GPU2D { -SoftRenderer::SoftRenderer() - : Renderer2D() +SoftRenderer::SoftRenderer(Melon::GPU& gpu) + : Renderer2D(), GPU(gpu) { // initialize mosaic table for (int m = 0; m < 16; m++) @@ -165,29 +166,29 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) { CurUnit = unit; - int stride = GPU3D::CurrentRenderer->Accelerated ? (256*3 + 1) : 256; + int stride = GPU.GPU3D.IsRendererAccelerated() ? (256*3 + 1) : 256; u32* dst = &Framebuffer[CurUnit->Num][stride * line]; int n3dline = line; - line = GPU::VCount; + line = GPU.VCount; if (CurUnit->Num == 0) { - auto bgDirty = GPU::VRAMDirty_ABG.DeriveState(GPU::VRAMMap_ABG); - GPU::MakeVRAMFlat_ABGCoherent(bgDirty); - auto bgExtPalDirty = GPU::VRAMDirty_ABGExtPal.DeriveState(GPU::VRAMMap_ABGExtPal); - GPU::MakeVRAMFlat_ABGExtPalCoherent(bgExtPalDirty); - auto objExtPalDirty = GPU::VRAMDirty_AOBJExtPal.DeriveState(&GPU::VRAMMap_AOBJExtPal); - GPU::MakeVRAMFlat_AOBJExtPalCoherent(objExtPalDirty); + auto bgDirty = GPU.VRAMDirty_ABG.DeriveState(GPU.VRAMMap_ABG, GPU); + GPU.MakeVRAMFlat_ABGCoherent(bgDirty); + auto bgExtPalDirty = GPU.VRAMDirty_ABGExtPal.DeriveState(GPU.VRAMMap_ABGExtPal, GPU); + GPU.MakeVRAMFlat_ABGExtPalCoherent(bgExtPalDirty); + auto objExtPalDirty = GPU.VRAMDirty_AOBJExtPal.DeriveState(&GPU.VRAMMap_AOBJExtPal, GPU); + GPU.MakeVRAMFlat_AOBJExtPalCoherent(objExtPalDirty); } else { - auto bgDirty = GPU::VRAMDirty_BBG.DeriveState(GPU::VRAMMap_BBG); - GPU::MakeVRAMFlat_BBGCoherent(bgDirty); - auto bgExtPalDirty = GPU::VRAMDirty_BBGExtPal.DeriveState(GPU::VRAMMap_BBGExtPal); - GPU::MakeVRAMFlat_BBGExtPalCoherent(bgExtPalDirty); - auto objExtPalDirty = GPU::VRAMDirty_BOBJExtPal.DeriveState(&GPU::VRAMMap_BOBJExtPal); - GPU::MakeVRAMFlat_BOBJExtPalCoherent(objExtPalDirty); + auto bgDirty = GPU.VRAMDirty_BBG.DeriveState(GPU.VRAMMap_BBG, GPU); + GPU.MakeVRAMFlat_BBGCoherent(bgDirty); + auto bgExtPalDirty = GPU.VRAMDirty_BBGExtPal.DeriveState(GPU.VRAMMap_BBGExtPal, GPU); + GPU.MakeVRAMFlat_BBGExtPalCoherent(bgExtPalDirty); + auto objExtPalDirty = GPU.VRAMDirty_BOBJExtPal.DeriveState(&GPU.VRAMMap_BOBJExtPal, GPU); + GPU.MakeVRAMFlat_BOBJExtPalCoherent(objExtPalDirty); } bool forceblank = false; @@ -205,11 +206,11 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) if (CurUnit->Num == 0) { - if (!GPU3D::CurrentRenderer->Accelerated) - _3DLine = GPU3D::GetLine(n3dline); + if (!GPU.GPU3D.IsRendererAccelerated()) + _3DLine = GPU.GPU3D.GetLine(n3dline); else if (CurUnit->CaptureLatch && (((CurUnit->CaptureCnt >> 29) & 0x3) != 1)) { - _3DLine = GPU3D::GetLine(n3dline); + _3DLine = GPU.GPU3D.GetLine(n3dline); //GPU3D::GLRenderer::PrepareCaptureFrame(); } } @@ -219,7 +220,7 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) for (int i = 0; i < 256; i++) dst[i] = 0xFFFFFFFF; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { dst[256*3] = 0; } @@ -253,9 +254,9 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) case 2: // VRAM display { u32 vrambank = (CurUnit->DispCnt >> 18) & 0x3; - if (GPU::VRAMMap_LCDC & (1<MasterBrightness; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { dst[256*3] = masterBrightness | (CurUnit->DispCnt & 0x30000); return; @@ -363,11 +364,11 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) void SoftRenderer::VBlankEnd(Unit* unitA, Unit* unitB) { #ifdef OGLRENDERER_ENABLED - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { if ((unitA->CaptureCnt & (1<<31)) && (((unitA->CaptureCnt >> 29) & 0x3) != 1)) { - reinterpret_cast(GPU3D::CurrentRenderer.get())->PrepareCaptureFrame(); + reinterpret_cast(GPU.GPU3D.GetCurrentRenderer())->PrepareCaptureFrame(); } } #endif @@ -380,10 +381,10 @@ void SoftRenderer::DoCapture(u32 line, u32 width) // TODO: confirm this // it should work like VRAM display mode, which requires VRAM to be mapped to LCDC - if (!(GPU::VRAMMap_LCDC & (1<> 18) & 0x3) << 14) + (line * width); // TODO: handle 3D in GPU3D::CurrentRenderer->Accelerated mode!! @@ -396,7 +397,7 @@ void SoftRenderer::DoCapture(u32 line, u32 width) else { srcA = BGOBJLine; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { // in GPU3D::CurrentRenderer->Accelerated mode, compositing is normally done on the GPU // but when doing display capture, we do need the composited output @@ -468,8 +469,8 @@ void SoftRenderer::DoCapture(u32 line, u32 width) else { u32 srcvram = (CurUnit->DispCnt >> 18) & 0x3; - if (GPU::VRAMMap_LCDC & (1<DispCnt >> 16) & 0x3) != 2) srcBaddr += ((captureCnt >> 26) & 0x3) << 14; @@ -478,8 +479,8 @@ void SoftRenderer::DoCapture(u32 line, u32 width) dstaddr &= 0xFFFF; srcBaddr &= 0xFFFF; - static_assert(GPU::VRAMDirtyGranularity == 512, ""); - GPU::VRAMDirty[dstvram][(dstaddr * 2) / GPU::VRAMDirtyGranularity] = true; + static_assert(Melon::VRAMDirtyGranularity == 512); + GPU.VRAMDirty[dstvram][(dstaddr * 2) / Melon::VRAMDirtyGranularity] = true; switch ((captureCnt >> 29) & 0x3) { @@ -600,12 +601,12 @@ void SoftRenderer::DoCapture(u32 line, u32 width) { \ if ((bgCnt[num] & 0x0040) && (CurUnit->BGMosaicSize[0] > 0)) \ { \ - if (GPU3D::CurrentRenderer->Accelerated) DrawBG_##type(line, num); \ + if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_##type(line, num); \ else DrawBG_##type(line, num); \ } \ else \ { \ - if (GPU3D::CurrentRenderer->Accelerated) DrawBG_##type(line, num); \ + if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_##type(line, num); \ else DrawBG_##type(line, num); \ } \ } while (false) @@ -615,18 +616,18 @@ void SoftRenderer::DoCapture(u32 line, u32 width) { \ if ((bgCnt[2] & 0x0040) && (CurUnit->BGMosaicSize[0] > 0)) \ { \ - if (GPU3D::CurrentRenderer->Accelerated) DrawBG_Large(line); \ + if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_Large(line); \ else DrawBG_Large(line); \ } \ else \ { \ - if (GPU3D::CurrentRenderer->Accelerated) DrawBG_Large(line); \ + if (GPU.GPU3D.IsRendererAccelerated()) DrawBG_Large(line); \ else DrawBG_Large(line); \ } \ } while (false) #define DoInterleaveSprites(prio) \ - if (GPU3D::CurrentRenderer->Accelerated) InterleaveSprites(prio); else InterleaveSprites(prio); + if (GPU.GPU3D.IsRendererAccelerated()) InterleaveSprites(prio); else InterleaveSprites(prio); template void SoftRenderer::DrawScanlineBGMode(u32 line) @@ -756,8 +757,8 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line) } u64 backdrop; - if (CurUnit->Num) backdrop = *(u16*)&GPU::Palette[0x400]; - else backdrop = *(u16*)&GPU::Palette[0]; + if (CurUnit->Num) backdrop = *(u16*)&GPU.Palette[0x400]; + else backdrop = *(u16*)&GPU.Palette[0]; { u8 r = (backdrop & 0x001F) << 1; @@ -794,7 +795,7 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line) // color special effects // can likely be optimized - if (!GPU3D::CurrentRenderer->Accelerated) + if (!GPU.GPU3D.IsRendererAccelerated()) { for (int i = 0; i < 256; i++) { @@ -940,7 +941,7 @@ void SoftRenderer::DrawBG_3D() { int i = 0; - if (GPU3D::CurrentRenderer->Accelerated) + if (GPU.GPU3D.IsRendererAccelerated()) { for (i = 0; i < 256; i++) { @@ -997,14 +998,14 @@ void SoftRenderer::DrawBG_Text(u32 line, u32 bgnum) tilesetaddr = ((bgcnt & 0x003C) << 12); tilemapaddr = ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0x400]; + pal = (u16*)&GPU.Palette[0x400]; } else { tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12); tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0]; + pal = (u16*)&GPU.Palette[0]; } // adjust Y position in tilemap @@ -1176,14 +1177,14 @@ void SoftRenderer::DrawBG_Affine(u32 line, u32 bgnum) tilesetaddr = ((bgcnt & 0x003C) << 12); tilemapaddr = ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0x400]; + pal = (u16*)&GPU.Palette[0x400]; } else { tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12); tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0]; + pal = (u16*)&GPU.Palette[0]; } u16 curtile; @@ -1330,8 +1331,8 @@ void SoftRenderer::DrawBG_Extended(u32 line, u32 bgnum) { // 256-color bitmap - if (CurUnit->Num) pal = (u16*)&GPU::Palette[0x400]; - else pal = (u16*)&GPU::Palette[0]; + if (CurUnit->Num) pal = (u16*)&GPU.Palette[0x400]; + else pal = (u16*)&GPU.Palette[0]; u8 color; @@ -1389,14 +1390,14 @@ void SoftRenderer::DrawBG_Extended(u32 line, u32 bgnum) tilesetaddr = ((bgcnt & 0x003C) << 12); tilemapaddr = ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0x400]; + pal = (u16*)&GPU.Palette[0x400]; } else { tilesetaddr = ((CurUnit->DispCnt & 0x07000000) >> 8) + ((bgcnt & 0x003C) << 12); tilemapaddr = ((CurUnit->DispCnt & 0x38000000) >> 11) + ((bgcnt & 0x1F00) << 3); - pal = (u16*)&GPU::Palette[0]; + pal = (u16*)&GPU.Palette[0]; } u16 curtile; @@ -1507,8 +1508,8 @@ void SoftRenderer::DrawBG_Large(u32 line) // BG is always BG2 // 256-color bitmap - if (CurUnit->Num) pal = (u16*)&GPU::Palette[0x400]; - else pal = (u16*)&GPU::Palette[0]; + if (CurUnit->Num) pal = (u16*)&GPU.Palette[0x400]; + else pal = (u16*)&GPU.Palette[0]; u8 color; @@ -1581,7 +1582,7 @@ template void SoftRenderer::InterleaveSprites(u32 prio) { u32* objLine = OBJLine[CurUnit->Num]; - u16* pal = (u16*)&GPU::Palette[CurUnit->Num ? 0x600 : 0x200]; + u16* pal = (u16*)&GPU.Palette[CurUnit->Num ? 0x600 : 0x200]; if (CurUnit->DispCnt & 0x80000000) { @@ -1655,13 +1656,13 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit) if (CurUnit->Num == 0) { - auto objDirty = GPU::VRAMDirty_AOBJ.DeriveState(GPU::VRAMMap_AOBJ); - GPU::MakeVRAMFlat_AOBJCoherent(objDirty); + auto objDirty = GPU.VRAMDirty_AOBJ.DeriveState(GPU.VRAMMap_AOBJ, GPU); + GPU.MakeVRAMFlat_AOBJCoherent(objDirty); } else { - auto objDirty = GPU::VRAMDirty_BOBJ.DeriveState(GPU::VRAMMap_BOBJ); - GPU::MakeVRAMFlat_BOBJCoherent(objDirty); + auto objDirty = GPU.VRAMDirty_BOBJ.DeriveState(GPU.VRAMMap_BOBJ, GPU); + GPU.MakeVRAMFlat_BOBJCoherent(objDirty); } NumSprites[CurUnit->Num] = 0; @@ -1669,7 +1670,7 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit) memset(OBJWindow[CurUnit->Num], 0, 256); if (!(CurUnit->DispCnt & 0x1000)) return; - u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0]; + u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0]; const s32 spritewidth[16] = { @@ -1764,7 +1765,7 @@ void SoftRenderer::DrawSprites(u32 line, Unit* unit) template void SoftRenderer::DrawSprite_Rotscale(u32 num, u32 boundwidth, u32 boundheight, u32 width, u32 height, s32 xpos, s32 ypos) { - u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0]; + u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0]; u16* attrib = &oam[num * 4]; u16* rotparams = &oam[(((attrib[1] >> 9) & 0x1F) * 16) + 3]; @@ -1976,7 +1977,7 @@ void SoftRenderer::DrawSprite_Rotscale(u32 num, u32 boundwidth, u32 boundheight, template void SoftRenderer::DrawSprite_Normal(u32 num, u32 width, u32 height, s32 xpos, s32 ypos) { - u16* oam = (u16*)&GPU::OAM[CurUnit->Num ? 0x400 : 0]; + u16* oam = (u16*)&GPU.OAM[CurUnit->Num ? 0x400 : 0]; u16* attrib = &oam[num * 4]; u32 pixelattr = ((attrib[2] & 0x0C00) << 6) | 0xC0000; diff --git a/src/GPU2D_Soft.h b/src/GPU2D_Soft.h index e1e6eedf..7d6d65f9 100644 --- a/src/GPU2D_Soft.h +++ b/src/GPU2D_Soft.h @@ -20,19 +20,25 @@ #include "GPU2D.h" +namespace Melon +{ +class GPU; +} + namespace GPU2D { class SoftRenderer : public Renderer2D { public: - SoftRenderer(); + SoftRenderer(Melon::GPU& gpu); ~SoftRenderer() override {} void DrawScanline(u32 line, Unit* unit) override; void DrawSprites(u32 line, Unit* unit) override; void VBlankEnd(Unit* unitA, Unit* unitB) override; private: + Melon::GPU& GPU; alignas(8) u32 BGOBJLine[256*3]; u32* _3DLine; diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 55d6de73..80c5d468 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -138,158 +138,9 @@ const u8 CmdNumParams[256] = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -typedef union -{ - u64 _contents; - struct - { - u32 Param; - u8 Command; - }; - -} CmdFIFOEntry; - -FIFO CmdFIFO; -FIFO CmdPIPE; - -FIFO CmdStallQueue; - -u32 NumCommands, CurCommand, ParamCount, TotalParams; - -bool GeometryEnabled; -bool RenderingEnabled; - -u32 DispCnt; -u8 AlphaRefVal, AlphaRef; - -u16 ToonTable[32]; -u16 EdgeTable[8]; - -u32 FogColor, FogOffset; -u8 FogDensityTable[32]; - -u32 ClearAttr1, ClearAttr2; - -u32 RenderDispCnt; -u8 RenderAlphaRef; - -u16 RenderToonTable[32]; -u16 RenderEdgeTable[8]; - -u32 RenderFogColor, RenderFogOffset, RenderFogShift; -u8 RenderFogDensityTable[34]; - -u32 RenderClearAttr1, RenderClearAttr2; - -bool RenderFrameIdentical; - -u16 RenderXPos; - -u32 ZeroDotWLimit; - -u32 GXStat; - -u32 ExecParams[32]; -u32 ExecParamCount; - -u64 Timestamp; -s32 CycleCount; -s32 VertexPipeline; -s32 NormalPipeline; -s32 PolygonPipeline; -s32 VertexSlotCounter; -u32 VertexSlotsFree; - -u32 NumPushPopCommands; -u32 NumTestCommands; - - -u32 MatrixMode; - -s32 ProjMatrix[16]; -s32 PosMatrix[16]; -s32 VecMatrix[16]; -s32 TexMatrix[16]; - -s32 ClipMatrix[16]; -bool ClipMatrixDirty; - -u32 Viewport[6]; - -s32 ProjMatrixStack[16]; -s32 PosMatrixStack[32][16]; -s32 VecMatrixStack[32][16]; -s32 TexMatrixStack[16]; -s32 ProjMatrixStackPointer; -s32 PosMatrixStackPointer; -s32 TexMatrixStackPointer; - void MatrixLoadIdentity(s32* m); -void UpdateClipMatrix(); - -u32 PolygonMode; -s16 CurVertex[3]; -u8 VertexColor[3]; -s16 TexCoords[2]; -s16 RawTexCoords[2]; -s16 Normal[3]; - -s16 LightDirection[4][3]; -u8 LightColor[4][3]; -u8 MatDiffuse[3]; -u8 MatAmbient[3]; -u8 MatSpecular[3]; -u8 MatEmission[3]; - -bool UseShininessTable; -u8 ShininessTable[128]; - -u32 PolygonAttr; -u32 CurPolygonAttr; - -u32 TexParam; -u32 TexPalette; - -s32 PosTestResult[4]; -s16 VecTestResult[3]; - -Vertex TempVertexBuffer[4]; -u32 VertexNum; -u32 VertexNumInPoly; -u32 NumConsecutivePolygons; -Polygon* LastStripPolygon; -u32 NumOpaquePolygons; - -Vertex VertexRAM[6144 * 2]; -Polygon PolygonRAM[2048 * 2]; - -Vertex* CurVertexRAM; -Polygon* CurPolygonRAM; -u32 NumVertices, NumPolygons; -u32 CurRAMBank; - -std::array RenderPolygonRAM; -u32 RenderNumPolygons; - -u32 FlushRequest; -u32 FlushAttributes; - -std::unique_ptr CurrentRenderer = {}; - -bool AbortFrame; - -bool Init() -{ - return true; -} - -void DeInit() -{ - CurrentRenderer = nullptr; -} - -void ResetRenderingState() +void GPU3D::ResetRenderingState() noexcept { RenderNumPolygons = 0; @@ -308,7 +159,7 @@ void ResetRenderingState() RenderClearAttr2 = 0x00007FFF; } -void Reset() +void GPU3D::Reset() noexcept { CmdFIFO.Clear(); CmdPIPE.Clear(); @@ -389,7 +240,7 @@ void Reset() AbortFrame = false; } -void DoSavestate(Savestate* file) +void GPU3D::DoSavestate(Savestate* file) noexcept { file->Section("GP3D"); @@ -634,7 +485,7 @@ void DoSavestate(Savestate* file) -void SetEnabled(bool geometry, bool rendering) +void GPU3D::SetEnabled(bool geometry, bool rendering) noexcept { GeometryEnabled = geometry; RenderingEnabled = rendering; @@ -767,7 +618,7 @@ void MatrixTranslate(s32* m, s32* s) m[15] += ((s64)s[0]*m[3] + (s64)s[1]*m[7] + (s64)s[2]*m[11]) >> 12; } -void UpdateClipMatrix() +void GPU3D::UpdateClipMatrix() noexcept { if (!ClipMatrixDirty) return; ClipMatrixDirty = false; @@ -778,7 +629,7 @@ void UpdateClipMatrix() -void AddCycles(s32 num) +void GPU3D::AddCycles(s32 num) noexcept { CycleCount += num; @@ -809,7 +660,7 @@ void AddCycles(s32 num) } } -void NextVertexSlot() +void GPU3D::NextVertexSlot() noexcept { s32 num = (9 - VertexSlotCounter) + 1; @@ -852,7 +703,7 @@ void NextVertexSlot() } } -void StallPolygonPipeline(s32 delay, s32 nonstalldelay) +void GPU3D::StallPolygonPipeline(s32 delay, s32 nonstalldelay) noexcept { if (PolygonPipeline > 0) { @@ -907,7 +758,7 @@ void ClipSegment(Vertex* outbuf, Vertex* vin, Vertex* vout) } template -int ClipAgainstPlane(Vertex* vertices, int nverts, int clipstart) +int ClipAgainstPlane(const GPU3D& gpu, Vertex* vertices, int nverts, int clipstart) { Vertex temp[10]; int prev, next; @@ -927,7 +778,7 @@ int ClipAgainstPlane(Vertex* vertices, int nverts, int clipstart) Vertex vtx = vertices[i]; if (vtx.Position[comp] > vtx.Position[3]) { - if ((comp == 2) && (!(CurPolygonAttr & (1<<12)))) return 0; + if ((comp == 2) && (!(gpu.CurPolygonAttr & (1<<12)))) return 0; Vertex* vprev = &vertices[prev]; if (vprev->Position[comp] <= vprev->Position[3]) @@ -988,7 +839,7 @@ int ClipAgainstPlane(Vertex* vertices, int nverts, int clipstart) } template -int ClipPolygon(Vertex* vertices, int nverts, int clipstart) +int ClipPolygon(GPU3D& gpu, Vertex* vertices, int nverts, int clipstart) { // clip. // for each vertex: @@ -1001,13 +852,13 @@ int ClipPolygon(Vertex* vertices, int nverts, int clipstart) // clipping seems to process the Y plane before the X plane. // Z clipping - nverts = ClipAgainstPlane<2, attribs>(vertices, nverts, clipstart); + nverts = ClipAgainstPlane<2, attribs>(gpu, vertices, nverts, clipstart); // Y clipping - nverts = ClipAgainstPlane<1, attribs>(vertices, nverts, clipstart); + nverts = ClipAgainstPlane<1, attribs>(gpu, vertices, nverts, clipstart); // X clipping - nverts = ClipAgainstPlane<0, attribs>(vertices, nverts, clipstart); + nverts = ClipAgainstPlane<0, attribs>(gpu, vertices, nverts, clipstart); return nverts; } @@ -1020,7 +871,7 @@ bool ClipCoordsEqual(Vertex* a, Vertex* b) a->Position[3] == b->Position[3]; } -void SubmitPolygon() +void GPU3D::SubmitPolygon() noexcept { Vertex clippedvertices[10]; Vertex* reusedvertices[2]; @@ -1153,7 +1004,7 @@ void SubmitPolygon() // clipping - nverts = ClipPolygon(clippedvertices, nverts, clipstart); + nverts = ClipPolygon(*this, clippedvertices, nverts, clipstart); if (nverts == 0) { LastStripPolygon = NULL; @@ -1425,7 +1276,7 @@ void SubmitPolygon() LastStripPolygon = NULL; } -void SubmitVertex() +void GPU3D::SubmitVertex() noexcept { s64 vertex[4] = {(s64)CurVertex[0], (s64)CurVertex[1], (s64)CurVertex[2], 0x1000}; Vertex* vertextrans = &TempVertexBuffer[VertexNumInPoly]; @@ -1523,7 +1374,7 @@ void SubmitVertex() AddCycles(3); } -void CalculateLighting() +void GPU3D::CalculateLighting() noexcept { if ((TexParam >> 30) == 2) { @@ -1598,7 +1449,7 @@ void CalculateLighting() } -void BoxTest(u32* params) +void GPU3D::BoxTest(u32* params) noexcept { Vertex cube[8]; Vertex face[10]; @@ -1642,7 +1493,7 @@ void BoxTest(u32* params) // front face (-Z) face[0] = cube[0]; face[1] = cube[1]; face[2] = cube[2]; face[3] = cube[3]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1651,7 +1502,7 @@ void BoxTest(u32* params) // back face (+Z) face[0] = cube[4]; face[1] = cube[5]; face[2] = cube[6]; face[3] = cube[7]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1660,7 +1511,7 @@ void BoxTest(u32* params) // left face (-X) face[0] = cube[0]; face[1] = cube[3]; face[2] = cube[4]; face[3] = cube[5]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1669,7 +1520,7 @@ void BoxTest(u32* params) // right face (+X) face[0] = cube[1]; face[1] = cube[2]; face[2] = cube[7]; face[3] = cube[6]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1678,7 +1529,7 @@ void BoxTest(u32* params) // bottom face (-Y) face[0] = cube[0]; face[1] = cube[1]; face[2] = cube[6]; face[3] = cube[5]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1687,7 +1538,7 @@ void BoxTest(u32* params) // top face (+Y) face[0] = cube[2]; face[1] = cube[3]; face[2] = cube[4]; face[3] = cube[7]; - res = ClipPolygon(face, 4, 0); + res = ClipPolygon(*this, face, 4, 0); if (res > 0) { GXStat |= (1<<1); @@ -1695,7 +1546,7 @@ void BoxTest(u32* params) } } -void PosTest() +void GPU3D::PosTest() noexcept { s64 vertex[4] = {(s64)CurVertex[0], (s64)CurVertex[1], (s64)CurVertex[2], 0x1000}; @@ -1708,7 +1559,7 @@ void PosTest() AddCycles(5); } -void VecTest(u32 param) +void GPU3D::VecTest(u32 param) noexcept { // TODO: maybe it overwrites the normal registers, too @@ -1731,7 +1582,7 @@ void VecTest(u32 param) -void CmdFIFOWrite(CmdFIFOEntry& entry) +void GPU3D::CmdFIFOWrite(CmdFIFOEntry& entry) noexcept { if (CmdFIFO.IsEmpty() && !CmdPIPE.IsFull()) { @@ -1767,7 +1618,7 @@ void CmdFIFOWrite(CmdFIFOEntry& entry) } } -CmdFIFOEntry CmdFIFORead() +GPU3D::CmdFIFOEntry GPU3D::CmdFIFORead() noexcept { CmdFIFOEntry ret = CmdPIPE.Read(); @@ -1800,39 +1651,7 @@ CmdFIFOEntry CmdFIFORead() return ret; } -inline void VertexPipelineSubmitCmd() -{ - // vertex commands 0x24, 0x25, 0x26, 0x27, 0x28 - if (!(VertexSlotsFree & 0x1)) NextVertexSlot(); - else AddCycles(1); - NormalPipeline = 0; -} - -inline void VertexPipelineCmdDelayed6() -{ - // commands 0x20, 0x30, 0x31, 0x72 that can run 6 cycles after a vertex - if (VertexPipeline > 2) AddCycles((VertexPipeline - 2) + 1); - else AddCycles(NormalPipeline + 1); - NormalPipeline = 0; -} - -inline void VertexPipelineCmdDelayed8() -{ - // commands 0x29, 0x2A, 0x2B, 0x33, 0x34, 0x41, 0x60, 0x71 that can run 8 cycles after a vertex - if (VertexPipeline > 0) AddCycles(VertexPipeline + 1); - else AddCycles(NormalPipeline + 1); - NormalPipeline = 0; -} - -inline void VertexPipelineCmdDelayed4() -{ - // all other commands can run 4 cycles after a vertex - // no need to do much here since that is the minimum - AddCycles(NormalPipeline + 1); - NormalPipeline = 0; -} - -void ExecuteCommand() +void GPU3D::ExecuteCommand() noexcept { CmdFIFOEntry entry = CmdFIFORead(); @@ -2430,13 +2249,13 @@ void ExecuteCommand() } } -s32 CyclesToRunFor() +s32 GPU3D::CyclesToRunFor() const noexcept { if (CycleCount < 0) return 0; return CycleCount; } -void FinishWork(s32 cycles) +void GPU3D::FinishWork(s32 cycles) noexcept { AddCycles(cycles); if (NormalPipeline) @@ -2450,7 +2269,7 @@ void FinishWork(s32 cycles) GXStat &= ~(1<<27); } -void Run() +void GPU3D::Run() noexcept { if (!GeometryEnabled || FlushRequest || (CmdPIPE.IsEmpty() && !(GXStat & (1<<27)))) @@ -2485,7 +2304,7 @@ void Run() } -void CheckFIFOIRQ() +void GPU3D::CheckFIFOIRQ() noexcept { bool irq = false; switch (GXStat >> 30) @@ -2498,18 +2317,18 @@ void CheckFIFOIRQ() else NDS::ClearIRQ(0, NDS::IRQ_GXFIFO); } -void CheckFIFODMA() +void GPU3D::CheckFIFODMA() noexcept { if (CmdFIFO.Level() < 128) NDS::CheckDMAs(0, 0x07); } -void VCount144() +void GPU3D::VCount144() noexcept { CurrentRenderer->VCount144(); } -void RestartFrame() +void GPU3D::RestartFrame() noexcept { CurrentRenderer->RestartFrame(); } @@ -2527,7 +2346,7 @@ bool YSort(Polygon* a, Polygon* b) return a->SortKey < b->SortKey; } -void VBlank() +void GPU3D::VBlank() noexcept { if (GeometryEnabled) { @@ -2604,21 +2423,20 @@ void VBlank() } } -void VCount215() +void GPU3D::VCount215() noexcept { CurrentRenderer->RenderFrame(); } -void SetRenderXPos(u16 xpos) +void GPU3D::SetRenderXPos(u16 xpos) noexcept { if (!RenderingEnabled) return; RenderXPos = xpos & 0x01FF; } -u32 ScrolledLine[256]; -u32* GetLine(int line) +u32* GPU3D::GetLine(int line) noexcept { if (!AbortFrame) { @@ -2653,8 +2471,12 @@ u32* GetLine(int line) return ScrolledLine; } +bool GPU3D::IsRendererAccelerated() const noexcept +{ + return CurrentRenderer && CurrentRenderer->Accelerated; +} -void WriteToGXFIFO(u32 val) +void GPU3D::WriteToGXFIFO(u32 val) noexcept { if (NumCommands == 0) { @@ -2693,7 +2515,7 @@ void WriteToGXFIFO(u32 val) } -u8 Read8(u32 addr) +u8 GPU3D::Read8(u32 addr) noexcept { switch (addr) { @@ -2732,7 +2554,7 @@ u8 Read8(u32 addr) return 0; } -u16 Read16(u32 addr) +u16 GPU3D::Read16(u32 addr) noexcept { switch (addr) { @@ -2776,7 +2598,7 @@ u16 Read16(u32 addr) return 0; } -u32 Read32(u32 addr) +u32 GPU3D::Read32(u32 addr) noexcept { switch (addr) { @@ -2829,7 +2651,7 @@ u32 Read32(u32 addr) return 0; } -void Write8(u32 addr, u8 val) +void GPU3D::Write8(u32 addr, u8 val) noexcept { if (!RenderingEnabled && addr >= 0x04000320 && addr < 0x04000400) return; if (!GeometryEnabled && addr >= 0x04000400 && addr < 0x04000700) return; @@ -2879,7 +2701,7 @@ void Write8(u32 addr, u8 val) Log(LogLevel::Debug, "unknown GPU3D write8 %08X %02X\n", addr, val); } -void Write16(u32 addr, u16 val) +void GPU3D::Write16(u32 addr, u16 val) noexcept { if (!RenderingEnabled && addr >= 0x04000320 && addr < 0x04000400) return; if (!GeometryEnabled && addr >= 0x04000400 && addr < 0x04000700) return; @@ -2966,7 +2788,7 @@ void Write16(u32 addr, u16 val) Log(LogLevel::Debug, "unknown GPU3D write16 %08X %04X\n", addr, val); } -void Write32(u32 addr, u32 val) +void GPU3D::Write32(u32 addr, u32 val) noexcept { if (!RenderingEnabled && addr >= 0x04000320 && addr < 0x04000400) return; if (!GeometryEnabled && addr >= 0x04000400 && addr < 0x04000700) return; diff --git a/src/GPU3D.h b/src/GPU3D.h index 1a0bfa0f..b8a2d55d 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -22,8 +22,13 @@ #include #include -#include "GPU.h" #include "Savestate.h" +#include "FIFO.h" + +namespace Melon +{ +struct RenderSettings; +} namespace GPU3D { @@ -79,60 +84,246 @@ struct Polygon }; -extern u32 RenderDispCnt; -extern u8 RenderAlphaRef; +class Renderer3D; -extern u16 RenderToonTable[32]; -extern u16 RenderEdgeTable[8]; +class GPU3D +{ +public: + GPU3D() noexcept = default; + ~GPU3D() noexcept = default; + void Reset() noexcept; -extern u32 RenderFogColor, RenderFogOffset, RenderFogShift; -extern u8 RenderFogDensityTable[34]; + void DoSavestate(Savestate* file) noexcept; -extern u32 RenderClearAttr1, RenderClearAttr2; + void SetEnabled(bool geometry, bool rendering) noexcept; -extern bool RenderFrameIdentical; + void ExecuteCommand() noexcept; -extern u16 RenderXPos; + s32 CyclesToRunFor() const noexcept; + void Run() noexcept; + void CheckFIFOIRQ() noexcept; + void CheckFIFODMA() noexcept; -extern std::array RenderPolygonRAM; -extern u32 RenderNumPolygons; + void VCount144() noexcept; + void VBlank() noexcept; + void VCount215() noexcept; -extern bool AbortFrame; + void RestartFrame() noexcept; -extern u64 Timestamp; + void SetRenderXPos(u16 xpos) noexcept; + [[nodiscard]] u16 GetRenderXPos() const noexcept { return RenderXPos; } + u32* GetLine(int line) noexcept; -bool Init(); -void DeInit(); -void Reset(); + void WriteToGXFIFO(u32 val) noexcept; -void DoSavestate(Savestate* file); + [[nodiscard]] bool IsRendererAccelerated() const noexcept; + [[nodiscard]] Renderer3D* GetCurrentRenderer() noexcept { return CurrentRenderer.get(); } + [[nodiscard]] const Renderer3D* GetCurrentRenderer() const noexcept { return CurrentRenderer.get(); } + void SetCurrentRenderer(std::unique_ptr&& renderer) noexcept { CurrentRenderer = std::move(renderer); } -void SetEnabled(bool geometry, bool rendering); + u8 Read8(u32 addr) noexcept; + u16 Read16(u32 addr) noexcept; + u32 Read32(u32 addr) noexcept; + void Write8(u32 addr, u8 val) noexcept; + void Write16(u32 addr, u16 val) noexcept; + void Write32(u32 addr, u32 val) noexcept; +private: + typedef union + { + u64 _contents; + struct + { + u32 Param; + u8 Command; + }; -void ExecuteCommand(); + } CmdFIFOEntry; -s32 CyclesToRunFor(); -void Run(); -void CheckFIFOIRQ(); -void CheckFIFODMA(); + void UpdateClipMatrix() noexcept; + void ResetRenderingState() noexcept; + void AddCycles(s32 num) noexcept; + void NextVertexSlot() noexcept; + void StallPolygonPipeline(s32 delay, s32 nonstalldelay) noexcept; + void SubmitPolygon() noexcept; + void SubmitVertex() noexcept; + void CalculateLighting() noexcept; + void BoxTest(u32* params) noexcept; + void PosTest() noexcept; + void VecTest(u32 param) noexcept; + void CmdFIFOWrite(CmdFIFOEntry& entry) noexcept; + CmdFIFOEntry CmdFIFORead() noexcept; + void FinishWork(s32 cycles) noexcept; + void VertexPipelineSubmitCmd() noexcept + { + // vertex commands 0x24, 0x25, 0x26, 0x27, 0x28 + if (!(VertexSlotsFree & 0x1)) NextVertexSlot(); + else AddCycles(1); + NormalPipeline = 0; + } -void VCount144(); -void VBlank(); -void VCount215(); + void VertexPipelineCmdDelayed6() noexcept + { + // commands 0x20, 0x30, 0x31, 0x72 that can run 6 cycles after a vertex + if (VertexPipeline > 2) AddCycles((VertexPipeline - 2) + 1); + else AddCycles(NormalPipeline + 1); + NormalPipeline = 0; + } -void RestartFrame(); + void VertexPipelineCmdDelayed8() noexcept + { + // commands 0x29, 0x2A, 0x2B, 0x33, 0x34, 0x41, 0x60, 0x71 that can run 8 cycles after a vertex + if (VertexPipeline > 0) AddCycles(VertexPipeline + 1); + else AddCycles(NormalPipeline + 1); + NormalPipeline = 0; + } -void SetRenderXPos(u16 xpos); -u32* GetLine(int line); + void VertexPipelineCmdDelayed4() noexcept + { + // all other commands can run 4 cycles after a vertex + // no need to do much here since that is the minimum + AddCycles(NormalPipeline + 1); + NormalPipeline = 0; + } -void WriteToGXFIFO(u32 val); + std::unique_ptr CurrentRenderer = nullptr; -u8 Read8(u32 addr); -u16 Read16(u32 addr); -u32 Read32(u32 addr); -void Write8(u32 addr, u8 val); -void Write16(u32 addr, u16 val); -void Write32(u32 addr, u32 val); + u16 RenderXPos = 0; + +public: + FIFO CmdFIFO {}; + FIFO CmdPIPE {}; + + FIFO CmdStallQueue {}; + + u32 ZeroDotWLimit = 0; + + u32 GXStat = 0; + + u32 ExecParams[32] {}; + u32 ExecParamCount = 0; + + s32 CycleCount = 0; + s32 VertexPipeline = 0; + s32 NormalPipeline = 0; + s32 PolygonPipeline = 0; + s32 VertexSlotCounter = 0; + u32 VertexSlotsFree = 0; + + u32 NumPushPopCommands = 0; + u32 NumTestCommands = 0; + + + u32 MatrixMode = 0; + + s32 ProjMatrix[16] {}; + s32 PosMatrix[16] {}; + s32 VecMatrix[16] {}; + s32 TexMatrix[16] {}; + + s32 ClipMatrix[16] {}; + bool ClipMatrixDirty = false; + + u32 Viewport[6] {}; + + s32 ProjMatrixStack[16] {}; + s32 PosMatrixStack[32][16] {}; + s32 VecMatrixStack[32][16] {}; + s32 TexMatrixStack[16] {}; + s32 ProjMatrixStackPointer = 0; + s32 PosMatrixStackPointer = 0; + s32 TexMatrixStackPointer = 0; + + u32 NumCommands = 0; + u32 CurCommand = 0; + u32 ParamCount = 0; + u32 TotalParams = 0; + + bool GeometryEnabled = false; + bool RenderingEnabled = false; + + u32 DispCnt = 0; + u8 AlphaRefVal = 0; + u8 AlphaRef = 0; + + u16 ToonTable[32] {}; + u16 EdgeTable[8] {}; + + u32 FogColor = 0; + u32 FogOffset = 0; + u8 FogDensityTable[32] {}; + + u32 ClearAttr1 = 0; + u32 ClearAttr2 = 0; + u32 RenderDispCnt = 0; + u8 RenderAlphaRef = 0; + + u16 RenderToonTable[32] {}; + u16 RenderEdgeTable[8] {}; + + u32 RenderFogColor = 0; + u32 RenderFogOffset = 0; + u32 RenderFogShift = 0; + u8 RenderFogDensityTable[34] {}; + + u32 RenderClearAttr1 = 0; + u32 RenderClearAttr2 = 0; + + bool RenderFrameIdentical = false; + + bool AbortFrame = false; + + u64 Timestamp = 0; + + + u32 PolygonMode = 0; + s16 CurVertex[3] {}; + u8 VertexColor[3] {}; + s16 TexCoords[2] {}; + s16 RawTexCoords[2] {}; + s16 Normal[3] {}; + + s16 LightDirection[4][3] {}; + u8 LightColor[4][3] {}; + u8 MatDiffuse[3] {}; + u8 MatAmbient[3] {}; + u8 MatSpecular[3] {}; + u8 MatEmission[3] {}; + + bool UseShininessTable = false; + u8 ShininessTable[128] {}; + + u32 PolygonAttr = 0; + u32 CurPolygonAttr = 0; + + u32 TexParam = 0; + u32 TexPalette = 0; + + s32 PosTestResult[4] {}; + s16 VecTestResult[3] {}; + + Vertex TempVertexBuffer[4] {}; + u32 VertexNum = 0; + u32 VertexNumInPoly = 0; + u32 NumConsecutivePolygons = 0; + Polygon* LastStripPolygon = nullptr; + u32 NumOpaquePolygons = 0; + + Vertex VertexRAM[6144 * 2] {}; + Polygon PolygonRAM[2048 * 2] {}; + + Vertex* CurVertexRAM = nullptr; + Polygon* CurPolygonRAM = nullptr; + u32 NumVertices = 0; + u32 NumPolygons = 0; + u32 CurRAMBank = 0; + + std::array RenderPolygonRAM {}; + u32 RenderNumPolygons = 0; + + u32 FlushRequest = 0; + u32 FlushAttributes = 0; + u32 ScrolledLine[256]; +}; class Renderer3D { @@ -149,7 +340,7 @@ public: // are more detailed "traits" that we can ask of the Renderer3D type const bool Accelerated; - virtual void SetRenderSettings(GPU::RenderSettings& settings) = 0; + virtual void SetRenderSettings(const Melon::RenderSettings& settings) noexcept = 0; virtual void VCount144() {}; @@ -160,15 +351,6 @@ protected: Renderer3D(bool Accelerated); }; -extern int Renderer; -extern std::unique_ptr CurrentRenderer; - } -#include "GPU3D_Soft.h" - -#ifdef OGLRENDERER_ENABLED -#include "GPU3D_OpenGL.h" -#endif - #endif diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 2d0b15a9..629ec157 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -97,20 +97,20 @@ void SetupDefaultTexParams(GLuint tex) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } -GLRenderer::GLRenderer() noexcept : Renderer3D(true) +GLRenderer::GLRenderer(Melon::GPU& gpu) noexcept : Renderer3D(true), GPU(gpu) { // 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. } -std::unique_ptr GLRenderer::New() noexcept +std::unique_ptr GLRenderer::New(Melon::GPU& gpu) noexcept { assert(glEnable != nullptr); // Will be returned if the initialization succeeds, // or cleaned up via RAII if it fails. - std::unique_ptr result = std::unique_ptr(new GLRenderer()); + std::unique_ptr result = std::unique_ptr(new GLRenderer(gpu)); glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); @@ -329,7 +329,7 @@ void GLRenderer::Reset() { } -void GLRenderer::SetRenderSettings(GPU::RenderSettings& settings) +void GLRenderer::SetRenderSettings(const Melon::RenderSettings& settings) noexcept { int scale = settings.GL_ScaleFactor; @@ -766,11 +766,11 @@ int GLRenderer::RenderPolygonEdgeBatch(int i) void GLRenderer::RenderSceneChunk(int y, int h) { u32 flags = 0; - if (RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; + if (GPU.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; if (h != 192) glScissor(0, y<PolyData->IsShadow) { // shadow against clear-plane will only pass if its polyID matches that of the clear plane - u32 clrpolyid = (RenderClearAttr1 >> 24) & 0x3F; + u32 clrpolyid = (GPU.GPU3D.RenderClearAttr1 >> 24) & 0x3F; if (polyid != clrpolyid) { i++; continue; } glEnable(GL_BLEND); @@ -1066,7 +1066,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) } } - if (RenderDispCnt & 0x00A0) // fog/edge enabled + if (GPU.GPU3D.RenderDispCnt & 0x00A0) // fog/edge enabled { glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glColorMaski(1, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); @@ -1088,7 +1088,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) glBindBuffer(GL_ARRAY_BUFFER, ClearVertexBufferID); glBindVertexArray(ClearVertexArrayID); - if (RenderDispCnt & (1<<5)) + if (GPU.GPU3D.RenderDispCnt & (1<<5)) { // edge marking // TODO: depth/polyid values at screen edges @@ -1100,19 +1100,19 @@ void GLRenderer::RenderSceneChunk(int y, int h) glDrawArrays(GL_TRIANGLES, 0, 2*3); } - if (RenderDispCnt & (1<<7)) + if (GPU.GPU3D.RenderDispCnt & (1<<7)) { // fog glUseProgram(FinalPassFogShader[2]); - if (RenderDispCnt & (1<<6)) + if (GPU.GPU3D.RenderDispCnt & (1<<6)) glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA); else glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA); { - u32 c = RenderFogColor; + u32 c = GPU.GPU3D.RenderFogColor; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1136,11 +1136,11 @@ void GLRenderer::RenderFrame() ShaderConfig.uScreenSize[0] = ScreenW; ShaderConfig.uScreenSize[1] = ScreenH; - ShaderConfig.uDispCnt = RenderDispCnt; + ShaderConfig.uDispCnt = GPU.GPU3D.RenderDispCnt; for (int i = 0; i < 32; i++) { - u16 c = RenderToonTable[i]; + u16 c = GPU.GPU3D.RenderToonTable[i]; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1152,7 +1152,7 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 8; i++) { - u16 c = RenderEdgeTable[i]; + u16 c = GPU.GPU3D.RenderEdgeTable[i]; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1163,7 +1163,7 @@ void GLRenderer::RenderFrame() } { - u32 c = RenderFogColor; + u32 c = GPU.GPU3D.RenderFogColor; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1177,12 +1177,12 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 34; i++) { - u8 d = RenderFogDensityTable[i]; + u8 d = GPU.GPU3D.RenderFogDensityTable[i]; ShaderConfig.uFogDensity[i][0] = (float)d / 127.0; } - ShaderConfig.uFogOffset = RenderFogOffset; - ShaderConfig.uFogShift = RenderFogShift; + ShaderConfig.uFogOffset = GPU.GPU3D.RenderFogOffset; + ShaderConfig.uFogShift = GPU.GPU3D.RenderFogShift; glBindBuffer(GL_UNIFORM_BUFFER, ShaderConfigUBO); void* unibuf = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY); @@ -1195,13 +1195,13 @@ void GLRenderer::RenderFrame() glBindTexture(GL_TEXTURE_2D, TexMemID); for (int i = 0; i < 4; i++) { - u32 mask = GPU::VRAMMap_Texture[i]; + u32 mask = GPU.VRAMMap_Texture[i]; u8* vram; if (!mask) continue; - else if (mask & (1<<0)) vram = GPU::VRAM_A; - else if (mask & (1<<1)) vram = GPU::VRAM_B; - else if (mask & (1<<2)) vram = GPU::VRAM_C; - else if (mask & (1<<3)) vram = GPU::VRAM_D; + else if (mask & (1<<0)) vram = GPU.VRAM_A; + else if (mask & (1<<1)) vram = GPU.VRAM_B; + else if (mask & (1<<2)) vram = GPU.VRAM_C; + else if (mask & (1<<3)) vram = GPU.VRAM_D; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i*128, 1024, 128, GL_RED_INTEGER, GL_UNSIGNED_BYTE, vram); } @@ -1211,12 +1211,12 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 6; i++) { // 6 x 16K chunks - u32 mask = GPU::VRAMMap_TexPal[i]; + u32 mask = GPU.VRAMMap_TexPal[i]; u8* vram; if (!mask) continue; - else if (mask & (1<<4)) vram = &GPU::VRAM_E[(i&3)*0x4000]; - else if (mask & (1<<5)) vram = GPU::VRAM_F; - else if (mask & (1<<6)) vram = GPU::VRAM_G; + else if (mask & (1<<4)) vram = &GPU.VRAM_E[(i&3)*0x4000]; + else if (mask & (1<<5)) vram = GPU.VRAM_F; + else if (mask & (1<<6)) vram = GPU.VRAM_G; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i*8, 1024, 8, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, vram); } @@ -1241,13 +1241,13 @@ void GLRenderer::RenderFrame() glUseProgram(ClearShaderPlain[2]); glDepthFunc(GL_ALWAYS); - u32 r = RenderClearAttr1 & 0x1F; - u32 g = (RenderClearAttr1 >> 5) & 0x1F; - u32 b = (RenderClearAttr1 >> 10) & 0x1F; - u32 fog = (RenderClearAttr1 >> 15) & 0x1; - u32 a = (RenderClearAttr1 >> 16) & 0x1F; - u32 polyid = (RenderClearAttr1 >> 24) & 0x3F; - u32 z = ((RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; + u32 r = GPU.GPU3D.RenderClearAttr1 & 0x1F; + u32 g = (GPU.GPU3D.RenderClearAttr1 >> 5) & 0x1F; + u32 b = (GPU.GPU3D.RenderClearAttr1 >> 10) & 0x1F; + u32 fog = (GPU.GPU3D.RenderClearAttr1 >> 15) & 0x1; + u32 a = (GPU.GPU3D.RenderClearAttr1 >> 16) & 0x1F; + u32 polyid = (GPU.GPU3D.RenderClearAttr1 >> 24) & 0x3F; + u32 z = ((GPU.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; glStencilFunc(GL_ALWAYS, 0xFF, 0xFF); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); @@ -1266,20 +1266,20 @@ void GLRenderer::RenderFrame() glDrawArrays(GL_TRIANGLES, 0, 2*3); } - if (RenderNumPolygons) + if (GPU.GPU3D.RenderNumPolygons) { // render shit here u32 flags = 0; - if (RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; + if (GPU.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; int npolys = 0; int firsttrans = -1; - for (u32 i = 0; i < RenderNumPolygons; i++) + for (u32 i = 0; i < GPU.GPU3D.RenderNumPolygons; i++) { - if (RenderPolygonRAM[i]->Degenerate) continue; + if (GPU.GPU3D.RenderPolygonRAM[i]->Degenerate) continue; - SetupPolygon(&PolygonList[npolys], RenderPolygonRAM[i]); - if (firsttrans < 0 && RenderPolygonRAM[i]->Translucent) + SetupPolygon(&PolygonList[npolys], GPU.GPU3D.RenderPolygonRAM[i]); + if (firsttrans < 0 && GPU.GPU3D.RenderPolygonRAM[i]->Translucent) firsttrans = npolys; npolys++; diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index 02d83037..3657a12a 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -18,10 +18,15 @@ #pragma once +#ifdef OGLRENDERER_ENABLED #include "GPU3D.h" #include "OpenGLSupport.h" +namespace Melon +{ +class GPU; +} namespace GPU3D { @@ -31,7 +36,7 @@ public: virtual ~GLRenderer() override; virtual void Reset() override; - virtual void SetRenderSettings(GPU::RenderSettings& settings) override; + virtual void SetRenderSettings(const Melon::RenderSettings& settings) noexcept override; virtual void VCount144() override {}; virtual void RenderFrame() override; @@ -40,10 +45,10 @@ public: void SetupAccelFrame(); void PrepareCaptureFrame(); - static std::unique_ptr New() noexcept; + static std::unique_ptr New(Melon::GPU& gpu) noexcept; private: // Used by New() - GLRenderer() noexcept; + GLRenderer(Melon::GPU& gpu) noexcept; // GL version requirements // * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS) @@ -63,6 +68,7 @@ private: u32 RenderKey; }; + Melon::GPU& GPU; RendererPolygon PolygonList[2048] {}; bool BuildRenderShader(u32 flags, const char* vs, const char* fs); @@ -151,3 +157,4 @@ private: }; } +#endif \ No newline at end of file diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 19154ba8..823f7520 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -72,8 +72,8 @@ void SoftRenderer::SetupRenderThread() } -SoftRenderer::SoftRenderer() noexcept - : Renderer3D(false) +SoftRenderer::SoftRenderer(Melon::GPU& gpu) noexcept + : Renderer3D(false), GPU(gpu) { Sema_RenderStart = Platform::Semaphore_Create(); Sema_RenderDone = Platform::Semaphore_Create(); @@ -105,7 +105,7 @@ void SoftRenderer::Reset() SetupRenderThread(); } -void SoftRenderer::SetRenderSettings(GPU::RenderSettings& settings) +void SoftRenderer::SetRenderSettings(const Melon::RenderSettings& settings) noexcept { Threaded = settings.Soft_Threaded; SetupRenderThread(); @@ -387,7 +387,7 @@ bool DepthTest_LessThan_FrontFacing(s32 dstz, s32 z, u32 dstattr) return false; } -u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) +u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept { u32 dstalpha = dstcolor >> 24; @@ -398,7 +398,7 @@ u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) u32 srcG = (srccolor >> 8) & 0x3F; u32 srcB = (srccolor >> 16) & 0x3F; - if (RenderDispCnt & (1<<3)) + if (GPU.GPU3D.RenderDispCnt & (1<<3)) { u32 dstR = dstcolor & 0x3F; u32 dstG = (dstcolor >> 8) & 0x3F; @@ -427,7 +427,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 if (blendmode == 2) { - if (RenderDispCnt & (1<<1)) + if (GPU.GPU3D.RenderDispCnt & (1<<1)) { // highlight mode: color is calculated normally // except all vertex color components are set @@ -441,7 +441,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 { // toon mode: vertex color is replaced by toon color - u16 tooncolor = RenderToonTable[vr >> 1]; + u16 tooncolor = GPU.GPU3D.RenderToonTable[vr >> 1]; vr = (tooncolor << 1) & 0x3E; if (vr) vr++; vg = (tooncolor >> 4) & 0x3E; if (vg) vg++; @@ -449,7 +449,7 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 } } - if ((RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0)) + if ((GPU.GPU3D.RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0)) { u8 tr, tg, tb; @@ -502,9 +502,9 @@ u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 a = polyalpha; } - if ((blendmode == 2) && (RenderDispCnt & (1<<1))) + if ((blendmode == 2) && (GPU.GPU3D.RenderDispCnt & (1<<1))) { - u16 tooncolor = RenderToonTable[vr >> 1]; + u16 tooncolor = GPU.GPU3D.RenderToonTable[vr >> 1]; vr = (tooncolor << 1) & 0x3E; if (vr) vr++; vg = (tooncolor >> 4) & 0x3E; if (vg) vg++; @@ -748,7 +748,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) std::swap(zl, zr); // CHECKME: edge fill rules for swapped opaque shadow mask polygons - if ((polyalpha < 31) || (RenderDispCnt & (3<<4))) + if ((polyalpha < 31) || (GPU.GPU3D.RenderDispCnt & (3<<4))) { l_filledge = true; r_filledge = true; @@ -776,7 +776,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) rp->SlopeR.EdgeParams(&r_edgelen, &r_edgecov); // CHECKME: edge fill rules for unswapped opaque shadow mask polygons - if ((polyalpha < 31) || (RenderDispCnt & (3<<4))) + if ((polyalpha < 31) || (GPU.GPU3D.RenderDispCnt & (3<<4))) { l_filledge = true; r_filledge = true; @@ -797,7 +797,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) // similarly, we can perform alpha test early (checkme) if (wireframe) polyalpha = 31; - if (polyalpha <= RenderAlphaRef) return; + if (polyalpha <= GPU.GPU3D.RenderAlphaRef) return; // in wireframe mode, there are special rules for equal Z (TODO) @@ -982,7 +982,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) // * the bottom-most pixel of negative x-major slopes are filled if they are next to a flat bottom edge // edges are always filled if antialiasing/edgemarking are enabled or if the pixels are translucent // checkme: do swapped line polygons exist? - if ((polyalpha < 31) || wireframe || (RenderDispCnt & ((1<<4)|(1<<5)))) + if ((polyalpha < 31) || wireframe || (GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5)))) { l_filledge = true; r_filledge = true; @@ -1016,7 +1016,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) // * the bottom-most pixel of negative x-major slopes are filled if they are next to a flat bottom edge // * edges are filled if both sides are identical and fully overlapping // edges are always filled if antialiasing/edgemarking are enabled or if the pixels are translucent - if ((polyalpha < 31) || wireframe || (RenderDispCnt & ((1<<4)|(1<<5)))) + if ((polyalpha < 31) || wireframe || (GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5)))) { l_filledge = true; r_filledge = true; @@ -1119,13 +1119,13 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) u8 alpha = color >> 24; // alpha test - if (alpha <= RenderAlphaRef) continue; + if (alpha <= GPU.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - if (RenderDispCnt & (1<<4)) + if (GPU.GPU3D.RenderDispCnt & (1<<4)) { // anti-aliasing: all edges are rendered @@ -1215,13 +1215,13 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) u8 alpha = color >> 24; // alpha test - if (alpha <= RenderAlphaRef) continue; + if (alpha <= GPU.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - if ((RenderDispCnt & (1<<4)) && (attr & 0xF)) + if ((GPU.GPU3D.RenderDispCnt & (1<<4)) && (attr & 0xF)) { // anti-aliasing: all edges are rendered @@ -1307,13 +1307,13 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) u8 alpha = color >> 24; // alpha test - if (alpha <= RenderAlphaRef) continue; + if (alpha <= GPU.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - if (RenderDispCnt & (1<<4)) + if (GPU.GPU3D.RenderDispCnt & (1<<4)) { // anti-aliasing: all edges are rendered @@ -1377,7 +1377,7 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) u32 z = DepthBuffer[pixeladdr]; u32 densityid, densityfrac; - if (z < RenderFogOffset) + if (z < GPU.GPU3D.RenderFogOffset) { densityid = 0; densityfrac = 0; @@ -1389,8 +1389,8 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) // on hardware, the final value can overflow the 32-bit range with a shift big enough, // causing fog to 'wrap around' and accidentally apply to larger Z ranges - z -= RenderFogOffset; - z = (z >> 2) << RenderFogShift; + z -= GPU.GPU3D.RenderFogOffset; + z = (z >> 2) << GPU.GPU3D.RenderFogShift; densityid = z >> 17; if (densityid >= 32) @@ -1404,8 +1404,8 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) // checkme (may be too precise?) u32 density = - ((RenderFogDensityTable[densityid] * (0x20000-densityfrac)) + - (RenderFogDensityTable[densityid+1] * densityfrac)) >> 17; + ((GPU.GPU3D.RenderFogDensityTable[densityid] * (0x20000-densityfrac)) + + (GPU.GPU3D.RenderFogDensityTable[densityid+1] * densityfrac)) >> 17; if (density >= 127) density = 128; return density; @@ -1417,7 +1417,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) // clearing all polygon fog flags if the master flag isn't set? // merging all final pass loops into one? - if (RenderDispCnt & (1<<5)) + if (GPU.GPU3D.RenderDispCnt & (1<<5)) { // edge marking // only applied to topmost pixels @@ -1437,7 +1437,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) ((polyid != (AttrBuffer[pixeladdr-ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr-ScanlineWidth])) || ((polyid != (AttrBuffer[pixeladdr+ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr+ScanlineWidth]))) { - u16 edgecolor = RenderEdgeTable[polyid >> 3]; + u16 edgecolor = GPU.GPU3D.RenderEdgeTable[polyid >> 3]; u32 edgeR = (edgecolor << 1) & 0x3E; if (edgeR) edgeR++; u32 edgeG = (edgecolor >> 4) & 0x3E; if (edgeG) edgeG++; u32 edgeB = (edgecolor >> 9) & 0x3E; if (edgeB) edgeB++; @@ -1450,7 +1450,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) } } - if (RenderDispCnt & (1<<7)) + if (GPU.GPU3D.RenderDispCnt & (1<<7)) { // fog @@ -1463,12 +1463,12 @@ void SoftRenderer::ScanlineFinalPass(s32 y) // TODO: check the 'fog alpha glitch with small Z' GBAtek talks about - bool fogcolor = !(RenderDispCnt & (1<<6)); + bool fogcolor = !(GPU.GPU3D.RenderDispCnt & (1<<6)); - u32 fogR = (RenderFogColor << 1) & 0x3E; if (fogR) fogR++; - u32 fogG = (RenderFogColor >> 4) & 0x3E; if (fogG) fogG++; - u32 fogB = (RenderFogColor >> 9) & 0x3E; if (fogB) fogB++; - u32 fogA = (RenderFogColor >> 16) & 0x1F; + u32 fogR = (GPU.GPU3D.RenderFogColor << 1) & 0x3E; if (fogR) fogR++; + u32 fogG = (GPU.GPU3D.RenderFogColor >> 4) & 0x3E; if (fogG) fogG++; + u32 fogB = (GPU.GPU3D.RenderFogColor >> 9) & 0x3E; if (fogB) fogB++; + u32 fogA = (GPU.GPU3D.RenderFogColor >> 16) & 0x1F; for (int x = 0; x < 256; x++) { @@ -1528,7 +1528,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) } } - if (RenderDispCnt & (1<<4)) + if (GPU.GPU3D.RenderDispCnt & (1<<4)) { // anti-aliasing @@ -1583,8 +1583,8 @@ void SoftRenderer::ScanlineFinalPass(s32 y) void SoftRenderer::ClearBuffers() { - u32 clearz = ((RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; - u32 polyid = RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID + u32 clearz = ((GPU.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; + u32 polyid = GPU.GPU3D.RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID // fill screen borders for edge marking @@ -1614,10 +1614,10 @@ void SoftRenderer::ClearBuffers() // clear the screen - if (RenderDispCnt & (1<<14)) + if (GPU.GPU3D.RenderDispCnt & (1<<14)) { - u8 xoff = (RenderClearAttr2 >> 16) & 0xFF; - u8 yoff = (RenderClearAttr2 >> 24) & 0xFF; + u8 xoff = (GPU.GPU3D.RenderClearAttr2 >> 16) & 0xFF; + u8 yoff = (GPU.GPU3D.RenderClearAttr2 >> 24) & 0xFF; for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth) { @@ -1649,13 +1649,13 @@ void SoftRenderer::ClearBuffers() else { // TODO: confirm color conversion - u32 r = (RenderClearAttr1 << 1) & 0x3E; if (r) r++; - u32 g = (RenderClearAttr1 >> 4) & 0x3E; if (g) g++; - u32 b = (RenderClearAttr1 >> 9) & 0x3E; if (b) b++; - u32 a = (RenderClearAttr1 >> 16) & 0x1F; + u32 r = (GPU.GPU3D.RenderClearAttr1 << 1) & 0x3E; if (r) r++; + u32 g = (GPU.GPU3D.RenderClearAttr1 >> 4) & 0x3E; if (g) g++; + u32 b = (GPU.GPU3D.RenderClearAttr1 >> 9) & 0x3E; if (b) b++; + u32 a = (GPU.GPU3D.RenderClearAttr1 >> 16) & 0x1F; u32 color = r | (g << 8) | (b << 16) | (a << 24); - polyid |= (RenderClearAttr1 & 0x8000); + polyid |= (GPU.GPU3D.RenderClearAttr1 & 0x8000); for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth) { @@ -1698,19 +1698,19 @@ void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys) void SoftRenderer::VCount144() { - if (RenderThreadRunning.load(std::memory_order_relaxed) && !GPU3D::AbortFrame) + if (RenderThreadRunning.load(std::memory_order_relaxed) && !GPU.GPU3D.AbortFrame) Platform::Semaphore_Wait(Sema_RenderDone); } void SoftRenderer::RenderFrame() { - auto textureDirty = GPU::VRAMDirty_Texture.DeriveState(GPU::VRAMMap_Texture); - auto texPalDirty = GPU::VRAMDirty_TexPal.DeriveState(GPU::VRAMMap_TexPal); + auto textureDirty = GPU.VRAMDirty_Texture.DeriveState(GPU.VRAMMap_Texture, GPU); + auto texPalDirty = GPU.VRAMDirty_TexPal.DeriveState(GPU.VRAMMap_TexPal, GPU); - bool textureChanged = GPU::MakeVRAMFlat_TextureCoherent(textureDirty); - bool texPalChanged = GPU::MakeVRAMFlat_TexPalCoherent(texPalDirty); + bool textureChanged = GPU.MakeVRAMFlat_TextureCoherent(textureDirty); + bool texPalChanged = GPU.MakeVRAMFlat_TexPalCoherent(texPalDirty); - FrameIdentical = !(textureChanged || texPalChanged) && RenderFrameIdentical; + FrameIdentical = !(textureChanged || texPalChanged) && GPU.GPU3D.RenderFrameIdentical; if (RenderThreadRunning.load(std::memory_order_relaxed)) { @@ -1719,7 +1719,7 @@ void SoftRenderer::RenderFrame() else if (!FrameIdentical) { ClearBuffers(); - RenderPolygons(false, &RenderPolygonRAM[0], RenderNumPolygons); + RenderPolygons(false, &GPU.GPU3D.RenderPolygonRAM[0], GPU.GPU3D.RenderNumPolygons); } } @@ -1743,7 +1743,7 @@ void SoftRenderer::RenderThreadFunc() else { ClearBuffers(); - RenderPolygons(true, &RenderPolygonRAM[0], RenderNumPolygons); + RenderPolygons(true, &GPU.GPU3D.RenderPolygonRAM[0], GPU.GPU3D.RenderNumPolygons); } Platform::Semaphore_Post(Sema_RenderDone); diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 16257df9..b1cfb2f1 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -18,6 +18,7 @@ #pragma once +#include "GPU.h" #include "GPU3D.h" #include "Platform.h" #include @@ -28,11 +29,11 @@ namespace GPU3D class SoftRenderer : public Renderer3D { public: - SoftRenderer() noexcept; + SoftRenderer(Melon::GPU& gpu) noexcept; virtual ~SoftRenderer() override; virtual void Reset() override; - virtual void SetRenderSettings(GPU::RenderSettings& settings) override; + virtual void SetRenderSettings(const Melon::RenderSettings& settings) noexcept override; virtual void VCount144() override; virtual void RenderFrame() override; @@ -429,13 +430,14 @@ private: template inline T ReadVRAM_Texture(u32 addr) { - return *(T*)&GPU::VRAMFlat_Texture[addr & 0x7FFFF]; + return *(T*)&GPU.VRAMFlat_Texture[addr & 0x7FFFF]; } template inline T ReadVRAM_TexPal(u32 addr) { - return *(T*)&GPU::VRAMFlat_TexPal[addr & 0x1FFFF]; + return *(T*)&GPU.VRAMFlat_TexPal[addr & 0x1FFFF]; } + u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept; struct RendererPolygon { @@ -449,6 +451,7 @@ private: }; + Melon::GPU& GPU; RendererPolygon PolygonList[2048]; void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha); u32 RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t); diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp index 32633a7a..d0649bb1 100644 --- a/src/GPU_OpenGL.cpp +++ b/src/GPU_OpenGL.cpp @@ -28,12 +28,12 @@ #include "OpenGLSupport.h" #include "GPU_OpenGL_shaders.h" -namespace GPU +namespace Melon { using namespace OpenGL; -std::unique_ptr GLCompositor::New() noexcept +std::unique_ptr GLCompositor::New(Melon::GPU& gpu) noexcept { assert(glBindAttribLocation != nullptr); @@ -50,10 +50,10 @@ std::unique_ptr GLCompositor::New() noexcept // if linking the shaders together failed. return nullptr; - return std::unique_ptr(new GLCompositor(CompShader)); + return std::unique_ptr(new GLCompositor(CompShader, gpu)); } -GLCompositor::GLCompositor(std::array compShader) noexcept : CompShader(compShader) +GLCompositor::GLCompositor(std::array compShader, Melon::GPU& gpu) noexcept : CompShader(compShader), GPU(gpu) { CompScaleLoc = glGetUniformLocation(CompShader[2], "u3DScale"); Comp3DXPosLoc = glGetUniformLocation(CompShader[2], "u3DXPos"); @@ -144,7 +144,7 @@ void GLCompositor::Reset() } -void GLCompositor::SetRenderSettings(RenderSettings& settings) +void GLCompositor::SetRenderSettings(const RenderSettings& settings) noexcept { int scale = settings.GL_ScaleFactor; @@ -174,7 +174,7 @@ void GLCompositor::Stop() { for (int i = 0; i < 2; i++) { - int frontbuf = GPU::FrontBuffer; + int frontbuf = GPU.FrontBuffer; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]); @@ -186,7 +186,7 @@ void GLCompositor::Stop() void GLCompositor::RenderFrame() { - int frontbuf = GPU::FrontBuffer; + int frontbuf = GPU.FrontBuffer; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]); @@ -204,21 +204,21 @@ void GLCompositor::RenderFrame() glUniform1ui(CompScaleLoc, Scale); // TODO: support setting this midframe, if ever needed - glUniform1i(Comp3DXPosLoc, ((int)GPU3D::RenderXPos << 23) >> 23); + glUniform1i(Comp3DXPosLoc, ((int)GPU.GPU3D.GetRenderXPos() << 23) >> 23); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); - if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) + if (GPU.Framebuffer[frontbuf][0] && GPU.Framebuffer[frontbuf][1]) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); + GL_UNSIGNED_BYTE, GPU.Framebuffer[frontbuf][0]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); + GL_UNSIGNED_BYTE, GPU.Framebuffer[frontbuf][1]); } glActiveTexture(GL_TEXTURE1); - reinterpret_cast(GPU3D::CurrentRenderer.get())->SetupAccelFrame(); + reinterpret_cast(GPU.GPU3D.GetCurrentRenderer())->SetupAccelFrame(); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); glBindVertexArray(CompVertexArrayID); diff --git a/src/GPU_OpenGL.h b/src/GPU_OpenGL.h index 3841df14..3763b6b4 100644 --- a/src/GPU_OpenGL.h +++ b/src/GPU_OpenGL.h @@ -23,29 +23,29 @@ #include #include -namespace GPU +namespace Melon { - +class GPU; struct RenderSettings; class GLCompositor { public: - static std::unique_ptr New() noexcept; + static std::unique_ptr New(Melon::GPU& gpu) noexcept; GLCompositor(const GLCompositor&) = delete; GLCompositor& operator=(const GLCompositor&) = delete; ~GLCompositor(); void Reset(); - void SetRenderSettings(RenderSettings& settings); + void SetRenderSettings(const RenderSettings& settings) noexcept; void Stop(); void RenderFrame(); void BindOutputTexture(int buf); private: - GLCompositor(std::array CompShader) noexcept; - + GLCompositor(std::array CompShader, Melon::GPU& gpu) noexcept; + Melon::GPU& GPU; int Scale; int ScreenH, ScreenW; diff --git a/src/NDS.cpp b/src/NDS.cpp index 79359290..5290423e 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -185,6 +185,7 @@ class RTC* RTC; class Wifi* Wifi; std::unique_ptr NDSCartSlot; std::unique_ptr GBACartSlot; +std::unique_ptr GPU; class AREngine* AREngine; bool Running; @@ -204,8 +205,9 @@ bool Init() RegisterEventFunc(Event_Div, 0, DivDone); RegisterEventFunc(Event_Sqrt, 0, SqrtDone); - ARM9 = new ARMv5(); - ARM7 = new ARMv4(); + GPU = std::make_unique(); + ARM9 = new ARMv5(*GPU); + ARM7 = new ARMv4(*GPU); #ifdef JIT_ENABLED ARMJIT::Init(); @@ -215,14 +217,14 @@ bool Init() SharedWRAM = new u8[SharedWRAMSize]; #endif - DMAs[0] = new DMA(0, 0); - DMAs[1] = new DMA(0, 1); - DMAs[2] = new DMA(0, 2); - DMAs[3] = new DMA(0, 3); - DMAs[4] = new DMA(1, 0); - DMAs[5] = new DMA(1, 1); - DMAs[6] = new DMA(1, 2); - DMAs[7] = new DMA(1, 3); + DMAs[0] = new DMA(0, 0, *GPU); + DMAs[1] = new DMA(0, 1, *GPU); + DMAs[2] = new DMA(0, 2, *GPU); + DMAs[3] = new DMA(0, 3, *GPU); + DMAs[4] = new DMA(1, 0, *GPU); + DMAs[5] = new DMA(1, 1, *GPU); + DMAs[6] = new DMA(1, 2, *GPU); + DMAs[7] = new DMA(1, 3, *GPU); SPU = new class SPU; SPI = new class SPIHost(); @@ -230,7 +232,6 @@ bool Init() Wifi = new class Wifi(); NDSCartSlot = std::make_unique(); GBACartSlot = std::make_unique(); - if (!GPU::Init()) return false; if (!DSi::Init()) return false; @@ -261,7 +262,7 @@ void DeInit() NDSCartSlot = nullptr; GBACartSlot = nullptr; - GPU::DeInit(); + GPU = nullptr; DSi::DeInit(); @@ -521,7 +522,7 @@ void SetupDirectBoot(const std::string& romname) PostFlag7 = 0x01; PowerControl9 = 0x820F; - GPU::SetPowerCnt(PowerControl9); + GPU->SetPowerCnt(PowerControl9); // checkme RCnt = 0x8000; @@ -644,9 +645,9 @@ void Reset() KeyCnt[1] = 0; RCnt = 0; + GPU->Reset(); NDSCartSlot->Reset(); GBACartSlot->Reset(); - GPU::Reset(); SPU->Reset(); SPI->Reset(); RTC->Reset(); @@ -720,7 +721,7 @@ void Stop(Platform::StopReason reason) Log(level, "Stopping emulated console (Reason: %s)\n", StopReasonName(reason)); Running = false; Platform::SignalStop(reason); - GPU::Stop(); + GPU->Stop(); SPU->Stop(); if (ConsoleType == 1) @@ -848,7 +849,7 @@ bool DoSavestate(Savestate* file) NDSCartSlot->DoSavestate(file); if (ConsoleType == 0) GBACartSlot->DoSavestate(file); - GPU::DoSavestate(file); + GPU->DoSavestate(file); SPU->DoSavestate(file); SPI->DoSavestate(file); RTC->DoSavestate(file); @@ -859,7 +860,7 @@ bool DoSavestate(Savestate* file) if (!file->Saving) { - GPU::SetPowerCnt(PowerControl9); + GPU->SetPowerCnt(PowerControl9); SPU->SetPowerCnt(PowerControl7 & 0x0001); Wifi->SetPowerCnt(PowerControl7 & 0x0002); @@ -1071,7 +1072,7 @@ u32 RunFrame() { FrameStartTimestamp = SysTimestamp; - GPU::TotalScanlines = 0; + GPU->TotalScanlines = 0; LagFrameFlag = true; bool runFrame = Running && !(CPUStop & CPUStop_Sleep); @@ -1095,7 +1096,7 @@ u32 RunFrame() ARM7Timestamp = target; TimerTimestamp[0] = target; TimerTimestamp[1] = target; - GPU3D::Timestamp = target; + GPU->GPU3D.Timestamp = target; RunSystemSleep(target); if (!(CPUStop & CPUStop_Sleep)) @@ -1103,7 +1104,7 @@ u32 RunFrame() } if (SysTimestamp >= frametarget) - GPU::BlankFrame(); + GPU->BlankFrame(); } else { @@ -1112,11 +1113,11 @@ u32 RunFrame() if (!(CPUStop & CPUStop_Wakeup)) { - GPU::StartFrame(); + GPU->StartFrame(); } CPUStop &= ~CPUStop_Wakeup; - while (Running && GPU::TotalScanlines==0) + while (Running && GPU->TotalScanlines==0) { u64 target = NextTarget(); ARM9Target = target << ARM9ClockShift; @@ -1125,7 +1126,7 @@ u32 RunFrame() if (CPUStop & CPUStop_GXStall) { // GXFIFO stall - s32 cycles = GPU3D::CyclesToRunFor(); + s32 cycles = GPU->GPU3D.CyclesToRunFor(); ARM9Timestamp = std::min(ARM9Target, ARM9Timestamp+(cycles<GPU3D.Run(); target = ARM9Timestamp >> ARM9ClockShift; CurCPU = 1; @@ -1187,7 +1188,7 @@ u32 RunFrame() } } - if (GPU::TotalScanlines == 0) + if (GPU->TotalScanlines == 0) continue; #ifdef DEBUG_CHECK_DESYNC @@ -1208,7 +1209,7 @@ u32 RunFrame() NumLagFrames++; if (Running) - return GPU::TotalScanlines; + return GPU->TotalScanlines; else return 263; } @@ -1531,7 +1532,7 @@ void SetIRQ(u32 cpu, u32 irq) { CPUStop &= ~CPUStop_Sleep; CPUStop |= CPUStop_Wakeup; - GPU3D::RestartFrame(); + GPU->GPU3D.RestartFrame(); } } } @@ -1708,7 +1709,7 @@ void NocashPrint(u32 ncpu, u32 addr) else if (!strcmp(cmd, "lr")) sprintf(subs, "%08X", cpu->R[14]); else if (!strcmp(cmd, "pc")) sprintf(subs, "%08X", cpu->R[15]); else if (!strcmp(cmd, "frame")) sprintf(subs, "%u", NumFrames); - else if (!strcmp(cmd, "scanline")) sprintf(subs, "%u", GPU::VCount); + else if (!strcmp(cmd, "scanline")) sprintf(subs, "%u", GPU->VCount); else if (!strcmp(cmd, "totalclks")) sprintf(subs, "%" PRIu64, GetSysClockCycles(0)); else if (!strcmp(cmd, "lastclks")) sprintf(subs, "%" PRIu64, GetSysClockCycles(1)); else if (!strcmp(cmd, "zeroclks")) @@ -2081,7 +2082,7 @@ void debug(u32 param) Log(LogLevel::Debug, "ARM7 IME=%08X IE=%08X IF=%08X IE2=%04X IF2=%04X\n", IME[1], IE[1], IF[1], IE2, IF2); //for (int i = 0; i < 9; i++) - // printf("VRAM %c: %02X\n", 'A'+i, GPU::VRAMCNT[i]); + // printf("VRAM %c: %02X\n", 'A'+i, GPU->VRAMCNT[i]); FILE* shit = fopen("debug/DSfirmware.bin", "wb"); @@ -2149,21 +2150,21 @@ u8 ARM9Read8(u32 addr) case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadPalette(addr); + return GPU->ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU::ReadVRAM_ABG(addr); - case 0x00200000: return GPU::ReadVRAM_BBG(addr); - case 0x00400000: return GPU::ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU::ReadVRAM_BOBJ(addr); - default: return GPU::ReadVRAM_LCDC(addr); + case 0x00000000: return GPU->ReadVRAM_ABG(addr); + case 0x00200000: return GPU->ReadVRAM_BBG(addr); + case 0x00400000: return GPU->ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU->ReadVRAM_BOBJ(addr); + default: return GPU->ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadOAM(addr); + return GPU->ReadOAM(addr); case 0x08000000: case 0x09000000: @@ -2211,21 +2212,21 @@ u16 ARM9Read16(u32 addr) case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadPalette(addr); + return GPU->ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU::ReadVRAM_ABG(addr); - case 0x00200000: return GPU::ReadVRAM_BBG(addr); - case 0x00400000: return GPU::ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU::ReadVRAM_BOBJ(addr); - default: return GPU::ReadVRAM_LCDC(addr); + case 0x00000000: return GPU->ReadVRAM_ABG(addr); + case 0x00200000: return GPU->ReadVRAM_BBG(addr); + case 0x00400000: return GPU->ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU->ReadVRAM_BOBJ(addr); + default: return GPU->ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadOAM(addr); + return GPU->ReadOAM(addr); case 0x08000000: case 0x09000000: @@ -2273,21 +2274,21 @@ u32 ARM9Read32(u32 addr) case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadPalette(addr); + return GPU->ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU::ReadVRAM_ABG(addr); - case 0x00200000: return GPU::ReadVRAM_BBG(addr); - case 0x00400000: return GPU::ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU::ReadVRAM_BOBJ(addr); - default: return GPU::ReadVRAM_LCDC(addr); + case 0x00000000: return GPU->ReadVRAM_ABG(addr); + case 0x00200000: return GPU->ReadVRAM_BBG(addr); + case 0x00400000: return GPU->ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU->ReadVRAM_BOBJ(addr); + default: return GPU->ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU::ReadOAM(addr & 0x7FF); + return GPU->ReadOAM(addr & 0x7FF); case 0x08000000: case 0x09000000: @@ -2382,7 +2383,7 @@ void ARM9Write16(u32 addr, u16 val) case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU::WritePalette(addr, val); + GPU->WritePalette(addr, val); return; case 0x06000000: @@ -2391,16 +2392,16 @@ void ARM9Write16(u32 addr, u16 val) #endif switch (addr & 0x00E00000) { - case 0x00000000: GPU::WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU::WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU::WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU::WriteVRAM_BOBJ(addr, val); return; - default: GPU::WriteVRAM_LCDC(addr, val); return; + case 0x00000000: GPU->WriteVRAM_ABG(addr, val); return; + case 0x00200000: GPU->WriteVRAM_BBG(addr, val); return; + case 0x00400000: GPU->WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: GPU->WriteVRAM_BOBJ(addr, val); return; + default: GPU->WriteVRAM_LCDC(addr, val); return; } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU::WriteOAM(addr, val); + GPU->WriteOAM(addr, val); return; case 0x08000000: @@ -2450,7 +2451,7 @@ void ARM9Write32(u32 addr, u32 val) case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU::WritePalette(addr, val); + GPU->WritePalette(addr, val); return; case 0x06000000: @@ -2459,16 +2460,16 @@ void ARM9Write32(u32 addr, u32 val) #endif switch (addr & 0x00E00000) { - case 0x00000000: GPU::WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU::WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU::WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU::WriteVRAM_BOBJ(addr, val); return; - default: GPU::WriteVRAM_LCDC(addr, val); return; + case 0x00000000: GPU->WriteVRAM_ABG(addr, val); return; + case 0x00200000: GPU->WriteVRAM_BBG(addr, val); return; + case 0x00400000: GPU->WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: GPU->WriteVRAM_BOBJ(addr, val); return; + default: GPU->WriteVRAM_LCDC(addr, val); return; } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU::WriteOAM(addr, val); + GPU->WriteOAM(addr, val); return; case 0x08000000: @@ -2570,7 +2571,7 @@ u8 ARM7Read8(u32 addr) case 0x06000000: case 0x06800000: - return GPU::ReadVRAM_ARM7(addr); + return GPU->ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: @@ -2638,7 +2639,7 @@ u16 ARM7Read16(u32 addr) case 0x06000000: case 0x06800000: - return GPU::ReadVRAM_ARM7(addr); + return GPU->ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: @@ -2706,7 +2707,7 @@ u32 ARM7Read32(u32 addr) case 0x06000000: case 0x06800000: - return GPU::ReadVRAM_ARM7(addr); + return GPU->ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: @@ -2777,7 +2778,7 @@ void ARM7Write8(u32 addr, u8 val) #ifdef JIT_ENABLED ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); #endif - GPU::WriteVRAM_ARM7(addr, val); + GPU->WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -2856,7 +2857,7 @@ void ARM7Write16(u32 addr, u16 val) #ifdef JIT_ENABLED ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); #endif - GPU::WriteVRAM_ARM7(addr, val); + GPU->WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -2938,7 +2939,7 @@ void ARM7Write32(u32 addr, u32 val) #ifdef JIT_ENABLED ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); #endif - GPU::WriteVRAM_ARM7(addr, val); + GPU->WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -3073,16 +3074,16 @@ u8 ARM9IORead8(u32 addr) case 0x04000208: return IME[0]; - case 0x04000240: return GPU::VRAMCNT[0]; - case 0x04000241: return GPU::VRAMCNT[1]; - case 0x04000242: return GPU::VRAMCNT[2]; - case 0x04000243: return GPU::VRAMCNT[3]; - case 0x04000244: return GPU::VRAMCNT[4]; - case 0x04000245: return GPU::VRAMCNT[5]; - case 0x04000246: return GPU::VRAMCNT[6]; + case 0x04000240: return GPU->VRAMCNT[0]; + case 0x04000241: return GPU->VRAMCNT[1]; + case 0x04000242: return GPU->VRAMCNT[2]; + case 0x04000243: return GPU->VRAMCNT[3]; + case 0x04000244: return GPU->VRAMCNT[4]; + case 0x04000245: return GPU->VRAMCNT[5]; + case 0x04000246: return GPU->VRAMCNT[6]; case 0x04000247: return WRAMCnt; - case 0x04000248: return GPU::VRAMCNT[7]; - case 0x04000249: return GPU::VRAMCNT[8]; + case 0x04000248: return GPU->VRAMCNT[7]; + case 0x04000249: return GPU->VRAMCNT[8]; CASE_READ8_16BIT(0x04000280, DivCnt) CASE_READ8_32BIT(0x04000290, DivNumerator[0]) @@ -3104,15 +3105,15 @@ u8 ARM9IORead8(u32 addr) if (addr >= 0x04000000 && addr < 0x04000060) { - return GPU::GPU2D_A.Read8(addr); + return GPU->GPU2D_A.Read8(addr); } if (addr >= 0x04001000 && addr < 0x04001060) { - return GPU::GPU2D_B.Read8(addr); + return GPU->GPU2D_B.Read8(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU3D::Read8(addr); + return GPU->GPU3D.Read8(addr); } // NO$GBA debug register "Emulation ID" if(addr >= 0x04FFFA00 && addr < 0x04FFFA10) @@ -3132,12 +3133,12 @@ u16 ARM9IORead16(u32 addr) { switch (addr) { - case 0x04000004: return GPU::DispStat[0]; - case 0x04000006: return GPU::VCount; + case 0x04000004: return GPU->DispStat[0]; + case 0x04000006: return GPU->VCount; - case 0x04000060: return GPU3D::Read16(addr); + case 0x04000060: return GPU->GPU3D.Read16(addr); case 0x04000064: - case 0x04000066: return GPU::GPU2D_A.Read16(addr); + case 0x04000066: return GPU->GPU2D_A.Read16(addr); case 0x040000B8: return DMAs[0]->Cnt & 0xFFFF; case 0x040000BA: return DMAs[0]->Cnt >> 16; @@ -3215,11 +3216,11 @@ u16 ARM9IORead16(u32 addr) case 0x04000210: return IE[0] & 0xFFFF; case 0x04000212: return IE[0] >> 16; - case 0x04000240: return GPU::VRAMCNT[0] | (GPU::VRAMCNT[1] << 8); - case 0x04000242: return GPU::VRAMCNT[2] | (GPU::VRAMCNT[3] << 8); - case 0x04000244: return GPU::VRAMCNT[4] | (GPU::VRAMCNT[5] << 8); - case 0x04000246: return GPU::VRAMCNT[6] | (WRAMCnt << 8); - case 0x04000248: return GPU::VRAMCNT[7] | (GPU::VRAMCNT[8] << 8); + case 0x04000240: return GPU->VRAMCNT[0] | (GPU->VRAMCNT[1] << 8); + case 0x04000242: return GPU->VRAMCNT[2] | (GPU->VRAMCNT[3] << 8); + case 0x04000244: return GPU->VRAMCNT[4] | (GPU->VRAMCNT[5] << 8); + case 0x04000246: return GPU->VRAMCNT[6] | (WRAMCnt << 8); + case 0x04000248: return GPU->VRAMCNT[7] | (GPU->VRAMCNT[8] << 8); case 0x04000280: return DivCnt; case 0x04000290: return DivNumerator[0] & 0xFFFF; @@ -3259,15 +3260,15 @@ u16 ARM9IORead16(u32 addr) if ((addr >= 0x04000000 && addr < 0x04000060) || (addr == 0x0400006C)) { - return GPU::GPU2D_A.Read16(addr); + return GPU->GPU2D_A.Read16(addr); } if ((addr >= 0x04001000 && addr < 0x04001060) || (addr == 0x0400106C)) { - return GPU::GPU2D_B.Read16(addr); + return GPU->GPU2D_B.Read16(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU3D::Read16(addr); + return GPU->GPU3D.Read16(addr); } if ((addr & 0xFFFFF000) != 0x04004000) @@ -3279,10 +3280,10 @@ u32 ARM9IORead32(u32 addr) { switch (addr) { - case 0x04000004: return GPU::DispStat[0] | (GPU::VCount << 16); + case 0x04000004: return GPU->DispStat[0] | (GPU->VCount << 16); - case 0x04000060: return GPU3D::Read32(addr); - case 0x04000064: return GPU::GPU2D_A.Read32(addr); + case 0x04000060: return GPU->GPU3D.Read32(addr); + case 0x04000064: return GPU->GPU2D_A.Read32(addr); case 0x040000B0: return DMAs[0]->SrcAddr; case 0x040000B4: return DMAs[0]->DstAddr; @@ -3342,9 +3343,9 @@ u32 ARM9IORead32(u32 addr) case 0x04000210: return IE[0]; case 0x04000214: return IF[0]; - case 0x04000240: return GPU::VRAMCNT[0] | (GPU::VRAMCNT[1] << 8) | (GPU::VRAMCNT[2] << 16) | (GPU::VRAMCNT[3] << 24); - case 0x04000244: return GPU::VRAMCNT[4] | (GPU::VRAMCNT[5] << 8) | (GPU::VRAMCNT[6] << 16) | (WRAMCnt << 24); - case 0x04000248: return GPU::VRAMCNT[7] | (GPU::VRAMCNT[8] << 8); + case 0x04000240: return GPU->VRAMCNT[0] | (GPU->VRAMCNT[1] << 8) | (GPU->VRAMCNT[2] << 16) | (GPU->VRAMCNT[3] << 24); + case 0x04000244: return GPU->VRAMCNT[4] | (GPU->VRAMCNT[5] << 8) | (GPU->VRAMCNT[6] << 16) | (WRAMCnt << 24); + case 0x04000248: return GPU->VRAMCNT[7] | (GPU->VRAMCNT[8] << 8); case 0x04000280: return DivCnt; case 0x04000290: return DivNumerator[0]; @@ -3403,15 +3404,15 @@ u32 ARM9IORead32(u32 addr) if ((addr >= 0x04000000 && addr < 0x04000060) || (addr == 0x0400006C)) { - return GPU::GPU2D_A.Read32(addr); + return GPU->GPU2D_A.Read32(addr); } if ((addr >= 0x04001000 && addr < 0x04001060) || (addr == 0x0400106C)) { - return GPU::GPU2D_B.Read32(addr); + return GPU->GPU2D_B.Read32(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU3D::Read32(addr); + return GPU->GPU3D.Read32(addr); } if ((addr & 0xFFFFF000) != 0x04004000) @@ -3424,9 +3425,9 @@ void ARM9IOWrite8(u32 addr, u8 val) switch (addr) { case 0x0400006C: - case 0x0400006D: GPU::GPU2D_A.Write8(addr, val); return; + case 0x0400006D: GPU->GPU2D_A.Write8(addr, val); return; case 0x0400106C: - case 0x0400106D: GPU::GPU2D_B.Write8(addr, val); return; + case 0x0400106D: GPU->GPU2D_B.Write8(addr, val); return; case 0x04000132: KeyCnt[0] = (KeyCnt[0] & 0xFF00) | val; @@ -3463,16 +3464,16 @@ void ARM9IOWrite8(u32 addr, u8 val) case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return; - case 0x04000240: GPU::MapVRAM_AB(0, val); return; - case 0x04000241: GPU::MapVRAM_AB(1, val); return; - case 0x04000242: GPU::MapVRAM_CD(2, val); return; - case 0x04000243: GPU::MapVRAM_CD(3, val); return; - case 0x04000244: GPU::MapVRAM_E(4, val); return; - case 0x04000245: GPU::MapVRAM_FG(5, val); return; - case 0x04000246: GPU::MapVRAM_FG(6, val); return; + case 0x04000240: GPU->MapVRAM_AB(0, val); return; + case 0x04000241: GPU->MapVRAM_AB(1, val); return; + case 0x04000242: GPU->MapVRAM_CD(2, val); return; + case 0x04000243: GPU->MapVRAM_CD(3, val); return; + case 0x04000244: GPU->MapVRAM_E(4, val); return; + case 0x04000245: GPU->MapVRAM_FG(5, val); return; + case 0x04000246: GPU->MapVRAM_FG(6, val); return; case 0x04000247: MapSharedWRAM(val); return; - case 0x04000248: GPU::MapVRAM_H(7, val); return; - case 0x04000249: GPU::MapVRAM_I(8, val); return; + case 0x04000248: GPU->MapVRAM_H(7, val); return; + case 0x04000249: GPU->MapVRAM_I(8, val); return; case 0x04000300: if (PostFlag9 & 0x01) val |= 0x01; @@ -3482,17 +3483,17 @@ void ARM9IOWrite8(u32 addr, u8 val) if (addr >= 0x04000000 && addr < 0x04000060) { - GPU::GPU2D_A.Write8(addr, val); + GPU->GPU2D_A.Write8(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU::GPU2D_B.Write8(addr, val); + GPU->GPU2D_B.Write8(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU3D::Write8(addr, val); + GPU->GPU3D.Write8(addr, val); return; } @@ -3503,16 +3504,16 @@ void ARM9IOWrite16(u32 addr, u16 val) { switch (addr) { - case 0x04000004: GPU::SetDispStat(0, val); return; - case 0x04000006: GPU::SetVCount(val); return; + case 0x04000004: GPU->SetDispStat(0, val); return; + case 0x04000006: GPU->SetVCount(val); return; - case 0x04000060: GPU3D::Write16(addr, val); return; + case 0x04000060: GPU->GPU3D.Write16(addr, val); return; case 0x04000068: - case 0x0400006A: GPU::GPU2D_A.Write16(addr, val); return; + case 0x0400006A: GPU->GPU2D_A.Write16(addr, val); return; - case 0x0400006C: GPU::GPU2D_A.Write16(addr, val); return; - case 0x0400106C: GPU::GPU2D_B.Write16(addr, val); return; + case 0x0400006C: GPU->GPU2D_A.Write16(addr, val); return; + case 0x0400106C: GPU->GPU2D_B.Write16(addr, val); return; case 0x040000B8: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0xFFFF0000) | val); return; case 0x040000BA: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0x0000FFFF) | (val << 16)); return; @@ -3629,24 +3630,24 @@ void ARM9IOWrite16(u32 addr, u16 val) // TODO: what happens when writing to IF this way?? case 0x04000240: - GPU::MapVRAM_AB(0, val & 0xFF); - GPU::MapVRAM_AB(1, val >> 8); + GPU->MapVRAM_AB(0, val & 0xFF); + GPU->MapVRAM_AB(1, val >> 8); return; case 0x04000242: - GPU::MapVRAM_CD(2, val & 0xFF); - GPU::MapVRAM_CD(3, val >> 8); + GPU->MapVRAM_CD(2, val & 0xFF); + GPU->MapVRAM_CD(3, val >> 8); return; case 0x04000244: - GPU::MapVRAM_E(4, val & 0xFF); - GPU::MapVRAM_FG(5, val >> 8); + GPU->MapVRAM_E(4, val & 0xFF); + GPU->MapVRAM_FG(5, val >> 8); return; case 0x04000246: - GPU::MapVRAM_FG(6, val & 0xFF); + GPU->MapVRAM_FG(6, val & 0xFF); MapSharedWRAM(val >> 8); return; case 0x04000248: - GPU::MapVRAM_H(7, val & 0xFF); - GPU::MapVRAM_I(8, val >> 8); + GPU->MapVRAM_H(7, val & 0xFF); + GPU->MapVRAM_I(8, val >> 8); return; case 0x04000280: DivCnt = val; StartDiv(); return; @@ -3660,23 +3661,23 @@ void ARM9IOWrite16(u32 addr, u16 val) case 0x04000304: PowerControl9 = val & 0x820F; - GPU::SetPowerCnt(PowerControl9); + GPU->SetPowerCnt(PowerControl9); return; } if (addr >= 0x04000000 && addr < 0x04000060) { - GPU::GPU2D_A.Write16(addr, val); + GPU->GPU2D_A.Write16(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU::GPU2D_B.Write16(addr, val); + GPU->GPU2D_B.Write16(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU3D::Write16(addr, val); + GPU->GPU3D.Write16(addr, val); return; } @@ -3688,16 +3689,16 @@ void ARM9IOWrite32(u32 addr, u32 val) switch (addr) { case 0x04000004: - GPU::SetDispStat(0, val & 0xFFFF); - GPU::SetVCount(val >> 16); + GPU->SetDispStat(0, val & 0xFFFF); + GPU->SetVCount(val >> 16); return; - case 0x04000060: GPU3D::Write32(addr, val); return; + case 0x04000060: GPU->GPU3D.Write32(addr, val); return; case 0x04000064: - case 0x04000068: GPU::GPU2D_A.Write32(addr, val); return; + case 0x04000068: GPU->GPU2D_A.Write32(addr, val); return; - case 0x0400006C: GPU::GPU2D_A.Write16(addr, val&0xFFFF); return; - case 0x0400106C: GPU::GPU2D_B.Write16(addr, val&0xFFFF); return; + case 0x0400006C: GPU->GPU2D_A.Write16(addr, val&0xFFFF); return; + case 0x0400106C: GPU->GPU2D_B.Write16(addr, val&0xFFFF); return; case 0x040000B0: DMAs[0]->SrcAddr = val; return; case 0x040000B4: DMAs[0]->DstAddr = val; return; @@ -3793,23 +3794,23 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return; case 0x04000210: IE[0] = val; UpdateIRQ(0); return; - case 0x04000214: IF[0] &= ~val; GPU3D::CheckFIFOIRQ(); UpdateIRQ(0); return; + case 0x04000214: IF[0] &= ~val; GPU->GPU3D.CheckFIFOIRQ(); UpdateIRQ(0); return; case 0x04000240: - GPU::MapVRAM_AB(0, val & 0xFF); - GPU::MapVRAM_AB(1, (val >> 8) & 0xFF); - GPU::MapVRAM_CD(2, (val >> 16) & 0xFF); - GPU::MapVRAM_CD(3, val >> 24); + GPU->MapVRAM_AB(0, val & 0xFF); + GPU->MapVRAM_AB(1, (val >> 8) & 0xFF); + GPU->MapVRAM_CD(2, (val >> 16) & 0xFF); + GPU->MapVRAM_CD(3, val >> 24); return; case 0x04000244: - GPU::MapVRAM_E(4, val & 0xFF); - GPU::MapVRAM_FG(5, (val >> 8) & 0xFF); - GPU::MapVRAM_FG(6, (val >> 16) & 0xFF); + GPU->MapVRAM_E(4, val & 0xFF); + GPU->MapVRAM_FG(5, (val >> 8) & 0xFF); + GPU->MapVRAM_FG(6, (val >> 16) & 0xFF); MapSharedWRAM(val >> 24); return; case 0x04000248: - GPU::MapVRAM_H(7, val & 0xFF); - GPU::MapVRAM_I(8, (val >> 8) & 0xFF); + GPU->MapVRAM_H(7, val & 0xFF); + GPU->MapVRAM_I(8, (val >> 8) & 0xFF); return; case 0x04000280: DivCnt = val; StartDiv(); return; @@ -3826,7 +3827,7 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x04000304: PowerControl9 = val & 0x820F; - GPU::SetPowerCnt(PowerControl9); + GPU->SetPowerCnt(PowerControl9); return; case 0x04100010: @@ -3864,17 +3865,17 @@ void ARM9IOWrite32(u32 addr, u32 val) if (addr >= 0x04000000 && addr < 0x04000060) { - GPU::GPU2D_A.Write32(addr, val); + GPU->GPU2D_A.Write32(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU::GPU2D_B.Write32(addr, val); + GPU->GPU2D_B.Write32(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU3D::Write32(addr, val); + GPU->GPU3D.Write32(addr, val); return; } @@ -3939,7 +3940,7 @@ u8 ARM7IORead8(u32 addr) case 0x04000208: return IME[1]; - case 0x04000240: return GPU::VRAMSTAT; + case 0x04000240: return GPU->VRAMSTAT; case 0x04000241: return WRAMCnt; case 0x04000300: return PostFlag7; @@ -3960,8 +3961,8 @@ u16 ARM7IORead16(u32 addr) { switch (addr) { - case 0x04000004: return GPU::DispStat[1]; - case 0x04000006: return GPU::VCount; + case 0x04000004: return GPU->DispStat[1]; + case 0x04000006: return GPU->VCount; case 0x040000B8: return DMAs[4]->Cnt & 0xFFFF; case 0x040000BA: return DMAs[4]->Cnt >> 16; @@ -4054,7 +4055,7 @@ u32 ARM7IORead32(u32 addr) { switch (addr) { - case 0x04000004: return GPU::DispStat[1] | (GPU::VCount << 16); + case 0x04000004: return GPU->DispStat[1] | (GPU->VCount << 16); case 0x040000B0: return DMAs[4]->SrcAddr; case 0x040000B4: return DMAs[4]->DstAddr; @@ -4232,8 +4233,8 @@ void ARM7IOWrite16(u32 addr, u16 val) { switch (addr) { - case 0x04000004: GPU::SetDispStat(1, val); return; - case 0x04000006: GPU::SetVCount(val); return; + case 0x04000004: GPU->SetDispStat(1, val); return; + case 0x04000006: GPU->SetVCount(val); return; case 0x040000B8: DMAs[4]->WriteCnt((DMAs[4]->Cnt & 0xFFFF0000) | val); return; case 0x040000BA: DMAs[4]->WriteCnt((DMAs[4]->Cnt & 0x0000FFFF) | (val << 16)); return; @@ -4388,8 +4389,8 @@ void ARM7IOWrite32(u32 addr, u32 val) switch (addr) { case 0x04000004: - GPU::SetDispStat(1, val & 0xFFFF); - GPU::SetVCount(val >> 16); + GPU->SetDispStat(1, val & 0xFFFF); + GPU->SetVCount(val >> 16); return; case 0x040000B0: DMAs[4]->SrcAddr = val; return; diff --git a/src/NDS.h b/src/NDS.h index 127510dd..88a1f2f3 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -19,6 +19,7 @@ #ifndef NDS_H #define NDS_H +#include #include #include #include @@ -40,6 +41,11 @@ class Wifi; class AREngine; +namespace Melon +{ +class GPU; +} + namespace NDS { @@ -262,6 +268,7 @@ extern class RTC* RTC; extern class Wifi* Wifi; extern std::unique_ptr NDSCartSlot; extern std::unique_ptr GBACartSlot; +extern std::unique_ptr GPU; extern class AREngine* AREngine; const u32 ARM7WRAMSize = 0x10000; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a4e56d44..b820b508 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -163,7 +163,7 @@ EmuThread* emuThread; int autoScreenSizing = 0; int videoRenderer; -GPU::RenderSettings videoSettings; +Melon::RenderSettings videoSettings; bool videoSettingsDirty; CameraManager* camManager[2]; @@ -340,8 +340,8 @@ void EmuThread::run() videoRenderer = 0; } - GPU::InitRenderer(videoRenderer); - GPU::SetRenderSettings(videoRenderer, videoSettings); + NDS::GPU->InitRenderer(videoRenderer); + NDS::GPU->SetRenderSettings(videoRenderer, videoSettings); NDS::SPU->SetInterpolation(Config::AudioInterp); @@ -472,7 +472,7 @@ void EmuThread::run() videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; - GPU::SetRenderSettings(videoRenderer, videoSettings); + NDS::GPU->SetRenderSettings(videoRenderer, videoSettings); } // process input and hotkeys @@ -534,12 +534,12 @@ void EmuThread::run() if (!oglContext) { FrontBufferLock.lock(); - FrontBuffer = GPU::FrontBuffer; + FrontBuffer = NDS::GPU->FrontBuffer; FrontBufferLock.unlock(); } else { - FrontBuffer = GPU::FrontBuffer; + FrontBuffer = NDS::GPU->FrontBuffer; drawScreenGL(); } @@ -676,7 +676,7 @@ void EmuThread::run() EmuStatus = emuStatus_Exit; - GPU::DeInitRenderer(); + NDS::GPU->DeInitRenderer(); NDS::DeInit(); //Platform::LAN_DeInit(); } @@ -780,10 +780,10 @@ void EmuThread::drawScreenGL() glActiveTexture(GL_TEXTURE0); #ifdef OGLRENDERER_ENABLED - if (GPU::Renderer != 0) + if (NDS::GPU->Renderer != 0) { // hardware-accelerated render - GPU::CurGLCompositor->BindOutputTexture(frontbuf); + NDS::GPU->CurGLCompositor->BindOutputTexture(frontbuf); } else #endif @@ -791,12 +791,12 @@ void EmuThread::drawScreenGL() // regular render glBindTexture(GL_TEXTURE_2D, screenTexture); - if (GPU::Framebuffer[frontbuf][0] && GPU::Framebuffer[frontbuf][1]) + if (NDS::GPU->Framebuffer[frontbuf][0] && NDS::GPU->Framebuffer[frontbuf][1]) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][0]); + GL_UNSIGNED_BYTE, NDS::GPU->Framebuffer[frontbuf][0]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, GPU::Framebuffer[frontbuf][1]); + GL_UNSIGNED_BYTE, NDS::GPU->Framebuffer[frontbuf][1]); } } @@ -1082,14 +1082,14 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) { emuThread->FrontBufferLock.lock(); int frontbuf = emuThread->FrontBuffer; - if (!GPU::Framebuffer[frontbuf][0] || !GPU::Framebuffer[frontbuf][1]) + if (!NDS::GPU->Framebuffer[frontbuf][0] || !NDS::GPU->Framebuffer[frontbuf][1]) { emuThread->FrontBufferLock.unlock(); return; } - memcpy(screen[0].scanLine(0), GPU::Framebuffer[frontbuf][0], 256 * 192 * 4); - memcpy(screen[1].scanLine(0), GPU::Framebuffer[frontbuf][1], 256 * 192 * 4); + memcpy(screen[0].scanLine(0), NDS::GPU->Framebuffer[frontbuf][0], 256 * 192 * 4); + memcpy(screen[1].scanLine(0), NDS::GPU->Framebuffer[frontbuf][1], 256 * 192 * 4); emuThread->FrontBufferLock.unlock(); QRect screenrc(0, 0, 256, 192); From e63e29ca91ba5fc1630634fbb9f064b9cce6cc1f Mon Sep 17 00:00:00 2001 From: orbea Date: Sat, 11 Nov 2023 10:31:10 -0800 Subject: [PATCH 040/157] DSi_Camera: fix gcc-14 build issue melonDS/src/DSi_Camera.cpp:190:23: error: 'clamp' is not a member of 'std' 190 | r1 = std::clamp(r1, 0, 255); g1 = std::clamp(g1, 0, 255); b1 = std::clamp(b1, 0, 255); | ^~~~~ --- src/DSi_Camera.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 74916293..2b259c59 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -16,6 +16,7 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include #include #include #include "DSi.h" From f2d7a290156b5aa62edc00644c55b00de73b6229 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 15 Nov 2023 17:26:01 +0100 Subject: [PATCH 041/157] fix forgotten include --- src/NDSCart.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NDSCart.h b/src/NDSCart.h index 1b172518..5696fd7e 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -21,6 +21,7 @@ #include #include +#include #include "types.h" #include "Savestate.h" From 544fefa27f698f3a0d799a782dc03d3eb47561db Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 18 Nov 2023 10:40:54 -0500 Subject: [PATCH 042/157] Refactor the JIT to be object-oriented (#1879) * Move TinyVector to a new file - So it's less sensitive to #include ordering * Forgot to include assert.h * Refactor ARMJIT_Memory into an object * Oops, forgot a declaration * Refactor ARMJIT to be contained in an object * Remove an unused function declaration * Add a missing #include * Remove a now-unused global * Use ARMJIT_Memory's own memory access functions * Fix some omissions in the ARM JIT * Move libandroid to be a member of ARMJIT_Memory instead of a global * Default-initialize most fields in ARMJIT_Compiler.h * Define NOOP_IF_NO_JIT * Finish refactoring the JIT to be object-oriented --- src/ARM.cpp | 38 ++-- src/ARM.h | 15 +- src/ARMJIT.cpp | 244 +++++++------------------ src/ARMJIT.h | 142 ++++++++++++--- src/ARMJIT_A64/ARMJIT_Compiler.cpp | 9 +- src/ARMJIT_A64/ARMJIT_Compiler.h | 6 +- src/ARMJIT_A64/ARMJIT_LoadStore.cpp | 26 +-- src/ARMJIT_Compiler.h | 11 +- src/ARMJIT_Internal.h | 150 +-------------- src/ARMJIT_Memory.cpp | 272 +++++++++++----------------- src/ARMJIT_Memory.h | 251 +++++++++++++++++++------ src/ARMJIT_RegisterCache.h | 1 - src/ARMJIT_x64/ARMJIT_ALU.cpp | 1 + src/ARMJIT_x64/ARMJIT_Branch.cpp | 1 + src/ARMJIT_x64/ARMJIT_Compiler.cpp | 7 +- src/ARMJIT_x64/ARMJIT_Compiler.h | 58 +++--- src/ARMJIT_x64/ARMJIT_LoadStore.cpp | 31 ++-- src/ARM_InstrInfo.cpp | 4 +- src/ARM_InstrInfo.h | 2 +- src/CP15.cpp | 23 +-- src/DSi.cpp | 140 ++++---------- src/GPU.cpp | 8 +- src/GPU.h | 8 +- src/JitBlock.h | 61 +++++++ src/NDS.cpp | 136 ++++---------- src/NDS.h | 6 + src/NDSCart.h | 1 + src/TinyVector.h | 131 ++++++++++++++ 28 files changed, 894 insertions(+), 889 deletions(-) create mode 100644 src/JitBlock.h create mode 100644 src/TinyVector.h diff --git a/src/ARM.cpp b/src/ARM.cpp index 18d50fe2..a361d778 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -26,11 +26,7 @@ #include "ARMJIT.h" #include "Platform.h" #include "GPU.h" - -#ifdef JIT_ENABLED -#include "ARMJIT.h" #include "ARMJIT_Memory.h" -#endif using Platform::Log; using Platform::LogLevel; @@ -88,7 +84,7 @@ void ARM::GdbCheckC() {} -u32 ARM::ConditionTable[16] = +const u32 ARM::ConditionTable[16] = { 0xF0F0, // EQ 0x0F0F, // NE @@ -108,16 +104,14 @@ u32 ARM::ConditionTable[16] = 0x0000 // NE }; - -ARM::ARM(u32 num, Melon::GPU& gpu) : +ARM::ARM(u32 num, ARMJIT::ARMJIT& jit, Melon::GPU& gpu) : #ifdef GDBSTUB_ENABLED GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)), #endif + JIT(jit), + Num(num), // well uh GPU(gpu) { - // well uh - Num = num; - #ifdef GDBSTUB_ENABLED if (Platform::GetConfigBool(Platform::GdbEnabled) #ifdef JIT_ENABLED @@ -134,25 +128,21 @@ ARM::~ARM() // dorp } -ARMv5::ARMv5(Melon::GPU& gpu) : ARM(0, gpu) +ARMv5::ARMv5(ARMJIT::ARMJIT& jit, Melon::GPU& gpu) : ARM(0, jit, gpu) { -#ifndef JIT_ENABLED - DTCM = new u8[DTCMPhysicalSize]; -#endif + DTCM = JIT.Memory.GetARM9DTCM(); PU_Map = PU_PrivMap; } -ARMv4::ARMv4(Melon::GPU& gpu) : ARM(1, gpu) +ARMv4::ARMv4(ARMJIT::ARMJIT& jit, Melon::GPU& gpu) : ARM(1, jit, gpu) { // } ARMv5::~ARMv5() { -#ifndef JIT_ENABLED - delete[] DTCM; -#endif + // DTCM is owned by Memory, not going to delete it } void ARM::Reset() @@ -752,19 +742,19 @@ void ARMv5::ExecuteJIT() u32 instrAddr = R[15] - ((CPSR&0x20)?2:4); if ((instrAddr < FastBlockLookupStart || instrAddr >= (FastBlockLookupStart + FastBlockLookupSize)) - && !ARMJIT::SetupExecutableRegion(0, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) + && !JIT.SetupExecutableRegion(0, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) { NDS::ARM9Timestamp = NDS::ARM9Target; Log(LogLevel::Error, "ARMv5 PC in non executable region %08X\n", R[15]); return; } - ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(0, FastBlockLookup, + ARMJIT::JitBlockEntry block = JIT.LookUpBlock(0, FastBlockLookup, instrAddr - FastBlockLookupStart, instrAddr); if (block) ARM_Dispatch(this, block); else - ARMJIT::CompileBlock(this); + JIT.CompileBlock(this); if (StopExecution) { @@ -909,19 +899,19 @@ void ARMv4::ExecuteJIT() u32 instrAddr = R[15] - ((CPSR&0x20)?2:4); if ((instrAddr < FastBlockLookupStart || instrAddr >= (FastBlockLookupStart + FastBlockLookupSize)) - && !ARMJIT::SetupExecutableRegion(1, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) + && !JIT.SetupExecutableRegion(1, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) { NDS::ARM7Timestamp = NDS::ARM7Target; Log(LogLevel::Error, "ARMv4 PC in non executable region %08X\n", R[15]); return; } - ARMJIT::JitBlockEntry block = ARMJIT::LookUpBlock(1, FastBlockLookup, + ARMJIT::JitBlockEntry block = JIT.LookUpBlock(1, FastBlockLookup, instrAddr - FastBlockLookupStart, instrAddr); if (block) ARM_Dispatch(this, block); else - ARMJIT::CompileBlock(this); + JIT.CompileBlock(this); if (StopExecution) { diff --git a/src/ARM.h b/src/ARM.h index d2d0ea90..c8f596a7 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -42,18 +42,24 @@ enum const u32 ITCMPhysicalSize = 0x8000; const u32 DTCMPhysicalSize = 0x4000; +namespace ARMJIT +{ +class ARMJIT; +} namespace Melon { class GPU; } +class ARMJIT_Memory; + class ARM #ifdef GDBSTUB_ENABLED : public Gdb::StubCallbacks #endif { public: - ARM(u32 num, Melon::GPU& gpu); + ARM(u32 num, ARMJIT::ARMJIT& jit, Melon::GPU& gpu); virtual ~ARM(); // destroy shit virtual void Reset(); @@ -179,11 +185,12 @@ public: u64* FastBlockLookup; #endif - static u32 ConditionTable[16]; + static const u32 ConditionTable[16]; #ifdef GDBSTUB_ENABLED Gdb::GdbStub GdbStub; #endif + ARMJIT::ARMJIT& JIT; protected: u8 (*BusRead8)(u32 addr); u16 (*BusRead16)(u32 addr); @@ -221,7 +228,7 @@ private: class ARMv5 : public ARM { public: - ARMv5(Melon::GPU& gpu); + ARMv5(ARMJIT::ARMJIT& jit, Melon::GPU& gpu); ~ARMv5(); void Reset() override; @@ -365,7 +372,7 @@ public: class ARMv4 : public ARM { public: - ARMv4(Melon::GPU& gpu); + ARMv4(ARMJIT::ARMJIT& jit, Melon::GPU& gpu); void Reset() override; diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index 19b4438b..c5512135 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -17,7 +17,7 @@ */ #include "ARMJIT.h" - +#include "ARMJIT_Memory.h" #include #include #include @@ -58,49 +58,6 @@ namespace ARMJIT #define JIT_DEBUGPRINT(msg, ...) //#define JIT_DEBUGPRINT(msg, ...) Platform::Log(Platform::LogLevel::Debug, msg, ## __VA_ARGS__) -Compiler* JITCompiler; - -int MaxBlockSize; -bool LiteralOptimizations; -bool BranchOptimizations; -bool FastMemory; - - -std::unordered_map JitBlocks9; -std::unordered_map JitBlocks7; - -std::unordered_map RestoreCandidates; - -TinyVector InvalidLiterals; - -AddressRange CodeIndexITCM[ITCMPhysicalSize / 512]; -AddressRange CodeIndexMainRAM[NDS::MainRAMMaxSize / 512]; -AddressRange CodeIndexSWRAM[NDS::SharedWRAMSize / 512]; -AddressRange CodeIndexVRAM[0x100000 / 512]; -AddressRange CodeIndexARM9BIOS[sizeof(NDS::ARM9BIOS) / 512]; -AddressRange CodeIndexARM7BIOS[sizeof(NDS::ARM7BIOS) / 512]; -AddressRange CodeIndexARM7WRAM[NDS::ARM7WRAMSize / 512]; -AddressRange CodeIndexARM7WVRAM[0x40000 / 512]; -AddressRange CodeIndexBIOS9DSi[0x10000 / 512]; -AddressRange CodeIndexBIOS7DSi[0x10000 / 512]; -AddressRange CodeIndexNWRAM_A[DSi::NWRAMSize / 512]; -AddressRange CodeIndexNWRAM_B[DSi::NWRAMSize / 512]; -AddressRange CodeIndexNWRAM_C[DSi::NWRAMSize / 512]; - -u64 FastBlockLookupITCM[ITCMPhysicalSize / 2]; -u64 FastBlockLookupMainRAM[NDS::MainRAMMaxSize / 2]; -u64 FastBlockLookupSWRAM[NDS::SharedWRAMSize / 2]; -u64 FastBlockLookupVRAM[0x100000 / 2]; -u64 FastBlockLookupARM9BIOS[sizeof(NDS::ARM9BIOS) / 2]; -u64 FastBlockLookupARM7BIOS[sizeof(NDS::ARM7BIOS) / 2]; -u64 FastBlockLookupARM7WRAM[NDS::ARM7WRAMSize / 2]; -u64 FastBlockLookupARM7WVRAM[0x40000 / 2]; -u64 FastBlockLookupBIOS9DSi[0x10000 / 2]; -u64 FastBlockLookupBIOS7DSi[0x10000 / 2]; -u64 FastBlockLookupNWRAM_A[DSi::NWRAMSize / 2]; -u64 FastBlockLookupNWRAM_B[DSi::NWRAMSize / 2]; -u64 FastBlockLookupNWRAM_C[DSi::NWRAMSize / 2]; - const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] = { 0, @@ -123,58 +80,14 @@ const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] = DSi::NWRAMSize, }; -AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] = -{ - NULL, - CodeIndexITCM, - NULL, - CodeIndexARM9BIOS, - CodeIndexMainRAM, - CodeIndexSWRAM, - NULL, - CodeIndexVRAM, - CodeIndexARM7BIOS, - CodeIndexARM7WRAM, - NULL, - NULL, - CodeIndexARM7WVRAM, - CodeIndexBIOS9DSi, - CodeIndexBIOS7DSi, - CodeIndexNWRAM_A, - CodeIndexNWRAM_B, - CodeIndexNWRAM_C -}; - -u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] = -{ - NULL, - FastBlockLookupITCM, - NULL, - FastBlockLookupARM9BIOS, - FastBlockLookupMainRAM, - FastBlockLookupSWRAM, - NULL, - FastBlockLookupVRAM, - FastBlockLookupARM7BIOS, - FastBlockLookupARM7WRAM, - NULL, - NULL, - FastBlockLookupARM7WVRAM, - FastBlockLookupBIOS9DSi, - FastBlockLookupBIOS7DSi, - FastBlockLookupNWRAM_A, - FastBlockLookupNWRAM_B, - FastBlockLookupNWRAM_C -}; - -u32 LocaliseCodeAddress(u32 num, u32 addr) +u32 ARMJIT::LocaliseCodeAddress(u32 num, u32 addr) const noexcept { int region = num == 0 - ? ARMJIT_Memory::ClassifyAddress9(addr) - : ARMJIT_Memory::ClassifyAddress7(addr); + ? Memory.ClassifyAddress9(addr) + : Memory.ClassifyAddress7(addr); if (CodeMemRegions[region]) - return ARMJIT_Memory::LocaliseAddress(region, num, addr); + return Memory.LocaliseAddress(region, num, addr); return 0; } @@ -202,6 +115,26 @@ T SlowRead9(u32 addr, ARMv5* cpu) return val; } +template +T SlowRead7(u32 addr) +{ + u32 offset = addr & 0x3; + addr &= ~(sizeof(T) - 1); + + T val; + if (std::is_same::value) + val = (ConsoleType == 0 ? NDS::ARM7Read32 : DSi::ARM7Read32)(addr); + else if (std::is_same::value) + val = (ConsoleType == 0 ? NDS::ARM7Read16 : DSi::ARM7Read16)(addr); + else + val = (ConsoleType == 0 ? NDS::ARM7Read8 : DSi::ARM7Read8)(addr); + + if (std::is_same::value) + return ROR(val, offset << 3); + else + return val; +} + template void SlowWrite9(u32 addr, ARMv5* cpu, u32 val) { @@ -209,7 +142,7 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val) if (addr < cpu->ITCMSize) { - CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + cpu->JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); *(T*)&cpu->ITCM[addr & 0x7FFF] = val; } else if ((addr & cpu->DTCMMask) == cpu->DTCMBase) @@ -230,26 +163,6 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val) } } -template -T SlowRead7(u32 addr) -{ - u32 offset = addr & 0x3; - addr &= ~(sizeof(T) - 1); - - T val; - if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM7Read32 : DSi::ARM7Read32)(addr); - else if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM7Read16 : DSi::ARM7Read16)(addr); - else - val = (ConsoleType == 0 ? NDS::ARM7Read8 : DSi::ARM7Read8)(addr); - - if (std::is_same::value) - return ROR(val, offset << 3); - else - return val; -} - template void SlowWrite7(u32 addr, u32 val) { @@ -316,24 +229,13 @@ void SlowBlockTransfer7(u32 addr, u64* data, u32 num) INSTANTIATE_SLOWMEM(0) INSTANTIATE_SLOWMEM(1) -void Init() -{ - JITCompiler = new Compiler(); - - ARMJIT_Memory::Init(); -} - -void DeInit() +ARMJIT::~ARMJIT() noexcept { JitEnableWrite(); ResetBlockCache(); - ARMJIT_Memory::DeInit(); - - delete JITCompiler; - JITCompiler = nullptr; } -void Reset() +void ARMJIT::Reset() noexcept { MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize); LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations); @@ -348,7 +250,7 @@ void Reset() JitEnableWrite(); ResetBlockCache(); - ARMJIT_Memory::Reset(); + Memory.Reset(); } void FloodFillSetFlags(FetchedInstr instrs[], int start, u8 flags) @@ -575,7 +477,7 @@ InterpreterFunc InterpretTHUMB[ARMInstrInfo::tk_Count] = }; #undef F -void RetireJitBlock(JitBlock* block) +void ARMJIT::RetireJitBlock(JitBlock* block) noexcept { auto it = RestoreCandidates.find(block->InstrHash); if (it != RestoreCandidates.end()) @@ -589,7 +491,7 @@ void RetireJitBlock(JitBlock* block) } } -void CompileBlock(ARM* cpu) +void ARMJIT::CompileBlock(ARM* cpu) noexcept { bool thumb = cpu->CPSR & 0x20; @@ -616,7 +518,7 @@ void CompileBlock(ARM* cpu) u64* entry = &FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]; *entry = ((u64)blockAddr | cpu->Num) << 32; - *entry |= JITCompiler->SubEntryOffset(existingBlockIt->second->EntryPoint); + *entry |= JITCompiler.SubEntryOffset(existingBlockIt->second->EntryPoint); return; } @@ -717,7 +619,7 @@ void CompileBlock(ARM* cpu) nextInstr[1] = cpuv4->CodeRead32(r15); instrs[i].CodeCycles = cpu->CodeCycles; } - instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr); + instrs[i].Info = ARMInstrInfo::Decode(thumb, cpu->Num, instrs[i].Instr, LiteralOptimizations); hasMemoryInstr |= thumb ? (instrs[i].Info.Kind >= ARMInstrInfo::tk_LDR_PCREL && instrs[i].Info.Kind <= ARMInstrInfo::tk_STMIA) @@ -875,7 +777,7 @@ void CompileBlock(ARM* cpu) i++; - bool canCompile = JITCompiler->CanCompile(thumb, instrs[i - 1].Info.Kind); + bool canCompile = JITCompiler.CanCompile(thumb, instrs[i - 1].Info.Kind); bool secondaryFlagReadCond = !canCompile || (instrs[i - 1].BranchFlags & (branch_FollowCondTaken | branch_FollowCondNotTaken)); if (instrs[i - 1].Info.ReadFlags != 0 || secondaryFlagReadCond) FloodFillSetFlags(instrs, i - 2, !secondaryFlagReadCond ? instrs[i - 1].Info.ReadFlags : 0xF); @@ -956,7 +858,7 @@ void CompileBlock(ARM* cpu) FloodFillSetFlags(instrs, i - 1, 0xF); JitEnableWrite(); - block->EntryPoint = JITCompiler->CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr); + block->EntryPoint = JITCompiler.CompileBlock(cpu, thumb, instrs, i, hasMemoryInstr); JitEnableExecute(); JIT_DEBUGPRINT("block start %p\n", block->EntryPoint); @@ -977,7 +879,7 @@ void CompileBlock(ARM* cpu) AddressRange* region = CodeMemRegions[addressRanges[j] >> 27]; if (!PageContainsCode(®ion[(addressRanges[j] & 0x7FFF000) / 512])) - ARMJIT_Memory::SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true); + Memory.SetCodeProtection(addressRanges[j] >> 27, addressRanges[j] & 0x7FFFFFF, true); AddressRange* range = ®ion[(addressRanges[j] & 0x7FFFFFF) / 512]; range->Code |= addressMasks[j]; @@ -991,10 +893,10 @@ void CompileBlock(ARM* cpu) u64* entry = &FastBlockLookupRegions[(localAddr >> 27)][(localAddr & 0x7FFFFFF) / 2]; *entry = ((u64)blockAddr | cpu->Num) << 32; - *entry |= JITCompiler->SubEntryOffset(block->EntryPoint); + *entry |= JITCompiler.SubEntryOffset(block->EntryPoint); } -void InvalidateByAddr(u32 localAddr) +void ARMJIT::InvalidateByAddr(u32 localAddr) noexcept { JIT_DEBUGPRINT("invalidating by addr %x\n", localAddr); @@ -1031,7 +933,7 @@ void InvalidateByAddr(u32 localAddr) if (range->Blocks.Length == 0 && !PageContainsCode(®ion[(localAddr & 0x7FFF000) / 512])) { - ARMJIT_Memory::SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false); + Memory.SetCodeProtection(localAddr >> 27, localAddr & 0x7FFFFFF, false); } bool literalInvalidation = false; @@ -1064,7 +966,7 @@ void InvalidateByAddr(u32 localAddr) if (otherRange->Blocks.Length == 0) { if (!PageContainsCode(&otherRegion[(addr & 0x7FFF000) / 512])) - ARMJIT_Memory::SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false); + Memory.SetCodeProtection(addr >> 27, addr & 0x7FFFFFF, false); otherRange->Code = 0; } @@ -1088,7 +990,7 @@ void InvalidateByAddr(u32 localAddr) } } -void CheckAndInvalidateITCM() +void ARMJIT::CheckAndInvalidateITCM() noexcept { for (u32 i = 0; i < ITCMPhysicalSize; i+=512) { @@ -1106,7 +1008,7 @@ void CheckAndInvalidateITCM() } } -void CheckAndInvalidateWVRAM(int bank) +void ARMJIT::CheckAndInvalidateWVRAM(int bank) noexcept { u32 start = bank == 1 ? 0x20000 : 0; for (u32 i = start; i < start+0x20000; i+=512) @@ -1122,38 +1024,30 @@ void CheckAndInvalidateWVRAM(int bank) } } -template -void CheckAndInvalidate(u32 addr) -{ - u32 localAddr = ARMJIT_Memory::LocaliseAddress(region, num, addr); - if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16))) - InvalidateByAddr(localAddr); -} - -JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) +JitBlockEntry ARMJIT::LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept { u64* entry = &entries[offset / 2]; if (*entry >> 32 == (addr | num)) - return JITCompiler->AddEntryOffset((u32)*entry); + return JITCompiler.AddEntryOffset((u32)*entry); return NULL; } -void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) +void ARMJIT::blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept { u32 localAddr = LocaliseCodeAddress(num, blockAddr); - assert(JITCompiler->AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry); + assert(JITCompiler.AddEntryOffset((u32)FastBlockLookupRegions[localAddr >> 27][(localAddr & 0x7FFFFFF) / 2]) == entry); } -bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) +bool ARMJIT::SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept { // amazingly ignoring the DTCM is the proper behaviour for code fetches int region = num == 0 - ? ARMJIT_Memory::ClassifyAddress9(blockAddr) - : ARMJIT_Memory::ClassifyAddress7(blockAddr); + ? Memory.ClassifyAddress9(blockAddr) + : Memory.ClassifyAddress7(blockAddr); u32 memoryOffset; if (FastBlockLookupRegions[region] - && ARMJIT_Memory::GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size)) + && Memory.GetMirrorLocation(region, num, blockAddr, memoryOffset, start, size)) { //printf("setup exec region %d %d %08x %08x %x %x\n", num, region, blockAddr, start, size, memoryOffset); entry = FastBlockLookupRegions[region] + memoryOffset / 2; @@ -1162,28 +1056,28 @@ bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& return false; } -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32); -template void CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32); -template void CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32); +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32); +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32); +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32); +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32); +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32); +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32); +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32); -void ResetBlockCache() +void ARMJIT::ResetBlockCache() noexcept { Log(LogLevel::Debug, "Resetting JIT block cache...\n"); // could be replace through a function which only resets // the permissions but we're too lazy - ARMJIT_Memory::Reset(); + Memory.Reset(); InvalidLiterals.Clear(); for (int i = 0; i < ARMJIT_Memory::memregions_Count; i++) @@ -1221,10 +1115,10 @@ void ResetBlockCache() JitBlocks9.clear(); JitBlocks7.clear(); - JITCompiler->Reset(); + JITCompiler.Reset(); } -void JitEnableWrite() +void ARMJIT::JitEnableWrite() noexcept { #if defined(__APPLE__) && defined(__aarch64__) if (__builtin_available(macOS 11.0, *)) @@ -1232,7 +1126,7 @@ void JitEnableWrite() #endif } -void JitEnableExecute() +void ARMJIT::JitEnableExecute() noexcept { #if defined(__APPLE__) && defined(__aarch64__) if (__builtin_available(macOS 11.0, *)) diff --git a/src/ARMJIT.h b/src/ARMJIT.h index cd97561c..074e2a13 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -19,49 +19,147 @@ #ifndef ARMJIT_H #define ARMJIT_H +#include #include "types.h" -#include "ARM.h" -#include "ARM_InstrInfo.h" +#include "ARMJIT_Memory.h" +#include "JitBlock.h" #if defined(__APPLE__) && defined(__aarch64__) #include #endif +#include "ARMJIT_Compiler.h" + +class ARM; + namespace ARMJIT { +class JitBlock; +class ARMJIT +{ +public: + ARMJIT() noexcept : JITCompiler(*this), Memory(*this) {} + ~ARMJIT() noexcept NOOP_IF_NO_JIT; + void InvalidateByAddr(u32) noexcept NOOP_IF_NO_JIT; + void CheckAndInvalidateWVRAM(int) noexcept NOOP_IF_NO_JIT; + void CheckAndInvalidateITCM() noexcept NOOP_IF_NO_JIT; + void Reset() noexcept NOOP_IF_NO_JIT; + void JitEnableWrite() noexcept NOOP_IF_NO_JIT; + void JitEnableExecute() noexcept NOOP_IF_NO_JIT; + void CompileBlock(ARM* cpu) noexcept NOOP_IF_NO_JIT; + void ResetBlockCache() noexcept NOOP_IF_NO_JIT; -typedef void (*JitBlockEntry)(); +#ifdef JIT_ENABLED + template + void CheckAndInvalidate(u32 addr) noexcept + { + u32 localAddr = Memory.LocaliseAddress(region, num, addr); + if (CodeMemRegions[region][(localAddr & 0x7FFFFFF) / 512].Code & (1 << ((localAddr & 0x1FF) / 16))) + InvalidateByAddr(localAddr); + } + JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept; + bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept; + u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept; +#else + template + void CheckAndInvalidate(u32) noexcept {} +#endif -extern int MaxBlockSize; -extern bool LiteralOptimizations; -extern bool BranchOptimizations; -extern bool FastMemory; + ARMJIT_Memory Memory; + int MaxBlockSize {}; + bool LiteralOptimizations = false; + bool BranchOptimizations = false; + bool FastMemory = false; -void Init(); -void DeInit(); + TinyVector InvalidLiterals {}; +private: + friend class ::ARMJIT_Memory; + void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept; + void RetireJitBlock(JitBlock* block) noexcept; -void Reset(); + Compiler JITCompiler; + std::unordered_map JitBlocks9 {}; + std::unordered_map JitBlocks7 {}; -void CheckAndInvalidateITCM(); -void CheckAndInvalidateWVRAM(int bank); + std::unordered_map RestoreCandidates {}; -void InvalidateByAddr(u32 pseudoPhysical); -template -void CheckAndInvalidate(u32 addr); + AddressRange CodeIndexITCM[ITCMPhysicalSize / 512] {}; + AddressRange CodeIndexMainRAM[NDS::MainRAMMaxSize / 512] {}; + AddressRange CodeIndexSWRAM[NDS::SharedWRAMSize / 512] {}; + AddressRange CodeIndexVRAM[0x100000 / 512] {}; + AddressRange CodeIndexARM9BIOS[sizeof(NDS::ARM9BIOS) / 512] {}; + AddressRange CodeIndexARM7BIOS[sizeof(NDS::ARM7BIOS) / 512] {}; + AddressRange CodeIndexARM7WRAM[NDS::ARM7WRAMSize / 512] {}; + AddressRange CodeIndexARM7WVRAM[0x40000 / 512] {}; + AddressRange CodeIndexBIOS9DSi[0x10000 / 512] {}; + AddressRange CodeIndexBIOS7DSi[0x10000 / 512] {}; + AddressRange CodeIndexNWRAM_A[DSi::NWRAMSize / 512] {}; + AddressRange CodeIndexNWRAM_B[DSi::NWRAMSize / 512] {}; + AddressRange CodeIndexNWRAM_C[DSi::NWRAMSize / 512] {}; -void CompileBlock(ARM* cpu); + u64 FastBlockLookupITCM[ITCMPhysicalSize / 2] {}; + u64 FastBlockLookupMainRAM[NDS::MainRAMMaxSize / 2] {}; + u64 FastBlockLookupSWRAM[NDS::SharedWRAMSize / 2] {}; + u64 FastBlockLookupVRAM[0x100000 / 2] {}; + u64 FastBlockLookupARM9BIOS[sizeof(NDS::ARM9BIOS) / 2] {}; + u64 FastBlockLookupARM7BIOS[sizeof(NDS::ARM7BIOS) / 2] {}; + u64 FastBlockLookupARM7WRAM[NDS::ARM7WRAMSize / 2] {}; + u64 FastBlockLookupARM7WVRAM[0x40000 / 2] {}; + u64 FastBlockLookupBIOS9DSi[0x10000 / 2] {}; + u64 FastBlockLookupBIOS7DSi[0x10000 / 2] {}; + u64 FastBlockLookupNWRAM_A[DSi::NWRAMSize / 2] {}; + u64 FastBlockLookupNWRAM_B[DSi::NWRAMSize / 2] {}; + u64 FastBlockLookupNWRAM_C[DSi::NWRAMSize / 2] {}; -void ResetBlockCache(); + AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] = + { + NULL, + CodeIndexITCM, + NULL, + CodeIndexARM9BIOS, + CodeIndexMainRAM, + CodeIndexSWRAM, + NULL, + CodeIndexVRAM, + CodeIndexARM7BIOS, + CodeIndexARM7WRAM, + NULL, + NULL, + CodeIndexARM7WVRAM, + CodeIndexBIOS9DSi, + CodeIndexBIOS7DSi, + CodeIndexNWRAM_A, + CodeIndexNWRAM_B, + CodeIndexNWRAM_C + }; -JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr); -bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size); - -void JitEnableWrite(); -void JitEnableExecute(); + u64* const FastBlockLookupRegions[ARMJIT_Memory::memregions_Count] = + { + NULL, + FastBlockLookupITCM, + NULL, + FastBlockLookupARM9BIOS, + FastBlockLookupMainRAM, + FastBlockLookupSWRAM, + NULL, + FastBlockLookupVRAM, + FastBlockLookupARM7BIOS, + FastBlockLookupARM7WRAM, + NULL, + NULL, + FastBlockLookupARM7WVRAM, + FastBlockLookupBIOS9DSi, + FastBlockLookupBIOS7DSi, + FastBlockLookupNWRAM_A, + FastBlockLookupNWRAM_B, + FastBlockLookupNWRAM_C + }; +}; } +// Defined in assembly extern "C" void ARM_Dispatch(ARM* cpu, ARMJIT::JitBlockEntry entry); #endif diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp index 55bca846..90940b0c 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp @@ -20,6 +20,7 @@ #include "../ARMJIT_Internal.h" #include "../ARMInterpreter.h" +#include "../ARMJIT.h" #if defined(__SWITCH__) #include @@ -219,7 +220,7 @@ void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged) } } -Compiler::Compiler() +Compiler::Compiler(ARMJIT& jit) : Arm64Gen::ARM64XEmitter(), JIT(jit) { #ifdef __SWITCH__ JitRWBase = aligned_alloc(0x1000, JitMemSize); @@ -704,12 +705,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] if (JitMemMainSize - GetCodeOffset() < 1024 * 16) { Log(LogLevel::Debug, "JIT near memory full, resetting...\n"); - ResetBlockCache(); + JIT.ResetBlockCache(); } if ((JitMemMainSize + JitMemSecondarySize) - OtherCodeRegion < 1024 * 8) { Log(LogLevel::Debug, "JIT far memory full, resetting...\n"); - ResetBlockCache(); + JIT.ResetBlockCache(); } JitBlockEntry res = (JitBlockEntry)GetRXPtr(); @@ -722,7 +723,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] CPSRDirty = false; if (hasMemInstr) - MOVP2R(RMemBase, Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start); + MOVP2R(RMemBase, Num == 0 ? JIT.Memory.FastMem9Start : JIT.Memory.FastMem7Start); for (int i = 0; i < instrsCount; i++) { diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index 5045cb5f..1f79f3d3 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -20,7 +20,6 @@ #define ARMJIT_A64_COMPILER_H #include "../ARM.h" -#include "../ARMJIT.h" #include "../dolphin/Arm64Emitter.h" @@ -31,7 +30,7 @@ namespace ARMJIT { - +class ARMJIT; const Arm64Gen::ARM64Reg RMemBase = Arm64Gen::X26; const Arm64Gen::ARM64Reg RCPSR = Arm64Gen::W27; const Arm64Gen::ARM64Reg RCycles = Arm64Gen::W28; @@ -97,7 +96,7 @@ class Compiler : public Arm64Gen::ARM64XEmitter public: typedef void (Compiler::*CompileFunc)(); - Compiler(); + Compiler(ARMJIT& jit); ~Compiler(); void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true); @@ -243,6 +242,7 @@ public: OtherCodeRegion = offset; } + ARMJIT& JIT; ptrdiff_t OtherCodeRegion; bool Exit; diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp index ee8aabe2..a779a727 100644 --- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp @@ -62,9 +62,9 @@ u8* Compiler::RewriteMemAccess(u8* pc) bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) { - u32 localAddr = LocaliseCodeAddress(Num, addr); + u32 localAddr = JIT.LocaliseCodeAddress(Num, addr); - int invalidLiteralIdx = InvalidLiterals.Find(localAddr); + int invalidLiteralIdx = JIT.InvalidLiterals.Find(localAddr); if (invalidLiteralIdx != -1) { return false; @@ -111,7 +111,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) if (size == 16) addressMask = ~1; - if (ARMJIT::LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) + if (JIT.LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) { u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1); @@ -146,7 +146,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) MOV(W0, rnMapped); } - bool addrIsStatic = ARMJIT::LiteralOptimizations + bool addrIsStatic = JIT.LiteralOptimizations && RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post)); u32 staticAddress; if (addrIsStatic) @@ -185,10 +185,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) MOV(rnMapped, W0); u32 expectedTarget = Num == 0 - ? ARMJIT_Memory::ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion) - : ARMJIT_Memory::ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion); + ? JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion) + : JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion); - if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget))) + if (JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || JIT.Memory.IsFastmemCompatible(expectedTarget))) { ptrdiff_t memopStart = GetCodeOffset(); LoadStorePatch patch; @@ -225,7 +225,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) { void* func = NULL; if (addrIsStatic) - func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); + func = JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); PushRegs(false, false); @@ -452,7 +452,7 @@ void Compiler::T_Comp_LoadPCRel() u32 offset = ((CurInstr.Instr & 0xFF) << 2); u32 addr = (R15 & ~0x2) + offset; - if (!ARMJIT::LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) + if (!JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0); } @@ -494,11 +494,11 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc Comp_AddCycles_CDI(); int expectedTarget = Num == 0 - ? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion) - : ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion); + ? JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); - bool compileFastPath = ARMJIT::FastMemory - && store && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)); + bool compileFastPath = JIT.FastMemory + && store && !usermode && (CurInstr.Cond() < 0xE || JIT.Memory.IsFastmemCompatible(expectedTarget)); { s32 offset = decrement diff --git a/src/ARMJIT_Compiler.h b/src/ARMJIT_Compiler.h index c5348f48..4ece834f 100644 --- a/src/ARMJIT_Compiler.h +++ b/src/ARMJIT_Compiler.h @@ -19,6 +19,12 @@ #ifndef ARMJIT_COMPILER_H #define ARMJIT_COMPILER_H +#ifdef JIT_ENABLED +#define NOOP_IF_NO_JIT +#else +#define NOOP_IF_NO_JIT {} +#endif + #if defined(__x86_64__) #include "ARMJIT_x64/ARMJIT_Compiler.h" #elif defined(__aarch64__) @@ -27,9 +33,4 @@ #error "The current target platform doesn't have a JIT backend" #endif -namespace ARMJIT -{ -extern Compiler* JITCompiler; -} - #endif diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h index fb803072..7a8eb6bb 100644 --- a/src/ARMJIT_Internal.h +++ b/src/ARMJIT_Internal.h @@ -24,8 +24,12 @@ #include #include -#include "ARMJIT.h" -#include "ARMJIT_Memory.h" +#include "ARM_InstrInfo.h" +#include "JitBlock.h" +#include "TinyVector.h" + +class ARM; +class ARMv5; // here lands everything which doesn't fit into ARMJIT.h // where it would be included by pretty much everything @@ -69,139 +73,6 @@ struct FetchedInstr ARMInstrInfo::Info Info; }; -/* - TinyVector - - because reinventing the wheel is the best! - - - meant to be used very often, with not so many elements - max 1 << 16 elements - - doesn't allocate while no elements are inserted - - not stl confirmant of course - - probably only works with POD types - - remove operations don't preserve order, but O(1)! -*/ -template -struct __attribute__((packed)) TinyVector -{ - T* Data = NULL; - u16 Capacity = 0; - u16 Length = 0; - - ~TinyVector() - { - delete[] Data; - } - - void MakeCapacity(u32 capacity) - { - assert(capacity <= UINT16_MAX); - assert(capacity > Capacity); - T* newMem = new T[capacity]; - if (Data != NULL) - memcpy(newMem, Data, sizeof(T) * Length); - - T* oldData = Data; - Data = newMem; - if (oldData != NULL) - delete[] oldData; - - Capacity = capacity; - } - - void SetLength(u16 length) - { - if (Capacity < length) - MakeCapacity(length); - - Length = length; - } - - void Clear() - { - Length = 0; - } - - void Add(T element) - { - assert(Length + 1 <= UINT16_MAX); - if (Length + 1 > Capacity) - MakeCapacity(((Capacity + 4) * 3) / 2); - - Data[Length++] = element; - } - - void Remove(int index) - { - assert(Length > 0); - assert(index >= 0 && index < Length); - - Length--; - Data[index] = Data[Length]; - /*for (int i = index; i < Length; i++) - Data[i] = Data[i + 1];*/ - } - - int Find(T needle) - { - for (int i = 0; i < Length; i++) - { - if (Data[i] == needle) - return i; - } - return -1; - } - - bool RemoveByValue(T needle) - { - for (int i = 0; i < Length; i++) - { - if (Data[i] == needle) - { - Remove(i); - return true; - } - } - return false; - } - - T& operator[](int index) - { - assert(index >= 0 && index < Length); - return Data[index]; - } -}; - -class JitBlock -{ -public: - JitBlock(u32 num, u32 literalHash, u32 numAddresses, u32 numLiterals) - { - Num = num; - NumAddresses = numAddresses; - NumLiterals = numLiterals; - Data.SetLength(numAddresses * 2 + numLiterals); - } - - u32 StartAddr; - u32 StartAddrLocal; - u32 InstrHash, LiteralHash; - u8 Num; - u16 NumAddresses; - u16 NumLiterals; - - JitBlockEntry EntryPoint; - - u32* AddressRanges() - { return &Data[0]; } - u32* AddressMasks() - { return &Data[NumAddresses]; } - u32* Literals() - { return &Data[NumAddresses * 2]; } - -private: - TinyVector Data; -}; - // size should be 16 bytes because I'm to lazy to use mul and whatnot struct __attribute__((packed)) AddressRange { @@ -214,10 +85,6 @@ typedef void (*InterpreterFunc)(ARM* cpu); extern InterpreterFunc InterpretARM[]; extern InterpreterFunc InterpretTHUMB[]; -extern TinyVector InvalidLiterals; - -extern AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count]; - inline bool PageContainsCode(AddressRange* range) { for (int i = 0; i < 8; i++) @@ -228,11 +95,6 @@ inline bool PageContainsCode(AddressRange* range) return false; } -u32 LocaliseCodeAddress(u32 num, u32 addr); - -template -void LinkBlock(ARM* cpu, u32 codeOffset); - template T SlowRead9(u32 addr, ARMv5* cpu); template void SlowWrite9(u32 addr, ARMv5* cpu, u32 val); template T SlowRead7(u32 addr); diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 3591a25d..361a1ed3 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -34,6 +34,7 @@ #include #endif +#include "ARMJIT.h" #include "ARMJIT_Memory.h" #include "ARMJIT_Internal.h" @@ -72,17 +73,6 @@ using Platform::LogLevel; */ -namespace ARMJIT_Memory -{ -struct FaultDescription -{ - u32 EmulatedFaultAddr; - u8* FaultPC; -}; - -bool FaultHandler(FaultDescription& faultDesc); -} - // Yes I know this looks messy, but better here than somewhere else in the code #if defined(__x86_64__) #if defined(_WIN32) @@ -112,7 +102,6 @@ bool FaultHandler(FaultDescription& faultDesc); #if defined(__ANDROID__) #define ASHMEM_DEVICE "/dev/ashmem" -Platform::DynamicLibrary* Libandroid = nullptr; #endif #if defined(__SWITCH__) @@ -146,7 +135,7 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx) integerRegisters[31] = ctx->sp.x; integerRegisters[32] = ctx->pc.x; - if (ARMJIT_Memory::FaultHandler(desc)) + if (Melon::FaultHandler(desc)) { integerRegisters[32] = (u64)desc.FaultPC; @@ -160,19 +149,19 @@ void __libnx_exception_handler(ThreadExceptionDump* ctx) #elif defined(_WIN32) -static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) +LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) { if (exceptionInfo->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) { return EXCEPTION_CONTINUE_SEARCH; } - ARMJIT_Memory::FaultDescription desc; - u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start); + u8* curArea = (u8*)(NDS::CurCPU == 0 ? NDS::JIT->Memory.FastMem9Start : NDS::JIT->Memory.FastMem7Start); + FaultDescription desc {}; desc.EmulatedFaultAddr = (u8*)exceptionInfo->ExceptionRecord->ExceptionInformation[1] - curArea; desc.FaultPC = (u8*)exceptionInfo->ContextRecord->CONTEXT_PC; - if (ARMJIT_Memory::FaultHandler(desc)) + if (FaultHandler(desc, *NDS::JIT)) { exceptionInfo->ContextRecord->CONTEXT_PC = (u64)desc.FaultPC; return EXCEPTION_CONTINUE_EXECUTION; @@ -186,7 +175,7 @@ static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) static struct sigaction OldSaSegv; static struct sigaction OldSaBus; -static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext) +void ARMJIT_Memory::SigsegvHandler(int sig, siginfo_t* info, void* rawContext) { if (sig != SIGSEGV && sig != SIGBUS) { @@ -201,13 +190,13 @@ static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext) ucontext_t* context = (ucontext_t*)rawContext; - ARMJIT_Memory::FaultDescription desc; - u8* curArea = (u8*)(NDS::CurCPU == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start); + FaultDescription desc {}; + u8* curArea = (u8*)(NDS::CurCPU == 0 ? NDS::JIT->Memory.FastMem9Start : NDS::JIT->Memory.FastMem7Start); desc.EmulatedFaultAddr = (u8*)info->si_addr - curArea; desc.FaultPC = (u8*)context->CONTEXT_PC; - if (ARMJIT_Memory::FaultHandler(desc)) + if (FaultHandler(desc, *NDS::JIT)) { context->CONTEXT_PC = (u64)desc.FaultPC; return; @@ -239,33 +228,7 @@ static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext) #endif -namespace ARMJIT_Memory -{ - -void* FastMem9Start, *FastMem7Start; - -#ifdef _WIN32 -inline u32 RoundUp(u32 size) -{ - return (size + 0xFFFF) & ~0xFFFF; -} -#else -inline u32 RoundUp(u32 size) -{ - return size; -} -#endif - -const u32 MemBlockMainRAMOffset = 0; -const u32 MemBlockSWRAMOffset = RoundUp(NDS::MainRAMMaxSize); -const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(NDS::SharedWRAMSize); -const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(NDS::ARM7WRAMSize); -const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize); -const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(DSi::NWRAMSize); -const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(DSi::NWRAMSize); -const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(DSi::NWRAMSize); - -const u32 OffsetsPerRegion[memregions_Count] = +const u32 OffsetsPerRegion[ARMJIT_Memory::memregions_Count] = { UINT32_MAX, UINT32_MAX, @@ -295,23 +258,9 @@ enum memstate_MappedProtected, }; -u8 MappingStatus9[1 << (32-12)]; -u8 MappingStatus7[1 << (32-12)]; -#if defined(__SWITCH__) -VirtmemReservation* FastMem9Reservation, *FastMem7Reservation; -u8* MemoryBase; -u8* MemoryBaseCodeMem; -#elif defined(_WIN32) -u8* MemoryBase; -HANDLE MemoryFile; -LPVOID ExceptionHandlerHandle; -#else -u8* MemoryBase; -int MemoryFile = -1; -#endif -bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) +bool ARMJIT_Memory::MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept { u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; #ifdef __SWITCH__ @@ -326,7 +275,7 @@ bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) #endif } -bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) +bool ARMJIT_Memory::UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept { u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; #ifdef __SWITCH__ @@ -341,7 +290,7 @@ bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) } #ifndef __SWITCH__ -void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) +void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept { u8* dst = (u8*)(num == 0 ? FastMem9Start : FastMem7Start) + addr; #if defined(_WIN32) @@ -367,82 +316,74 @@ void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) } #endif -struct Mapping +void ARMJIT_Memory::Mapping::Unmap(int region, ARMJIT_Memory& memory) noexcept { - u32 Addr; - u32 Size, LocalOffset; - u32 Num; - - void Unmap(int region) + u32 dtcmStart = NDS::ARM9->DTCMBase; + u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1; + bool skipDTCM = Num == 0 && region != memregion_DTCM; + u8* statuses = Num == 0 ? memory.MappingStatus9 : memory.MappingStatus7; + u32 offset = 0; + while (offset < Size) { - u32 dtcmStart = NDS::ARM9->DTCMBase; - u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1; - bool skipDTCM = Num == 0 && region != memregion_DTCM; - u8* statuses = Num == 0 ? MappingStatus9 : MappingStatus7; - u32 offset = 0; - while (offset < Size) + if (skipDTCM && Addr + offset == dtcmStart) { - if (skipDTCM && Addr + offset == dtcmStart) + offset += dtcmSize; + } + else + { + u32 segmentOffset = offset; + u8 status = statuses[(Addr + offset) >> 12]; + while (statuses[(Addr + offset) >> 12] == status + && offset < Size + && (!skipDTCM || Addr + offset != dtcmStart)) { - offset += dtcmSize; + assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped); + statuses[(Addr + offset) >> 12] = memstate_Unmapped; + offset += 0x1000; } - else - { - u32 segmentOffset = offset; - u8 status = statuses[(Addr + offset) >> 12]; - while (statuses[(Addr + offset) >> 12] == status - && offset < Size - && (!skipDTCM || Addr + offset != dtcmStart)) - { - assert(statuses[(Addr + offset) >> 12] != memstate_Unmapped); - statuses[(Addr + offset) >> 12] = memstate_Unmapped; - offset += 0x1000; - } #ifdef __SWITCH__ - if (status == memstate_MappedRW) - { - u32 segmentSize = offset - segmentOffset; - Log(LogLevel::Debug, "unmapping %x %x %x %x\n", Addr + segmentOffset, Num, segmentOffset + LocalOffset + OffsetsPerRegion[region], segmentSize); - bool success = UnmapFromRange(Addr + segmentOffset, Num, segmentOffset + LocalOffset + OffsetsPerRegion[region], segmentSize); - assert(success); - } -#endif + if (status == memstate_MappedRW) + { + u32 segmentSize = offset - segmentOffset; + Log(LogLevel::Debug, "unmapping %x %x %x %x\n", Addr + segmentOffset, Num, segmentOffset + LocalOffset + OffsetsPerRegion[region], segmentSize); + bool success = memory.UnmapFromRange(Addr + segmentOffset, Num, segmentOffset + LocalOffset + OffsetsPerRegion[region], segmentSize); + assert(success); } +#endif } + } #ifndef __SWITCH__ #ifndef _WIN32 - u32 dtcmEnd = dtcmStart + dtcmSize; - if (Num == 0 - && dtcmEnd >= Addr - && dtcmStart < Addr + Size) + u32 dtcmEnd = dtcmStart + dtcmSize; + if (Num == 0 + && dtcmEnd >= Addr + && dtcmStart < Addr + Size) + { + bool success; + if (dtcmStart > Addr) { - bool success; - if (dtcmStart > Addr) - { - success = UnmapFromRange(Addr, 0, OffsetsPerRegion[region] + LocalOffset, dtcmStart - Addr); - assert(success); - } - if (dtcmEnd < Addr + Size) - { - u32 offset = dtcmStart - Addr + dtcmSize; - success = UnmapFromRange(dtcmEnd, 0, OffsetsPerRegion[region] + LocalOffset + offset, Size - offset); - assert(success); - } + success = memory.UnmapFromRange(Addr, 0, OffsetsPerRegion[region] + LocalOffset, dtcmStart - Addr); + assert(success); } - else -#endif + if (dtcmEnd < Addr + Size) { - bool succeded = UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size); - assert(succeded); + u32 offset = dtcmStart - Addr + dtcmSize; + success = memory.UnmapFromRange(dtcmEnd, 0, OffsetsPerRegion[region] + LocalOffset + offset, Size - offset); + assert(success); } -#endif } -}; -ARMJIT::TinyVector Mappings[memregions_Count]; + else +#endif + { + bool succeded = memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size); + assert(succeded); + } +#endif +} -void SetCodeProtection(int region, u32 offset, bool protect) +void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noexcept { offset &= ~0xFFF; //printf("set code protection %d %x %d\n", region, offset, protect); @@ -479,7 +420,7 @@ void SetCodeProtection(int region, u32 offset, bool protect) } } -void RemapDTCM(u32 newBase, u32 newSize) +void ARMJIT_Memory::RemapDTCM(u32 newBase, u32 newSize) noexcept { // this first part could be made more efficient // by unmapping DTCM first and then map the holes @@ -510,7 +451,7 @@ void RemapDTCM(u32 newBase, u32 newSize) if (mapping.Num == 0 && overlap) { - mapping.Unmap(region); + mapping.Unmap(region, *this); Mappings[region].Remove(i); } else @@ -522,12 +463,12 @@ void RemapDTCM(u32 newBase, u32 newSize) for (int i = 0; i < Mappings[memregion_DTCM].Length; i++) { - Mappings[memregion_DTCM][i].Unmap(memregion_DTCM); + Mappings[memregion_DTCM][i].Unmap(memregion_DTCM, *this); } Mappings[memregion_DTCM].Clear(); } -void RemapNWRAM(int num) +void ARMJIT_Memory::RemapNWRAM(int num) noexcept { for (int i = 0; i < Mappings[memregion_SharedWRAM].Length;) { @@ -535,7 +476,7 @@ void RemapNWRAM(int num) if (DSi::NWRAMStart[mapping.Num][num] < mapping.Addr + mapping.Size && DSi::NWRAMEnd[mapping.Num][num] > mapping.Addr) { - mapping.Unmap(memregion_SharedWRAM); + mapping.Unmap(memregion_SharedWRAM, *this); Mappings[memregion_SharedWRAM].Remove(i); } else @@ -545,12 +486,12 @@ void RemapNWRAM(int num) } for (int i = 0; i < Mappings[memregion_NewSharedWRAM_A + num].Length; i++) { - Mappings[memregion_NewSharedWRAM_A + num][i].Unmap(memregion_NewSharedWRAM_A + num); + Mappings[memregion_NewSharedWRAM_A + num][i].Unmap(memregion_NewSharedWRAM_A + num, *this); } Mappings[memregion_NewSharedWRAM_A + num].Clear(); } -void RemapSWRAM() +void ARMJIT_Memory::RemapSWRAM() noexcept { Log(LogLevel::Debug, "remapping SWRAM\n"); for (int i = 0; i < Mappings[memregion_WRAM7].Length;) @@ -558,7 +499,7 @@ void RemapSWRAM() Mapping& mapping = Mappings[memregion_WRAM7][i]; if (mapping.Addr + mapping.Size <= 0x03800000) { - mapping.Unmap(memregion_WRAM7); + mapping.Unmap(memregion_WRAM7, *this); Mappings[memregion_WRAM7].Remove(i); } else @@ -566,12 +507,12 @@ void RemapSWRAM() } for (int i = 0; i < Mappings[memregion_SharedWRAM].Length; i++) { - Mappings[memregion_SharedWRAM][i].Unmap(memregion_SharedWRAM); + Mappings[memregion_SharedWRAM][i].Unmap(memregion_SharedWRAM, *this); } Mappings[memregion_SharedWRAM].Clear(); } -bool MapAtAddress(u32 addr) +bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept { u32 num = NDS::CurCPU; @@ -589,7 +530,7 @@ bool MapAtAddress(u32 addr) u8* states = num == 0 ? MappingStatus9 : MappingStatus7; //printf("mapping mirror %x, %x %x %d %d\n", mirrorStart, mirrorSize, memoryOffset, region, num); - bool isExecutable = ARMJIT::CodeMemRegions[region]; + bool isExecutable = JIT.CodeMemRegions[region]; u32 dtcmStart = NDS::ARM9->DTCMBase; u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1; @@ -621,7 +562,7 @@ bool MapAtAddress(u32 addr) } #endif - ARMJIT::AddressRange* range = ARMJIT::CodeMemRegions[region] + memoryOffset / 512; + ARMJIT::AddressRange* range = JIT.CodeMemRegions[region] + memoryOffset / 512; // this overcomplicated piece of code basically just finds whole pieces of code memory // which can be mapped/protected @@ -676,19 +617,19 @@ bool MapAtAddress(u32 addr) return true; } -bool FaultHandler(FaultDescription& faultDesc) +bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, ARMJIT::ARMJIT& jit) { - if (ARMJIT::JITCompiler->IsJITFault(faultDesc.FaultPC)) + if (jit.JITCompiler.IsJITFault(faultDesc.FaultPC)) { bool rewriteToSlowPath = true; - u8* memStatus = NDS::CurCPU == 0 ? MappingStatus9 : MappingStatus7; + u8* memStatus = NDS::CurCPU == 0 ? jit.Memory.MappingStatus9 : jit.Memory.MappingStatus7; if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped) - rewriteToSlowPath = !MapAtAddress(faultDesc.EmulatedFaultAddr); + rewriteToSlowPath = !jit.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr); if (rewriteToSlowPath) - faultDesc.FaultPC = ARMJIT::JITCompiler->RewriteMemAccess(faultDesc.FaultPC); + faultDesc.FaultPC = jit.JITCompiler.RewriteMemAccess(faultDesc.FaultPC); return true; } @@ -697,7 +638,7 @@ bool FaultHandler(FaultDescription& faultDesc) const u64 AddrSpaceSize = 0x100000000; -void Init() +ARMJIT_Memory::ARMJIT_Memory(ARMJIT::ARMJIT& jit) noexcept : JIT(jit) { #if defined(__SWITCH__) MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize); @@ -740,8 +681,6 @@ void Init() MemoryBase = MemoryBase + AddrSpaceSize*3; MapViewOfFileEx(MemoryFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, MemoryTotalSize, MemoryBase); - - u8* basePtr = MemoryBase; #else // this used to be allocated with three different mmaps // The idea was to give the OS more freedom where to position the buffers, @@ -798,16 +737,9 @@ void Init() u8* basePtr = MemoryBase; #endif - NDS::MainRAM = basePtr + MemBlockMainRAMOffset; - NDS::SharedWRAM = basePtr + MemBlockSWRAMOffset; - NDS::ARM7WRAM = basePtr + MemBlockARM7WRAMOffset; - NDS::ARM9->DTCM = basePtr + MemBlockDTCMOffset; - DSi::NWRAM_A = basePtr + MemBlockNWRAM_AOffset; - DSi::NWRAM_B = basePtr + MemBlockNWRAM_BOffset; - DSi::NWRAM_C = basePtr + MemBlockNWRAM_COffset; } -void DeInit() +ARMJIT_Memory::~ARMJIT_Memory() noexcept { #if defined(__SWITCH__) virtmemLock(); @@ -875,12 +807,12 @@ void DeInit() #endif } -void Reset() +void ARMJIT_Memory::Reset() noexcept { for (int region = 0; region < memregions_Count; region++) { for (int i = 0; i < Mappings[region].Length; i++) - Mappings[region][i].Unmap(region); + Mappings[region][i].Unmap(region, *this); Mappings[region].Clear(); } @@ -893,7 +825,7 @@ void Reset() Log(LogLevel::Debug, "done resetting jit mem\n"); } -bool IsFastmemCompatible(int region) +bool ARMJIT_Memory::IsFastmemCompatible(int region) const noexcept { #ifdef _WIN32 /* @@ -909,7 +841,7 @@ bool IsFastmemCompatible(int region) return OffsetsPerRegion[region] != UINT32_MAX; } -bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) +bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) const noexcept { memoryOffset = 0; switch (region) @@ -955,14 +887,14 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi { mirrorStart = addr & ~NDS::SWRAM_ARM9.Mask; mirrorSize = NDS::SWRAM_ARM9.Mask + 1; - memoryOffset = NDS::SWRAM_ARM9.Mem - NDS::SharedWRAM; + memoryOffset = NDS::SWRAM_ARM9.Mem - GetSharedWRAM(); return true; } else if (num == 1 && NDS::SWRAM_ARM7.Mem) { mirrorStart = addr & ~NDS::SWRAM_ARM7.Mask; mirrorSize = NDS::SWRAM_ARM7.Mask + 1; - memoryOffset = NDS::SWRAM_ARM7.Mem - NDS::SharedWRAM; + memoryOffset = NDS::SWRAM_ARM7.Mem - GetSharedWRAM(); return true; } return false; @@ -995,7 +927,7 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi u8* ptr = DSi::NWRAMMap_A[num][(addr >> 16) & DSi::NWRAMMask[num][0]]; if (ptr) { - memoryOffset = ptr - DSi::NWRAM_A; + memoryOffset = ptr - GetNWRAM_A(); mirrorStart = addr & ~0xFFFF; mirrorSize = 0x10000; return true; @@ -1007,7 +939,7 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi u8* ptr = DSi::NWRAMMap_B[num][(addr >> 15) & DSi::NWRAMMask[num][1]]; if (ptr) { - memoryOffset = ptr - DSi::NWRAM_B; + memoryOffset = ptr - GetNWRAM_B(); mirrorStart = addr & ~0x7FFF; mirrorSize = 0x8000; return true; @@ -1019,7 +951,7 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi u8* ptr = DSi::NWRAMMap_C[num][(addr >> 15) & DSi::NWRAMMask[num][2]]; if (ptr) { - memoryOffset = ptr - DSi::NWRAM_C; + memoryOffset = ptr - GetNWRAM_C(); mirrorStart = addr & ~0x7FFF; mirrorSize = 0x8000; return true; @@ -1048,7 +980,7 @@ bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mi } } -u32 LocaliseAddress(int region, u32 num, u32 addr) +u32 ARMJIT_Memory::LocaliseAddress(int region, u32 num, u32 addr) const noexcept { switch (region) { @@ -1062,9 +994,9 @@ u32 LocaliseAddress(int region, u32 num, u32 addr) return (addr & 0x3FFF) | (memregion_BIOS7 << 27); case memregion_SharedWRAM: if (num == 0) - return ((addr & NDS::SWRAM_ARM9.Mask) + (NDS::SWRAM_ARM9.Mem - NDS::SharedWRAM)) | (memregion_SharedWRAM << 27); + return ((addr & NDS::SWRAM_ARM9.Mask) + (NDS::SWRAM_ARM9.Mem - GetSharedWRAM())) | (memregion_SharedWRAM << 27); else - return ((addr & NDS::SWRAM_ARM7.Mask) + (NDS::SWRAM_ARM7.Mem - NDS::SharedWRAM)) | (memregion_SharedWRAM << 27); + return ((addr & NDS::SWRAM_ARM7.Mask) + (NDS::SWRAM_ARM7.Mem - GetSharedWRAM())) | (memregion_SharedWRAM << 27); case memregion_WRAM7: return (addr & (NDS::ARM7WRAMSize - 1)) | (memregion_WRAM7 << 27); case memregion_VRAM: @@ -1077,7 +1009,7 @@ u32 LocaliseAddress(int region, u32 num, u32 addr) { u8* ptr = DSi::NWRAMMap_A[num][(addr >> 16) & DSi::NWRAMMask[num][0]]; if (ptr) - return (ptr - DSi::NWRAM_A + (addr & 0xFFFF)) | (memregion_NewSharedWRAM_A << 27); + return (ptr - GetNWRAM_A() + (addr & 0xFFFF)) | (memregion_NewSharedWRAM_A << 27); else return memregion_Other << 27; // zero filled memory } @@ -1085,7 +1017,7 @@ u32 LocaliseAddress(int region, u32 num, u32 addr) { u8* ptr = DSi::NWRAMMap_B[num][(addr >> 15) & DSi::NWRAMMask[num][1]]; if (ptr) - return (ptr - DSi::NWRAM_B + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_B << 27); + return (ptr - GetNWRAM_B() + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_B << 27); else return memregion_Other << 27; } @@ -1093,7 +1025,7 @@ u32 LocaliseAddress(int region, u32 num, u32 addr) { u8* ptr = DSi::NWRAMMap_C[num][(addr >> 15) & DSi::NWRAMMask[num][2]]; if (ptr) - return (ptr - DSi::NWRAM_C + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_C << 27); + return (ptr - GetNWRAM_C() + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_C << 27); else return memregion_Other << 27; } @@ -1106,7 +1038,7 @@ u32 LocaliseAddress(int region, u32 num, u32 addr) } } -int ClassifyAddress9(u32 addr) +int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept { if (addr < NDS::ARM9->ITCMSize) { @@ -1160,7 +1092,7 @@ int ClassifyAddress9(u32 addr) } } -int ClassifyAddress7(u32 addr) +int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept { if (NDS::ConsoleType == 1 && addr < 0x00010000 && !(DSi::SCFG_BIOS & (1<<9))) { @@ -1295,7 +1227,7 @@ u32 NDSCartSlot_ReadROMData() return NDS::NDSCartSlot->ReadROMData(); } -void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) +void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept { if (cpu->Num == 0) { @@ -1433,5 +1365,3 @@ void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) } return NULL; } - -} diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index 6a7e13d4..1cf4f811 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -20,62 +20,209 @@ #define ARMJIT_MEMORY #include "types.h" +#include "TinyVector.h" #include "ARM.h" +#include "DSi.h" -namespace ARMJIT_Memory +#if defined(__SWITCH__) +#include +#elif defined(_WIN32) +#include +#else +#include +#include +#include +#include +#include +#endif + +#ifndef JIT_ENABLED +#include +#include "NDS.h" +#endif + +namespace ARMJIT { - -extern void* FastMem9Start; -extern void* FastMem7Start; - -void Init(); -void DeInit(); - -void Reset(); - -enum -{ - memregion_Other = 0, - memregion_ITCM, - memregion_DTCM, - memregion_BIOS9, - memregion_MainRAM, - memregion_SharedWRAM, - memregion_IO9, - memregion_VRAM, - memregion_BIOS7, - memregion_WRAM7, - memregion_IO7, - memregion_Wifi, - memregion_VWRAM, - - // DSi - memregion_BIOS9DSi, - memregion_BIOS7DSi, - memregion_NewSharedWRAM_A, - memregion_NewSharedWRAM_B, - memregion_NewSharedWRAM_C, - - memregions_Count -}; - -int ClassifyAddress9(u32 addr); -int ClassifyAddress7(u32 addr); - -bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize); -u32 LocaliseAddress(int region, u32 num, u32 addr); - -bool IsFastmemCompatible(int region); - -void RemapDTCM(u32 newBase, u32 newSize); -void RemapSWRAM(); -void RemapNWRAM(int num); - -void SetCodeProtection(int region, u32 offset, bool protect); - -void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size); - +class Compiler; +class ARMJIT; } +constexpr u32 RoundUp(u32 size) noexcept +{ +#ifdef _WIN32 + return (size + 0xFFFF) & ~0xFFFF; +#else + return size; +#endif +} + +const u32 MemBlockMainRAMOffset = 0; +const u32 MemBlockSWRAMOffset = RoundUp(NDS::MainRAMMaxSize); +const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(NDS::SharedWRAMSize); +const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(NDS::ARM7WRAMSize); +const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize); +const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(DSi::NWRAMSize); +const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(DSi::NWRAMSize); +const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(DSi::NWRAMSize); + +class ARMJIT_Memory +{ +public: + enum + { + memregion_Other = 0, + memregion_ITCM, + memregion_DTCM, + memregion_BIOS9, + memregion_MainRAM, + memregion_SharedWRAM, + memregion_IO9, + memregion_VRAM, + memregion_BIOS7, + memregion_WRAM7, + memregion_IO7, + memregion_Wifi, + memregion_VWRAM, + + // DSi + memregion_BIOS9DSi, + memregion_BIOS7DSi, + memregion_NewSharedWRAM_A, + memregion_NewSharedWRAM_B, + memregion_NewSharedWRAM_C, + + memregions_Count + }; + +#ifdef JIT_ENABLED +public: + explicit ARMJIT_Memory(ARMJIT::ARMJIT& jit) noexcept; + ~ARMJIT_Memory() noexcept; + ARMJIT_Memory(const ARMJIT_Memory&) = delete; + ARMJIT_Memory(ARMJIT_Memory&&) = delete; + ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete; + ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete; + void Reset() noexcept; + void RemapDTCM(u32 newBase, u32 newSize) noexcept; + void RemapSWRAM() noexcept; + void RemapNWRAM(int num) noexcept; + void SetCodeProtection(int region, u32 offset, bool protect) noexcept; + + [[nodiscard]] u8* GetMainRAM() noexcept { return MemoryBase + MemBlockMainRAMOffset; } + [[nodiscard]] const u8* GetMainRAM() const noexcept { return MemoryBase + MemBlockMainRAMOffset; } + + [[nodiscard]] u8* GetSharedWRAM() noexcept { return MemoryBase + MemBlockSWRAMOffset; } + [[nodiscard]] const u8* GetSharedWRAM() const noexcept { return MemoryBase + MemBlockSWRAMOffset; } + + [[nodiscard]] u8* GetARM7WRAM() noexcept { return MemoryBase + MemBlockARM7WRAMOffset; } + [[nodiscard]] const u8* GetARM7WRAM() const noexcept { return MemoryBase + MemBlockARM7WRAMOffset; } + + [[nodiscard]] u8* GetARM9DTCM() noexcept { return MemoryBase + MemBlockDTCMOffset; } + [[nodiscard]] const u8* GetARM9DTCM() const noexcept { return MemoryBase + MemBlockDTCMOffset; } + + [[nodiscard]] u8* GetNWRAM_A() noexcept { return MemoryBase + MemBlockNWRAM_AOffset; } + [[nodiscard]] const u8* GetNWRAM_A() const noexcept { return MemoryBase + MemBlockNWRAM_AOffset; } + + [[nodiscard]] u8* GetNWRAM_B() noexcept { return MemoryBase + MemBlockNWRAM_BOffset; } + [[nodiscard]] const u8* GetNWRAM_B() const noexcept { return MemoryBase + MemBlockNWRAM_BOffset; } + + [[nodiscard]] u8* GetNWRAM_C() noexcept { return MemoryBase + MemBlockNWRAM_COffset; } + [[nodiscard]] const u8* GetNWRAM_C() const noexcept { return MemoryBase + MemBlockNWRAM_COffset; } + + int ClassifyAddress9(u32 addr) const noexcept; + int ClassifyAddress7(u32 addr) const noexcept; + bool GetMirrorLocation(int region, u32 num, u32 addr, u32& memoryOffset, u32& mirrorStart, u32& mirrorSize) const noexcept; + u32 LocaliseAddress(int region, u32 num, u32 addr) const noexcept; + bool IsFastmemCompatible(int region) const noexcept; + void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept; + bool MapAtAddress(u32 addr) noexcept; +private: + friend class ARMJIT::Compiler; + struct Mapping + { + u32 Addr; + u32 Size, LocalOffset; + u32 Num; + + void Unmap(int region, ARMJIT_Memory& memory) noexcept; + }; + + struct FaultDescription + { + u32 EmulatedFaultAddr; + u8* FaultPC; + }; + static bool FaultHandler(FaultDescription& faultDesc, ARMJIT::ARMJIT& jit); + bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept; + bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept; + void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept; + + ARMJIT::ARMJIT& JIT; + void* FastMem9Start; + void* FastMem7Start; + u8* MemoryBase = nullptr; +#if defined(__SWITCH__) + VirtmemReservation* FastMem9Reservation, *FastMem7Reservation; + u8* MemoryBaseCodeMem; +#elif defined(_WIN32) + static LONG ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo); + HANDLE MemoryFile = INVALID_HANDLE_VALUE; + LPVOID ExceptionHandlerHandle = nullptr; +#else + static void SigsegvHandler(int sig, siginfo_t* info, void* rawContext); + int MemoryFile = -1; +#endif +#ifdef ANDROID + Platform::DynamicLibrary* Libandroid = nullptr; +#endif + u8 MappingStatus9[1 << (32-12)] {}; + u8 MappingStatus7[1 << (32-12)] {}; + ARMJIT::TinyVector Mappings[memregions_Count] {}; +#else +public: + explicit ARMJIT_Memory(ARMJIT::ARMJIT&) {}; + ~ARMJIT_Memory() = default; + ARMJIT_Memory(const ARMJIT_Memory&) = delete; + ARMJIT_Memory(ARMJIT_Memory&&) = delete; + ARMJIT_Memory& operator=(const ARMJIT_Memory&) = delete; + ARMJIT_Memory& operator=(ARMJIT_Memory&&) = delete; + + void Reset() noexcept {} + void RemapDTCM(u32 newBase, u32 newSize) noexcept {} + void RemapSWRAM() noexcept {} + void RemapNWRAM(int num) noexcept {} + void SetCodeProtection(int region, u32 offset, bool protect) noexcept {} + + [[nodiscard]] u8* GetMainRAM() noexcept { return MainRAM.data(); } + [[nodiscard]] const u8* GetMainRAM() const noexcept { return MainRAM.data(); } + + [[nodiscard]] u8* GetSharedWRAM() noexcept { return SharedWRAM.data(); } + [[nodiscard]] const u8* GetSharedWRAM() const noexcept { return SharedWRAM.data(); } + + [[nodiscard]] u8* GetARM7WRAM() noexcept { return ARM7WRAM.data(); } + [[nodiscard]] const u8* GetARM7WRAM() const noexcept { return ARM7WRAM.data(); } + + [[nodiscard]] u8* GetARM9DTCM() noexcept { return DTCM.data(); } + [[nodiscard]] const u8* GetARM9DTCM() const noexcept { return DTCM.data(); } + + [[nodiscard]] u8* GetNWRAM_A() noexcept { return NWRAM_A.data(); } + [[nodiscard]] const u8* GetNWRAM_A() const noexcept { return NWRAM_A.data(); } + + [[nodiscard]] u8* GetNWRAM_B() noexcept { return NWRAM_B.data(); } + [[nodiscard]] const u8* GetNWRAM_B() const noexcept { return NWRAM_B.data(); } + + [[nodiscard]] u8* GetNWRAM_C() noexcept { return NWRAM_C.data(); } + [[nodiscard]] const u8* GetNWRAM_C() const noexcept { return NWRAM_C.data(); } +private: + std::array MainRAM {}; + std::array ARM7WRAM {}; + std::array SharedWRAM {}; + std::array DTCM {}; + std::array NWRAM_A {}; + std::array NWRAM_B {}; + std::array NWRAM_C {}; +#endif +}; + #endif diff --git a/src/ARMJIT_RegisterCache.h b/src/ARMJIT_RegisterCache.h index 7ea44ed3..16105300 100644 --- a/src/ARMJIT_RegisterCache.h +++ b/src/ARMJIT_RegisterCache.h @@ -19,7 +19,6 @@ #ifndef ARMJIT_REGCACHE_H #define ARMJIT_REGCACHE_H -#include "ARMJIT.h" #include "ARMJIT_Internal.h" #include "Platform.h" diff --git a/src/ARMJIT_x64/ARMJIT_ALU.cpp b/src/ARMJIT_x64/ARMJIT_ALU.cpp index 069dd536..bdc17e8b 100644 --- a/src/ARMJIT_x64/ARMJIT_ALU.cpp +++ b/src/ARMJIT_x64/ARMJIT_ALU.cpp @@ -17,6 +17,7 @@ */ #include "ARMJIT_Compiler.h" +#include "../ARM.h" using namespace Gen; diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp index b36f5b72..ae7d1aef 100644 --- a/src/ARMJIT_x64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp @@ -17,6 +17,7 @@ */ #include "ARMJIT_Compiler.h" +#include "../ARM.h" using namespace Gen; diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp index 45a27518..5506db7e 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp @@ -18,6 +18,7 @@ #include "ARMJIT_Compiler.h" +#include "../ARMJIT.h" #include "../ARMInterpreter.h" #include @@ -232,7 +233,7 @@ void Compiler::A_Comp_MSR() */ u8 CodeMemory[1024 * 1024 * 32]; -Compiler::Compiler() +Compiler::Compiler(ARMJIT& jit) : XEmitter(), JIT(jit) { { #ifdef _WIN32 @@ -712,12 +713,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess... { Log(LogLevel::Debug, "near reset\n"); - ResetBlockCache(); + JIT.ResetBlockCache(); } if (FarSize - (FarCode - FarStart) < 1024 * 32) // guess... { Log(LogLevel::Debug, "far reset\n"); - ResetBlockCache(); + JIT.ResetBlockCache(); } ConstantCycles = 0; diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index 680146f0..84efb353 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -21,7 +21,6 @@ #include "../dolphin/x64Emitter.h" -#include "../ARMJIT.h" #include "../ARMJIT_Internal.h" #include "../ARMJIT_RegisterCache.h" @@ -31,9 +30,11 @@ #include +class ARMJIT_Memory; + namespace ARMJIT { - +class ARMJIT; const Gen::X64Reg RCPU = Gen::RBP; const Gen::X64Reg RCPSR = Gen::R15; @@ -79,7 +80,11 @@ struct Op2 class Compiler : public Gen::XEmitter { public: - Compiler(); +#ifdef JIT_ENABLED + explicit Compiler(ARMJIT& jit); +#else + explicit Compiler(ARMJIT& jit) : XEmitter(), JIT(jit) {} +#endif void Reset(); @@ -238,42 +243,43 @@ public: void CreateMethod(const char* namefmt, void* start, ...); #endif - u8* FarCode; - u8* NearCode; - u32 FarSize; - u32 NearSize; + ARMJIT& JIT; + u8* FarCode {}; + u8* NearCode {}; + u32 FarSize {}; + u32 NearSize {}; - u8* NearStart; - u8* FarStart; + u8* NearStart {}; + u8* FarStart {}; - void* PatchedStoreFuncs[2][2][3][16]; - void* PatchedLoadFuncs[2][2][3][2][16]; + void* PatchedStoreFuncs[2][2][3][16] {}; + void* PatchedLoadFuncs[2][2][3][2][16] {}; - std::unordered_map LoadStorePatches; + std::unordered_map LoadStorePatches {}; - u8* ResetStart; - u32 CodeMemSize; + u8* ResetStart {}; + u32 CodeMemSize {}; - bool Exit; - bool IrregularCycles; + bool Exit {}; + bool IrregularCycles {}; - void* ReadBanked; - void* WriteBanked; + void* ReadBanked {}; + void* WriteBanked {}; bool CPSRDirty = false; - FetchedInstr CurInstr; + FetchedInstr CurInstr {}; - RegisterCache RegCache; + RegisterCache RegCache {}; - bool Thumb; - u32 Num; - u32 R15; - u32 CodeRegion; + bool Thumb {}; + u32 Num {}; + u32 R15 {}; + u32 CodeRegion {}; - u32 ConstantCycles; + u32 ConstantCycles {}; - ARM* CurCPU; + ARM* CurCPU {}; }; } diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp index 718f1bc7..14334293 100644 --- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp @@ -17,6 +17,7 @@ */ #include "ARMJIT_Compiler.h" +#include "../ARMJIT.h" using namespace Gen; @@ -67,9 +68,9 @@ u8* Compiler::RewriteMemAccess(u8* pc) bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) { - u32 localAddr = LocaliseCodeAddress(Num, addr); + u32 localAddr = JIT.LocaliseCodeAddress(Num, addr); - int invalidLiteralIdx = InvalidLiterals.Find(localAddr); + int invalidLiteralIdx = JIT.InvalidLiterals.Find(localAddr); if (invalidLiteralIdx != -1) { return false; @@ -117,7 +118,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag if (size == 16) addressMask = ~1; - if (LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) + if (JIT.LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) { u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1); @@ -134,7 +135,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag Comp_AddCycles_CDI(); } - bool addrIsStatic = LiteralOptimizations + bool addrIsStatic = JIT.LiteralOptimizations && RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post)); u32 staticAddress; if (addrIsStatic) @@ -195,10 +196,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag MOV(32, rnMapped, R(finalAddr)); u32 expectedTarget = Num == 0 - ? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion) - : ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion); + ? JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); - if (ARMJIT::FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || ARMJIT_Memory::IsFastmemCompatible(expectedTarget))) + if (JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || JIT.Memory.IsFastmemCompatible(expectedTarget))) { if (rdMapped.IsImm()) { @@ -216,7 +217,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag assert(patch.PatchFunc != NULL); - MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start)); + MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? JIT.Memory.FastMem9Start : JIT.Memory.FastMem7Start)); X64Reg maskedAddr = RSCRATCH3; if (size > 8) @@ -267,7 +268,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag void* func = NULL; if (addrIsStatic) - func = ARMJIT_Memory::GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); + func = JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); if (func) { @@ -421,16 +422,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc s32 offset = (regsCount * 4) * (decrement ? -1 : 1); int expectedTarget = Num == 0 - ? ARMJIT_Memory::ClassifyAddress9(CurInstr.DataRegion) - : ARMJIT_Memory::ClassifyAddress7(CurInstr.DataRegion); + ? JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); if (!store) Comp_AddCycles_CDI(); else Comp_AddCycles_CD(); - bool compileFastPath = FastMemory - && !usermode && (CurInstr.Cond() < 0xE || ARMJIT_Memory::IsFastmemCompatible(expectedTarget)); + bool compileFastPath = JIT.FastMemory + && !usermode && (CurInstr.Cond() < 0xE || JIT.Memory.IsFastmemCompatible(expectedTarget)); // we need to make sure that the stack stays aligned to 16 bytes #ifdef _WIN32 @@ -453,7 +454,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc u8* fastPathStart = GetWritableCodePtr(); u8* loadStoreAddr[16]; - MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? ARMJIT_Memory::FastMem9Start : ARMJIT_Memory::FastMem7Start)); + MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? JIT.Memory.FastMem9Start : JIT.Memory.FastMem7Start)); ADD(64, R(RSCRATCH2), R(RSCRATCH4)); u32 offset = 0; @@ -807,7 +808,7 @@ void Compiler::T_Comp_LoadPCRel() { u32 offset = (CurInstr.Instr & 0xFF) << 2; u32 addr = (R15 & ~0x2) + offset; - if (!LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) + if (!JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0); } diff --git a/src/ARM_InstrInfo.cpp b/src/ARM_InstrInfo.cpp index a5466782..ea9f6810 100644 --- a/src/ARM_InstrInfo.cpp +++ b/src/ARM_InstrInfo.cpp @@ -315,7 +315,7 @@ const u32 T_SVC = T_BranchAlways | T_WriteR14 | tk(tk_SVC); #include "ARM_InstrTable.h" #undef INSTRFUNC_PROTO -Info Decode(bool thumb, u32 num, u32 instr) +Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations) { const u8 FlagsReadPerCond[7] = { flag_Z, @@ -386,7 +386,7 @@ Info Decode(bool thumb, u32 num, u32 instr) { if (res.Kind == tk_LDR_PCREL) { - if (!ARMJIT::LiteralOptimizations) + if (!literaloptimizations) res.SrcRegs |= 1 << 15; res.SpecialKind = special_LoadLiteral; } diff --git a/src/ARM_InstrInfo.h b/src/ARM_InstrInfo.h index 56f6e623..3442c9af 100644 --- a/src/ARM_InstrInfo.h +++ b/src/ARM_InstrInfo.h @@ -274,7 +274,7 @@ struct Info } }; -Info Decode(bool thumb, u32 num, u32 instr); +Info Decode(bool thumb, u32 num, u32 instr, bool literaloptimizations); } diff --git a/src/CP15.cpp b/src/CP15.cpp index b8a77e24..e8d8c1af 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -22,11 +22,8 @@ #include "DSi.h" #include "ARM.h" #include "Platform.h" - -#ifdef JIT_ENABLED -#include "ARMJIT.h" #include "ARMJIT_Memory.h" -#endif +#include "ARMJIT.h" using Platform::Log; using Platform::LogLevel; @@ -125,9 +122,7 @@ void ARMv5::UpdateDTCMSetting() if (newDTCMBase != DTCMBase || newDTCMMask != DTCMMask) { -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapDTCM(newDTCMBase, newDTCMSize); -#endif + JIT.Memory.RemapDTCM(newDTCMBase, newDTCMSize); DTCMBase = newDTCMBase; DTCMMask = newDTCMMask; } @@ -926,9 +921,7 @@ void ARMv5::DataWrite8(u32 addr, u8 val) { DataCycles = 1; *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -958,9 +951,7 @@ void ARMv5::DataWrite16(u32 addr, u16 val) { DataCycles = 1; *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -990,9 +981,7 @@ void ARMv5::DataWrite32(u32 addr, u32 val) { DataCycles = 1; *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); -#endif + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -1015,7 +1004,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val) DataCycles += 1; *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; #ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); #endif return; } diff --git a/src/DSi.cpp b/src/DSi.cpp index f2937b0d..5c4b5422 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -28,10 +28,8 @@ #include "DSi_SPI_TSC.h" #include "Platform.h" -#ifdef JIT_ENABLED #include "ARMJIT.h" #include "ARMJIT_Memory.h" -#endif #include "DSi_NDMA.h" #include "DSi_I2C.h" @@ -99,11 +97,10 @@ void Set_SCFG_MC(u32 val); bool Init() { -#ifndef JIT_ENABLED - NWRAM_A = new u8[NWRAMSize]; - NWRAM_B = new u8[NWRAMSize]; - NWRAM_C = new u8[NWRAMSize]; -#endif + // Memory is owned by ARMJIT_Memory, don't free it + NWRAM_A = NDS::JIT->Memory.GetNWRAM_A(); + NWRAM_B = NDS::JIT->Memory.GetNWRAM_B(); + NWRAM_C = NDS::JIT->Memory.GetNWRAM_C(); NDMAs[0] = new DSi_NDMA(0, 0, *NDS::GPU); NDMAs[1] = new DSi_NDMA(0, 1, *NDS::GPU); @@ -127,15 +124,10 @@ bool Init() void DeInit() { -#ifndef JIT_ENABLED - delete[] NWRAM_A; - delete[] NWRAM_B; - delete[] NWRAM_C; - + // Memory is owned externally NWRAM_A = nullptr; NWRAM_B = nullptr; NWRAM_C = nullptr; -#endif for (int i = 0; i < 8; i++) { @@ -684,10 +676,8 @@ void SoftReset() // also, BPTWL[0x70] could be abused to quickly boot specific titles -#ifdef JIT_ENABLED - ARMJIT_Memory::Reset(); - ARMJIT::CheckAndInvalidateITCM(); -#endif + NDS::JIT->Reset(); + NDS::JIT->CheckAndInvalidateITCM(); NDS::ARM9->Reset(); NDS::ARM7->Reset(); @@ -1043,9 +1033,7 @@ void MapNWRAM_A(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapNWRAM(0); -#endif + NDS::JIT->Memory.RemapNWRAM(0); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1090,9 +1078,7 @@ void MapNWRAM_B(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapNWRAM(1); -#endif + NDS::JIT->Memory.RemapNWRAM(1); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1139,9 +1125,7 @@ void MapNWRAM_C(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapNWRAM(2); -#endif + NDS::JIT->Memory.RemapNWRAM(2); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1190,9 +1174,7 @@ void MapNWRAMRange(u32 cpu, u32 num, u32 val) u32 oldval = MBK[cpu][5+num]; if (oldval == val) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapNWRAM(num); -#endif + NDS::JIT->Memory.RemapNWRAM(num); MBK[cpu][5+num] = val; @@ -1468,9 +1450,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u8*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1488,9 +1468,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1508,9 +1486,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1523,9 +1499,7 @@ void ARM9Write8(u32 addr, u8 val) case 0x06000000: if (!(SCFG_EXT[0] & (1<<13))) return; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { case 0x00000000: NDS::GPU->WriteVRAM_ABG(addr, val); return; @@ -1541,9 +1515,7 @@ void ARM9Write8(u32 addr, u8 val) return; case 0x0C000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -1574,9 +1546,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u16*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1594,9 +1564,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1614,9 +1582,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1633,9 +1599,7 @@ void ARM9Write16(u32 addr, u16 val) return; case 0x0C000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -1666,9 +1630,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u32*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1686,9 +1648,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1706,9 +1666,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1725,9 +1683,7 @@ void ARM9Write32(u32 addr, u32 val) return; case 0x0C000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -1970,9 +1926,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u8*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1990,9 +1944,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -2010,9 +1962,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -2033,9 +1983,7 @@ void ARM7Write8(u32 addr, u8 val) case 0x0C000000: case 0x0C800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -2067,9 +2015,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u16*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -2087,9 +2033,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -2107,9 +2051,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -2130,9 +2072,7 @@ void ARM7Write16(u32 addr, u16 val) case 0x0C000000: case 0x0C800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -2164,9 +2104,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u32*)&ptr[addr & 0xFFFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -2184,9 +2122,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -2204,9 +2140,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -2227,9 +2161,7 @@ void ARM7Write32(u32 addr, u32 val) case 0x0C000000: case 0x0C800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } diff --git a/src/GPU.cpp b/src/GPU.cpp index 987068d9..5c67cfbb 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -20,9 +20,7 @@ #include "NDS.h" #include "GPU.h" -#ifdef JIT_ENABLED #include "ARMJIT.h" -#endif #include "GPU2D_Soft.h" #include "GPU3D_Soft.h" @@ -66,7 +64,7 @@ enum VRAMDirty need to be reset for the respective VRAM bank. */ -GPU::GPU() noexcept : GPU2D_A(0, *this), GPU2D_B(1, *this) +GPU::GPU(ARMJIT::ARMJIT& jit) noexcept : GPU2D_A(0, *this), GPU2D_B(1, *this), JIT(jit) { NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); @@ -590,9 +588,7 @@ void GPU::MapVRAM_CD(u32 bank, u8 cnt) noexcept VRAMMap_ARM7[ofs] |= bankmask; memset(VRAMDirty[bank].Data, 0xFF, sizeof(VRAMDirty[bank].Data)); VRAMSTAT |= (1 << (bank-2)); -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidateWVRAM(ofs); -#endif + JIT.CheckAndInvalidateWVRAM(ofs); break; case 3: // texture diff --git a/src/GPU.h b/src/GPU.h index 17164ab7..c3941eb3 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -35,6 +35,11 @@ namespace GPU3D class GPU3D; } +namespace ARMJIT +{ +class ARMJIT; +} + namespace Melon { static constexpr u32 VRAMDirtyGranularity = 512; @@ -70,7 +75,7 @@ struct RenderSettings class GPU { public: - GPU() noexcept; + GPU(ARMJIT::ARMJIT& jit) noexcept; ~GPU() noexcept; void Reset() noexcept; void Stop() noexcept; @@ -539,6 +544,7 @@ public: void SyncDirtyFlags() noexcept; + ARMJIT::ARMJIT& JIT; u16 VCount = 0; u16 TotalScanlines = 0; u16 DispStat[2] {}; diff --git a/src/JitBlock.h b/src/JitBlock.h new file mode 100644 index 00000000..abd435be --- /dev/null +++ b/src/JitBlock.h @@ -0,0 +1,61 @@ +/* + 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_JITBLOCK_H +#define MELONDS_JITBLOCK_H + +#include "types.h" +#include "TinyVector.h" + +namespace ARMJIT +{ +typedef void (*JitBlockEntry)(); + +class JitBlock +{ +public: + JitBlock(u32 num, u32 literalHash, u32 numAddresses, u32 numLiterals) + { + Num = num; + NumAddresses = numAddresses; + NumLiterals = numLiterals; + Data.SetLength(numAddresses * 2 + numLiterals); + } + + u32 StartAddr; + u32 StartAddrLocal; + u32 InstrHash, LiteralHash; + u8 Num; + u16 NumAddresses; + u16 NumLiterals; + + JitBlockEntry EntryPoint; + + u32* AddressRanges() + { return &Data[0]; } + u32* AddressMasks() + { return &Data[NumAddresses]; } + u32* Literals() + { return &Data[NumAddresses * 2]; } + +private: + TinyVector Data; +}; +} + +#endif //MELONDS_JITBLOCK_H diff --git a/src/NDS.cpp b/src/NDS.cpp index 5290423e..d13fd91f 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -35,16 +35,13 @@ #include "Platform.h" #include "FreeBIOS.h" -#ifdef JIT_ENABLED -#include "ARMJIT.h" -#include "ARMJIT_Memory.h" -#endif - #include "DSi.h" #include "DSi_SPI_TSC.h" #include "DSi_NWifi.h" #include "DSi_Camera.h" #include "DSi_DSP.h" +#include "ARMJIT.h" +#include "ARMJIT_Memory.h" using namespace Platform; @@ -186,6 +183,7 @@ class Wifi* Wifi; std::unique_ptr NDSCartSlot; std::unique_ptr GBACartSlot; std::unique_ptr GPU; +std::unique_ptr JIT; class AREngine* AREngine; bool Running; @@ -205,17 +203,15 @@ bool Init() RegisterEventFunc(Event_Div, 0, DivDone); RegisterEventFunc(Event_Sqrt, 0, SqrtDone); - GPU = std::make_unique(); - ARM9 = new ARMv5(*GPU); - ARM7 = new ARMv4(*GPU); + JIT = std::make_unique(); + GPU = std::make_unique(*JIT); -#ifdef JIT_ENABLED - ARMJIT::Init(); -#else - MainRAM = new u8[0x1000000]; - ARM7WRAM = new u8[ARM7WRAMSize]; - SharedWRAM = new u8[SharedWRAMSize]; -#endif + MainRAM = JIT->Memory.GetMainRAM(); + SharedWRAM = JIT->Memory.GetSharedWRAM(); + ARM7WRAM = JIT->Memory.GetARM7WRAM(); + + ARM9 = new ARMv5(*JIT, *GPU); + ARM7 = new ARMv4(*JIT, *GPU); DMAs[0] = new DMA(0, 0, *GPU); DMAs[1] = new DMA(0, 1, *GPU); @@ -242,10 +238,6 @@ bool Init() void DeInit() { -#ifdef JIT_ENABLED - ARMJIT::DeInit(); -#endif - delete ARM9; ARM9 = nullptr; delete ARM7; ARM7 = nullptr; @@ -270,6 +262,8 @@ void DeInit() UnregisterEventFunc(Event_Div, 0); UnregisterEventFunc(Event_Sqrt, 0); + + JIT = nullptr; } @@ -548,9 +542,7 @@ void Reset() // BIOS files are now loaded by the frontend -#ifdef JIT_ENABLED - ARMJIT::Reset(); -#endif + JIT->Reset(); if (ConsoleType == 1) { @@ -869,8 +861,8 @@ bool DoSavestate(Savestate* file) #ifdef JIT_ENABLED if (!file->Saving) { - ARMJIT::ResetBlockCache(); - ARMJIT_Memory::Reset(); + JIT->ResetBlockCache(); + JIT->Memory.Reset(); } #endif @@ -1401,9 +1393,7 @@ void MapSharedWRAM(u8 val) if (val == WRAMCnt) return; -#ifdef JIT_ENABLED - ARMJIT_Memory::RemapSWRAM(); -#endif + NDS::JIT->Memory.RemapSWRAM(); WRAMCnt = val; @@ -2315,18 +2305,14 @@ void ARM9Write8(u32 addr, u8 val) switch (addr & 0xFF000000) { case 0x02000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM9.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u8*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; @@ -2361,18 +2347,14 @@ void ARM9Write16(u32 addr, u16 val) switch (addr & 0xFF000000) { case 0x02000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM9.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u16*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; @@ -2387,9 +2369,7 @@ void ARM9Write16(u32 addr, u16 val) return; case 0x06000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { case 0x00000000: GPU->WriteVRAM_ABG(addr, val); return; @@ -2429,18 +2409,14 @@ void ARM9Write32(u32 addr, u32 val) switch (addr & 0xFF000000) { case 0x02000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&MainRAM[addr & MainRAMMask] = val; return ; case 0x03000000: if (SWRAM_ARM9.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u32*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; @@ -2455,9 +2431,7 @@ void ARM9Write32(u32 addr, u32 val) return; case 0x06000000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { case 0x00000000: GPU->WriteVRAM_ABG(addr, val); return; @@ -2738,34 +2712,26 @@ void ARM7Write8(u32 addr, u8 val) { case 0x02000000: case 0x02800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u8*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u8*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u8*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; @@ -2775,9 +2741,7 @@ void ARM7Write8(u32 addr, u8 val) case 0x06000000: case 0x06800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); GPU->WriteVRAM_ARM7(addr, val); return; @@ -2808,34 +2772,26 @@ void ARM7Write16(u32 addr, u16 val) { case 0x02000000: case 0x02800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u16*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u16*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u16*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; @@ -2854,9 +2810,7 @@ void ARM7Write16(u32 addr, u16 val) case 0x06000000: case 0x06800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); GPU->WriteVRAM_ARM7(addr, val); return; @@ -2889,34 +2843,26 @@ void ARM7Write32(u32 addr, u32 val) { case 0x02000000: case 0x02800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u32*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u32*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u32*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; @@ -2936,9 +2882,7 @@ void ARM7Write32(u32 addr, u32 val) case 0x06000000: case 0x06800000: -#ifdef JIT_ENABLED - ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); -#endif + NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); GPU->WriteVRAM_ARM7(addr, val); return; diff --git a/src/NDS.h b/src/NDS.h index 88a1f2f3..a1155ba0 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -46,6 +46,11 @@ namespace Melon class GPU; } +namespace ARMJIT +{ +class ARMJIT; +} + namespace NDS { @@ -269,6 +274,7 @@ extern class Wifi* Wifi; extern std::unique_ptr NDSCartSlot; extern std::unique_ptr GBACartSlot; extern std::unique_ptr GPU; +extern std::unique_ptr JIT; extern class AREngine* AREngine; const u32 ARM7WRAMSize = 0x10000; diff --git a/src/NDSCart.h b/src/NDSCart.h index 5696fd7e..33a17bba 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -19,6 +19,7 @@ #ifndef NDSCART_H #define NDSCART_H +#include #include #include #include diff --git a/src/TinyVector.h b/src/TinyVector.h new file mode 100644 index 00000000..63e7cafa --- /dev/null +++ b/src/TinyVector.h @@ -0,0 +1,131 @@ +/* + Copyright 2016-2023 melonDS team, RSDuck + + 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_TINYVECTOR_H +#define MELONDS_TINYVECTOR_H + +#include +#include +#include "types.h" + +namespace ARMJIT +{ +/* + TinyVector + - because reinventing the wheel is the best! + + - meant to be used very often, with not so many elements + max 1 << 16 elements + - doesn't allocate while no elements are inserted + - not stl confirmant of course + - probably only works with POD types + - remove operations don't preserve order, but O(1)! +*/ +template +struct __attribute__((packed)) TinyVector +{ + T* Data = NULL; + u16 Capacity = 0; + u16 Length = 0; + + ~TinyVector() + { + delete[] Data; + } + + void MakeCapacity(u32 capacity) + { + assert(capacity <= UINT16_MAX); + assert(capacity > Capacity); + T* newMem = new T[capacity]; + if (Data != NULL) + memcpy(newMem, Data, sizeof(T) * Length); + + T* oldData = Data; + Data = newMem; + if (oldData != NULL) + delete[] oldData; + + Capacity = capacity; + } + + void SetLength(u16 length) + { + if (Capacity < length) + MakeCapacity(length); + + Length = length; + } + + void Clear() + { + Length = 0; + } + + void Add(T element) + { + assert(Length + 1 <= UINT16_MAX); + if (Length + 1 > Capacity) + MakeCapacity(((Capacity + 4) * 3) / 2); + + Data[Length++] = element; + } + + void Remove(int index) + { + assert(Length > 0); + assert(index >= 0 && index < Length); + + Length--; + Data[index] = Data[Length]; + /*for (int i = index; i < Length; i++) + Data[i] = Data[i + 1];*/ + } + + int Find(T needle) + { + for (int i = 0; i < Length; i++) + { + if (Data[i] == needle) + return i; + } + return -1; + } + + bool RemoveByValue(T needle) + { + for (int i = 0; i < Length; i++) + { + if (Data[i] == needle) + { + Remove(i); + return true; + } + } + return false; + } + + T& operator[](int index) + { + assert(index >= 0 && index < Length); + return Data[index]; + } +}; +} + +#endif //MELONDS_TINYVECTOR_H From 679c37ddcedc7e6300a98ec5800f133c752615e7 Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Thu, 23 Nov 2023 18:48:05 +0000 Subject: [PATCH 043/157] Add support for saving animated icons Add support for exporting animated DSi icons as GIF using the header-only gif.h library. --- src/frontend/qt_sdl/ROMInfoDialog.cpp | 39 +- src/frontend/qt_sdl/ROMInfoDialog.h | 2 + src/frontend/qt_sdl/ROMInfoDialog.ui | 78 ++- src/frontend/qt_sdl/ROMManager.cpp | 11 +- src/frontend/qt_sdl/ROMManager.h | 5 +- src/frontend/qt_sdl/gif-h/gif.h | 836 ++++++++++++++++++++++++++ 6 files changed, 942 insertions(+), 29 deletions(-) create mode 100644 src/frontend/qt_sdl/gif-h/gif.h diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp index b381803b..34917d00 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.cpp +++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp @@ -21,6 +21,8 @@ #include +#include "gif-h/gif.h" + #include "NDS.h" #include "NDSCart.h" #include "Platform.h" @@ -45,23 +47,24 @@ ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMI const NDSBanner* banner = NDS::NDSCartSlot->GetCart()->Banner(); const NDSHeader& header = NDS::NDSCartSlot->GetCart()->GetHeader(); + u32 iconData[32 * 32]; ROMManager::ROMIcon(banner->Icon, banner->Palette, iconData); - iconImage = QImage(reinterpret_cast(iconData), 32, 32, QImage::Format_ARGB32).copy(); + iconImage = QImage(reinterpret_cast(iconData), 32, 32, QImage::Format_RGBA8888).copy(); ui->iconImage->setPixmap(QPixmap::fromImage(iconImage)); if (banner->Version == 0x103) { - u32 animatedIconData[32 * 32 * 64] = {0}; + ui->saveAnimatedIconButton->setEnabled(true); + ROMManager::AnimatedROMIcon(banner->DSiIcon, banner->DSiPalette, banner->DSiSequence, animatedIconData, animatedSequence); - for (int i = 0; i < 64; i++) + for (u32* image: animatedIconData) { - if (animatedIconData[32 * 32 * i] == 0) + if (!image) break; - animatedIconImages.push_back(QPixmap::fromImage(QImage(reinterpret_cast(&animatedIconData[32 * 32 * i]), 32, 32, QImage::Format_ARGB32).copy())); + animatedIconImages.push_back(QPixmap::fromImage(QImage(reinterpret_cast(image), 32, 32, QImage::Format_RGBA8888).copy())); } - iconTimeline = new QTimeLine(animatedSequence.size() / 60 * 1000, this); iconTimeline->setFrameRange(0, animatedSequence.size() - 1); iconTimeline->setLoopCount(0); @@ -139,6 +142,30 @@ void ROMInfoDialog::on_saveIconButton_clicked() iconImage.save(filename, "PNG"); } +void ROMInfoDialog::on_saveAnimatedIconButton_clicked() +{ + QString filename = QFileDialog::getSaveFileName(this, + "Save Animated Icon", + QString::fromStdString(Config::LastROMFolder), + "GIF Images (*.gif)"); + if (filename.isEmpty()) + return; + + + GifWriter writer; + + // The GIF format only supports delays of 0.01 seconds, so 0.0166... (60fps) + // is rounded up to 0.02 (50fps) + GifBegin(&writer, filename.toStdString().c_str(), 32, 32, 2); + for (int i: animatedSequence) + { + if (animatedIconData[i] == 0) + break; + GifWriteFrame(&writer, reinterpret_cast(animatedIconData[i]), 32, 32, 2); + } + GifEnd(&writer); +} + void ROMInfoDialog::iconSetFrame(int frame) { ui->dsiIconImage->setPixmap(animatedIconImages[animatedSequence[frame]]); diff --git a/src/frontend/qt_sdl/ROMInfoDialog.h b/src/frontend/qt_sdl/ROMInfoDialog.h index 39630378..630ac22d 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.h +++ b/src/frontend/qt_sdl/ROMInfoDialog.h @@ -60,6 +60,7 @@ private slots: void done(int r); void on_saveIconButton_clicked(); + void on_saveAnimatedIconButton_clicked(); void iconSetFrame(int frame); @@ -68,6 +69,7 @@ private: QImage iconImage; QTimeLine* iconTimeline; + u32 animatedIconData[64][32*32] = {0}; std::vector animatedIconImages; std::vector animatedSequence; }; diff --git a/src/frontend/qt_sdl/ROMInfoDialog.ui b/src/frontend/qt_sdl/ROMInfoDialog.ui index 1c9d844b..25592889 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.ui +++ b/src/frontend/qt_sdl/ROMInfoDialog.ui @@ -6,8 +6,8 @@ 0 0 - 559 - 532 + 557 + 547 @@ -525,10 +525,17 @@ + + + + Save icon + + + - + 0 0 @@ -619,7 +626,7 @@ 1 - + @@ -644,16 +651,42 @@ + + + + Qt::Horizontal + + + + 20 + 20 + + + + + + + + Qt::Horizontal + + + + 20 + 20 + + + + - + - + 0 0 @@ -724,25 +757,40 @@ - + + + + false + + + Save animated icon + + + + Qt::Horizontal + + + 0 + 0 + + - - - - Save icon - - - - + Qt::Horizontal + + + 0 + 0 + + diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 0bb6b7fe..1c58055f 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -1478,7 +1478,7 @@ QString GBACartLabel() } -void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef) +void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32 (&iconRef)[32*32]) { int index = 0; for (int i = 0; i < 4; i++) @@ -1495,7 +1495,7 @@ void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef) u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31; u8 a = pal_index ? 255: 0; u32* row = &iconRef[256 * i + 32 * k + 8 * j]; - row[l] = (a << 24) | (r << 16) | (g << 8) | b; + row[l] = r | (g << 8) | (b << 16) | (a << 24); index++; } } @@ -1509,14 +1509,15 @@ void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef) #define SEQ_BMP(i) ((i & 0b0000011100000000) >> 8) #define SEQ_DUR(i) ((i & 0b0000000011111111) >> 0) -void AnimatedROMIcon(const u8 (&data)[8][512], const u16 (&palette)[8][16], const u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], std::vector &animatedSequenceRef) +void AnimatedROMIcon(const u8 (&data)[8][512], const u16 (&palette)[8][16], const u16 (&sequence)[64], u32 (&animatedIconRef)[64][32*32], std::vector &animatedSequenceRef) { for (int i = 0; i < 64; i++) { if (!sequence[i]) break; - u32* frame = &animatedTexRef[32 * 32 * i]; - ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], frame); + + ROMIcon(data[SEQ_BMP(sequence[i])], palette[SEQ_PAL(sequence[i])], animatedIconRef[i]); + u32* frame = animatedIconRef[i]; if (SEQ_FLIPH(sequence[i])) { diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index c4be23be..6d957631 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -62,11 +62,10 @@ void UndoStateLoad(); void EnableCheats(bool enable); ARCodeFile* GetCheatFile(); -void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32* iconRef); +void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32 (&iconRef)[32*32]); void AnimatedROMIcon(const u8 (&data)[8][512], const u16 (&palette)[8][16], - const u16 (&sequence)[64], u32 (&animatedTexRef)[32 * 32 * 64], + const u16 (&sequence)[64], u32 (&animatedIconRef)[64][32*32], std::vector &animatedSequenceRef); - } #endif // ROMMANAGER_H diff --git a/src/frontend/qt_sdl/gif-h/gif.h b/src/frontend/qt_sdl/gif-h/gif.h new file mode 100644 index 00000000..d89ffc38 --- /dev/null +++ b/src/frontend/qt_sdl/gif-h/gif.h @@ -0,0 +1,836 @@ +// +// gif.h +// by Charlie Tangora +// Public domain. +// Email me : ctangora -at- gmail -dot- com +// +// This file offers a simple, very limited way to create animated GIFs directly in code. +// +// Those looking for particular cleverness are likely to be disappointed; it's pretty +// much a straight-ahead implementation of the GIF format with optional Floyd-Steinberg +// dithering. (It does at least use delta encoding - only the changed portions of each +// frame are saved.) +// +// So resulting files are often quite large. The hope is that it will be handy nonetheless +// as a quick and easily-integrated way for programs to spit out animations. +// +// Only RGBA8 is currently supported as an input format. (The alpha is ignored.) +// +// If capturing a buffer with a bottom-left origin (such as OpenGL), define GIF_FLIP_VERT +// to automatically flip the buffer data when writing the image (the buffer itself is +// unchanged. +// +// USAGE: +// Create a GifWriter struct. Pass it to GifBegin() to initialize and write the header. +// Pass subsequent frames to GifWriteFrame(). +// Finally, call GifEnd() to close the file handle and free memory. +// + +#ifndef gif_h +#define gif_h + +#include // for FILE* +#include // for memcpy and bzero +#include // for integer typedefs +#include // for bool macros + +// Define these macros to hook into a custom memory allocator. +// TEMP_MALLOC and TEMP_FREE will only be called in stack fashion - frees in the reverse order of mallocs +// and any temp memory allocated by a function will be freed before it exits. +// MALLOC and FREE are used only by GifBegin and GifEnd respectively (to allocate a buffer the size of the image, which +// is used to find changed pixels for delta-encoding.) + +#ifndef GIF_TEMP_MALLOC +#include +#define GIF_TEMP_MALLOC malloc +#endif + +#ifndef GIF_TEMP_FREE +#include +#define GIF_TEMP_FREE free +#endif + +#ifndef GIF_MALLOC +#include +#define GIF_MALLOC malloc +#endif + +#ifndef GIF_FREE +#include +#define GIF_FREE free +#endif + +const int kGifTransIndex = 0; + +typedef struct +{ + int bitDepth; + + uint8_t r[256]; + uint8_t g[256]; + uint8_t b[256]; + + // k-d tree over RGB space, organized in heap fashion + // i.e. left child of node i is node i*2, right child is node i*2+1 + // nodes 256-511 are implicitly the leaves, containing a color + uint8_t treeSplitElt[256]; + uint8_t treeSplit[256]; +} GifPalette; + +// max, min, and abs functions +int GifIMax(int l, int r) { return l>r?l:r; } +int GifIMin(int l, int r) { return l (1<bitDepth)-1) + { + int ind = treeRoot-(1<bitDepth); + if(ind == kGifTransIndex) return; + + // check whether this color is better than the current winner + int r_err = r - ((int32_t)pPal->r[ind]); + int g_err = g - ((int32_t)pPal->g[ind]); + int b_err = b - ((int32_t)pPal->b[ind]); + int diff = GifIAbs(r_err)+GifIAbs(g_err)+GifIAbs(b_err); + + if(diff < *bestDiff) + { + *bestInd = ind; + *bestDiff = diff; + } + + return; + } + + // take the appropriate color (r, g, or b) for this node of the k-d tree + int comps[3]; comps[0] = r; comps[1] = g; comps[2] = b; + int splitComp = comps[pPal->treeSplitElt[treeRoot]]; + + int splitPos = pPal->treeSplit[treeRoot]; + if(splitPos > splitComp) + { + // check the left subtree + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); + if( *bestDiff > splitPos - splitComp ) + { + // cannot prove there's not a better value in the right subtree, check that too + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); + } + } + else + { + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); + if( *bestDiff > splitComp - splitPos ) + { + GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); + } + } +} + +void GifSwapPixels(uint8_t* image, int pixA, int pixB) +{ + uint8_t rA = image[pixA*4]; + uint8_t gA = image[pixA*4+1]; + uint8_t bA = image[pixA*4+2]; + uint8_t aA = image[pixA*4+3]; + + uint8_t rB = image[pixB*4]; + uint8_t gB = image[pixB*4+1]; + uint8_t bB = image[pixB*4+2]; + uint8_t aB = image[pixA*4+3]; + + image[pixA*4] = rB; + image[pixA*4+1] = gB; + image[pixA*4+2] = bB; + image[pixA*4+3] = aB; + + image[pixB*4] = rA; + image[pixB*4+1] = gA; + image[pixB*4+2] = bA; + image[pixB*4+3] = aA; +} + +// just the partition operation from quicksort +int GifPartition(uint8_t* image, const int left, const int right, const int elt, int pivotIndex) +{ + const int pivotValue = image[(pivotIndex)*4+elt]; + GifSwapPixels(image, pivotIndex, right-1); + int storeIndex = left; + bool split = 0; + for(int ii=left; ii neededCenter) + GifPartitionByMedian(image, left, pivotIndex, com, neededCenter); + + if(pivotIndex < neededCenter) + GifPartitionByMedian(image, pivotIndex+1, right, com, neededCenter); + } +} + +// Builds a palette by creating a balanced k-d tree of all pixels in the image +void GifSplitPalette(uint8_t* image, int numPixels, int firstElt, int lastElt, int splitElt, int splitDist, int treeNode, bool buildForDither, GifPalette* pal) +{ + if(lastElt <= firstElt || numPixels == 0) + return; + + // base case, bottom of the tree + if(lastElt == firstElt+1) + { + if(buildForDither) + { + // Dithering needs at least one color as dark as anything + // in the image and at least one brightest color - + // otherwise it builds up error and produces strange artifacts + if( firstElt == 1 ) + { + // special case: the darkest color in the image + uint32_t r=255, g=255, b=255; + for(int ii=0; iir[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + + return; + } + + if( firstElt == (1 << pal->bitDepth)-1 ) + { + // special case: the lightest color in the image + uint32_t r=0, g=0, b=0; + for(int ii=0; iir[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + + return; + } + } + + // otherwise, take the average of all colors in this subcube + uint64_t r=0, g=0, b=0; + for(int ii=0; iir[firstElt] = (uint8_t)r; + pal->g[firstElt] = (uint8_t)g; + pal->b[firstElt] = (uint8_t)b; + + return; + } + + // Find the axis with the largest range + int minR = 255, maxR = 0; + int minG = 255, maxG = 0; + int minB = 255, maxB = 0; + for(int ii=0; ii maxR) maxR = r; + if(r < minR) minR = r; + + if(g > maxG) maxG = g; + if(g < minG) minG = g; + + if(b > maxB) maxB = b; + if(b < minB) minB = b; + } + + int rRange = maxR - minR; + int gRange = maxG - minG; + int bRange = maxB - minB; + + // and split along that axis. (incidentally, this means this isn't a "proper" k-d tree but I don't know what else to call it) + int splitCom = 1; + if(bRange > gRange) splitCom = 2; + if(rRange > bRange && rRange > gRange) splitCom = 0; + + int subPixelsA = numPixels * (splitElt - firstElt) / (lastElt - firstElt); + int subPixelsB = numPixels-subPixelsA; + + GifPartitionByMedian(image, 0, numPixels, splitCom, subPixelsA); + + pal->treeSplitElt[treeNode] = (uint8_t)splitCom; + pal->treeSplit[treeNode] = image[subPixelsA*4+splitCom]; + + GifSplitPalette(image, subPixelsA, firstElt, splitElt, splitElt-splitDist, splitDist/2, treeNode*2, buildForDither, pal); + GifSplitPalette(image+subPixelsA*4, subPixelsB, splitElt, lastElt, splitElt+splitDist, splitDist/2, treeNode*2+1, buildForDither, pal); +} + +// Finds all pixels that have changed from the previous image and +// moves them to the fromt of th buffer. +// This allows us to build a palette optimized for the colors of the +// changed pixels only. +int GifPickChangedPixels( const uint8_t* lastFrame, uint8_t* frame, int numPixels ) +{ + int numChanged = 0; + uint8_t* writeIter = frame; + + for (int ii=0; iibitDepth = bitDepth; + + // SplitPalette is destructive (it sorts the pixels by color) so + // we must create a copy of the image for it to destroy + size_t imageSize = (size_t)(width * height * 4 * sizeof(uint8_t)); + uint8_t* destroyableImage = (uint8_t*)GIF_TEMP_MALLOC(imageSize); + memcpy(destroyableImage, nextFrame, imageSize); + + int numPixels = (int)(width * height); + if(lastFrame) + numPixels = GifPickChangedPixels(lastFrame, destroyableImage, numPixels); + + const int lastElt = 1 << bitDepth; + const int splitElt = lastElt/2; + const int splitDist = splitElt/2; + + GifSplitPalette(destroyableImage, numPixels, 1, lastElt, splitElt, splitDist, 1, buildForDither, pPal); + + GIF_TEMP_FREE(destroyableImage); + + // add the bottom node for the transparency index + pPal->treeSplit[1 << (bitDepth-1)] = 0; + pPal->treeSplitElt[1 << (bitDepth-1)] = 0; + + pPal->r[0] = pPal->g[0] = pPal->b[0] = 0; +} + +// Implements Floyd-Steinberg dithering, writes palette value to alpha +void GifDitherImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint8_t* outFrame, uint32_t width, uint32_t height, GifPalette* pPal ) +{ + int numPixels = (int)(width * height); + + // quantPixels initially holds color*256 for all pixels + // The extra 8 bits of precision allow for sub-single-color error values + // to be propagated + int32_t *quantPixels = (int32_t *)GIF_TEMP_MALLOC(sizeof(int32_t) * (size_t)numPixels * 4); + + for( int ii=0; iir[bestInd]) * 256; + int32_t g_err = nextPix[1] - (int32_t)(pPal->g[bestInd]) * 256; + int32_t b_err = nextPix[2] - (int32_t)(pPal->b[bestInd]) * 256; + + nextPix[0] = pPal->r[bestInd]; + nextPix[1] = pPal->g[bestInd]; + nextPix[2] = pPal->b[bestInd]; + nextPix[3] = bestInd; + + // Propagate the error to the four adjacent locations + // that we haven't touched yet + int quantloc_7 = (int)(yy * width + xx + 1); + int quantloc_3 = (int)(yy * width + width + xx - 1); + int quantloc_5 = (int)(yy * width + width + xx); + int quantloc_1 = (int)(yy * width + width + xx + 1); + + if(quantloc_7 < numPixels) + { + int32_t* pix7 = quantPixels+4*quantloc_7; + pix7[0] += GifIMax( -pix7[0], r_err * 7 / 16 ); + pix7[1] += GifIMax( -pix7[1], g_err * 7 / 16 ); + pix7[2] += GifIMax( -pix7[2], b_err * 7 / 16 ); + } + + if(quantloc_3 < numPixels) + { + int32_t* pix3 = quantPixels+4*quantloc_3; + pix3[0] += GifIMax( -pix3[0], r_err * 3 / 16 ); + pix3[1] += GifIMax( -pix3[1], g_err * 3 / 16 ); + pix3[2] += GifIMax( -pix3[2], b_err * 3 / 16 ); + } + + if(quantloc_5 < numPixels) + { + int32_t* pix5 = quantPixels+4*quantloc_5; + pix5[0] += GifIMax( -pix5[0], r_err * 5 / 16 ); + pix5[1] += GifIMax( -pix5[1], g_err * 5 / 16 ); + pix5[2] += GifIMax( -pix5[2], b_err * 5 / 16 ); + } + + if(quantloc_1 < numPixels) + { + int32_t* pix1 = quantPixels+4*quantloc_1; + pix1[0] += GifIMax( -pix1[0], r_err / 16 ); + pix1[1] += GifIMax( -pix1[1], g_err / 16 ); + pix1[2] += GifIMax( -pix1[2], b_err / 16 ); + } + } + } + + // Copy the palettized result to the output buffer + for( int ii=0; iir[bestInd]; + outFrame[1] = pPal->g[bestInd]; + outFrame[2] = pPal->b[bestInd]; + outFrame[3] = (uint8_t)bestInd; + } + + if(lastFrame) lastFrame += 4; + outFrame += 4; + nextFrame += 4; + } +} + +// Simple structure to write out the LZW-compressed portion of the image +// one bit at a time +typedef struct +{ + uint8_t bitIndex; // how many bits in the partial byte written so far + uint8_t byte; // current partial byte + + uint32_t chunkIndex; + uint8_t chunk[256]; // bytes are written in here until we have 256 of them, then written to the file +} GifBitStatus; + +// insert a single bit +void GifWriteBit( GifBitStatus* stat, uint32_t bit ) +{ + bit = bit & 1; + bit = bit << stat->bitIndex; + stat->byte |= bit; + + ++stat->bitIndex; + if( stat->bitIndex > 7 ) + { + // move the newly-finished byte to the chunk buffer + stat->chunk[stat->chunkIndex++] = stat->byte; + // and start a new byte + stat->bitIndex = 0; + stat->byte = 0; + } +} + +// write all bytes so far to the file +void GifWriteChunk( FILE* f, GifBitStatus* stat ) +{ + fputc((int)stat->chunkIndex, f); + fwrite(stat->chunk, 1, stat->chunkIndex, f); + + stat->bitIndex = 0; + stat->byte = 0; + stat->chunkIndex = 0; +} + +void GifWriteCode( FILE* f, GifBitStatus* stat, uint32_t code, uint32_t length ) +{ + for( uint32_t ii=0; ii> 1; + + if( stat->chunkIndex == 255 ) + { + GifWriteChunk(f, stat); + } + } +} + +// The LZW dictionary is a 256-ary tree constructed as the file is encoded, +// this is one node +typedef struct +{ + uint16_t m_next[256]; +} GifLzwNode; + +// write a 256-color (8-bit) image palette to the file +void GifWritePalette( const GifPalette* pPal, FILE* f ) +{ + fputc(0, f); // first color: transparency + fputc(0, f); + fputc(0, f); + + for(int ii=1; ii<(1 << pPal->bitDepth); ++ii) + { + uint32_t r = pPal->r[ii]; + uint32_t g = pPal->g[ii]; + uint32_t b = pPal->b[ii]; + + fputc((int)r, f); + fputc((int)g, f); + fputc((int)b, f); + } +} + +// write the image header, LZW-compress and write out the image +void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t delay, GifPalette* pPal) +{ + // graphics control extension + fputc(0x21, f); + fputc(0xf9, f); + fputc(0x04, f); + fputc(0x05, f); // leave prev frame in place, this frame has transparency + fputc(delay & 0xff, f); + fputc((delay >> 8) & 0xff, f); + fputc(kGifTransIndex, f); // transparent color index + fputc(0, f); + + fputc(0x2c, f); // image descriptor block + + fputc(left & 0xff, f); // corner of image in canvas space + fputc((left >> 8) & 0xff, f); + fputc(top & 0xff, f); + fputc((top >> 8) & 0xff, f); + + fputc(width & 0xff, f); // width and height of image + fputc((width >> 8) & 0xff, f); + fputc(height & 0xff, f); + fputc((height >> 8) & 0xff, f); + + //fputc(0, f); // no local color table, no transparency + //fputc(0x80, f); // no local color table, but transparency + + fputc(0x80 + pPal->bitDepth-1, f); // local color table present, 2 ^ bitDepth entries + GifWritePalette(pPal, f); + + const int minCodeSize = pPal->bitDepth; + const uint32_t clearCode = 1 << pPal->bitDepth; + + fputc(minCodeSize, f); // min code size 8 bits + + GifLzwNode* codetree = (GifLzwNode*)GIF_TEMP_MALLOC(sizeof(GifLzwNode)*4096); + + memset(codetree, 0, sizeof(GifLzwNode)*4096); + int32_t curCode = -1; + uint32_t codeSize = (uint32_t)minCodeSize + 1; + uint32_t maxCode = clearCode+1; + + GifBitStatus stat; + stat.byte = 0; + stat.bitIndex = 0; + stat.chunkIndex = 0; + + GifWriteCode(f, &stat, clearCode, codeSize); // start with a fresh LZW dictionary + + for(uint32_t yy=0; yy= (1ul << codeSize) ) + { + // dictionary entry count has broken a size barrier, + // we need more bits for codes + codeSize++; + } + if( maxCode == 4095 ) + { + // the dictionary is full, clear it out and begin anew + GifWriteCode(f, &stat, clearCode, codeSize); // clear tree + + memset(codetree, 0, sizeof(GifLzwNode)*4096); + codeSize = (uint32_t)(minCodeSize + 1); + maxCode = clearCode+1; + } + + curCode = nextValue; + } + } + } + + // compression footer + GifWriteCode(f, &stat, (uint32_t)curCode, codeSize); + GifWriteCode(f, &stat, clearCode, codeSize); + GifWriteCode(f, &stat, clearCode + 1, (uint32_t)minCodeSize + 1); + + // write out the last partial chunk + while( stat.bitIndex ) GifWriteBit(&stat, 0); + if( stat.chunkIndex ) GifWriteChunk(f, &stat); + + fputc(0, f); // image block terminator + + GIF_TEMP_FREE(codetree); +} + +typedef struct +{ + FILE* f; + uint8_t* oldImage; + bool firstFrame; +} GifWriter; + +// Creates a gif file. +// The input GIFWriter is assumed to be uninitialized. +// The delay value is the time between frames in hundredths of a second - note that not all viewers pay much attention to this value. +bool GifBegin( GifWriter* writer, const char* filename, uint32_t width, uint32_t height, uint32_t delay, int32_t bitDepth = 8, bool dither = false ) +{ + (void)bitDepth; (void)dither; // Mute "Unused argument" warnings +#if defined(_MSC_VER) && (_MSC_VER >= 1400) + writer->f = 0; + fopen_s(&writer->f, filename, "wb"); +#else + writer->f = fopen(filename, "wb"); +#endif + if(!writer->f) return false; + + writer->firstFrame = true; + + // allocate + writer->oldImage = (uint8_t*)GIF_MALLOC(width*height*4); + + fputs("GIF89a", writer->f); + + // screen descriptor + fputc(width & 0xff, writer->f); + fputc((width >> 8) & 0xff, writer->f); + fputc(height & 0xff, writer->f); + fputc((height >> 8) & 0xff, writer->f); + + fputc(0xf0, writer->f); // there is an unsorted global color table of 2 entries + fputc(0, writer->f); // background color + fputc(0, writer->f); // pixels are square (we need to specify this because it's 1989) + + // now the "global" palette (really just a dummy palette) + // color 0: black + fputc(0, writer->f); + fputc(0, writer->f); + fputc(0, writer->f); + // color 1: also black + fputc(0, writer->f); + fputc(0, writer->f); + fputc(0, writer->f); + + if( delay != 0 ) + { + // animation header + fputc(0x21, writer->f); // extension + fputc(0xff, writer->f); // application specific + fputc(11, writer->f); // length 11 + fputs("NETSCAPE2.0", writer->f); // yes, really + fputc(3, writer->f); // 3 bytes of NETSCAPE2.0 data + + fputc(1, writer->f); // JUST BECAUSE + fputc(0, writer->f); // loop infinitely (byte 0) + fputc(0, writer->f); // loop infinitely (byte 1) + + fputc(0, writer->f); // block terminator + } + + return true; +} + +// Writes out a new frame to a GIF in progress. +// The GIFWriter should have been created by GIFBegin. +// AFAIK, it is legal to use different bit depths for different frames of an image - +// this may be handy to save bits in animations that don't change much. +bool GifWriteFrame( GifWriter* writer, const uint8_t* image, uint32_t width, uint32_t height, uint32_t delay, int bitDepth = 8, bool dither = false ) +{ + if(!writer->f) return false; + + const uint8_t* oldImage = writer->firstFrame? NULL : writer->oldImage; + writer->firstFrame = false; + + GifPalette pal; + GifMakePalette((dither? NULL : oldImage), image, width, height, bitDepth, dither, &pal); + + if(dither) + GifDitherImage(oldImage, image, writer->oldImage, width, height, &pal); + else + GifThresholdImage(oldImage, image, writer->oldImage, width, height, &pal); + + GifWriteLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal); + + return true; +} + +// Writes the EOF code, closes the file handle, and frees temp memory used by a GIF. +// Many if not most viewers will still display a GIF properly if the EOF code is missing, +// but it's still a good idea to write it out. +bool GifEnd( GifWriter* writer ) +{ + if(!writer->f) return false; + + fputc(0x3b, writer->f); // end of file + fclose(writer->f); + GIF_FREE(writer->oldImage); + + writer->f = NULL; + writer->oldImage = NULL; + + return true; +} + +#endif From 651b0f680c4531558990a5beb0e638968009dc51 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Fri, 24 Nov 2023 13:17:22 -0500 Subject: [PATCH 044/157] Use Platform::File calls in NDS::debug (#1888) --- src/NDS.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index d13fd91f..a216fed2 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -2074,25 +2074,24 @@ void debug(u32 param) //for (int i = 0; i < 9; i++) // printf("VRAM %c: %02X\n", 'A'+i, GPU->VRAMCNT[i]); - FILE* - shit = fopen("debug/DSfirmware.bin", "wb"); - fwrite(ARM9->ITCM, 0x8000, 1, shit); + Platform::FileHandle* shit = Platform::OpenFile("debug/DSfirmware.bin", FileMode::Write); + Platform::FileWrite(ARM9->ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x02400000; i+=4) { u32 val = ARM7Read32(i); - fwrite(&val, 4, 1, shit); + Platform::FileWrite(&val, 4, 1, shit); } for (u32 i = 0x037F0000; i < 0x03810000; i+=4) { u32 val = ARM7Read32(i); - fwrite(&val, 4, 1, shit); + Platform::FileWrite(&val, 4, 1, shit); } for (u32 i = 0x06000000; i < 0x06040000; i+=4) { u32 val = ARM7Read32(i); - fwrite(&val, 4, 1, shit); + Platform::FileWrite(&val, 4, 1, shit); } - fclose(shit); + Platform::CloseFile(shit); /*FILE* shit = fopen("debug/directboot9.bin", "wb"); From 346dd4006ea1283136095d5c43f602324a095092 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Sat, 25 Nov 2023 12:32:09 -0500 Subject: [PATCH 045/157] Move all core types into namespaces (#1886) * Reorganize namespaces - Most types are now moved into the `melonDS` namespace - Only good chance to do this for a while, since a big refactor is next * Fix the build --- src/ARCodeFile.cpp | 4 +++ src/ARCodeFile.h | 3 ++ src/AREngine.cpp | 4 +++ src/AREngine.h | 3 ++ src/ARM.cpp | 13 ++++--- src/ARM.h | 23 ++++++------- src/ARMInterpreter.cpp | 7 ++-- src/ARMInterpreter.h | 3 ++ src/ARMInterpreter_ALU.cpp | 2 +- src/ARMInterpreter_ALU.h | 3 ++ src/ARMInterpreter_Branch.cpp | 5 ++- src/ARMInterpreter_Branch.h | 3 ++ src/ARMInterpreter_LoadStore.cpp | 2 +- src/ARMInterpreter_LoadStore.h | 2 +- src/ARMJIT.cpp | 34 +++++++++---------- src/ARMJIT.h | 8 ++--- src/ARMJIT_A64/ARMJIT_ALU.cpp | 4 +-- src/ARMJIT_A64/ARMJIT_Branch.cpp | 2 +- src/ARMJIT_A64/ARMJIT_Compiler.cpp | 4 +-- src/ARMJIT_A64/ARMJIT_Compiler.h | 2 +- src/ARMJIT_A64/ARMJIT_LoadStore.cpp | 4 +-- src/ARMJIT_Internal.h | 4 +-- src/ARMJIT_Memory.cpp | 20 ++++++----- src/ARMJIT_Memory.h | 17 +++++----- src/ARMJIT_RegisterCache.h | 3 +- src/ARMJIT_x64/ARMJIT_ALU.cpp | 4 +-- src/ARMJIT_x64/ARMJIT_Branch.cpp | 2 +- src/ARMJIT_x64/ARMJIT_Compiler.cpp | 5 +-- src/ARMJIT_x64/ARMJIT_Compiler.h | 6 ++-- src/ARMJIT_x64/ARMJIT_GenOffsets.cpp | 2 +- src/ARMJIT_x64/ARMJIT_LoadStore.cpp | 4 +-- src/ARM_InstrInfo.cpp | 2 +- src/ARM_InstrInfo.h | 2 +- src/CP15.cpp | 3 ++ src/CRC32.cpp | 4 +++ src/CRC32.h | 3 ++ src/DMA.cpp | 6 +++- src/DMA.h | 8 ++--- src/DMA_Timings.cpp | 2 +- src/DMA_Timings.h | 2 +- src/DSi.cpp | 4 +++ src/DSi.h | 3 ++ src/DSi_AES.cpp | 4 +++ src/DSi_AES.h | 3 ++ src/DSi_Camera.cpp | 4 +++ src/DSi_Camera.h | 3 ++ src/DSi_DSP.cpp | 4 +++ src/DSi_DSP.h | 3 ++ src/DSi_I2C.cpp | 4 +++ src/DSi_I2C.h | 3 ++ src/DSi_NAND.cpp | 4 +-- src/DSi_NAND.h | 2 +- src/DSi_NDMA.cpp | 6 +++- src/DSi_NDMA.h | 8 ++--- src/DSi_NWifi.cpp | 5 +++ src/DSi_NWifi.h | 3 ++ src/DSi_SD.cpp | 4 +++ src/DSi_SD.h | 3 ++ src/DSi_SPI_TSC.cpp | 4 +++ src/DSi_SPI_TSC.h | 3 ++ src/DSi_TMD.h | 2 +- src/FATIO.cpp | 6 ++-- src/FATIO.h | 4 ++- src/FATStorage.cpp | 4 +++ src/FATStorage.h | 4 ++- src/FIFO.h | 3 ++ src/FreeBIOS.cpp | 5 ++- src/FreeBIOS.h | 3 ++ src/GBACart.cpp | 4 +++ src/GBACart.h | 2 +- src/GPU.cpp | 14 ++++---- src/GPU.h | 16 +++------ src/GPU2D.cpp | 6 ++-- src/GPU2D.h | 8 ++--- src/GPU2D_Soft.cpp | 12 ++++--- src/GPU2D_Soft.h | 9 ++--- src/GPU3D.cpp | 5 ++- src/GPU3D.h | 8 ++--- src/GPU3D_OpenGL.cpp | 8 ++--- src/GPU3D_OpenGL.h | 13 +++---- src/GPU3D_OpenGL_shaders.h | 5 +-- src/GPU3D_Soft.cpp | 7 ++-- src/GPU3D_Soft.h | 8 ++--- src/GPU_OpenGL.cpp | 8 ++--- src/GPU_OpenGL.h | 8 ++--- src/GPU_OpenGL_shaders.h | 3 ++ src/JitBlock.h | 2 +- src/NDS.cpp | 12 ++++--- src/NDS.h | 15 +++----- src/NDSCart.cpp | 4 +++ src/NDSCart.h | 2 +- src/NDS_Header.h | 3 ++ src/NonStupidBitfield.h | 3 ++ src/OpenGLSupport.cpp | 5 +++ src/OpenGLSupport.h | 3 +- src/Platform.h | 3 ++ src/ROMList.cpp | 4 +++ src/ROMList.h | 3 ++ src/RTC.cpp | 4 +++ src/RTC.h | 3 ++ src/SPI.cpp | 4 +++ src/SPI.h | 3 ++ src/SPI_Firmware.cpp | 4 +++ src/SPI_Firmware.h | 3 ++ src/SPU.cpp | 4 +++ src/SPU.h | 3 ++ src/Savestate.cpp | 4 +++ src/Savestate.h | 3 ++ src/TinyVector.h | 2 +- src/Wifi.cpp | 4 +++ src/Wifi.h | 3 ++ src/WifiAP.cpp | 4 +++ src/WifiAP.h | 3 ++ src/debug/GdbArch.h | 1 + src/debug/GdbCmds.cpp | 1 + src/debug/GdbProto.cpp | 1 + src/debug/GdbProto.h | 1 + src/debug/GdbStub.cpp | 1 + src/debug/GdbStub.h | 1 + src/debug/hexutil.h | 3 ++ src/dolphin/Arm64Emitter.h | 2 ++ src/dolphin/ArmCommon.h | 2 +- src/dolphin/BitSet.h | 10 +++--- src/dolphin/MathUtil.h | 4 +-- src/dolphin/x64ABI.cpp | 6 ++-- src/dolphin/x64CPUDetect.cpp | 2 ++ src/dolphin/x64Emitter.cpp | 2 ++ src/dolphin/x64Emitter.h | 2 ++ src/frontend/FrontendUtil.h | 1 + src/frontend/Util_Audio.cpp | 1 + src/frontend/duckstation/gl/context.h | 1 + src/frontend/duckstation/gl/context_agl.h | 1 + src/frontend/duckstation/gl/context_wgl.cpp | 1 + src/frontend/duckstation/gl/x11_window.h | 1 + src/frontend/duckstation/window_info.h | 4 +-- src/frontend/mic_blow.h | 4 ++- src/frontend/qt_sdl/ArchiveUtil.cpp | 1 + src/frontend/qt_sdl/ArchiveUtil.h | 1 + src/frontend/qt_sdl/AudioInOut.cpp | 1 + src/frontend/qt_sdl/AudioSettingsDialog.cpp | 1 + src/frontend/qt_sdl/CLI.cpp | 4 +-- src/frontend/qt_sdl/CameraManager.cpp | 1 + src/frontend/qt_sdl/CameraManager.h | 18 +++++----- src/frontend/qt_sdl/CameraSettingsDialog.cpp | 1 + src/frontend/qt_sdl/CheatsDialog.cpp | 1 + src/frontend/qt_sdl/CheatsDialog.h | 6 ++-- src/frontend/qt_sdl/Config.cpp | 1 + src/frontend/qt_sdl/EmuSettingsDialog.cpp | 3 +- .../qt_sdl/FirmwareSettingsDialog.cpp | 2 ++ src/frontend/qt_sdl/Input.cpp | 1 + src/frontend/qt_sdl/Input.h | 1 + .../qt_sdl/InputConfig/InputConfigDialog.cpp | 1 + src/frontend/qt_sdl/InputConfig/MapButton.h | 2 +- src/frontend/qt_sdl/LAN_PCap.cpp | 1 + src/frontend/qt_sdl/LAN_PCap.h | 1 + src/frontend/qt_sdl/LAN_Socket.cpp | 1 + src/frontend/qt_sdl/LAN_Socket.h | 1 + src/frontend/qt_sdl/LocalMP.cpp | 3 ++ src/frontend/qt_sdl/LocalMP.h | 1 + src/frontend/qt_sdl/OSD.cpp | 2 ++ src/frontend/qt_sdl/OSD.h | 3 ++ src/frontend/qt_sdl/PathSettingsDialog.cpp | 2 ++ src/frontend/qt_sdl/Platform.cpp | 2 +- .../PowerManagement/PowerManagementDialog.cpp | 2 ++ .../PowerManagement/PowerManagementDialog.h | 2 +- src/frontend/qt_sdl/RAMInfoDialog.cpp | 1 + src/frontend/qt_sdl/RAMInfoDialog.h | 22 ++++++------ src/frontend/qt_sdl/ROMInfoDialog.cpp | 2 ++ src/frontend/qt_sdl/ROMInfoDialog.h | 2 +- src/frontend/qt_sdl/ROMManager.cpp | 3 +- src/frontend/qt_sdl/ROMManager.h | 1 + src/frontend/qt_sdl/SaveManager.cpp | 3 +- src/frontend/qt_sdl/SaveManager.h | 16 ++++----- src/frontend/qt_sdl/TitleManagerDialog.cpp | 3 +- src/frontend/qt_sdl/TitleManagerDialog.h | 18 +++++----- src/frontend/qt_sdl/main.cpp | 4 +-- src/melonDLDI.h | 3 ++ src/types.h | 3 ++ 178 files changed, 529 insertions(+), 268 deletions(-) diff --git a/src/ARCodeFile.cpp b/src/ARCodeFile.cpp index 595df7d3..602a2e7b 100644 --- a/src/ARCodeFile.cpp +++ b/src/ARCodeFile.cpp @@ -21,6 +21,8 @@ #include "ARCodeFile.h" #include "Platform.h" +namespace melonDS +{ using namespace Platform; // TODO: import codes from other sources (usrcheat.dat, ...) @@ -182,3 +184,5 @@ bool ARCodeFile::Save() CloseFile(f); return true; } + +} \ No newline at end of file diff --git a/src/ARCodeFile.h b/src/ARCodeFile.h index 53b2f752..11e71efe 100644 --- a/src/ARCodeFile.h +++ b/src/ARCodeFile.h @@ -24,6 +24,8 @@ #include #include "types.h" +namespace melonDS +{ struct ARCode { std::string Name; @@ -59,4 +61,5 @@ private: std::string Filename; }; +} #endif // ARCODEFILE_H diff --git a/src/AREngine.cpp b/src/AREngine.cpp index c8888ac6..1d992b81 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -23,6 +23,9 @@ #include "AREngine.h" #include "Platform.h" +namespace melonDS +{ + using Platform::Log; using Platform::LogLevel; @@ -429,3 +432,4 @@ void AREngine::RunCheats() } } } +} diff --git a/src/AREngine.h b/src/AREngine.h index 8557eb77..a6926abf 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -21,6 +21,8 @@ #include "ARCodeFile.h" +namespace melonDS +{ class AREngine { public: @@ -45,4 +47,5 @@ private: void (*BusWrite32)(u32 addr, u32 val); }; +} #endif // ARENGINE_H diff --git a/src/ARM.cpp b/src/ARM.cpp index a361d778..a1485e99 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -28,6 +28,8 @@ #include "GPU.h" #include "ARMJIT_Memory.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -104,7 +106,7 @@ const u32 ARM::ConditionTable[16] = 0x0000 // NE }; -ARM::ARM(u32 num, ARMJIT::ARMJIT& jit, Melon::GPU& gpu) : +ARM::ARM(u32 num, ARMJIT& jit, melonDS::GPU& gpu) : #ifdef GDBSTUB_ENABLED GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)), #endif @@ -128,14 +130,14 @@ ARM::~ARM() // dorp } -ARMv5::ARMv5(ARMJIT::ARMJIT& jit, Melon::GPU& gpu) : ARM(0, jit, gpu) +ARMv5::ARMv5(ARMJIT& jit, melonDS::GPU& gpu) : ARM(0, jit, gpu) { DTCM = JIT.Memory.GetARM9DTCM(); PU_Map = PU_PrivMap; } -ARMv4::ARMv4(ARMJIT::ARMJIT& jit, Melon::GPU& gpu) : ARM(1, jit, gpu) +ARMv4::ARMv4(ARMJIT& jit, melonDS::GPU& gpu) : ARM(1, jit, gpu) { // } @@ -749,7 +751,7 @@ void ARMv5::ExecuteJIT() return; } - ARMJIT::JitBlockEntry block = JIT.LookUpBlock(0, FastBlockLookup, + JitBlockEntry block = JIT.LookUpBlock(0, FastBlockLookup, instrAddr - FastBlockLookupStart, instrAddr); if (block) ARM_Dispatch(this, block); @@ -906,7 +908,7 @@ void ARMv4::ExecuteJIT() return; } - ARMJIT::JitBlockEntry block = JIT.LookUpBlock(1, FastBlockLookup, + JitBlockEntry block = JIT.LookUpBlock(1, FastBlockLookup, instrAddr - FastBlockLookupStart, instrAddr); if (block) ARM_Dispatch(this, block); @@ -1191,5 +1193,6 @@ u32 ARMv5::ReadMem(u32 addr, int size) return ARM::ReadMem(addr, size); } +} #endif diff --git a/src/ARM.h b/src/ARM.h index c8f596a7..9a99a072 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -28,6 +28,8 @@ #include "debug/GdbStub.h" #endif +namespace melonDS +{ inline u32 ROR(u32 x, u32 n) { return (x >> (n&0x1F)) | (x << ((32-n)&0x1F)); @@ -42,15 +44,9 @@ enum const u32 ITCMPhysicalSize = 0x8000; const u32 DTCMPhysicalSize = 0x4000; -namespace ARMJIT -{ -class ARMJIT; -} -namespace Melon -{ -class GPU; -} +class ARMJIT; +class GPU; class ARMJIT_Memory; class ARM @@ -59,7 +55,7 @@ class ARM #endif { public: - ARM(u32 num, ARMJIT::ARMJIT& jit, Melon::GPU& gpu); + ARM(u32 num, ARMJIT& jit, GPU& gpu); virtual ~ARM(); // destroy shit virtual void Reset(); @@ -190,7 +186,7 @@ public: Gdb::GdbStub GdbStub; #endif - ARMJIT::ARMJIT& JIT; + ARMJIT& JIT; protected: u8 (*BusRead8)(u32 addr); u16 (*BusRead16)(u32 addr); @@ -222,13 +218,13 @@ protected: void GdbCheckB(); void GdbCheckC(); private: - Melon::GPU& GPU; + melonDS::GPU& GPU; }; class ARMv5 : public ARM { public: - ARMv5(ARMJIT::ARMJIT& jit, Melon::GPU& gpu); + ARMv5(ARMJIT& jit, melonDS::GPU& gpu); ~ARMv5(); void Reset() override; @@ -372,7 +368,7 @@ public: class ARMv4 : public ARM { public: - ARMv4(ARMJIT::ARMJIT& jit, Melon::GPU& gpu); + ARMv4(ARMJIT& jit, melonDS::GPU& gpu); void Reset() override; @@ -541,4 +537,5 @@ extern ARMv4* ARM7; } +} #endif // ARM_H diff --git a/src/ARMInterpreter.cpp b/src/ARMInterpreter.cpp index 7aabf4c1..ff73e230 100644 --- a/src/ARMInterpreter.cpp +++ b/src/ARMInterpreter.cpp @@ -24,15 +24,14 @@ #include "ARMInterpreter_LoadStore.h" #include "Platform.h" -using Platform::Log; -using Platform::LogLevel; - #ifdef GDBSTUB_ENABLED #include "debug/GdbStub.h" #endif -namespace ARMInterpreter +namespace melonDS::ARMInterpreter { + using Platform::Log; + using Platform::LogLevel; void A_UNK(ARM* cpu) diff --git a/src/ARMInterpreter.h b/src/ARMInterpreter.h index 38225f00..cff4821a 100644 --- a/src/ARMInterpreter.h +++ b/src/ARMInterpreter.h @@ -22,6 +22,8 @@ #include "types.h" #include "ARM.h" +namespace melonDS +{ namespace ARMInterpreter { @@ -41,4 +43,5 @@ void A_BLX_IMM(ARM* cpu); // I'm a special one look at me } +} #endif // ARMINTERPRETER_H diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 9fa44db4..313f7641 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -19,7 +19,7 @@ #include #include "ARM.h" -namespace ARMInterpreter +namespace melonDS::ARMInterpreter { inline bool CarryAdd(u32 a, u32 b) diff --git a/src/ARMInterpreter_ALU.h b/src/ARMInterpreter_ALU.h index 202d6b04..6998b637 100644 --- a/src/ARMInterpreter_ALU.h +++ b/src/ARMInterpreter_ALU.h @@ -19,6 +19,8 @@ #ifndef ARMINTERPRETER_ALU_H #define ARMINTERPRETER_ALU_H +namespace melonDS +{ namespace ARMInterpreter { @@ -134,4 +136,5 @@ void T_ADD_SP(ARM* cpu); } +} #endif diff --git a/src/ARMInterpreter_Branch.cpp b/src/ARMInterpreter_Branch.cpp index ece783c9..015f5682 100644 --- a/src/ARMInterpreter_Branch.cpp +++ b/src/ARMInterpreter_Branch.cpp @@ -19,12 +19,11 @@ #include "ARM.h" #include "Platform.h" +namespace melonDS::ARMInterpreter +{ using Platform::Log; using Platform::LogLevel; -namespace ARMInterpreter -{ - void A_B(ARM* cpu) { diff --git a/src/ARMInterpreter_Branch.h b/src/ARMInterpreter_Branch.h index 3db6f408..51a561c1 100644 --- a/src/ARMInterpreter_Branch.h +++ b/src/ARMInterpreter_Branch.h @@ -19,6 +19,8 @@ #ifndef ARMINTERPRETER_BRANCH_H #define ARMINTERPRETER_BRANCH_H +namespace melonDS +{ namespace ARMInterpreter { @@ -36,4 +38,5 @@ void T_BL_LONG_2(ARM* cpu); } +} #endif diff --git a/src/ARMInterpreter_LoadStore.cpp b/src/ARMInterpreter_LoadStore.cpp index 3b4b35d7..91acaacc 100644 --- a/src/ARMInterpreter_LoadStore.cpp +++ b/src/ARMInterpreter_LoadStore.cpp @@ -20,7 +20,7 @@ #include "ARM.h" -namespace ARMInterpreter +namespace melonDS::ARMInterpreter { diff --git a/src/ARMInterpreter_LoadStore.h b/src/ARMInterpreter_LoadStore.h index 9016cfa9..32d6e4d2 100644 --- a/src/ARMInterpreter_LoadStore.h +++ b/src/ARMInterpreter_LoadStore.h @@ -19,7 +19,7 @@ #ifndef ARMINTERPRETER_LOADSTORE_H #define ARMINTERPRETER_LOADSTORE_H -namespace ARMInterpreter +namespace melonDS::ARMInterpreter { #define A_PROTO_WB_LDRSTR(x) \ diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index c5512135..45445821 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -43,17 +43,17 @@ #include "Wifi.h" #include "NDSCart.h" #include "Platform.h" +#include "ARMJIT_x64/ARMJIT_Offsets.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -#include "ARMJIT_x64/ARMJIT_Offsets.h" static_assert(offsetof(ARM, CPSR) == ARM_CPSR_offset, ""); static_assert(offsetof(ARM, Cycles) == ARM_Cycles_offset, ""); static_assert(offsetof(ARM, StopExecution) == ARM_StopExecution_offset, ""); -namespace ARMJIT -{ #define JIT_DEBUGPRINT(msg, ...) //#define JIT_DEBUGPRINT(msg, ...) Platform::Log(Platform::LogLevel::Debug, msg, ## __VA_ARGS__) @@ -1056,20 +1056,20 @@ bool ARMJIT::SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& sta return false; } -template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32); -template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32); -template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32); -template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32); -template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32); -template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32); -template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32); -template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32); -template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32); -template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32); -template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32); -template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32); -template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32); -template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32); +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept; +template void ARMJIT::CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(u32) noexcept; void ARMJIT::ResetBlockCache() noexcept { diff --git a/src/ARMJIT.h b/src/ARMJIT.h index 074e2a13..4191824c 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -31,10 +31,10 @@ #include "ARMJIT_Compiler.h" +namespace melonDS +{ class ARM; -namespace ARMJIT -{ class JitBlock; class ARMJIT { @@ -74,7 +74,7 @@ public: TinyVector InvalidLiterals {}; private: - friend class ::ARMJIT_Memory; + friend class ARMJIT_Memory; void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept; void RetireJitBlock(JitBlock* block) noexcept; @@ -160,6 +160,6 @@ private: } // Defined in assembly -extern "C" void ARM_Dispatch(ARM* cpu, ARMJIT::JitBlockEntry entry); +extern "C" void ARM_Dispatch(melonDS::ARM* cpu, melonDS::JitBlockEntry entry); #endif diff --git a/src/ARMJIT_A64/ARMJIT_ALU.cpp b/src/ARMJIT_A64/ARMJIT_ALU.cpp index 3d52f280..b25bcaa3 100644 --- a/src/ARMJIT_A64/ARMJIT_ALU.cpp +++ b/src/ARMJIT_A64/ARMJIT_ALU.cpp @@ -20,7 +20,7 @@ using namespace Arm64Gen; -namespace ARMJIT +namespace melonDS { void Compiler::Comp_RegShiftReg(int op, bool S, Op2& op2, ARM64Reg rs) @@ -480,7 +480,7 @@ void Compiler::A_Comp_GetOp2(bool S, Op2& op2) Comp_AddCycles_C(); u32 shift = (CurInstr.Instr >> 7) & 0x1E; - u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift); + u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift); if (S && shift && (CurInstr.SetFlags & 0x2)) { diff --git a/src/ARMJIT_A64/ARMJIT_Branch.cpp b/src/ARMJIT_A64/ARMJIT_Branch.cpp index c7b6e98e..d18a0e05 100644 --- a/src/ARMJIT_A64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_A64/ARMJIT_Branch.cpp @@ -23,7 +23,7 @@ using namespace Arm64Gen; // hack const int kCodeCacheTiming = 3; -namespace ARMJIT +namespace melonDS { template diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp index 90940b0c..1b037588 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp @@ -39,7 +39,7 @@ using namespace Arm64Gen; extern "C" void ARM_Ret(); -namespace ARMJIT +namespace melonDS { /* @@ -106,7 +106,7 @@ void Compiler::A_Comp_MSR() if (CurInstr.Instr & (1 << 25)) { val = W0; - MOVI2R(val, ::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E))); + MOVI2R(val, melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E))); } else { diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index 1f79f3d3..d1e5c446 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -28,7 +28,7 @@ #include -namespace ARMJIT +namespace melonDS { class ARMJIT; const Arm64Gen::ARM64Reg RMemBase = Arm64Gen::X26; diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp index a779a727..93cd75e6 100644 --- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp @@ -24,7 +24,7 @@ using namespace Arm64Gen; -namespace ARMJIT +namespace melonDS { bool Compiler::IsJITFault(u8* pc) @@ -79,7 +79,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) if (size == 32) { CurCPU->DataRead32(addr & ~0x3, &val); - val = ::ROR(val, (addr & 0x3) << 3); + val = melonDS::ROR(val, (addr & 0x3) << 3); } else if (size == 16) { diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h index 7a8eb6bb..72d40a5f 100644 --- a/src/ARMJIT_Internal.h +++ b/src/ARMJIT_Internal.h @@ -28,13 +28,13 @@ #include "JitBlock.h" #include "TinyVector.h" +namespace melonDS +{ class ARM; class ARMv5; // here lands everything which doesn't fit into ARMJIT.h // where it would be included by pretty much everything -namespace ARMJIT -{ enum { diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 361a1ed3..7ed9b8d1 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -49,9 +49,6 @@ #include -using Platform::Log; -using Platform::LogLevel; - /* We're handling fastmem here. @@ -100,6 +97,12 @@ using Platform::LogLevel; #endif #endif +namespace melonDS +{ + +using Platform::Log; +using Platform::LogLevel; + #if defined(__ANDROID__) #define ASHMEM_DEVICE "/dev/ashmem" #endif @@ -562,7 +565,7 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept } #endif - ARMJIT::AddressRange* range = JIT.CodeMemRegions[region] + memoryOffset / 512; + AddressRange* range = JIT.CodeMemRegions[region] + memoryOffset / 512; // this overcomplicated piece of code basically just finds whole pieces of code memory // which can be mapped/protected @@ -580,9 +583,9 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept else { u32 sectionOffset = offset; - bool hasCode = isExecutable && ARMJIT::PageContainsCode(&range[offset / 512]); + bool hasCode = isExecutable && PageContainsCode(&range[offset / 512]); while (offset < mirrorSize - && (!isExecutable || ARMJIT::PageContainsCode(&range[offset / 512]) == hasCode) + && (!isExecutable || PageContainsCode(&range[offset / 512]) == hasCode) && (!skipDTCM || mirrorStart + offset != NDS::ARM9->DTCMBase)) { assert(states[(mirrorStart + offset) >> 12] == memstate_Unmapped); @@ -617,7 +620,7 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept return true; } -bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, ARMJIT::ARMJIT& jit) +bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, ARMJIT& jit) { if (jit.JITCompiler.IsJITFault(faultDesc.FaultPC)) { @@ -638,7 +641,7 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, ARMJIT::ARMJIT& ji const u64 AddrSpaceSize = 0x100000000; -ARMJIT_Memory::ARMJIT_Memory(ARMJIT::ARMJIT& jit) noexcept : JIT(jit) +ARMJIT_Memory::ARMJIT_Memory(ARMJIT& jit) noexcept : JIT(jit) { #if defined(__SWITCH__) MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize); @@ -1365,3 +1368,4 @@ void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) co } return NULL; } +} \ No newline at end of file diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index 1cf4f811..ed484373 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -42,11 +42,10 @@ #include "NDS.h" #endif -namespace ARMJIT +namespace melonDS { class Compiler; class ARMJIT; -} constexpr u32 RoundUp(u32 size) noexcept { @@ -97,7 +96,7 @@ public: #ifdef JIT_ENABLED public: - explicit ARMJIT_Memory(ARMJIT::ARMJIT& jit) noexcept; + explicit ARMJIT_Memory(ARMJIT& jit) noexcept; ~ARMJIT_Memory() noexcept; ARMJIT_Memory(const ARMJIT_Memory&) = delete; ARMJIT_Memory(ARMJIT_Memory&&) = delete; @@ -138,7 +137,7 @@ public: void* GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept; bool MapAtAddress(u32 addr) noexcept; private: - friend class ARMJIT::Compiler; + friend class Compiler; struct Mapping { u32 Addr; @@ -153,12 +152,12 @@ private: u32 EmulatedFaultAddr; u8* FaultPC; }; - static bool FaultHandler(FaultDescription& faultDesc, ARMJIT::ARMJIT& jit); + static bool FaultHandler(FaultDescription& faultDesc, ARMJIT& jit); bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept; bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept; void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept; - ARMJIT::ARMJIT& JIT; + ARMJIT& JIT; void* FastMem9Start; void* FastMem7Start; u8* MemoryBase = nullptr; @@ -178,10 +177,10 @@ private: #endif u8 MappingStatus9[1 << (32-12)] {}; u8 MappingStatus7[1 << (32-12)] {}; - ARMJIT::TinyVector Mappings[memregions_Count] {}; + TinyVector Mappings[memregions_Count] {}; #else public: - explicit ARMJIT_Memory(ARMJIT::ARMJIT&) {}; + explicit ARMJIT_Memory(ARMJIT&) {}; ~ARMJIT_Memory() = default; ARMJIT_Memory(const ARMJIT_Memory&) = delete; ARMJIT_Memory(ARMJIT_Memory&&) = delete; @@ -224,5 +223,5 @@ private: std::array NWRAM_C {}; #endif }; - +} #endif diff --git a/src/ARMJIT_RegisterCache.h b/src/ARMJIT_RegisterCache.h index 16105300..3cb0f79f 100644 --- a/src/ARMJIT_RegisterCache.h +++ b/src/ARMJIT_RegisterCache.h @@ -27,10 +27,11 @@ #include -namespace ARMJIT +namespace melonDS { using Platform::Log; using Platform::LogLevel; + using namespace Common; // Imported inside the namespace so that other headers aren't polluted template diff --git a/src/ARMJIT_x64/ARMJIT_ALU.cpp b/src/ARMJIT_x64/ARMJIT_ALU.cpp index bdc17e8b..69449ff9 100644 --- a/src/ARMJIT_x64/ARMJIT_ALU.cpp +++ b/src/ARMJIT_x64/ARMJIT_ALU.cpp @@ -21,7 +21,7 @@ using namespace Gen; -namespace ARMJIT +namespace melonDS { // uses RSCRATCH3 @@ -129,7 +129,7 @@ OpArg Compiler::A_Comp_GetALUOp2(bool S, bool& carryUsed) Comp_AddCycles_C(); u32 shift = (CurInstr.Instr >> 7) & 0x1E; - u32 imm = ::ROR(CurInstr.Instr & 0xFF, shift); + u32 imm = melonDS::ROR(CurInstr.Instr & 0xFF, shift); carryUsed = false; if (S && shift) diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp index ae7d1aef..5d76447e 100644 --- a/src/ARMJIT_x64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp @@ -21,7 +21,7 @@ using namespace Gen; -namespace ARMJIT +namespace melonDS { template diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp index 5506db7e..9fc72370 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp @@ -34,10 +34,11 @@ #endif using namespace Gen; +using namespace Common; extern "C" void ARM_Ret(); -namespace ARMJIT +namespace melonDS { template <> const X64Reg RegisterCache::NativeRegAllocOrder[] = @@ -141,7 +142,7 @@ void Compiler::A_Comp_MSR() Comp_AddCycles_C(); OpArg val = CurInstr.Instr & (1 << 25) - ? Imm32(::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E))) + ? Imm32(melonDS::ROR((CurInstr.Instr & 0xFF), ((CurInstr.Instr >> 7) & 0x1E))) : MapReg(CurInstr.A_Reg(0)); u32 mask = 0; diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index 84efb353..f38a6c3a 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -30,11 +30,11 @@ #include -class ARMJIT_Memory; -namespace ARMJIT +namespace melonDS { class ARMJIT; +class ARMJIT_Memory; const Gen::X64Reg RCPU = Gen::RBP; const Gen::X64Reg RCPSR = Gen::R15; @@ -172,7 +172,7 @@ public: memop_SubtractOffset = 1 << 4 }; void Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flags); - s32 Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn); + s32 Comp_MemAccessBlock(int rn, Common::BitSet16 regs, bool store, bool preinc, bool decrement, bool usermode, bool skipLoadingRn); bool Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr); void Comp_ArithTriOp(void (Compiler::*op)(int, const Gen::OpArg&, const Gen::OpArg&), diff --git a/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp b/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp index 0961fdd9..e2a74eee 100644 --- a/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp +++ b/src/ARMJIT_x64/ARMJIT_GenOffsets.cpp @@ -17,7 +17,7 @@ */ #include "../ARM.h" - +using namespace melonDS; int main(int argc, char* argv[]) { FILE* f = fopen("ARMJIT_Offsets.h", "w"); diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp index 14334293..9b7d865f 100644 --- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp @@ -21,7 +21,7 @@ using namespace Gen; -namespace ARMJIT +namespace melonDS { template @@ -85,7 +85,7 @@ bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) if (size == 32) { CurCPU->DataRead32(addr & ~0x3, &val); - val = ::ROR(val, (addr & 0x3) << 3); + val = melonDS::ROR(val, (addr & 0x3) << 3); } else if (size == 16) { diff --git a/src/ARM_InstrInfo.cpp b/src/ARM_InstrInfo.cpp index ea9f6810..d53c88f0 100644 --- a/src/ARM_InstrInfo.cpp +++ b/src/ARM_InstrInfo.cpp @@ -22,7 +22,7 @@ #include "ARMJIT.h" -namespace ARMInstrInfo +namespace melonDS::ARMInstrInfo { #define ak(x) ((x) << 23) diff --git a/src/ARM_InstrInfo.h b/src/ARM_InstrInfo.h index 3442c9af..13f66b09 100644 --- a/src/ARM_InstrInfo.h +++ b/src/ARM_InstrInfo.h @@ -21,7 +21,7 @@ #include "types.h" -namespace ARMInstrInfo +namespace melonDS::ARMInstrInfo { // Instruction kinds, for faster dispatch diff --git a/src/CP15.cpp b/src/CP15.cpp index e8d8c1af..af23da3a 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -25,6 +25,8 @@ #include "ARMJIT_Memory.h" #include "ARMJIT.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -1031,3 +1033,4 @@ void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region) GetMemRegion(addr, false, &CodeMem); } +} diff --git a/src/CRC32.cpp b/src/CRC32.cpp index 8afa3ba3..0756c034 100644 --- a/src/CRC32.cpp +++ b/src/CRC32.cpp @@ -20,6 +20,8 @@ // http://www.codeproject.com/KB/recipes/crc32_large.aspx +namespace melonDS +{ constexpr u32 _reflect(u32 refl, char ch) { u32 value = 0; @@ -62,3 +64,5 @@ u32 CRC32(const u8 *data, int len, u32 start) return (crc ^ 0xFFFFFFFF); } + +} \ No newline at end of file diff --git a/src/CRC32.h b/src/CRC32.h index 4a1034ca..11879057 100644 --- a/src/CRC32.h +++ b/src/CRC32.h @@ -23,6 +23,9 @@ #include "types.h" +namespace melonDS +{ u32 CRC32(const u8* data, int len, u32 start=0); +} #endif // CRC32_H diff --git a/src/DMA.cpp b/src/DMA.cpp index 91f23454..8afc8971 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -24,6 +24,8 @@ #include "DMA_Timings.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -47,7 +49,7 @@ using Platform::LogLevel; // TODO: timings are nonseq when address is fixed/decrementing -DMA::DMA(u32 cpu, u32 num, Melon::GPU& gpu) : +DMA::DMA(u32 cpu, u32 num, melonDS::GPU& gpu) : CPU(cpu), Num(num), GPU(gpu) @@ -714,3 +716,5 @@ void DMA::Run() template void DMA::Run<0>(); template void DMA::Run<1>(); + +} \ No newline at end of file diff --git a/src/DMA.h b/src/DMA.h index af8b5171..5535661d 100644 --- a/src/DMA.h +++ b/src/DMA.h @@ -24,15 +24,14 @@ #include "Savestate.h" #include "DMA_Timings.h" -namespace Melon +namespace melonDS { class GPU; -} class DMA { public: - DMA(u32 cpu, u32 num, Melon::GPU& gpu); + DMA(u32 cpu, u32 num, GPU& gpu); ~DMA() = default; void Reset(); @@ -84,7 +83,7 @@ public: u32 Cnt {}; private: - Melon::GPU& GPU; + melonDS::GPU& GPU; u32 CPU {}; u32 Num {}; @@ -109,4 +108,5 @@ private: std::array MRAMBurstTable; }; +} #endif diff --git a/src/DMA_Timings.cpp b/src/DMA_Timings.cpp index cea32820..912e4e2e 100644 --- a/src/DMA_Timings.cpp +++ b/src/DMA_Timings.cpp @@ -19,7 +19,7 @@ #include "DMA_Timings.h" #include "types.h" -namespace DMATiming +namespace melonDS::DMATiming { // DMA timing tables diff --git a/src/DMA_Timings.h b/src/DMA_Timings.h index faf2e9c9..63dc4676 100644 --- a/src/DMA_Timings.h +++ b/src/DMA_Timings.h @@ -22,7 +22,7 @@ #include #include "types.h" -namespace DMATiming +namespace melonDS::DMATiming { // DMA timing tables diff --git a/src/DSi.cpp b/src/DSi.cpp index 5c4b5422..351d5ed4 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -41,6 +41,8 @@ #include "tiny-AES-c/aes.hpp" +namespace melonDS +{ using namespace Platform; namespace DSi @@ -3143,3 +3145,5 @@ void ARM7IOWrite32(u32 addr, u32 val) } } + +} \ No newline at end of file diff --git a/src/DSi.h b/src/DSi.h index 66a4e808..ac6bf772 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -22,6 +22,8 @@ #include "NDS.h" #include "DSi_SD.h" +namespace melonDS +{ class DSi_I2CHost; class DSi_CamModule; class DSi_AES; @@ -126,4 +128,5 @@ void ARM7IOWrite32(u32 addr, u32 val); } +} #endif // DSI_H diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index b1d89bd1..a29e43f1 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -23,6 +23,8 @@ #include "DSi_AES.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -572,3 +574,5 @@ void DSi_AES::WriteKeyY(u32 slot, u32 offset, u32 val, u32 mask) DeriveNormalKey(KeyX[slot], KeyY[slot], KeyNormal[slot]); } } + +} \ No newline at end of file diff --git a/src/DSi_AES.h b/src/DSi_AES.h index 1f1c6bf9..9c0a63fe 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -24,6 +24,8 @@ #include "FIFO.h" #include "tiny-AES-c/aes.hpp" +namespace melonDS +{ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wattributes" #if defined(__GNUC__) && (__GNUC__ >= 11) // gcc 11.* @@ -108,4 +110,5 @@ private: void ProcessBlock_CTR(); }; +} #endif // DSI_AES_H diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 2b259c59..708875c1 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -23,6 +23,8 @@ #include "DSi_Camera.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -793,3 +795,5 @@ void DSi_Camera::InputFrame(u32* data, int width, int height, bool rgb) } } } + +} \ No newline at end of file diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index c3165b3f..8a62e47d 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -23,6 +23,8 @@ #include "Savestate.h" #include "DSi_I2C.h" +namespace melonDS +{ class DSi_CamModule; class DSi_Camera : public DSi_I2CDevice @@ -121,4 +123,5 @@ private: static const u32 kTransferStart; }; +} #endif // DSI_CAMERA_H diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index 82a52aeb..61d2b162 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -24,6 +24,8 @@ #include "NDS.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -593,3 +595,5 @@ void DSi_DSP::DoSavestate(Savestate* file) // TODO: save the Teakra state!!! } + +} \ No newline at end of file diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h index 56850c0e..18ad3d1d 100644 --- a/src/DSi_DSP.h +++ b/src/DSi_DSP.h @@ -27,6 +27,8 @@ namespace Teakra { class Teakra; } +namespace melonDS +{ class DSi_DSP { public: @@ -104,5 +106,6 @@ private: u16 PDataDMAReadMMIO(); }; +} #endif // DSI_DSP_H diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index 8549ecb2..678c8be6 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -26,6 +26,8 @@ #include "SPI.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -592,3 +594,5 @@ void DSi_I2CHost::WriteData(u8 val) { Data = val; } + +} \ No newline at end of file diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index b55dd0bf..83d7e028 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -22,6 +22,8 @@ #include "types.h" #include "Savestate.h" +namespace melonDS +{ class DSi_I2CHost; class DSi_Camera; @@ -180,4 +182,5 @@ private: void GetCurDevice(); }; +} #endif // DSI_I2C_H diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 3db5621f..59f582fd 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -30,9 +30,9 @@ #include "fatfs/ff.h" -using namespace Platform; +using namespace melonDS::Platform; -namespace DSi_NAND +namespace melonDS::DSi_NAND { NANDImage::NANDImage(Platform::FileHandle* nandfile, const DSiKey& es_keyY) noexcept : NANDImage(nandfile, es_keyY.data()) diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 2bb3a02b..699397bd 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -31,7 +31,7 @@ struct AES_ctx; -namespace DSi_NAND +namespace melonDS::DSi_NAND { enum diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index 0afb2010..4f248eae 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -23,10 +23,12 @@ #include "GPU.h" #include "DSi_AES.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; -DSi_NDMA::DSi_NDMA(u32 cpu, u32 num, Melon::GPU& gpu) : GPU(gpu) +DSi_NDMA::DSi_NDMA(u32 cpu, u32 num, melonDS::GPU& gpu) : GPU(gpu) { CPU = cpu; Num = num; @@ -375,3 +377,5 @@ void DSi_NDMA::Run7() DSi::AES->CheckInputDMA(); DSi::AES->CheckOutputDMA(); } + +} \ No newline at end of file diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h index 732bc9d2..4479341b 100644 --- a/src/DSi_NDMA.h +++ b/src/DSi_NDMA.h @@ -22,15 +22,14 @@ #include "types.h" #include "Savestate.h" -namespace Melon +namespace melonDS { class GPU; -} class DSi_NDMA { public: - DSi_NDMA(u32 cpu, u32 num, Melon::GPU& gpu); + DSi_NDMA(u32 cpu, u32 num, GPU& gpu); ~DSi_NDMA(); void Reset(); @@ -78,7 +77,7 @@ public: u32 Cnt; private: - Melon::GPU& GPU; + melonDS::GPU& GPU; u32 CPU, Num; u32 StartMode; @@ -100,4 +99,5 @@ private: bool IsGXFIFODMA; }; +} #endif // DSI_NDMA_H diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 6e19421d..66320ba2 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -24,6 +24,9 @@ #include "WifiAP.h" #include "Platform.h" +namespace melonDS +{ + using Platform::Log; using Platform::LogLevel; @@ -1607,3 +1610,5 @@ void DSi_NWifi::MSTimer(u32 param) NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, 0, 0); } + +} \ No newline at end of file diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h index c3e577c2..f3e5e150 100644 --- a/src/DSi_NWifi.h +++ b/src/DSi_NWifi.h @@ -23,6 +23,8 @@ #include "FIFO.h" #include "Savestate.h" +namespace melonDS +{ class DSi_NWifi : public DSi_SDDevice { public: @@ -146,4 +148,5 @@ private: u8 LANBuffer[2048]; }; +} #endif // DSI_NWIFI_H diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 409a4083..5174af33 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -24,6 +24,8 @@ #include "DSi_NWifi.h" #include "Platform.h" +namespace melonDS +{ using namespace Platform; // observed IRQ behavior during transfers @@ -1095,3 +1097,5 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr) return len; } + +} \ No newline at end of file diff --git a/src/DSi_SD.h b/src/DSi_SD.h index 0940b547..faa42964 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -24,6 +24,8 @@ #include "FATStorage.h" #include "Savestate.h" +namespace melonDS +{ namespace DSi_NAND { class NANDImage; @@ -169,4 +171,5 @@ private: u32 WriteBlock(u64 addr); }; +} #endif // DSI_SD_H diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index 7040fccf..520e57ba 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -22,6 +22,8 @@ #include "DSi_SPI_TSC.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -219,3 +221,5 @@ void DSi_TSC::Release() DataPos = 0; } + +} \ No newline at end of file diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h index ebac3952..8e9ff912 100644 --- a/src/DSi_SPI_TSC.h +++ b/src/DSi_SPI_TSC.h @@ -23,6 +23,8 @@ #include "Savestate.h" #include "SPI.h" +namespace melonDS +{ class DSi_TSC : public TSC { public: @@ -50,4 +52,5 @@ private: u8 TSCMode; }; +} #endif // DSI_SPI_TSC diff --git a/src/DSi_TMD.h b/src/DSi_TMD.h index 12226c55..f07b3d1c 100644 --- a/src/DSi_TMD.h +++ b/src/DSi_TMD.h @@ -22,7 +22,7 @@ #include "types.h" #include -namespace DSi_TMD +namespace melonDS::DSi_TMD { struct TitleMetadataContent { diff --git a/src/FATIO.cpp b/src/FATIO.cpp index 3e91fbad..233014e4 100644 --- a/src/FATIO.cpp +++ b/src/FATIO.cpp @@ -20,13 +20,15 @@ #include "fatfs/ff.h" #include "fatfs/diskio.h" +using namespace melonDS; + static ff_disk_read_cb ReadCb; static ff_disk_write_cb WriteCb; static LBA_t SectorCount; static DSTATUS Status = STA_NOINIT | STA_NODISK; -void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt) +void melonDS::ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt) { if (!readcb) return; @@ -39,7 +41,7 @@ void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb else Status &= ~STA_PROTECT; } -void ff_disk_close() +void melonDS::ff_disk_close() { ReadCb = nullptr; WriteCb = nullptr; diff --git a/src/FATIO.h b/src/FATIO.h index b3b63f3f..6d8aa499 100644 --- a/src/FATIO.h +++ b/src/FATIO.h @@ -24,11 +24,13 @@ #include "fatfs/ff.h" // extra additions for interfacing with melonDS - +namespace melonDS +{ using ff_disk_read_cb = std::function; using ff_disk_write_cb = std::function; void ff_disk_open(const ff_disk_read_cb& readcb, const ff_disk_write_cb& writecb, LBA_t seccnt); void ff_disk_close(); +} #endif // FATIO_H diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index e357470b..cd0a03c8 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -25,6 +25,8 @@ #include "FATStorage.h" #include "Platform.h" +namespace melonDS +{ namespace fs = std::filesystem; using namespace Platform; @@ -1104,3 +1106,5 @@ bool FATStorage::Save() return true; } + +} \ No newline at end of file diff --git a/src/FATStorage.h b/src/FATStorage.h index 54adb5d4..2bdafad8 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -28,7 +28,8 @@ #include "types.h" #include "fatfs/ff.h" - +namespace melonDS +{ class FATStorage { public: @@ -99,4 +100,5 @@ private: std::map FileIndex; }; +} #endif // FATSTORAGE_H diff --git a/src/FIFO.h b/src/FIFO.h index c1cbdbf4..cbff4ab9 100644 --- a/src/FIFO.h +++ b/src/FIFO.h @@ -22,6 +22,8 @@ #include "types.h" #include "Savestate.h" +namespace melonDS +{ template class FIFO { @@ -189,4 +191,5 @@ private: u32 ReadPos, WritePos; }; +} #endif diff --git a/src/FreeBIOS.cpp b/src/FreeBIOS.cpp index 972f4b8e..7eef18b7 100644 --- a/src/FreeBIOS.cpp +++ b/src/FreeBIOS.cpp @@ -26,6 +26,8 @@ #include "FreeBIOS.h" +namespace melonDS +{ unsigned char bios_arm7_bin[] = { 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea, @@ -1738,4 +1740,5 @@ unsigned char bios_arm9_bin[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; \ No newline at end of file +}; +} \ No newline at end of file diff --git a/src/FreeBIOS.h b/src/FreeBIOS.h index 82cb3694..ce90725d 100644 --- a/src/FreeBIOS.h +++ b/src/FreeBIOS.h @@ -28,7 +28,10 @@ #ifndef FREEBIOS_H #define FREEBIOS_H +namespace melonDS +{ extern unsigned char bios_arm7_bin[16384]; extern unsigned char bios_arm9_bin[4096]; +} #endif // FREEBIOS_H diff --git a/src/GBACart.cpp b/src/GBACart.cpp index f20feea7..a0c5467e 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -24,6 +24,8 @@ #include "CRC32.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -897,3 +899,5 @@ void GBACartSlot::SRAMWrite(u32 addr, u8 val) noexcept } } + +} \ No newline at end of file diff --git a/src/GBACart.h b/src/GBACart.h index 7447f6f4..a5570897 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -23,7 +23,7 @@ #include "types.h" #include "Savestate.h" -namespace GBACart +namespace melonDS::GBACart { enum CartType diff --git a/src/GPU.cpp b/src/GPU.cpp index 5c67cfbb..27dcbfbb 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -26,6 +26,8 @@ #include "GPU3D_Soft.h" #include "GPU3D_OpenGL.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -33,8 +35,6 @@ using Platform::LogLevel; #define HBLANK_CYCLES (48+(256*6)) #define FRAME_CYCLES (LINE_CYCLES * 263) -namespace Melon -{ enum { LCD_StartHBlank = 0, @@ -64,7 +64,7 @@ enum VRAMDirty need to be reset for the respective VRAM bank. */ -GPU::GPU(ARMJIT::ARMJIT& jit) noexcept : GPU2D_A(0, *this), GPU2D_B(1, *this), JIT(jit) +GPU::GPU(ARMJIT& jit) noexcept : GPU2D_A(0, *this), GPU2D_B(1, *this), JIT(jit) { NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); @@ -319,21 +319,21 @@ void GPU::InitRenderer(int renderer) noexcept { // Fallback on software renderer renderer = 0; - GPU3D.SetCurrentRenderer(std::make_unique(*this)); + GPU3D.SetCurrentRenderer(std::make_unique(*this)); } - GPU3D.SetCurrentRenderer(GPU3D::GLRenderer::New(*this)); + GPU3D.SetCurrentRenderer(GLRenderer::New(*this)); if (!GPU3D.GetCurrentRenderer()) { // Fallback on software renderer CurGLCompositor.reset(); renderer = 0; - GPU3D.SetCurrentRenderer(std::make_unique(*this)); + GPU3D.SetCurrentRenderer(std::make_unique(*this)); } } else #endif { - GPU3D.SetCurrentRenderer(std::make_unique(*this)); + GPU3D.SetCurrentRenderer(std::make_unique(*this)); } Renderer = renderer; diff --git a/src/GPU.h b/src/GPU.h index c3941eb3..dbeec528 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -29,19 +29,11 @@ #include "GPU_OpenGL.h" #endif - -namespace GPU3D +namespace melonDS { class GPU3D; -} - -namespace ARMJIT -{ class ARMJIT; -} -namespace Melon -{ static constexpr u32 VRAMDirtyGranularity = 512; class GPU; @@ -75,7 +67,7 @@ struct RenderSettings class GPU { public: - GPU(ARMJIT::ARMJIT& jit) noexcept; + GPU(ARMJIT& jit) noexcept; ~GPU() noexcept; void Reset() noexcept; void Stop() noexcept; @@ -544,7 +536,7 @@ public: void SyncDirtyFlags() noexcept; - ARMJIT::ARMJIT& JIT; + ARMJIT& JIT; u16 VCount = 0; u16 TotalScanlines = 0; u16 DispStat[2] {}; @@ -590,7 +582,7 @@ public: GPU2D::Unit GPU2D_A; GPU2D::Unit GPU2D_B; - GPU3D::GPU3D GPU3D {}; + melonDS::GPU3D GPU3D {}; NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9] {}; VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG {}; diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index e892116c..e7fb9a29 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -21,6 +21,8 @@ #include "NDS.h" #include "GPU.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -84,8 +86,7 @@ using Platform::LogLevel; namespace GPU2D { - -Unit::Unit(u32 num, Melon::GPU& gpu) : Num(num), GPU(gpu) +Unit::Unit(u32 num, melonDS::GPU& gpu) : Num(num), GPU(gpu) { } @@ -722,3 +723,4 @@ void Unit::GetOBJVRAM(u8*& data, u32& mask) } } +} diff --git a/src/GPU2D.h b/src/GPU2D.h index ad052bf1..dd3bbf6b 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -22,10 +22,9 @@ #include "types.h" #include "Savestate.h" -namespace Melon +namespace melonDS { class GPU; -} namespace GPU2D { @@ -35,7 +34,7 @@ class Unit public: // take a reference to the GPU so we can access its state // and ensure that it's not null - Unit(u32 num, Melon::GPU& gpu); + Unit(u32 num, melonDS::GPU& gpu); Unit(const Unit&) = delete; Unit& operator=(const Unit&) = delete; @@ -124,7 +123,7 @@ public: u16 MasterBrightness; private: - Melon::GPU& GPU; + melonDS::GPU& GPU; }; class Renderer2D @@ -150,4 +149,5 @@ protected: } +} #endif diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 7447bcf9..9e7d8491 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -20,10 +20,11 @@ #include "GPU.h" #include "GPU3D_OpenGL.h" +namespace melonDS +{ namespace GPU2D { - -SoftRenderer::SoftRenderer(Melon::GPU& gpu) +SoftRenderer::SoftRenderer(melonDS::GPU& gpu) : Renderer2D(), GPU(gpu) { // initialize mosaic table @@ -368,7 +369,7 @@ void SoftRenderer::VBlankEnd(Unit* unitA, Unit* unitB) { if ((unitA->CaptureCnt & (1<<31)) && (((unitA->CaptureCnt >> 29) & 0x3) != 1)) { - reinterpret_cast(GPU.GPU3D.GetCurrentRenderer())->PrepareCaptureFrame(); + reinterpret_cast(GPU.GPU3D.GetCurrentRenderer())->PrepareCaptureFrame(); } } #endif @@ -479,8 +480,8 @@ void SoftRenderer::DoCapture(u32 line, u32 width) dstaddr &= 0xFFFF; srcBaddr &= 0xFFFF; - static_assert(Melon::VRAMDirtyGranularity == 512); - GPU.VRAMDirty[dstvram][(dstaddr * 2) / Melon::VRAMDirtyGranularity] = true; + static_assert(VRAMDirtyGranularity == 512); + GPU.VRAMDirty[dstvram][(dstaddr * 2) / VRAMDirtyGranularity] = true; switch ((captureCnt >> 29) & 0x3) { @@ -2226,3 +2227,4 @@ void SoftRenderer::DrawSprite_Normal(u32 num, u32 width, u32 height, s32 xpos, s } } +} diff --git a/src/GPU2D_Soft.h b/src/GPU2D_Soft.h index 7d6d65f9..86943d33 100644 --- a/src/GPU2D_Soft.h +++ b/src/GPU2D_Soft.h @@ -20,10 +20,9 @@ #include "GPU2D.h" -namespace Melon +namespace melonDS { class GPU; -} namespace GPU2D { @@ -31,14 +30,14 @@ namespace GPU2D class SoftRenderer : public Renderer2D { public: - SoftRenderer(Melon::GPU& gpu); + SoftRenderer(melonDS::GPU& gpu); ~SoftRenderer() override {} void DrawScanline(u32 line, Unit* unit) override; void DrawSprites(u32 line, Unit* unit) override; void VBlankEnd(Unit* unitA, Unit* unitB) override; private: - Melon::GPU& GPU; + melonDS::GPU& GPU; alignas(8) u32 BGOBJLine[256*3]; u32* _3DLine; @@ -84,3 +83,5 @@ private: }; } + +} \ No newline at end of file diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 80c5d468..b00e6fd2 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -24,6 +24,8 @@ #include "FIFO.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -98,9 +100,6 @@ using Platform::LogLevel; // * additionally, some commands (BEGIN, LIGHT_VECTOR, BOXTEST) stall the polygon pipeline -namespace GPU3D -{ - const u8 CmdNumParams[256] = { // 0x00 diff --git a/src/GPU3D.h b/src/GPU3D.h index b8a2d55d..f543ff86 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -25,13 +25,9 @@ #include "Savestate.h" #include "FIFO.h" -namespace Melon +namespace melonDS { struct RenderSettings; -} - -namespace GPU3D -{ struct Vertex { @@ -340,7 +336,7 @@ public: // are more detailed "traits" that we can ask of the Renderer3D type const bool Accelerated; - virtual void SetRenderSettings(const Melon::RenderSettings& settings) noexcept = 0; + virtual void SetRenderSettings(const RenderSettings& settings) noexcept = 0; virtual void VCount144() {}; diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 629ec157..fb9f2461 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -25,7 +25,7 @@ #include "GPU.h" #include "GPU3D_OpenGL_shaders.h" -namespace GPU3D +namespace melonDS { bool GLRenderer::BuildRenderShader(u32 flags, const char* vs, const char* fs) @@ -97,14 +97,14 @@ void SetupDefaultTexParams(GLuint tex) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } -GLRenderer::GLRenderer(Melon::GPU& gpu) noexcept : Renderer3D(true), GPU(gpu) +GLRenderer::GLRenderer(melonDS::GPU& gpu) noexcept : Renderer3D(true), GPU(gpu) { // 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. } -std::unique_ptr GLRenderer::New(Melon::GPU& gpu) noexcept +std::unique_ptr GLRenderer::New(melonDS::GPU& gpu) noexcept { assert(glEnable != nullptr); @@ -329,7 +329,7 @@ void GLRenderer::Reset() { } -void GLRenderer::SetRenderSettings(const Melon::RenderSettings& settings) noexcept +void GLRenderer::SetRenderSettings(const RenderSettings& settings) noexcept { int scale = settings.GL_ScaleFactor; diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index 3657a12a..0bc20d81 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -23,20 +23,17 @@ #include "OpenGLSupport.h" -namespace Melon +namespace melonDS { class GPU; -} -namespace GPU3D -{ class GLRenderer : public Renderer3D { public: virtual ~GLRenderer() override; virtual void Reset() override; - virtual void SetRenderSettings(const Melon::RenderSettings& settings) noexcept override; + virtual void SetRenderSettings(const RenderSettings& settings) noexcept override; virtual void VCount144() override {}; virtual void RenderFrame() override; @@ -45,10 +42,10 @@ public: void SetupAccelFrame(); void PrepareCaptureFrame(); - static std::unique_ptr New(Melon::GPU& gpu) noexcept; + static std::unique_ptr New(melonDS::GPU& gpu) noexcept; private: // Used by New() - GLRenderer(Melon::GPU& gpu) noexcept; + GLRenderer(melonDS::GPU& gpu) noexcept; // GL version requirements // * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS) @@ -68,7 +65,7 @@ private: u32 RenderKey; }; - Melon::GPU& GPU; + melonDS::GPU& GPU; RendererPolygon PolygonList[2048] {}; bool BuildRenderShader(u32 flags, const char* vs, const char* fs); diff --git a/src/GPU3D_OpenGL_shaders.h b/src/GPU3D_OpenGL_shaders.h index 81313812..13492b7f 100644 --- a/src/GPU3D_OpenGL_shaders.h +++ b/src/GPU3D_OpenGL_shaders.h @@ -21,7 +21,8 @@ #define kShaderHeader "#version 140" - +namespace melonDS +{ const char* kClearVS = kShaderHeader R"( in vec2 vPosition; @@ -802,5 +803,5 @@ void main() gl_FragDepth = fZ; } )"; - +} #endif // GPU3D_OPENGL_SHADERS_H diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 823f7520..c6b46312 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -24,8 +24,7 @@ #include "NDS.h" #include "GPU.h" - -namespace GPU3D +namespace melonDS { void RenderThreadFunc(); @@ -72,7 +71,7 @@ void SoftRenderer::SetupRenderThread() } -SoftRenderer::SoftRenderer(Melon::GPU& gpu) noexcept +SoftRenderer::SoftRenderer(melonDS::GPU& gpu) noexcept : Renderer3D(false), GPU(gpu) { Sema_RenderStart = Platform::Semaphore_Create(); @@ -105,7 +104,7 @@ void SoftRenderer::Reset() SetupRenderThread(); } -void SoftRenderer::SetRenderSettings(const Melon::RenderSettings& settings) noexcept +void SoftRenderer::SetRenderSettings(const RenderSettings& settings) noexcept { Threaded = settings.Soft_Threaded; SetupRenderThread(); diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index b1cfb2f1..9fb9a718 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -24,16 +24,16 @@ #include #include -namespace GPU3D +namespace melonDS { class SoftRenderer : public Renderer3D { public: - SoftRenderer(Melon::GPU& gpu) noexcept; + SoftRenderer(melonDS::GPU& gpu) noexcept; virtual ~SoftRenderer() override; virtual void Reset() override; - virtual void SetRenderSettings(const Melon::RenderSettings& settings) noexcept override; + virtual void SetRenderSettings(const RenderSettings& settings) noexcept override; virtual void VCount144() override; virtual void RenderFrame() override; @@ -451,7 +451,7 @@ private: }; - Melon::GPU& GPU; + melonDS::GPU& GPU; RendererPolygon PolygonList[2048]; void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha); u32 RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t); diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp index d0649bb1..2db38105 100644 --- a/src/GPU_OpenGL.cpp +++ b/src/GPU_OpenGL.cpp @@ -28,12 +28,12 @@ #include "OpenGLSupport.h" #include "GPU_OpenGL_shaders.h" -namespace Melon +namespace melonDS { using namespace OpenGL; -std::unique_ptr GLCompositor::New(Melon::GPU& gpu) noexcept +std::unique_ptr GLCompositor::New(melonDS::GPU& gpu) noexcept { assert(glBindAttribLocation != nullptr); @@ -53,7 +53,7 @@ std::unique_ptr GLCompositor::New(Melon::GPU& gpu) noexcept return std::unique_ptr(new GLCompositor(CompShader, gpu)); } -GLCompositor::GLCompositor(std::array compShader, Melon::GPU& gpu) noexcept : CompShader(compShader), GPU(gpu) +GLCompositor::GLCompositor(std::array compShader, melonDS::GPU& gpu) noexcept : CompShader(compShader), GPU(gpu) { CompScaleLoc = glGetUniformLocation(CompShader[2], "u3DScale"); Comp3DXPosLoc = glGetUniformLocation(CompShader[2], "u3DXPos"); @@ -218,7 +218,7 @@ void GLCompositor::RenderFrame() } glActiveTexture(GL_TEXTURE1); - reinterpret_cast(GPU.GPU3D.GetCurrentRenderer())->SetupAccelFrame(); + reinterpret_cast(GPU.GPU3D.GetCurrentRenderer())->SetupAccelFrame(); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); glBindVertexArray(CompVertexArrayID); diff --git a/src/GPU_OpenGL.h b/src/GPU_OpenGL.h index 3763b6b4..68462a9a 100644 --- a/src/GPU_OpenGL.h +++ b/src/GPU_OpenGL.h @@ -23,7 +23,7 @@ #include #include -namespace Melon +namespace melonDS { class GPU; struct RenderSettings; @@ -31,7 +31,7 @@ struct RenderSettings; class GLCompositor { public: - static std::unique_ptr New(Melon::GPU& gpu) noexcept; + static std::unique_ptr New(melonDS::GPU& gpu) noexcept; GLCompositor(const GLCompositor&) = delete; GLCompositor& operator=(const GLCompositor&) = delete; ~GLCompositor(); @@ -44,8 +44,8 @@ public: void RenderFrame(); void BindOutputTexture(int buf); private: - GLCompositor(std::array CompShader, Melon::GPU& gpu) noexcept; - Melon::GPU& GPU; + GLCompositor(std::array CompShader, melonDS::GPU& gpu) noexcept; + melonDS::GPU& GPU; int Scale; int ScreenH, ScreenW; diff --git a/src/GPU_OpenGL_shaders.h b/src/GPU_OpenGL_shaders.h index 9a7d6aba..a8c5b951 100644 --- a/src/GPU_OpenGL_shaders.h +++ b/src/GPU_OpenGL_shaders.h @@ -19,6 +19,8 @@ #ifndef GPU_OPENGL_SHADERS_H #define GPU_OPENGL_SHADERS_H +namespace melonDS +{ const char* kCompositorVS = R"(#version 140 in vec2 vPosition; @@ -866,5 +868,6 @@ void main() +} #endif // GPU_OPENGL_SHADERS_H diff --git a/src/JitBlock.h b/src/JitBlock.h index abd435be..6a187b27 100644 --- a/src/JitBlock.h +++ b/src/JitBlock.h @@ -22,7 +22,7 @@ #include "types.h" #include "TinyVector.h" -namespace ARMJIT +namespace melonDS { typedef void (*JitBlockEntry)(); diff --git a/src/NDS.cpp b/src/NDS.cpp index a216fed2..576eb9cd 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -43,6 +43,8 @@ #include "ARMJIT.h" #include "ARMJIT_Memory.h" +namespace melonDS +{ using namespace Platform; namespace NDS @@ -182,8 +184,8 @@ class RTC* RTC; class Wifi* Wifi; std::unique_ptr NDSCartSlot; std::unique_ptr GBACartSlot; -std::unique_ptr GPU; -std::unique_ptr JIT; +std::unique_ptr GPU; +std::unique_ptr JIT; class AREngine* AREngine; bool Running; @@ -203,8 +205,8 @@ bool Init() RegisterEventFunc(Event_Div, 0, DivDone); RegisterEventFunc(Event_Sqrt, 0, SqrtDone); - JIT = std::make_unique(); - GPU = std::make_unique(*JIT); + JIT = std::make_unique(); + GPU = std::make_unique(*JIT); MainRAM = JIT->Memory.GetMainRAM(); SharedWRAM = JIT->Memory.GetSharedWRAM(); @@ -4462,3 +4464,5 @@ void ARM7IOWrite32(u32 addr, u32 val) } } + +} \ No newline at end of file diff --git a/src/NDS.h b/src/NDS.h index a1155ba0..7bfad625 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -34,22 +34,16 @@ // with this enabled, to make sure it doesn't desync //#define DEBUG_CHECK_DESYNC +namespace melonDS +{ class SPU; class SPIHost; class RTC; class Wifi; class AREngine; - -namespace Melon -{ class GPU; -} - -namespace ARMJIT -{ class ARMJIT; -} namespace NDS { @@ -273,8 +267,8 @@ extern class RTC* RTC; extern class Wifi* Wifi; extern std::unique_ptr NDSCartSlot; extern std::unique_ptr GBACartSlot; -extern std::unique_ptr GPU; -extern std::unique_ptr JIT; +extern std::unique_ptr GPU; +extern std::unique_ptr JIT; extern class AREngine* AREngine; const u32 ARM7WRAMSize = 0x10000; @@ -394,4 +388,5 @@ void ARM7IOWrite32(u32 addr, u32 val); } +} #endif // NDS_H diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index bf2b7913..77262a70 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -27,6 +27,8 @@ #include "melonDLDI.h" #include "xxhash/xxhash.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -2068,3 +2070,5 @@ void NDSCartSlot::WriteSPIData(u8 val) noexcept } } + +} \ No newline at end of file diff --git a/src/NDSCart.h b/src/NDSCart.h index 33a17bba..18d1fb6d 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -30,7 +30,7 @@ #include "FATStorage.h" #include "ROMList.h" -namespace NDSCart +namespace melonDS::NDSCart { enum CartType diff --git a/src/NDS_Header.h b/src/NDS_Header.h index 626f80cb..de75f7c7 100644 --- a/src/NDS_Header.h +++ b/src/NDS_Header.h @@ -22,6 +22,8 @@ #include #include "types.h" +namespace melonDS +{ /// Set to indicate the console regions that a ROM (including DSiWare) /// can be played on. enum RegionMask : u32 @@ -242,5 +244,6 @@ struct NDSBanner static_assert(sizeof(NDSBanner) == 9152, "NDSBanner is not 9152 bytes!"); +} #endif //NDS_HEADER_H diff --git a/src/NonStupidBitfield.h b/src/NonStupidBitfield.h index a4fe7ec8..eb5e1f2b 100644 --- a/src/NonStupidBitfield.h +++ b/src/NonStupidBitfield.h @@ -29,6 +29,8 @@ // like std::bitset but less stupid and optimised for // our use case (keeping track of memory invalidations) +namespace melonDS +{ template struct NonStupidBitField { @@ -203,5 +205,6 @@ struct NonStupidBitField } }; +} #endif diff --git a/src/OpenGLSupport.cpp b/src/OpenGLSupport.cpp index 4962b4c1..0eb05c53 100644 --- a/src/OpenGLSupport.cpp +++ b/src/OpenGLSupport.cpp @@ -18,6 +18,9 @@ #include "OpenGLSupport.h" +namespace melonDS +{ + using Platform::Log; using Platform::LogLevel; @@ -142,3 +145,5 @@ void UseShaderProgram(GLuint* ids) } } + +} \ No newline at end of file diff --git a/src/OpenGLSupport.h b/src/OpenGLSupport.h index ed75f386..ee5b5043 100644 --- a/src/OpenGLSupport.h +++ b/src/OpenGLSupport.h @@ -25,8 +25,7 @@ #include "Platform.h" #include "PlatformOGL.h" - -namespace OpenGL +namespace melonDS::OpenGL { bool BuildShaderProgram(const char* vs, const char* fs, GLuint* ids, const char* name); diff --git a/src/Platform.h b/src/Platform.h index de1ca8b2..f8b0453c 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -24,6 +24,8 @@ #include #include +namespace melonDS +{ class Firmware; namespace Platform @@ -394,4 +396,5 @@ void DynamicLibrary_Unload(DynamicLibrary* lib); void* DynamicLibrary_LoadFunction(DynamicLibrary* lib, const char* name); } +} #endif // PLATFORM_H diff --git a/src/ROMList.cpp b/src/ROMList.cpp index 564f4a40..3ff771f8 100644 --- a/src/ROMList.cpp +++ b/src/ROMList.cpp @@ -18,6 +18,8 @@ #include "ROMList.h" +namespace melonDS +{ const ROMListEntry ROMList[] = { {0x41464141, 0x00800000, 0x00000004}, @@ -6801,3 +6803,5 @@ const ROMListEntry ROMList[] = }; const size_t ROMListEntryCount = sizeof(ROMList) / sizeof(ROMListEntry); + +} \ No newline at end of file diff --git a/src/ROMList.h b/src/ROMList.h index 11db1773..82ee0ccf 100644 --- a/src/ROMList.h +++ b/src/ROMList.h @@ -23,6 +23,8 @@ #include "types.h" +namespace melonDS +{ struct ROMListEntry { u32 GameCode; @@ -36,4 +38,5 @@ extern const ROMListEntry ROMList[]; /// The number of elements in \c ROMList. extern const size_t ROMListEntryCount; +} #endif // ROMLIST_H diff --git a/src/RTC.cpp b/src/RTC.cpp index d451585b..747348ce 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -21,6 +21,8 @@ #include "RTC.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -940,3 +942,5 @@ void RTC::Write(u16 val, bool byte) else IO = (IO & 0x0001) | (val & 0xFFFE); } + +} \ No newline at end of file diff --git a/src/RTC.h b/src/RTC.h index 313d2377..45d0f26b 100644 --- a/src/RTC.h +++ b/src/RTC.h @@ -22,6 +22,8 @@ #include "types.h" #include "Savestate.h" +namespace melonDS +{ class RTC { public: @@ -113,4 +115,5 @@ private: void ByteIn(u8 val); }; +} #endif diff --git a/src/SPI.cpp b/src/SPI.cpp index a95cd1c2..3ded7bcc 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -27,6 +27,8 @@ #include "DSi_SPI_TSC.h" #include "Platform.h" +namespace melonDS +{ using namespace Platform; @@ -641,3 +643,5 @@ void SPIHost::WriteData(u8 val) u32 delay = 8 * (8 << (Cnt & 0x3)); NDS::ScheduleEvent(NDS::Event_SPITransfer, false, delay, 0, 0); } + +} \ No newline at end of file diff --git a/src/SPI.h b/src/SPI.h index bf665d90..c889fb5e 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -28,6 +28,8 @@ #include "Savestate.h" #include "SPI_Firmware.h" +namespace melonDS +{ enum { SPIDevice_PowerMan = 0, @@ -164,4 +166,5 @@ private: SPIDevice* Devices[3]; }; +} #endif diff --git a/src/SPI_Firmware.cpp b/src/SPI_Firmware.cpp index 55071453..89c8fa61 100644 --- a/src/SPI_Firmware.cpp +++ b/src/SPI_Firmware.cpp @@ -20,6 +20,8 @@ #include "SPI.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -404,3 +406,5 @@ void Firmware::UpdateChecksums() u.UpdateChecksum(); } } + +} \ No newline at end of file diff --git a/src/SPI_Firmware.h b/src/SPI_Firmware.h index a264a6cb..c8ca25c3 100644 --- a/src/SPI_Firmware.h +++ b/src/SPI_Firmware.h @@ -24,6 +24,8 @@ #include "types.h" #include "Platform.h" +namespace melonDS +{ u16 CRC16(const u8* data, u32 len, u32 start); @@ -563,4 +565,5 @@ private: u32 FirmwareMask; }; +} #endif //MELONDS_SPI_FIRMWARE_H diff --git a/src/SPU.cpp b/src/SPU.cpp index 89b7fb3b..a5570d66 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -24,6 +24,8 @@ #include "DSi.h" #include "SPU.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -1208,3 +1210,5 @@ void SPU::Write32(u32 addr, u32 val) } } } + +} \ No newline at end of file diff --git a/src/SPU.h b/src/SPU.h index 407a3f62..144c19ba 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -22,6 +22,8 @@ #include "Savestate.h" #include "Platform.h" +namespace melonDS +{ class SPU; class SPUChannel @@ -257,4 +259,5 @@ private: SPUCaptureUnit* Capture[2]; }; +} #endif // SPU_H diff --git a/src/Savestate.cpp b/src/Savestate.cpp index 77c2e621..6d6a9a47 100644 --- a/src/Savestate.cpp +++ b/src/Savestate.cpp @@ -22,6 +22,8 @@ #include "Savestate.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -379,3 +381,5 @@ u32 Savestate::FindSection(const char* magic) const Log(LogLevel::Error, "savestate: section %s not found. blarg\n", magic); return NO_SECTION; } + +} \ No newline at end of file diff --git a/src/Savestate.h b/src/Savestate.h index ede3963c..236aa643 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -27,6 +27,8 @@ #define SAVESTATE_MAJOR 11 #define SAVESTATE_MINOR 1 +namespace melonDS +{ class Savestate { public: @@ -117,5 +119,6 @@ private: bool buffer_owned; bool finished; }; +} #endif // SAVESTATE_H diff --git a/src/TinyVector.h b/src/TinyVector.h index 63e7cafa..1904f2ad 100644 --- a/src/TinyVector.h +++ b/src/TinyVector.h @@ -23,7 +23,7 @@ #include #include "types.h" -namespace ARMJIT +namespace melonDS { /* TinyVector diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 1486b409..1441d1dd 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -24,6 +24,8 @@ #include "WifiAP.h" #include "Platform.h" +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -2265,3 +2267,5 @@ u8* Wifi::GetBSSID() { return (u8*)&IOPORT(W_BSSID0); } + +} \ No newline at end of file diff --git a/src/Wifi.h b/src/Wifi.h index 5a4f839e..c47a8833 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -21,6 +21,8 @@ #include "Savestate.h" +namespace melonDS +{ class WifiAP; class Wifi @@ -285,4 +287,5 @@ private: void RFTransfer_Type3(); }; +} #endif diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index 50100c50..efc34a5c 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -27,6 +27,8 @@ #include #endif +namespace melonDS +{ using Platform::Log; using Platform::LogLevel; @@ -401,3 +403,5 @@ int WifiAP::RecvPacket(u8* data) return 0; } + +} \ No newline at end of file diff --git a/src/WifiAP.h b/src/WifiAP.h index 34f71e7c..5d966687 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -21,6 +21,8 @@ #include "types.h" +namespace melonDS +{ class Wifi; class WifiAP @@ -61,4 +63,5 @@ private: int HandleManagementFrame(u8* data, int len); }; +} #endif diff --git a/src/debug/GdbArch.h b/src/debug/GdbArch.h index 45f1c1b2..1d45fb0b 100644 --- a/src/debug/GdbArch.h +++ b/src/debug/GdbArch.h @@ -5,6 +5,7 @@ namespace Gdb { +using namespace melonDS; enum class Register : int { r0, diff --git a/src/debug/GdbCmds.cpp b/src/debug/GdbCmds.cpp index 057502f2..c4706138 100644 --- a/src/debug/GdbCmds.cpp +++ b/src/debug/GdbCmds.cpp @@ -8,6 +8,7 @@ #include "GdbProto.h" +using namespace melonDS; using Platform::Log; using Platform::LogLevel; diff --git a/src/debug/GdbProto.cpp b/src/debug/GdbProto.cpp index dc803649..ebdf3be5 100644 --- a/src/debug/GdbProto.cpp +++ b/src/debug/GdbProto.cpp @@ -24,6 +24,7 @@ #include "GdbProto.h" +using namespace melonDS; using Platform::Log; using Platform::LogLevel; diff --git a/src/debug/GdbProto.h b/src/debug/GdbProto.h index ef8bdec9..68122f06 100644 --- a/src/debug/GdbProto.h +++ b/src/debug/GdbProto.h @@ -13,6 +13,7 @@ namespace Gdb { +using namespace melonDS; constexpr int GDBPROTO_BUFFER_CAPACITY = 1024+128; extern u8 Cmdbuf[GDBPROTO_BUFFER_CAPACITY]; diff --git a/src/debug/GdbStub.cpp b/src/debug/GdbStub.cpp index d756ce44..099f27f1 100644 --- a/src/debug/GdbStub.cpp +++ b/src/debug/GdbStub.cpp @@ -25,6 +25,7 @@ #include "../Platform.h" #include "GdbProto.h" +using namespace melonDS; using Platform::Log; using Platform::LogLevel; diff --git a/src/debug/GdbStub.h b/src/debug/GdbStub.h index b3bdab72..ace07bf6 100644 --- a/src/debug/GdbStub.h +++ b/src/debug/GdbStub.h @@ -13,6 +13,7 @@ namespace Gdb { +using namespace melonDS; enum class TgtStatus { NoEvent, diff --git a/src/debug/hexutil.h b/src/debug/hexutil.h index 9eb4ad22..63d1deb7 100644 --- a/src/debug/hexutil.h +++ b/src/debug/hexutil.h @@ -9,6 +9,8 @@ extern "C" #include +namespace melonDS +{ inline static uint8_t hex2nyb(uint8_t v) { if (v >= '0' && v <= '9') return v - '0'; @@ -71,5 +73,6 @@ inline static uint32_t unhex32(const uint8_t* src) } #endif +} #endif diff --git a/src/dolphin/Arm64Emitter.h b/src/dolphin/Arm64Emitter.h index 0b066ded..0fe4c919 100644 --- a/src/dolphin/Arm64Emitter.h +++ b/src/dolphin/Arm64Emitter.h @@ -14,6 +14,8 @@ namespace Arm64Gen { +using namespace melonDS; +using namespace Common; // X30 serves a dual purpose as a link register // Encoded as // Types: diff --git a/src/dolphin/ArmCommon.h b/src/dolphin/ArmCommon.h index 6d82e9d7..cae2ecb9 100644 --- a/src/dolphin/ArmCommon.h +++ b/src/dolphin/ArmCommon.h @@ -24,4 +24,4 @@ enum CCFlags CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same CC_LO = CC_CC, // Alias of CC_CC Unsigned lower }; -const u32 NO_COND = 0xE0000000; +const melonDS::u32 NO_COND = 0xE0000000; diff --git a/src/dolphin/BitSet.h b/src/dolphin/BitSet.h index 09cc1ce6..424364eb 100644 --- a/src/dolphin/BitSet.h +++ b/src/dolphin/BitSet.h @@ -10,6 +10,7 @@ namespace Common { +using namespace melonDS; #if defined(__GNUC__) || defined(__clang__) __attribute((always_inline)) static constexpr int CountSetBits(u8 val) { @@ -218,9 +219,10 @@ public: constexpr Iterator end() const { return Iterator(m_val, -1); } IntTy m_val; }; + +using BitSet8 = BitSet; +using BitSet16 = BitSet; +using BitSet32 = BitSet; +using BitSet64 = BitSet; } // namespace Common -using BitSet8 = Common::BitSet; -using BitSet16 = Common::BitSet; -using BitSet32 = Common::BitSet; -using BitSet64 = Common::BitSet; diff --git a/src/dolphin/MathUtil.h b/src/dolphin/MathUtil.h index b1dbbaec..800a3275 100644 --- a/src/dolphin/MathUtil.h +++ b/src/dolphin/MathUtil.h @@ -38,7 +38,7 @@ constexpr bool IsPow2(T imm) return imm > 0 && (imm & (imm - 1)) == 0; } -constexpr u32 NextPowerOf2(u32 value) +constexpr melonDS::u32 NextPowerOf2(melonDS::u32 value) { --value; value |= value >> 1; @@ -99,7 +99,7 @@ struct Rectangle float MathFloatVectorSum(const std::vector&); // Rounds down. 0 -> undefined -inline int IntLog2(u64 val) +inline int IntLog2(melonDS::u64 val) { #if defined(__GNUC__) return 63 - __builtin_clzll(val); diff --git a/src/dolphin/x64ABI.cpp b/src/dolphin/x64ABI.cpp index d86a1589..d85168a1 100644 --- a/src/dolphin/x64ABI.cpp +++ b/src/dolphin/x64ABI.cpp @@ -6,8 +6,9 @@ #include "x64ABI.h" #include "x64Emitter.h" -using namespace Gen; - +namespace Gen +{ +using namespace Common; // Shared code between Win64 and Unix64 void XEmitter::ABI_CalculateFrameSize(BitSet32 mask, size_t rsp_alignment, size_t needed_frame_size, @@ -117,3 +118,4 @@ void XEmitter::MOVTwo(int bits, Gen::X64Reg dst1, Gen::X64Reg src1, s32 offset1, ADD(bits, R(dst1), Imm32(offset1)); } } +} \ No newline at end of file diff --git a/src/dolphin/x64CPUDetect.cpp b/src/dolphin/x64CPUDetect.cpp index 49b51c9d..23390486 100644 --- a/src/dolphin/x64CPUDetect.cpp +++ b/src/dolphin/x64CPUDetect.cpp @@ -17,6 +17,8 @@ #include #endif +using namespace melonDS; + static inline void __cpuidex(int info[4], int function_id, int subfunction_id) { #ifdef __FreeBSD__ diff --git a/src/dolphin/x64Emitter.cpp b/src/dolphin/x64Emitter.cpp index fd90ba7f..a4543afd 100644 --- a/src/dolphin/x64Emitter.cpp +++ b/src/dolphin/x64Emitter.cpp @@ -12,6 +12,8 @@ #include "Compat.h" #include "CommonFuncs.h" +using namespace melonDS; + namespace Gen { // TODO(ector): Add EAX special casing, for ever so slightly smaller code. diff --git a/src/dolphin/x64Emitter.h b/src/dolphin/x64Emitter.h index 87996003..36603218 100644 --- a/src/dolphin/x64Emitter.h +++ b/src/dolphin/x64Emitter.h @@ -19,6 +19,8 @@ namespace Gen { +using namespace melonDS; +using namespace Common; enum CCFlags { CC_O = 0, diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 375769d2..8d94b37d 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -26,6 +26,7 @@ namespace Frontend { +using namespace melonDS; enum ScreenLayout { diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index 278a5a00..4a1d089e 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -27,6 +27,7 @@ #include "mic_blow.h" +using namespace melonDS; namespace Frontend { diff --git a/src/frontend/duckstation/gl/context.h b/src/frontend/duckstation/gl/context.h index 41d8a2cd..06e0bc5e 100644 --- a/src/frontend/duckstation/gl/context.h +++ b/src/frontend/duckstation/gl/context.h @@ -6,6 +6,7 @@ #include namespace GL { +using namespace melonDS; class Context { public: diff --git a/src/frontend/duckstation/gl/context_agl.h b/src/frontend/duckstation/gl/context_agl.h index 459bf2fd..4ae5202d 100644 --- a/src/frontend/duckstation/gl/context_agl.h +++ b/src/frontend/duckstation/gl/context_agl.h @@ -13,6 +13,7 @@ struct NSView; namespace GL { +using namespace melonDS; class ContextAGL final : public Context { public: diff --git a/src/frontend/duckstation/gl/context_wgl.cpp b/src/frontend/duckstation/gl/context_wgl.cpp index 03c18e87..09f994c9 100644 --- a/src/frontend/duckstation/gl/context_wgl.cpp +++ b/src/frontend/duckstation/gl/context_wgl.cpp @@ -3,6 +3,7 @@ #include "../log.h" #include "../scoped_guard.h" #include "loader.h" +using namespace melonDS; Log_SetChannel(GL::ContextWGL); // TODO: get rid of this diff --git a/src/frontend/duckstation/gl/x11_window.h b/src/frontend/duckstation/gl/x11_window.h index aff38b7b..e216a058 100644 --- a/src/frontend/duckstation/gl/x11_window.h +++ b/src/frontend/duckstation/gl/x11_window.h @@ -4,6 +4,7 @@ #include namespace GL { +using namespace melonDS; class X11Window { public: diff --git a/src/frontend/duckstation/window_info.h b/src/frontend/duckstation/window_info.h index 44912caa..43526172 100644 --- a/src/frontend/duckstation/window_info.h +++ b/src/frontend/duckstation/window_info.h @@ -28,8 +28,8 @@ struct WindowInfo Type type = Type::Surfaceless; void* display_connection = nullptr; void* window_handle = nullptr; - u32 surface_width = 0; - u32 surface_height = 0; + melonDS::u32 surface_width = 0; + melonDS::u32 surface_height = 0; float surface_refresh_rate = 0.0f; float surface_scale = 1.0f; SurfaceFormat surface_format = SurfaceFormat::RGB8; diff --git a/src/frontend/mic_blow.h b/src/frontend/mic_blow.h index 20d5017c..f8dbc107 100644 --- a/src/frontend/mic_blow.h +++ b/src/frontend/mic_blow.h @@ -19,7 +19,9 @@ #ifndef MIC_BLOW_H #define MIC_BLOW_H -const u16 mic_blow[] = +#include "types.h" + +const melonDS::u16 mic_blow[] = { 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFE, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFC, 0xFFFF, 0xFFFD, 0xFFFF, 0xFFFC, 0xFFFF, diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index 6f4b1348..7d1eca9c 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -19,6 +19,7 @@ #include "ArchiveUtil.h" #include "Platform.h" +using namespace melonDS; using Platform::Log; using Platform::LogLevel; diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index 14ff9968..eaffb0dd 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -35,6 +35,7 @@ namespace Archive { +using namespace melonDS; QVector ListArchive(QString path); s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize); //QVector ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer); diff --git a/src/frontend/qt_sdl/AudioInOut.cpp b/src/frontend/qt_sdl/AudioInOut.cpp index 7faac9f3..90708b20 100644 --- a/src/frontend/qt_sdl/AudioInOut.cpp +++ b/src/frontend/qt_sdl/AudioInOut.cpp @@ -28,6 +28,7 @@ #include "Input.h" #include "main.h" +using namespace melonDS; namespace AudioInOut { diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index 386519bf..cb42e641 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -31,6 +31,7 @@ #include "ui_AudioSettingsDialog.h" +using namespace melonDS; AudioSettingsDialog* AudioSettingsDialog::currentDlg = nullptr; extern std::string EmuDirectory; diff --git a/src/frontend/qt_sdl/CLI.cpp b/src/frontend/qt_sdl/CLI.cpp index dce8ab67..299ce65b 100644 --- a/src/frontend/qt_sdl/CLI.cpp +++ b/src/frontend/qt_sdl/CLI.cpp @@ -27,8 +27,8 @@ #include "CLI.h" #include "Platform.h" -using Platform::Log; -using Platform::LogLevel; +using melonDS::Platform::Log; +using melonDS::Platform::LogLevel; namespace CLI { diff --git a/src/frontend/qt_sdl/CameraManager.cpp b/src/frontend/qt_sdl/CameraManager.cpp index cc532ff1..cc575d2c 100644 --- a/src/frontend/qt_sdl/CameraManager.cpp +++ b/src/frontend/qt_sdl/CameraManager.cpp @@ -21,6 +21,7 @@ #include "CameraManager.h" #include "Config.h" +using namespace melonDS; #if QT_VERSION >= 0x060000 diff --git a/src/frontend/qt_sdl/CameraManager.h b/src/frontend/qt_sdl/CameraManager.h index 91e73fbe..882b051a 100644 --- a/src/frontend/qt_sdl/CameraManager.h +++ b/src/frontend/qt_sdl/CameraManager.h @@ -89,11 +89,11 @@ public: void setXFlip(bool flip); - void captureFrame(u32* frame, int width, int height, bool yuv); + void captureFrame(melonDS::u32* frame, int width, int height, bool yuv); - void feedFrame(u32* frame, int width, int height, bool yuv); - void feedFrame_UYVY(u32* frame, int width, int height); - void feedFrame_NV12(u8* planeY, u8* planeUV, int width, int height); + void feedFrame(melonDS::u32* frame, int width, int height, bool yuv); + void feedFrame_UYVY(melonDS::u32* frame, int width, int height); + void feedFrame_NV12(melonDS::u8* planeY, melonDS::u8* planeUV, int width, int height); signals: void camStartSignal(); @@ -120,15 +120,15 @@ private: int frameWidth, frameHeight; bool frameFormatYUV; - u32* frameBuffer; - u32* tempFrameBuffer; + melonDS::u32* frameBuffer; + melonDS::u32* tempFrameBuffer; QMutex frameMutex; bool xFlip; - void copyFrame_Straight(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip, bool yuv); - void copyFrame_RGBtoYUV(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip); - void copyFrame_YUVtoRGB(u32* src, int swidth, int sheight, u32* dst, int dwidth, int dheight, bool xflip); + void copyFrame_Straight(melonDS::u32* src, int swidth, int sheight, melonDS::u32* dst, int dwidth, int dheight, bool xflip, bool yuv); + void copyFrame_RGBtoYUV(melonDS::u32* src, int swidth, int sheight, melonDS::u32* dst, int dwidth, int dheight, bool xflip); + void copyFrame_YUVtoRGB(melonDS::u32* src, int swidth, int sheight, melonDS::u32* dst, int dwidth, int dheight, bool xflip); }; #endif // CAMERAMANAGER_H diff --git a/src/frontend/qt_sdl/CameraSettingsDialog.cpp b/src/frontend/qt_sdl/CameraSettingsDialog.cpp index 1da0318f..cf404173 100644 --- a/src/frontend/qt_sdl/CameraSettingsDialog.cpp +++ b/src/frontend/qt_sdl/CameraSettingsDialog.cpp @@ -26,6 +26,7 @@ #include "CameraSettingsDialog.h" #include "ui_CameraSettingsDialog.h" +using namespace melonDS; CameraSettingsDialog* CameraSettingsDialog::currentDlg = nullptr; diff --git a/src/frontend/qt_sdl/CheatsDialog.cpp b/src/frontend/qt_sdl/CheatsDialog.cpp index ceaf8032..df687230 100644 --- a/src/frontend/qt_sdl/CheatsDialog.cpp +++ b/src/frontend/qt_sdl/CheatsDialog.cpp @@ -29,6 +29,7 @@ #include "CheatsDialog.h" #include "ui_CheatsDialog.h" +using namespace melonDS; using Platform::Log; using Platform::LogLevel; diff --git a/src/frontend/qt_sdl/CheatsDialog.h b/src/frontend/qt_sdl/CheatsDialog.h index 8af75be9..ab2ac309 100644 --- a/src/frontend/qt_sdl/CheatsDialog.h +++ b/src/frontend/qt_sdl/CheatsDialog.h @@ -27,8 +27,8 @@ #include "ARCodeFile.h" -Q_DECLARE_METATYPE(ARCodeList::iterator) -Q_DECLARE_METATYPE(ARCodeCatList::iterator) +Q_DECLARE_METATYPE(melonDS::ARCodeList::iterator) +Q_DECLARE_METATYPE(melonDS::ARCodeCatList::iterator) namespace Ui { class CheatsDialog; } class CheatsDialog; @@ -87,7 +87,7 @@ private slots: private: Ui::CheatsDialog* ui; - ARCodeFile* codeFile; + melonDS::ARCodeFile* codeFile; ARCodeChecker* codeChecker; }; diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 9c6aff84..b6fca7d6 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -26,6 +26,7 @@ namespace Config { +using namespace melonDS; int KeyMapping[12]; int JoyMapping[12]; diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index 48b380b6..0a834a65 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -29,7 +29,8 @@ #include "EmuSettingsDialog.h" #include "ui_EmuSettingsDialog.h" -using namespace Platform; +using namespace melonDS::Platform; +using namespace melonDS; EmuSettingsDialog* EmuSettingsDialog::currentDlg = nullptr; diff --git a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp index 5f76b08a..1ec2e8c4 100644 --- a/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp +++ b/src/frontend/qt_sdl/FirmwareSettingsDialog.cpp @@ -24,6 +24,8 @@ #include "FirmwareSettingsDialog.h" #include "ui_FirmwareSettingsDialog.h" +using namespace melonDS::Platform; +namespace Platform = melonDS::Platform; FirmwareSettingsDialog* FirmwareSettingsDialog::currentDlg = nullptr; diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index 1d39a0fb..c429cd36 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -22,6 +22,7 @@ #include "Input.h" #include "Config.h" +using namespace melonDS; namespace Input { diff --git a/src/frontend/qt_sdl/Input.h b/src/frontend/qt_sdl/Input.h index 04f7a1f1..366c9c39 100644 --- a/src/frontend/qt_sdl/Input.h +++ b/src/frontend/qt_sdl/Input.h @@ -24,6 +24,7 @@ namespace Input { +using namespace melonDS; extern int JoystickID; extern SDL_Joystick* Joystick; diff --git a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp index 6d651c9d..02a76bb7 100644 --- a/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp +++ b/src/frontend/qt_sdl/InputConfig/InputConfigDialog.cpp @@ -32,6 +32,7 @@ #include "ui_InputConfigDialog.h" +using namespace melonDS; InputConfigDialog* InputConfigDialog::currentDlg = nullptr; const int dskeyorder[12] = {0, 1, 10, 11, 5, 4, 6, 7, 9, 8, 2, 3}; diff --git a/src/frontend/qt_sdl/InputConfig/MapButton.h b/src/frontend/qt_sdl/InputConfig/MapButton.h index b35cc1ff..5d4fb3eb 100644 --- a/src/frontend/qt_sdl/InputConfig/MapButton.h +++ b/src/frontend/qt_sdl/InputConfig/MapButton.h @@ -52,7 +52,7 @@ protected: { if (!isChecked()) return QPushButton::keyPressEvent(event); - Platform::Log(Platform::Debug, "KEY PRESSED = %08X %08X | %08X %08X %08X\n", event->key(), (int)event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode()); + Log(melonDS::Platform::Debug, "KEY PRESSED = %08X %08X | %08X %08X %08X\n", event->key(), (int)event->modifiers(), event->nativeVirtualKey(), event->nativeModifiers(), event->nativeScanCode()); int key = event->key(); int mod = event->modifiers(); diff --git a/src/frontend/qt_sdl/LAN_PCap.cpp b/src/frontend/qt_sdl/LAN_PCap.cpp index e1cbc11a..4a1ebc35 100644 --- a/src/frontend/qt_sdl/LAN_PCap.cpp +++ b/src/frontend/qt_sdl/LAN_PCap.cpp @@ -41,6 +41,7 @@ #endif #endif +using namespace melonDS; using Platform::Log; using Platform::LogLevel; diff --git a/src/frontend/qt_sdl/LAN_PCap.h b/src/frontend/qt_sdl/LAN_PCap.h index fe6e0009..2e03d66a 100644 --- a/src/frontend/qt_sdl/LAN_PCap.h +++ b/src/frontend/qt_sdl/LAN_PCap.h @@ -24,6 +24,7 @@ namespace LAN_PCap { +using namespace melonDS; struct AdapterData { char DeviceName[128]; diff --git a/src/frontend/qt_sdl/LAN_Socket.cpp b/src/frontend/qt_sdl/LAN_Socket.cpp index e1c86633..e938af80 100644 --- a/src/frontend/qt_sdl/LAN_Socket.cpp +++ b/src/frontend/qt_sdl/LAN_Socket.cpp @@ -37,6 +37,7 @@ #include #endif +using namespace melonDS; namespace LAN_Socket { diff --git a/src/frontend/qt_sdl/LAN_Socket.h b/src/frontend/qt_sdl/LAN_Socket.h index 9b97301d..043e1330 100644 --- a/src/frontend/qt_sdl/LAN_Socket.h +++ b/src/frontend/qt_sdl/LAN_Socket.h @@ -23,6 +23,7 @@ namespace LAN_Socket { +using namespace melonDS; // diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp index 4b3a6a64..a0dfdf76 100644 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ b/src/frontend/qt_sdl/LocalMP.cpp @@ -38,6 +38,9 @@ #include "LocalMP.h" #include "Platform.h" +using namespace melonDS; +using namespace melonDS::Platform; + using Platform::Log; using Platform::LogLevel; diff --git a/src/frontend/qt_sdl/LocalMP.h b/src/frontend/qt_sdl/LocalMP.h index 07c6f43c..e7b4188a 100644 --- a/src/frontend/qt_sdl/LocalMP.h +++ b/src/frontend/qt_sdl/LocalMP.h @@ -24,6 +24,7 @@ namespace LocalMP { +using namespace melonDS; bool Init(); void DeInit(); diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp index 02ec1587..25072df7 100644 --- a/src/frontend/qt_sdl/OSD.cpp +++ b/src/frontend/qt_sdl/OSD.cpp @@ -32,6 +32,8 @@ #include "Config.h" +using namespace melonDS; + extern MainWindow* mainWindow; namespace OSD diff --git a/src/frontend/qt_sdl/OSD.h b/src/frontend/qt_sdl/OSD.h index b399fb42..64131d5b 100644 --- a/src/frontend/qt_sdl/OSD.h +++ b/src/frontend/qt_sdl/OSD.h @@ -19,9 +19,12 @@ #ifndef OSD_H #define OSD_H +#include "types.h" + namespace OSD { +using namespace melonDS; bool Init(bool openGL); void DeInit(); diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp index 1f1c35cd..1d698537 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.cpp +++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp @@ -27,6 +27,8 @@ #include "PathSettingsDialog.h" #include "ui_PathSettingsDialog.h" +using namespace melonDS::Platform; +namespace Platform = melonDS::Platform; PathSettingsDialog* PathSettingsDialog::currentDlg = nullptr; diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index df97590f..c2e2f47b 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -54,7 +54,7 @@ extern CameraManager* camManager[2]; void emuStop(); -namespace Platform +namespace melonDS::Platform { QSharedMemory* IPCBuffer = nullptr; diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp index 1d69988b..14ccd51c 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp @@ -30,6 +30,8 @@ #include +using namespace melonDS; + PowerManagementDialog* PowerManagementDialog::currentDlg = nullptr; PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PowerManagementDialog) diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h index 03025864..cd2954a1 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h @@ -67,7 +67,7 @@ private: bool inited; bool oldDSBatteryLevel; - u8 oldDSiBatteryLevel; + melonDS::u8 oldDSiBatteryLevel; bool oldDSiBatteryCharging; void updateDSBatteryLevelControls(); diff --git a/src/frontend/qt_sdl/RAMInfoDialog.cpp b/src/frontend/qt_sdl/RAMInfoDialog.cpp index b13ff022..b58662c8 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.cpp +++ b/src/frontend/qt_sdl/RAMInfoDialog.cpp @@ -21,6 +21,7 @@ #include "main.h" +using namespace melonDS; extern EmuThread* emuThread; s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType) diff --git a/src/frontend/qt_sdl/RAMInfoDialog.h b/src/frontend/qt_sdl/RAMInfoDialog.h index 33afd988..adc9b284 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.h +++ b/src/frontend/qt_sdl/RAMInfoDialog.h @@ -53,22 +53,22 @@ enum ramInfo_Previous }; -s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType); +melonDS::s32 GetMainRAMValue(const melonDS::u32& addr, const ramInfo_ByteType& byteType); struct ramInfo_RowData { - u32 Address; - s32 Value; - s32 Previous; + melonDS::u32 Address; + melonDS::s32 Value; + melonDS::s32 Previous; void Update(const ramInfo_ByteType& byteType) { Value = GetMainRAMValue(Address, byteType); } - void SetValue(const s32& value) + void SetValue(const melonDS::s32& value) { - NDS::MainRAM[Address&NDS::MainRAMMask] = (u32)value; + melonDS::NDS::MainRAM[Address&melonDS::NDS::MainRAMMask] = (melonDS::u32)value; Value = value; } }; @@ -99,7 +99,7 @@ public: currentDlg = nullptr; } - s32 SearchValue = 0; + melonDS::s32 SearchValue = 0; void ClearTableContents(); @@ -115,7 +115,7 @@ private slots: void OnSearchFinished(); void ShowRowsInTable(); - void SetProgressbarValue(const u32& value); + void SetProgressbarValue(const melonDS::u32& value); private: Ui::RAMInfoDialog* ui; @@ -132,7 +132,7 @@ public: explicit RAMSearchThread(RAMInfoDialog* dialog); ~RAMSearchThread() override; - void Start(const s32& searchValue, const ramInfoSTh_SearchMode& searchMode = ramInfoSTh_Default); + void Start(const melonDS::s32& searchValue, const ramInfoSTh_SearchMode& searchMode = ramInfoSTh_Default); void Start(const ramInfoSTh_SearchMode& searchMode); void SetSearchByteType(const ramInfo_ByteType& bytetype); @@ -148,14 +148,14 @@ private: bool SearchRunning = false; ramInfoSTh_SearchMode SearchMode; - s32 SearchValue; + melonDS::s32 SearchValue; ramInfo_ByteType SearchByteType = ramInfo_OneByte; std::vector* RowDataVector = nullptr; void ClearTableContents(); signals: - void SetProgressbarValue(const u32& value); + void SetProgressbarValue(const melonDS::u32& value); }; #endif // RAMINFODIALOG_H diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp index 34917d00..6a46b18e 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.cpp +++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp @@ -28,6 +28,8 @@ #include "Platform.h" #include "Config.h" +using namespace melonDS; + QString IntToHex(u64 num) { return ("0x" + QString::number(num, 16).toUpper()); diff --git a/src/frontend/qt_sdl/ROMInfoDialog.h b/src/frontend/qt_sdl/ROMInfoDialog.h index 630ac22d..7316e98a 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.h +++ b/src/frontend/qt_sdl/ROMInfoDialog.h @@ -69,7 +69,7 @@ private: QImage iconImage; QTimeLine* iconTimeline; - u32 animatedIconData[64][32*32] = {0}; + melonDS::u32 animatedIconData[64][32*32] = {0}; std::vector animatedIconImages; std::vector animatedSequence; }; diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 1c58055f..9584c381 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -51,7 +51,8 @@ using std::string; using std::tie; using std::unique_ptr; using std::wstring_convert; -using namespace Platform; +using namespace melonDS; +using namespace melonDS::Platform; namespace ROMManager { diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 6d957631..92285608 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -31,6 +31,7 @@ namespace ROMManager { +using namespace melonDS; extern SaveManager* NDSSave; extern SaveManager* GBASave; extern std::unique_ptr FirmwareSave; diff --git a/src/frontend/qt_sdl/SaveManager.cpp b/src/frontend/qt_sdl/SaveManager.cpp index 78d4b0ec..55279dca 100644 --- a/src/frontend/qt_sdl/SaveManager.cpp +++ b/src/frontend/qt_sdl/SaveManager.cpp @@ -22,7 +22,8 @@ #include "SaveManager.h" #include "Platform.h" -using namespace Platform; +using namespace melonDS; +using namespace melonDS::Platform; SaveManager::SaveManager(const std::string& path) : QThread() { diff --git a/src/frontend/qt_sdl/SaveManager.h b/src/frontend/qt_sdl/SaveManager.h index 6ffb4f86..d7132e69 100644 --- a/src/frontend/qt_sdl/SaveManager.h +++ b/src/frontend/qt_sdl/SaveManager.h @@ -41,31 +41,31 @@ public: std::string GetPath(); void SetPath(const std::string& path, bool reload); - void RequestFlush(const u8* savedata, u32 savelen, u32 writeoffset, u32 writelen); + void RequestFlush(const melonDS::u8* savedata, melonDS::u32 savelen, melonDS::u32 writeoffset, melonDS::u32 writelen); void CheckFlush(); bool NeedsFlush(); - void FlushSecondaryBuffer(u8* dst = nullptr, u32 dstLength = 0); + void FlushSecondaryBuffer(melonDS::u8* dst = nullptr, melonDS::u32 dstLength = 0); private: std::string Path; std::atomic_bool Running; - std::unique_ptr Buffer; - u32 Length; + std::unique_ptr Buffer; + melonDS::u32 Length; bool FlushRequested; QMutex* SecondaryBufferLock; - std::unique_ptr SecondaryBuffer; - u32 SecondaryBufferLength; + std::unique_ptr SecondaryBuffer; + melonDS::u32 SecondaryBufferLength; time_t TimeAtLastFlushRequest; // We keep versions in case the user closes the application before // a flush cycle is finished. - u32 PreviousFlushVersion; - u32 FlushVersion; + melonDS::u32 PreviousFlushVersion; + melonDS::u32 FlushVersion; }; #endif // SAVEMANAGER_H diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index eb6b594f..eda1bbce 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -30,7 +30,8 @@ #include "ui_TitleManagerDialog.h" #include "ui_TitleImportDialog.h" -using namespace Platform; +using namespace melonDS; +using namespace melonDS::Platform; std::unique_ptr TitleManagerDialog::nand = nullptr; TitleManagerDialog* TitleManagerDialog::currentDlg = nullptr; diff --git a/src/frontend/qt_sdl/TitleManagerDialog.h b/src/frontend/qt_sdl/TitleManagerDialog.h index 7209f96a..2e392ebf 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.h +++ b/src/frontend/qt_sdl/TitleManagerDialog.h @@ -46,10 +46,10 @@ class TitleManagerDialog : public QDialog Q_OBJECT public: - explicit TitleManagerDialog(QWidget* parent, DSi_NAND::NANDImage& image); + explicit TitleManagerDialog(QWidget* parent, melonDS::DSi_NAND::NANDImage& image); ~TitleManagerDialog(); - static std::unique_ptr nand; + static std::unique_ptr nand; static bool openNAND(); static void closeNAND(); @@ -94,17 +94,17 @@ private slots: void onExportTitleData(); private: - DSi_NAND::NANDMount nandmount; + melonDS::DSi_NAND::NANDMount nandmount; Ui::TitleManagerDialog* ui; QString importAppPath; - DSi_TMD::TitleMetadata importTmdData; + melonDS::DSi_TMD::TitleMetadata importTmdData; bool importReadOnly; QAction* actImportTitleData[3]; QAction* actExportTitleData[3]; - void createTitleItem(u32 category, u32 titleid); + void createTitleItem(melonDS::u32 category, melonDS::u32 titleid); }; class TitleImportDialog : public QDialog @@ -112,7 +112,7 @@ class TitleImportDialog : public QDialog Q_OBJECT public: - explicit TitleImportDialog(QWidget* parent, QString& apppath, const DSi_TMD::TitleMetadata* tmd, bool& readonly, DSi_NAND::NANDMount& nand); + explicit TitleImportDialog(QWidget* parent, QString& apppath, const melonDS::DSi_TMD::TitleMetadata* tmd, bool& readonly, melonDS::DSi_NAND::NANDMount& nand); ~TitleImportDialog(); private slots: @@ -125,7 +125,7 @@ private slots: private: Ui::TitleImportDialog* ui; - DSi_NAND::NANDMount& nandmount; + melonDS::DSi_NAND::NANDMount& nandmount; QButtonGroup* grpTmdSource; @@ -133,10 +133,10 @@ private: QNetworkReply* netreply; QString& appPath; - const DSi_TMD::TitleMetadata* tmdData; + const melonDS::DSi_TMD::TitleMetadata* tmdData; bool& readOnly; - u32 titleid[2]; + melonDS::u32 titleid[2]; }; #endif // TITLEMANAGERDIALOG_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index b820b508..42864986 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -105,7 +105,7 @@ #include "CLI.h" // TODO: uniform variable spelling - +using namespace melonDS; const QString NdsRomMimeType = "application/x-nintendo-ds-rom"; const QStringList NdsRomExtensions { ".nds", ".srl", ".dsi", ".ids" }; @@ -163,7 +163,7 @@ EmuThread* emuThread; int autoScreenSizing = 0; int videoRenderer; -Melon::RenderSettings videoSettings; +RenderSettings videoSettings; bool videoSettingsDirty; CameraManager* camManager[2]; diff --git a/src/melonDLDI.h b/src/melonDLDI.h index 1fe1e599..908a2f60 100644 --- a/src/melonDLDI.h +++ b/src/melonDLDI.h @@ -21,6 +21,8 @@ #include "types.h" +namespace melonDS +{ const u8 melonDLDI[] = { 0xED, 0xA5, 0x8D, 0xBF, 0x20, 0x43, 0x68, 0x69, 0x73, 0x68, 0x6D, 0x00, 0x01, 0x09, 0x00, 0x00, @@ -54,4 +56,5 @@ const u8 melonDLDI[] = 0x1E, 0xFF, 0x2F, 0xE1 }; +} #endif // MELONDLDI_H diff --git a/src/types.h b/src/types.h index 38556440..d37b2251 100644 --- a/src/types.h +++ b/src/types.h @@ -21,6 +21,8 @@ #include +namespace melonDS +{ typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; @@ -30,4 +32,5 @@ typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; +} #endif // TYPES_H From ad7b1a8c614cf82bbe5129c6ef5d740fb9dd1e5d Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Sat, 25 Nov 2023 12:40:07 -0500 Subject: [PATCH 046/157] only fill edges when translucent if blending is enabled (#1882) --- src/GPU3D_Soft.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index c6b46312..809bd593 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -747,7 +747,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) std::swap(zl, zr); // CHECKME: edge fill rules for swapped opaque shadow mask polygons - if ((polyalpha < 31) || (GPU.GPU3D.RenderDispCnt & (3<<4))) + if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -775,7 +775,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) rp->SlopeR.EdgeParams(&r_edgelen, &r_edgecov); // CHECKME: edge fill rules for unswapped opaque shadow mask polygons - if ((polyalpha < 31) || (GPU.GPU3D.RenderDispCnt & (3<<4))) + if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -979,9 +979,10 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) // * right edge is filled if slope > 1, or if the left edge = 0, but is never filled if it is < -1 // * left edge is filled if slope <= 1 // * the bottom-most pixel of negative x-major slopes are filled if they are next to a flat bottom edge - // edges are always filled if antialiasing/edgemarking are enabled or if the pixels are translucent + // edges are always filled if antialiasing/edgemarking are enabled, + // if the pixels are translucent and alpha blending is enabled, or if the polygon is wireframe // checkme: do swapped line polygons exist? - if ((polyalpha < 31) || wireframe || (GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5)))) + if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -1014,8 +1015,9 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) // * edges with slope = 0 are always filled // * the bottom-most pixel of negative x-major slopes are filled if they are next to a flat bottom edge // * edges are filled if both sides are identical and fully overlapping - // edges are always filled if antialiasing/edgemarking are enabled or if the pixels are translucent - if ((polyalpha < 31) || wireframe || (GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5)))) + // edges are always filled if antialiasing/edgemarking are enabled, + // if the pixels are translucent and alpha blending is enabled, or if the polygon is wireframe + if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; From c84cb174628c5a2e8e6cc0179e16de3eab47864a Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Sun, 26 Nov 2023 20:07:31 +0100 Subject: [PATCH 047/157] DSi_SD: implement command 17, 24 (#1877) --- src/DSi_SD.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 5174af33..54f03cea 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -951,6 +951,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) Host->SendResponse(CSR, true); return; + case 17: // read single block case 18: // read multiple blocks //printf("READ_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize); RWAddress = param; @@ -959,12 +960,14 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) RWAddress <<= 9; BlockSize = 512; } - RWCommand = 18; + if (cmd == 18) + RWCommand = 18; Host->SendResponse(CSR, true); RWAddress += ReadBlock(RWAddress); SetState(0x05); return; + case 24: // write single block case 25: // write multiple blocks //printf("WRITE_MULTIPLE_BLOCKS addr=%08X size=%08X\n", param, BlockSize); RWAddress = param; @@ -973,7 +976,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) RWAddress <<= 9; BlockSize = 512; } - RWCommand = 25; + if (cmd == 25) + RWCommand = 25; Host->SendResponse(CSR, true); RWAddress += WriteBlock(RWAddress); SetState(0x04); @@ -1098,4 +1102,4 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr) return len; } -} \ No newline at end of file +} From e973236203292637eb7bd009a4cfbd6fd785181f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 28 Nov 2023 17:16:41 -0500 Subject: [PATCH 048/157] Refactor `NDS` and `DSi` to be objects (#1893) * First crack at refactoring NDS and DSi into objects - Remove all global/`static` variables in `NDS` and related classes - Rely more on virtual dispatch when we need to pick methods at runtime - Pass `NDS&` or `DSi&` to its constituent components where necessary - Introduce some headers or move some definitions to break `#include` cycles * Refactor the frontend to accommodate the core's changes * Move up `SchedList`'s declaration - Move it to before the components are initialized so the `map`s inside are initialized - Fields in C++ are initialized in the order they're declared * Fix a crash when allocating memory * Fix JIT-free builds * Fix GDB-free builds * Fix Linux builds - Explicitly qualify some member types in NDS, since they share the same name as their classes * Remove an unnecessary template argument - This was causing the build to fail on macOS * Fix ARM and Android builds * Rename `Constants.h` to `MemConstants.h` * Add `NDS::IsRunning()` * Use an `#include` guard instead of `#pragma once` --- src/AREngine.cpp | 81 +- src/AREngine.h | 14 +- src/ARM.cpp | 345 ++- src/ARM.h | 199 +- src/ARMInterpreter_ALU.cpp | 5 +- src/ARMJIT.cpp | 38 +- src/ARMJIT.h | 37 +- src/ARMJIT_A64/ARMJIT_Branch.cpp | 7 +- src/ARMJIT_A64/ARMJIT_Compiler.cpp | 19 +- src/ARMJIT_A64/ARMJIT_Compiler.h | 8 +- src/ARMJIT_A64/ARMJIT_LoadStore.cpp | 43 +- src/ARMJIT_Memory.cpp | 336 +-- src/ARMJIT_Memory.h | 37 +- src/ARMJIT_x64/ARMJIT_Branch.cpp | 5 +- src/ARMJIT_x64/ARMJIT_Compiler.cpp | 17 +- src/ARMJIT_x64/ARMJIT_Compiler.h | 7 +- src/ARMJIT_x64/ARMJIT_LoadStore.cpp | 47 +- src/CP15.cpp | 28 +- src/DMA.cpp | 160 +- src/DMA.h | 13 +- src/DSi.cpp | 1010 +++++---- src/DSi.h | 183 +- src/DSi_AES.cpp | 22 +- src/DSi_AES.h | 4 +- src/DSi_Camera.cpp | 30 +- src/DSi_Camera.h | 16 +- src/DSi_DSP.cpp | 64 +- src/DSi_DSP.h | 4 +- src/DSi_I2C.cpp | 18 +- src/DSi_I2C.h | 10 +- src/DSi_NDMA.cpp | 62 +- src/DSi_NDMA.h | 6 +- src/DSi_NWifi.cpp | 41 +- src/DSi_NWifi.h | 3 +- src/DSi_SD.cpp | 40 +- src/DSi_SD.h | 4 +- src/DSi_SPI_TSC.cpp | 4 +- src/DSi_SPI_TSC.h | 3 +- src/GBACart.cpp | 2 +- src/GPU.cpp | 60 +- src/GPU.h | 6 +- src/GPU3D.cpp | 20 +- src/GPU3D.h | 4 +- src/MemConstants.h | 35 + src/MemRegion.h | 33 + src/NDS.cpp | 1989 +++++++---------- src/NDS.h | 379 ++-- src/NDSCart.cpp | 107 +- src/NDSCart.h | 21 +- src/RTC.cpp | 22 +- src/RTC.h | 6 +- src/SPI.cpp | 57 +- src/SPI.h | 17 +- src/SPU.cpp | 35 +- src/SPU.h | 12 +- src/Wifi.cpp | 18 +- src/Wifi.h | 5 +- src/frontend/FrontendUtil.h | 10 +- src/frontend/Util_Audio.cpp | 16 +- src/frontend/qt_sdl/AudioInOut.cpp | 22 +- src/frontend/qt_sdl/AudioInOut.h | 13 +- src/frontend/qt_sdl/AudioSettingsDialog.cpp | 23 +- src/frontend/qt_sdl/AudioSettingsDialog.h | 8 +- .../PowerManagement/PowerManagementDialog.cpp | 83 +- .../PowerManagement/PowerManagementDialog.h | 8 +- src/frontend/qt_sdl/RAMInfoDialog.cpp | 22 +- src/frontend/qt_sdl/RAMInfoDialog.h | 18 +- src/frontend/qt_sdl/ROMInfoDialog.cpp | 7 +- src/frontend/qt_sdl/ROMInfoDialog.h | 8 +- src/frontend/qt_sdl/ROMManager.cpp | 400 +++- src/frontend/qt_sdl/ROMManager.h | 46 +- src/frontend/qt_sdl/main.cpp | 222 +- src/frontend/qt_sdl/main.h | 9 +- 73 files changed, 3537 insertions(+), 3176 deletions(-) create mode 100644 src/MemConstants.h create mode 100644 src/MemRegion.h diff --git a/src/AREngine.cpp b/src/AREngine.cpp index 1d992b81..4e13a39a 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -29,40 +29,11 @@ namespace melonDS using Platform::Log; using Platform::LogLevel; - -AREngine::AREngine() +AREngine::AREngine(melonDS::NDS& nds) : NDS(nds) { CodeFile = nullptr; } -AREngine::~AREngine() -{ -} - -void AREngine::Reset() -{ - if (NDS::ConsoleType == 1) - { - BusRead8 = DSi::ARM7Read8; - BusRead16 = DSi::ARM7Read16; - BusRead32 = DSi::ARM7Read32; - BusWrite8 = DSi::ARM7Write8; - BusWrite16 = DSi::ARM7Write16; - BusWrite32 = DSi::ARM7Write32; - } - else - { - BusRead8 = NDS::ARM7Read8; - BusRead16 = NDS::ARM7Read16; - BusRead32 = NDS::ARM7Read32; - BusWrite8 = NDS::ARM7Write8; - BusWrite16 = NDS::ARM7Write16; - BusWrite32 = NDS::ARM7Write32; - } -} - - - #define case16(x) \ case ((x)+0x00): case ((x)+0x01): case ((x)+0x02): case ((x)+0x03): \ case ((x)+0x04): case ((x)+0x05): case ((x)+0x06): case ((x)+0x07): \ @@ -113,15 +84,15 @@ void AREngine::RunCheat(ARCode& arcode) switch (op) { case16(0x00): // 32-bit write - BusWrite32((a & 0x0FFFFFFF) + offset, b); + NDS.ARM7Write32((a & 0x0FFFFFFF) + offset, b); break; case16(0x10): // 16-bit write - BusWrite16((a & 0x0FFFFFFF) + offset, b & 0xFFFF); + NDS.ARM7Write16((a & 0x0FFFFFFF) + offset, b & 0xFFFF); break; case16(0x20): // 8-bit write - BusWrite8((a & 0x0FFFFFFF) + offset, b & 0xFF); + NDS.ARM7Write8((a & 0x0FFFFFFF) + offset, b & 0xFF); break; case16(0x30): // IF b > u32[a] @@ -131,7 +102,7 @@ void AREngine::RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u32 chk = BusRead32(addr); + u32 chk = NDS.ARM7Read32(addr); cond = (b > chk) ? 1:0; } @@ -144,7 +115,7 @@ void AREngine::RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u32 chk = BusRead32(addr); + u32 chk = NDS.ARM7Read32(addr); cond = (b < chk) ? 1:0; } @@ -157,7 +128,7 @@ void AREngine::RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u32 chk = BusRead32(addr); + u32 chk = NDS.ARM7Read32(addr); cond = (b == chk) ? 1:0; } @@ -170,7 +141,7 @@ void AREngine::RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u32 chk = BusRead32(addr); + u32 chk = NDS.ARM7Read32(addr); cond = (b != chk) ? 1:0; } @@ -183,7 +154,7 @@ void AREngine::RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u16 val = BusRead16(addr); + u16 val = NDS.ARM7Read16(addr); u16 chk = ~(b >> 16); chk &= val; @@ -198,7 +169,7 @@ void AREngine::RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u16 val = BusRead16(addr); + u16 val = NDS.ARM7Read16(addr); u16 chk = ~(b >> 16); chk &= val; @@ -213,7 +184,7 @@ void AREngine::RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u16 val = BusRead16(addr); + u16 val = NDS.ARM7Read16(addr); u16 chk = ~(b >> 16); chk &= val; @@ -228,7 +199,7 @@ void AREngine::RunCheat(ARCode& arcode) u32 addr = a & 0x0FFFFFFF; if (!addr) addr = offset; - u16 val = BusRead16(addr); + u16 val = NDS.ARM7Read16(addr); u16 chk = ~(b >> 16); chk &= val; @@ -237,7 +208,7 @@ void AREngine::RunCheat(ARCode& arcode) break; case16(0xB0): // offset = u32[a + offset] - offset = BusRead32((a & 0x0FFFFFFF) + offset); + offset = NDS.ARM7Read32((a & 0x0FFFFFFF) + offset); break; case 0xC0: // FOR 0..b @@ -274,7 +245,7 @@ void AREngine::RunCheat(ARCode& arcode) break; case 0xC6: // u32[b] = offset - BusWrite32(b, offset); + NDS.ARM7Write32(b, offset); break; case 0xD0: // ENDIF @@ -323,30 +294,30 @@ void AREngine::RunCheat(ARCode& arcode) break; case 0xD6: // u32[b+offset] = datareg / offset += 4 - BusWrite32(b + offset, datareg); + NDS.ARM7Write32(b + offset, datareg); offset += 4; break; case 0xD7: // u16[b+offset] = datareg / offset += 2 - BusWrite16(b + offset, datareg & 0xFFFF); + NDS.ARM7Write16(b + offset, datareg & 0xFFFF); offset += 2; break; case 0xD8: // u8[b+offset] = datareg / offset += 1 - BusWrite8(b + offset, datareg & 0xFF); + NDS.ARM7Write8(b + offset, datareg & 0xFF); offset += 1; break; case 0xD9: // datareg = u32[b+offset] - datareg = BusRead32(b + offset); + datareg = NDS.ARM7Read32(b + offset); break; case 0xDA: // datareg = u16[b+offset] - datareg = BusRead16(b + offset); + datareg = NDS.ARM7Read16(b + offset); break; case 0xDB: // datareg = u8[b+offset] - datareg = BusRead8(b + offset); + datareg = NDS.ARM7Read8(b + offset); break; case 0xDC: // offset += b @@ -361,8 +332,8 @@ void AREngine::RunCheat(ARCode& arcode) u32 bytesleft = b; while (bytesleft >= 8) { - BusWrite32(dstaddr, *code++); dstaddr += 4; - BusWrite32(dstaddr, *code++); dstaddr += 4; + NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4; + NDS.ARM7Write32(dstaddr, *code++); dstaddr += 4; bytesleft -= 8; } if (bytesleft > 0) @@ -371,13 +342,13 @@ void AREngine::RunCheat(ARCode& arcode) code += 2; if (bytesleft >= 4) { - BusWrite32(dstaddr, *(u32*)leftover); dstaddr += 4; + NDS.ARM7Write32(dstaddr, *(u32*)leftover); dstaddr += 4; leftover += 4; bytesleft -= 4; } while (bytesleft > 0) { - BusWrite8(dstaddr, *leftover++); dstaddr++; + NDS.ARM7Write8(dstaddr, *leftover++); dstaddr++; bytesleft--; } } @@ -393,14 +364,14 @@ void AREngine::RunCheat(ARCode& arcode) u32 bytesleft = b; while (bytesleft >= 4) { - BusWrite32(dstaddr, BusRead32(srcaddr)); + NDS.ARM7Write32(dstaddr, NDS.ARM7Read32(srcaddr)); srcaddr += 4; dstaddr += 4; bytesleft -= 4; } while (bytesleft > 0) { - BusWrite8(dstaddr, BusRead8(srcaddr)); + NDS.ARM7Write8(dstaddr, NDS.ARM7Read8(srcaddr)); srcaddr++; dstaddr++; bytesleft--; diff --git a/src/AREngine.h b/src/AREngine.h index a6926abf..1f2ee186 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -23,12 +23,11 @@ namespace melonDS { +class NDS; class AREngine { public: - AREngine(); - ~AREngine(); - void Reset(); + AREngine(melonDS::NDS& nds); ARCodeFile* GetCodeFile() { return CodeFile; } void SetCodeFile(ARCodeFile* file) { CodeFile = file; } @@ -36,15 +35,8 @@ public: void RunCheats(); void RunCheat(ARCode& arcode); private: + melonDS::NDS& NDS; ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this - - // TEMPORARY - u8 (*BusRead8)(u32 addr); - u16 (*BusRead16)(u32 addr); - u32 (*BusRead32)(u32 addr); - void (*BusWrite8)(u32 addr, u8 val); - void (*BusWrite16)(u32 addr, u16 val); - void (*BusWrite32)(u32 addr, u32 val); }; } diff --git a/src/ARM.cpp b/src/ARM.cpp index a1485e99..659d303b 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -106,13 +106,12 @@ const u32 ARM::ConditionTable[16] = 0x0000 // NE }; -ARM::ARM(u32 num, ARMJIT& jit, melonDS::GPU& gpu) : +ARM::ARM(u32 num, melonDS::NDS& nds) : #ifdef GDBSTUB_ENABLED GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)), #endif - JIT(jit), Num(num), // well uh - GPU(gpu) + NDS(nds) { #ifdef GDBSTUB_ENABLED if (Platform::GetConfigBool(Platform::GdbEnabled) @@ -130,14 +129,14 @@ ARM::~ARM() // dorp } -ARMv5::ARMv5(ARMJIT& jit, melonDS::GPU& gpu) : ARM(0, jit, gpu) +ARMv5::ARMv5(melonDS::NDS& nds) : ARM(0, nds) { - DTCM = JIT.Memory.GetARM9DTCM(); + DTCM = NDS.JIT.Memory.GetARM9DTCM(); PU_Map = PU_PrivMap; } -ARMv4::ARMv4(ARMJIT& jit, melonDS::GPU& gpu) : ARM(1, jit, gpu) +ARMv4::ARMv4(melonDS::NDS& nds) : ARM(1, nds) { // } @@ -198,56 +197,11 @@ void ARM::Reset() void ARMv5::Reset() { - if (NDS::ConsoleType == 1) - { - BusRead8 = DSi::ARM9Read8; - BusRead16 = DSi::ARM9Read16; - BusRead32 = DSi::ARM9Read32; - BusWrite8 = DSi::ARM9Write8; - BusWrite16 = DSi::ARM9Write16; - BusWrite32 = DSi::ARM9Write32; - GetMemRegion = DSi::ARM9GetMemRegion; - } - else - { - BusRead8 = NDS::ARM9Read8; - BusRead16 = NDS::ARM9Read16; - BusRead32 = NDS::ARM9Read32; - BusWrite8 = NDS::ARM9Write8; - BusWrite16 = NDS::ARM9Write16; - BusWrite32 = NDS::ARM9Write32; - GetMemRegion = NDS::ARM9GetMemRegion; - } - PU_Map = PU_PrivMap; ARM::Reset(); } -void ARMv4::Reset() -{ - if (NDS::ConsoleType) - { - BusRead8 = DSi::ARM7Read8; - BusRead16 = DSi::ARM7Read16; - BusRead32 = DSi::ARM7Read32; - BusWrite8 = DSi::ARM7Write8; - BusWrite16 = DSi::ARM7Write16; - BusWrite32 = DSi::ARM7Write32; - } - else - { - BusRead8 = NDS::ARM7Read8; - BusRead16 = NDS::ARM7Read16; - BusRead32 = NDS::ARM7Read32; - BusWrite8 = NDS::ARM7Write8; - BusWrite16 = NDS::ARM7Write16; - BusWrite32 = NDS::ARM7Write32; - } - - ARM::Reset(); -} - void ARM::DoSavestate(Savestate* file) { @@ -270,7 +224,7 @@ void ARM::DoSavestate(Savestate* file) file->VarArray(R_UND, 3*sizeof(u32)); file->Var32(&CurInstr); #ifdef JIT_ENABLED - if (file->Saving && NDS::EnableJIT) + if (file->Saving && NDS.EnableJIT) { // hack, the JIT doesn't really pipeline // but we still want JIT save states to be @@ -396,7 +350,7 @@ void ARMv5::JumpTo(u32 addr, bool restorecpsr) return; } - NDS::MonitorARM9Jump(addr); + NDS.MonitorARM9Jump(addr); } void ARMv4::JumpTo(u32 addr, bool restorecpsr) @@ -424,7 +378,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) NextInstr[0] = CodeRead16(addr); NextInstr[1] = CodeRead16(addr+2); - Cycles += NDS::ARM7MemTimings[CodeCycles][0] + NDS::ARM7MemTimings[CodeCycles][1]; + Cycles += NDS.ARM7MemTimings[CodeCycles][0] + NDS.ARM7MemTimings[CodeCycles][1]; CPSR |= 0x20; } @@ -437,7 +391,7 @@ void ARMv4::JumpTo(u32 addr, bool restorecpsr) NextInstr[0] = CodeRead32(addr); NextInstr[1] = CodeRead32(addr+4); - Cycles += NDS::ARM7MemTimings[CodeCycles][2] + NDS::ARM7MemTimings[CodeCycles][3]; + Cycles += NDS.ARM7MemTimings[CodeCycles][2] + NDS.ARM7MemTimings[CodeCycles][3]; CPSR &= ~0x20; } @@ -582,8 +536,8 @@ void ARM::TriggerIRQ() // normally, those work by hijacking the ARM7 VBlank handler if (Num == 1) { - if ((NDS::IF[1] & NDS::IE[1]) & (1<RunCheats(); + if ((NDS.IF[1] & NDS.IE[1]) & (1<>12] & 0x04)) { Log(LogLevel::Error, "!!!!! EXCEPTION REGION NOT EXECUTABLE. THIS IS VERY BAD!!\n"); - NDS::Stop(Platform::StopReason::BadExceptionRegion); + NDS.Stop(Platform::StopReason::BadExceptionRegion); return; } @@ -639,20 +593,20 @@ void ARMv5::Execute() { Halted = 0; } - else if (NDS::HaltInterrupted(0)) + else if (NDS.HaltInterrupted(0)) { Halted = 0; - if (NDS::IME[0] & 0x1) + if (NDS.IME[0] & 0x1) TriggerIRQ(); } else { - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; return; } } - while (NDS::ARM9Timestamp < NDS::ARM9Target) + while (NDS.ARM9Timestamp < NDS.ARM9Target) { if (CPSR & 0x20) // THUMB { @@ -696,9 +650,9 @@ void ARMv5::Execute() // TODO optimize this shit!!! if (Halted) { - if (Halted == 1 && NDS::ARM9Timestamp < NDS::ARM9Target) + if (Halted == 1 && NDS.ARM9Timestamp < NDS.ARM9Target) { - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; } break; } @@ -709,7 +663,7 @@ void ARMv5::Execute() }*/ if (IRQ) TriggerIRQ(); - NDS::ARM9Timestamp += Cycles; + NDS.ARM9Timestamp += Cycles; Cycles = 0; } @@ -726,37 +680,37 @@ void ARMv5::ExecuteJIT() { Halted = 0; } - else if (NDS::HaltInterrupted(0)) + else if (NDS.HaltInterrupted(0)) { Halted = 0; - if (NDS::IME[0] & 0x1) + if (NDS.IME[0] & 0x1) TriggerIRQ(); } else { - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; return; } } - while (NDS::ARM9Timestamp < NDS::ARM9Target) + while (NDS.ARM9Timestamp < NDS.ARM9Target) { u32 instrAddr = R[15] - ((CPSR&0x20)?2:4); if ((instrAddr < FastBlockLookupStart || instrAddr >= (FastBlockLookupStart + FastBlockLookupSize)) - && !JIT.SetupExecutableRegion(0, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) + && !NDS.JIT.SetupExecutableRegion(0, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) { - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; Log(LogLevel::Error, "ARMv5 PC in non executable region %08X\n", R[15]); return; } - JitBlockEntry block = JIT.LookUpBlock(0, FastBlockLookup, + JitBlockEntry block = NDS.JIT.LookUpBlock(0, FastBlockLookup, instrAddr - FastBlockLookupStart, instrAddr); if (block) ARM_Dispatch(this, block); else - JIT.CompileBlock(this); + NDS.JIT.CompileBlock(this); if (StopExecution) { @@ -766,17 +720,17 @@ void ARMv5::ExecuteJIT() if (Halted || IdleLoop) { - if ((Halted == 1 || IdleLoop) && NDS::ARM9Timestamp < NDS::ARM9Target) + if ((Halted == 1 || IdleLoop) && NDS.ARM9Timestamp < NDS.ARM9Target) { Cycles = 0; - NDS::ARM9Timestamp = NDS::ARM9Target; + NDS.ARM9Timestamp = NDS.ARM9Target; } IdleLoop = 0; break; } } - NDS::ARM9Timestamp += Cycles; + NDS.ARM9Timestamp += Cycles; Cycles = 0; } @@ -795,20 +749,20 @@ void ARMv4::Execute() { Halted = 0; } - else if (NDS::HaltInterrupted(1)) + else if (NDS.HaltInterrupted(1)) { Halted = 0; - if (NDS::IME[1] & 0x1) + if (NDS.IME[1] & 0x1) TriggerIRQ(); } else { - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; return; } } - while (NDS::ARM7Timestamp < NDS::ARM7Target) + while (NDS.ARM7Timestamp < NDS.ARM7Target) { if (CPSR & 0x20) // THUMB { @@ -847,9 +801,9 @@ void ARMv4::Execute() // TODO optimize this shit!!! if (Halted) { - if (Halted == 1 && NDS::ARM7Timestamp < NDS::ARM7Target) + if (Halted == 1 && NDS.ARM7Timestamp < NDS.ARM7Target) { - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; } break; } @@ -860,7 +814,7 @@ void ARMv4::Execute() }*/ if (IRQ) TriggerIRQ(); - NDS::ARM7Timestamp += Cycles; + NDS.ARM7Timestamp += Cycles; Cycles = 0; } @@ -869,7 +823,9 @@ void ARMv4::Execute() if (Halted == 4) { - DSi::SoftReset(); + assert(NDS.ConsoleType == 1); + auto& dsi = dynamic_cast(NDS); + dsi.SoftReset(); Halted = 2; } } @@ -883,37 +839,37 @@ void ARMv4::ExecuteJIT() { Halted = 0; } - else if (NDS::HaltInterrupted(1)) + else if (NDS.HaltInterrupted(1)) { Halted = 0; - if (NDS::IME[1] & 0x1) + if (NDS.IME[1] & 0x1) TriggerIRQ(); } else { - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; return; } } - while (NDS::ARM7Timestamp < NDS::ARM7Target) + while (NDS.ARM7Timestamp < NDS.ARM7Target) { u32 instrAddr = R[15] - ((CPSR&0x20)?2:4); if ((instrAddr < FastBlockLookupStart || instrAddr >= (FastBlockLookupStart + FastBlockLookupSize)) - && !JIT.SetupExecutableRegion(1, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) + && !NDS.JIT.SetupExecutableRegion(1, instrAddr, FastBlockLookup, FastBlockLookupStart, FastBlockLookupSize)) { - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; Log(LogLevel::Error, "ARMv4 PC in non executable region %08X\n", R[15]); return; } - JitBlockEntry block = JIT.LookUpBlock(1, FastBlockLookup, + JitBlockEntry block = NDS.JIT.LookUpBlock(1, FastBlockLookup, instrAddr - FastBlockLookupStart, instrAddr); if (block) ARM_Dispatch(this, block); else - JIT.CompileBlock(this); + NDS.JIT.CompileBlock(this); if (StopExecution) { @@ -922,17 +878,17 @@ void ARMv4::ExecuteJIT() if (Halted || IdleLoop) { - if ((Halted == 1 || IdleLoop) && NDS::ARM7Timestamp < NDS::ARM7Target) + if ((Halted == 1 || IdleLoop) && NDS.ARM7Timestamp < NDS.ARM7Target) { Cycles = 0; - NDS::ARM7Timestamp = NDS::ARM7Target; + NDS.ARM7Timestamp = NDS.ARM7Target; } IdleLoop = 0; break; } } - NDS::ARM7Timestamp += Cycles; + NDS.ARM7Timestamp += Cycles; Cycles = 0; } @@ -941,7 +897,9 @@ void ARMv4::ExecuteJIT() if (Halted == 4) { - DSi::SoftReset(); + assert(NDS.ConsoleType == 1); + auto& dsi = dynamic_cast(NDS); + dsi.SoftReset(); Halted = 2; } } @@ -1136,8 +1094,8 @@ void ARM::WriteMem(u32 addr, int size, u32 v) void ARM::ResetGdb() { - NDS::Reset(); - GPU.StartFrame(); // need this to properly kick off the scheduler & frame output + NDS.Reset(); + NDS.GPU.StartFrame(); // need this to properly kick off the scheduler & frame output } int ARM::RemoteCmd(const u8* cmd, size_t len) { @@ -1193,6 +1151,195 @@ u32 ARMv5::ReadMem(u32 addr, int size) return ARM::ReadMem(addr, size); } -} #endif +void ARMv4::DataRead8(u32 addr, u32* val) +{ + *val = BusRead8(addr); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][0]; +} + +void ARMv4::DataRead16(u32 addr, u32* val) +{ + addr &= ~1; + + *val = BusRead16(addr); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][0]; +} + +void ARMv4::DataRead32(u32 addr, u32* val) +{ + addr &= ~3; + + *val = BusRead32(addr); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][2]; +} + +void ARMv4::DataRead32S(u32 addr, u32* val) +{ + addr &= ~3; + + *val = BusRead32(addr); + DataCycles += NDS.ARM7MemTimings[addr >> 15][3]; +} + +void ARMv4::DataWrite8(u32 addr, u8 val) +{ + BusWrite8(addr, val); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][0]; +} + +void ARMv4::DataWrite16(u32 addr, u16 val) +{ + addr &= ~1; + + BusWrite16(addr, val); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][0]; +} + +void ARMv4::DataWrite32(u32 addr, u32 val) +{ + addr &= ~3; + + BusWrite32(addr, val); + DataRegion = addr; + DataCycles = NDS.ARM7MemTimings[addr >> 15][2]; +} + +void ARMv4::DataWrite32S(u32 addr, u32 val) +{ + addr &= ~3; + + BusWrite32(addr, val); + DataCycles += NDS.ARM7MemTimings[addr >> 15][3]; +} + + +void ARMv4::AddCycles_C() +{ + // code only. this code fetch is sequential. + Cycles += NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3]; +} + +void ARMv4::AddCycles_CI(s32 num) +{ + // code+internal. results in a nonseq code fetch. + Cycles += NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num; +} + +void ARMv4::AddCycles_CDI() +{ + // LDR/LDM cycles. + s32 numC = NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; + s32 numD = DataCycles; + + if ((DataRegion >> 24) == 0x02) // mainRAM + { + if (CodeRegion == 0x02) + Cycles += numC + numD; + else + { + numC++; + Cycles += std::max(numC + numD - 3, std::max(numC, numD)); + } + } + else if (CodeRegion == 0x02) + { + numD++; + Cycles += std::max(numC + numD - 3, std::max(numC, numD)); + } + else + { + Cycles += numC + numD + 1; + } +} + +void ARMv4::AddCycles_CD() +{ + // TODO: max gain should be 5c when writing to mainRAM + s32 numC = NDS.ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; + s32 numD = DataCycles; + + if ((DataRegion >> 24) == 0x02) + { + if (CodeRegion == 0x02) + Cycles += numC + numD; + else + Cycles += std::max(numC + numD - 3, std::max(numC, numD)); + } + else if (CodeRegion == 0x02) + { + Cycles += std::max(numC + numD - 3, std::max(numC, numD)); + } + else + { + Cycles += numC + numD; + } +} + +u8 ARMv5::BusRead8(u32 addr) +{ + return NDS.ARM9Read8(addr); +} + +u16 ARMv5::BusRead16(u32 addr) +{ + return NDS.ARM9Read16(addr); +} + +u32 ARMv5::BusRead32(u32 addr) +{ + return NDS.ARM9Read32(addr); +} + +void ARMv5::BusWrite8(u32 addr, u8 val) +{ + NDS.ARM9Write8(addr, val); +} + +void ARMv5::BusWrite16(u32 addr, u16 val) +{ + NDS.ARM9Write16(addr, val); +} + +void ARMv5::BusWrite32(u32 addr, u32 val) +{ + NDS.ARM9Write32(addr, val); +} + +u8 ARMv4::BusRead8(u32 addr) +{ + return NDS.ARM7Read8(addr); +} + +u16 ARMv4::BusRead16(u32 addr) +{ + return NDS.ARM7Read16(addr); +} + +u32 ARMv4::BusRead32(u32 addr) +{ + return NDS.ARM7Read32(addr); +} + +void ARMv4::BusWrite8(u32 addr, u8 val) +{ + NDS.ARM7Write8(addr, val); +} + +void ARMv4::BusWrite16(u32 addr, u16 val) +{ + NDS.ARM7Write16(addr, val); +} + +void ARMv4::BusWrite32(u32 addr, u32 val) +{ + NDS.ARM7Write32(addr, val); +} +} + diff --git a/src/ARM.h b/src/ARM.h index 9a99a072..4becff02 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -22,7 +22,7 @@ #include #include "types.h" -#include "NDS.h" +#include "MemRegion.h" #ifdef GDBSTUB_ENABLED #include "debug/GdbStub.h" @@ -48,6 +48,8 @@ const u32 DTCMPhysicalSize = 0x4000; class ARMJIT; class GPU; class ARMJIT_Memory; +class NDS; +class Savestate; class ARM #ifdef GDBSTUB_ENABLED @@ -55,7 +57,7 @@ class ARM #endif { public: - ARM(u32 num, ARMJIT& jit, GPU& gpu); + ARM(u32 num, NDS& nds); virtual ~ARM(); // destroy shit virtual void Reset(); @@ -73,6 +75,7 @@ public: Halted = halt; } + void NocashPrint(u32 addr) noexcept; virtual void Execute() = 0; #ifdef JIT_ENABLED virtual void ExecuteJIT() = 0; @@ -174,7 +177,7 @@ public: u32 ExceptionBase; - NDS::MemRegion CodeMem; + MemRegion CodeMem; #ifdef JIT_ENABLED u32 FastBlockLookupStart, FastBlockLookupSize; @@ -186,14 +189,14 @@ public: Gdb::GdbStub GdbStub; #endif - ARMJIT& JIT; + melonDS::NDS& NDS; protected: - u8 (*BusRead8)(u32 addr); - u16 (*BusRead16)(u32 addr); - u32 (*BusRead32)(u32 addr); - void (*BusWrite8)(u32 addr, u8 val); - void (*BusWrite16)(u32 addr, u16 val); - void (*BusWrite32)(u32 addr, u32 val); + virtual u8 BusRead8(u32 addr) = 0; + virtual u16 BusRead16(u32 addr) = 0; + virtual u32 BusRead32(u32 addr) = 0; + virtual void BusWrite8(u32 addr, u8 val) = 0; + virtual void BusWrite16(u32 addr, u16 val) = 0; + virtual void BusWrite32(u32 addr, u32 val) = 0; #ifdef GDBSTUB_ENABLED bool IsSingleStep; @@ -217,14 +220,12 @@ protected: void GdbCheckA(); void GdbCheckB(); void GdbCheckC(); -private: - melonDS::GPU& GPU; }; class ARMv5 : public ARM { public: - ARMv5(ARMJIT& jit, melonDS::GPU& gpu); + ARMv5(melonDS::NDS& nds); ~ARMv5(); void Reset() override; @@ -296,7 +297,7 @@ public: // Cycles += numC + numD; } - void GetCodeMemRegion(u32 addr, NDS::MemRegion* region); + void GetCodeMemRegion(u32 addr, MemRegion* region); void CP15Reset(); void CP15DoSavestate(Savestate* file); @@ -357,20 +358,26 @@ public: u8* CurICacheLine; - bool (*GetMemRegion)(u32 addr, bool write, NDS::MemRegion* region); + bool (*GetMemRegion)(u32 addr, bool write, MemRegion* region); #ifdef GDBSTUB_ENABLED u32 ReadMem(u32 addr, int size) override; void WriteMem(u32 addr, int size, u32 v) override; #endif + +protected: + u8 BusRead8(u32 addr) override; + u16 BusRead16(u32 addr) override; + u32 BusRead32(u32 addr) override; + void BusWrite8(u32 addr, u8 val) override; + void BusWrite16(u32 addr, u16 val) override; + void BusWrite32(u32 addr, u32 val) override; }; class ARMv4 : public ARM { public: - ARMv4(ARMJIT& jit, melonDS::GPU& gpu); - - void Reset() override; + ARMv4(melonDS::NDS& nds); void FillPipeline() override; @@ -391,134 +398,25 @@ public: return BusRead32(addr); } - void DataRead8(u32 addr, u32* val) override - { - *val = BusRead8(addr); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; - } - - void DataRead16(u32 addr, u32* val) override - { - addr &= ~1; - - *val = BusRead16(addr); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; - } - - void DataRead32(u32 addr, u32* val) override - { - addr &= ~3; - - *val = BusRead32(addr); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][2]; - } - - void DataRead32S(u32 addr, u32* val) override - { - addr &= ~3; - - *val = BusRead32(addr); - DataCycles += NDS::ARM7MemTimings[addr >> 15][3]; - } - - void DataWrite8(u32 addr, u8 val) override - { - BusWrite8(addr, val); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; - } - - void DataWrite16(u32 addr, u16 val) override - { - addr &= ~1; - - BusWrite16(addr, val); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][0]; - } - - void DataWrite32(u32 addr, u32 val) override - { - addr &= ~3; - - BusWrite32(addr, val); - DataRegion = addr; - DataCycles = NDS::ARM7MemTimings[addr >> 15][2]; - } - - void DataWrite32S(u32 addr, u32 val) override - { - addr &= ~3; - - BusWrite32(addr, val); - DataCycles += NDS::ARM7MemTimings[addr >> 15][3]; - } - - - void AddCycles_C() override - { - // code only. this code fetch is sequential. - Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?1:3]; - } - - void AddCycles_CI(s32 num) override - { - // code+internal. results in a nonseq code fetch. - Cycles += NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2] + num; - } - - void AddCycles_CDI() override - { - // LDR/LDM cycles. - s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; - s32 numD = DataCycles; - - if ((DataRegion >> 24) == 0x02) // mainRAM - { - if (CodeRegion == 0x02) - Cycles += numC + numD; - else - { - numC++; - Cycles += std::max(numC + numD - 3, std::max(numC, numD)); - } - } - else if (CodeRegion == 0x02) - { - numD++; - Cycles += std::max(numC + numD - 3, std::max(numC, numD)); - } - else - { - Cycles += numC + numD + 1; - } - } - - void AddCycles_CD() override - { - // TODO: max gain should be 5c when writing to mainRAM - s32 numC = NDS::ARM7MemTimings[CodeCycles][(CPSR&0x20)?0:2]; - s32 numD = DataCycles; - - if ((DataRegion >> 24) == 0x02) - { - if (CodeRegion == 0x02) - Cycles += numC + numD; - else - Cycles += std::max(numC + numD - 3, std::max(numC, numD)); - } - else if (CodeRegion == 0x02) - { - Cycles += std::max(numC + numD - 3, std::max(numC, numD)); - } - else - { - Cycles += numC + numD; - } - } + void DataRead8(u32 addr, u32* val) override; + void DataRead16(u32 addr, u32* val) override; + void DataRead32(u32 addr, u32* val) override; + void DataRead32S(u32 addr, u32* val) override; + void DataWrite8(u32 addr, u8 val) override; + void DataWrite16(u32 addr, u16 val) override; + void DataWrite32(u32 addr, u32 val) override; + void DataWrite32S(u32 addr, u32 val) override; + void AddCycles_C() override; + void AddCycles_CI(s32 num) override; + void AddCycles_CDI() override; + void AddCycles_CD() override; +protected: + u8 BusRead8(u32 addr) override; + u16 BusRead16(u32 addr) override; + u32 BusRead32(u32 addr) override; + void BusWrite8(u32 addr, u8 val) override; + void BusWrite16(u32 addr, u16 val) override; + void BusWrite32(u32 addr, u32 val) override; }; namespace ARMInterpreter @@ -528,14 +426,5 @@ void A_UNK(ARM* cpu); void T_UNK(ARM* cpu); } - -namespace NDS -{ - -extern ARMv5* ARM9; -extern ARMv4* ARM7; - -} - } #endif // ARM_H diff --git a/src/ARMInterpreter_ALU.cpp b/src/ARMInterpreter_ALU.cpp index 313f7641..315d59d0 100644 --- a/src/ARMInterpreter_ALU.cpp +++ b/src/ARMInterpreter_ALU.cpp @@ -18,6 +18,7 @@ #include #include "ARM.h" +#include "NDS.h" namespace melonDS::ARMInterpreter { @@ -692,7 +693,7 @@ void A_MOV_REG_LSL_IMM_DBG(ARM* cpu) // but since they serve no purpose ATTOW, we can skip them u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags // TODO: Pass flags to NocashPrint - NDS::NocashPrint(cpu->Num, addr); + cpu->NDS.NocashPrint(cpu->Num, addr); } } @@ -1561,7 +1562,7 @@ void T_MOV_HIREG(ARM* cpu) // but since they serve no purpose ATTOW, we can skip them u32 addr = cpu->R[15] + 4; // Skip 2nd ID and flags // TODO: Pass flags to NocashPrint - NDS::NocashPrint(cpu->Num, addr); + cpu->NDS.NocashPrint(cpu->Num, addr); } } diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index 45445821..b938dfb8 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -64,20 +64,20 @@ const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] = ITCMPhysicalSize, 0, sizeof(NDS::ARM9BIOS), - NDS::MainRAMMaxSize, - NDS::SharedWRAMSize, + MainRAMMaxSize, + SharedWRAMSize, 0, 0x100000, sizeof(NDS::ARM7BIOS), - NDS::ARM7WRAMSize, + ARM7WRAMSize, 0, 0, 0x40000, 0x10000, 0x10000, - DSi::NWRAMSize, - DSi::NWRAMSize, - DSi::NWRAMSize, + NWRAMSize, + NWRAMSize, + NWRAMSize, }; u32 ARMJIT::LocaliseCodeAddress(u32 num, u32 addr) const noexcept @@ -103,11 +103,11 @@ T SlowRead9(u32 addr, ARMv5* cpu) else if ((addr & cpu->DTCMMask) == cpu->DTCMBase) val = *(T*)&cpu->DTCM[addr & 0x3FFF]; else if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM9Read32 : DSi::ARM9Read32)(addr); + val = NDS::Current->ARM9Read32(addr); else if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM9Read16 : DSi::ARM9Read16)(addr); + val = NDS::Current->ARM9Read16(addr); else - val = (ConsoleType == 0 ? NDS::ARM9Read8 : DSi::ARM9Read8)(addr); + val = NDS::Current->ARM9Read8(addr); if (std::is_same::value) return ROR(val, offset << 3); @@ -123,11 +123,11 @@ T SlowRead7(u32 addr) T val; if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM7Read32 : DSi::ARM7Read32)(addr); + val = NDS::Current->ARM7Read32(addr); else if (std::is_same::value) - val = (ConsoleType == 0 ? NDS::ARM7Read16 : DSi::ARM7Read16)(addr); + val = NDS::Current->ARM7Read16(addr); else - val = (ConsoleType == 0 ? NDS::ARM7Read8 : DSi::ARM7Read8)(addr); + val = NDS::Current->ARM7Read8(addr); if (std::is_same::value) return ROR(val, offset << 3); @@ -142,7 +142,7 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val) if (addr < cpu->ITCMSize) { - cpu->JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + cpu->NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); *(T*)&cpu->ITCM[addr & 0x7FFF] = val; } else if ((addr & cpu->DTCMMask) == cpu->DTCMBase) @@ -151,15 +151,15 @@ void SlowWrite9(u32 addr, ARMv5* cpu, u32 val) } else if (std::is_same::value) { - (ConsoleType == 0 ? NDS::ARM9Write32 : DSi::ARM9Write32)(addr, val); + NDS::Current->ARM9Write32(addr, val); } else if (std::is_same::value) { - (ConsoleType == 0 ? NDS::ARM9Write16 : DSi::ARM9Write16)(addr, val); + NDS::Current->ARM9Write16(addr, val); } else { - (ConsoleType == 0 ? NDS::ARM9Write8 : DSi::ARM9Write8)(addr, val); + NDS::Current->ARM9Write8(addr, val); } } @@ -169,11 +169,11 @@ void SlowWrite7(u32 addr, u32 val) addr &= ~(sizeof(T) - 1); if (std::is_same::value) - (ConsoleType == 0 ? NDS::ARM7Write32 : DSi::ARM7Write32)(addr, val); + NDS::Current->ARM7Write32(addr, val); else if (std::is_same::value) - (ConsoleType == 0 ? NDS::ARM7Write16 : DSi::ARM7Write16)(addr, val); + NDS::Current->ARM7Write16(addr, val); else - (ConsoleType == 0 ? NDS::ARM7Write8 : DSi::ARM7Write8)(addr, val); + NDS::Current->ARM7Write8(addr, val); } template diff --git a/src/ARMJIT.h b/src/ARMJIT.h index 4191824c..9e1ca074 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -30,6 +30,7 @@ #endif #include "ARMJIT_Compiler.h" +#include "MemConstants.h" namespace melonDS { @@ -39,7 +40,7 @@ class JitBlock; class ARMJIT { public: - ARMJIT() noexcept : JITCompiler(*this), Memory(*this) {} + ARMJIT(melonDS::NDS& nds) noexcept : NDS(nds), Memory(nds), JITCompiler(nds) {}; ~ARMJIT() noexcept NOOP_IF_NO_JIT; void InvalidateByAddr(u32) noexcept NOOP_IF_NO_JIT; void CheckAndInvalidateWVRAM(int) noexcept NOOP_IF_NO_JIT; @@ -72,8 +73,8 @@ public: bool BranchOptimizations = false; bool FastMemory = false; + melonDS::NDS& NDS; TinyVector InvalidLiterals {}; -private: friend class ARMJIT_Memory; void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept; void RetireJitBlock(JitBlock* block) noexcept; @@ -86,32 +87,32 @@ private: AddressRange CodeIndexITCM[ITCMPhysicalSize / 512] {}; - AddressRange CodeIndexMainRAM[NDS::MainRAMMaxSize / 512] {}; - AddressRange CodeIndexSWRAM[NDS::SharedWRAMSize / 512] {}; + AddressRange CodeIndexMainRAM[MainRAMMaxSize / 512] {}; + AddressRange CodeIndexSWRAM[SharedWRAMSize / 512] {}; AddressRange CodeIndexVRAM[0x100000 / 512] {}; - AddressRange CodeIndexARM9BIOS[sizeof(NDS::ARM9BIOS) / 512] {}; - AddressRange CodeIndexARM7BIOS[sizeof(NDS::ARM7BIOS) / 512] {}; - AddressRange CodeIndexARM7WRAM[NDS::ARM7WRAMSize / 512] {}; + AddressRange CodeIndexARM9BIOS[ARM9BIOSSize / 512] {}; + AddressRange CodeIndexARM7BIOS[ARM7BIOSSize / 512] {}; + AddressRange CodeIndexARM7WRAM[ARM7WRAMSize / 512] {}; AddressRange CodeIndexARM7WVRAM[0x40000 / 512] {}; AddressRange CodeIndexBIOS9DSi[0x10000 / 512] {}; AddressRange CodeIndexBIOS7DSi[0x10000 / 512] {}; - AddressRange CodeIndexNWRAM_A[DSi::NWRAMSize / 512] {}; - AddressRange CodeIndexNWRAM_B[DSi::NWRAMSize / 512] {}; - AddressRange CodeIndexNWRAM_C[DSi::NWRAMSize / 512] {}; + AddressRange CodeIndexNWRAM_A[NWRAMSize / 512] {}; + AddressRange CodeIndexNWRAM_B[NWRAMSize / 512] {}; + AddressRange CodeIndexNWRAM_C[NWRAMSize / 512] {}; u64 FastBlockLookupITCM[ITCMPhysicalSize / 2] {}; - u64 FastBlockLookupMainRAM[NDS::MainRAMMaxSize / 2] {}; - u64 FastBlockLookupSWRAM[NDS::SharedWRAMSize / 2] {}; + u64 FastBlockLookupMainRAM[MainRAMMaxSize / 2] {}; + u64 FastBlockLookupSWRAM[SharedWRAMSize / 2] {}; u64 FastBlockLookupVRAM[0x100000 / 2] {}; - u64 FastBlockLookupARM9BIOS[sizeof(NDS::ARM9BIOS) / 2] {}; - u64 FastBlockLookupARM7BIOS[sizeof(NDS::ARM7BIOS) / 2] {}; - u64 FastBlockLookupARM7WRAM[NDS::ARM7WRAMSize / 2] {}; + u64 FastBlockLookupARM9BIOS[ARM9BIOSSize / 2] {}; + u64 FastBlockLookupARM7BIOS[ARM7BIOSSize / 2] {}; + u64 FastBlockLookupARM7WRAM[ARM7WRAMSize / 2] {}; u64 FastBlockLookupARM7WVRAM[0x40000 / 2] {}; u64 FastBlockLookupBIOS9DSi[0x10000 / 2] {}; u64 FastBlockLookupBIOS7DSi[0x10000 / 2] {}; - u64 FastBlockLookupNWRAM_A[DSi::NWRAMSize / 2] {}; - u64 FastBlockLookupNWRAM_B[DSi::NWRAMSize / 2] {}; - u64 FastBlockLookupNWRAM_C[DSi::NWRAMSize / 2] {}; + u64 FastBlockLookupNWRAM_A[NWRAMSize / 2] {}; + u64 FastBlockLookupNWRAM_B[NWRAMSize / 2] {}; + u64 FastBlockLookupNWRAM_C[NWRAMSize / 2] {}; AddressRange* const CodeMemRegions[ARMJIT_Memory::memregions_Count] = { diff --git a/src/ARMJIT_A64/ARMJIT_Branch.cpp b/src/ARMJIT_A64/ARMJIT_Branch.cpp index d18a0e05..92717e91 100644 --- a/src/ARMJIT_A64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_A64/ARMJIT_Branch.cpp @@ -17,6 +17,7 @@ */ #include "ARMJIT_Compiler.h" +#include "../NDS.h" using namespace Arm64Gen; @@ -132,7 +133,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) u32 compileTimePC = CurCPU->R[15]; CurCPU->R[15] = newPC; - cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1]; + cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1]; CurCPU->R[15] = compileTimePC; } @@ -144,7 +145,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) u32 compileTimePC = CurCPU->R[15]; CurCPU->R[15] = newPC; - cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3]; + cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3]; CurCPU->R[15] = compileTimePC; } @@ -235,7 +236,7 @@ void* Compiler::Gen_JumpTo7(int kind) LSR(W1, W0, 15); STR(INDEX_UNSIGNED, W1, RCPU, offsetof(ARM, CodeCycles)); - MOVP2R(X2, NDS::ARM7MemTimings); + MOVP2R(X2, NDS.ARM7MemTimings); LDR(W3, X2, ArithOption(W1, true)); FixupBranch switchToThumb; diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp index 1b037588..c306dd84 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp @@ -21,6 +21,7 @@ #include "../ARMJIT_Internal.h" #include "../ARMInterpreter.h" #include "../ARMJIT.h" +#include "../NDS.h" #if defined(__SWITCH__) #include @@ -220,7 +221,7 @@ void Compiler::PopRegs(bool saveHiRegs, bool saveRegsToBeChanged) } } -Compiler::Compiler(ARMJIT& jit) : Arm64Gen::ARM64XEmitter(), JIT(jit) +Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds) { #ifdef __SWITCH__ JitRWBase = aligned_alloc(0x1000, JitMemSize); @@ -705,12 +706,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] if (JitMemMainSize - GetCodeOffset() < 1024 * 16) { Log(LogLevel::Debug, "JIT near memory full, resetting...\n"); - JIT.ResetBlockCache(); + NDS.JIT.ResetBlockCache(); } if ((JitMemMainSize + JitMemSecondarySize) - OtherCodeRegion < 1024 * 8) { Log(LogLevel::Debug, "JIT far memory full, resetting...\n"); - JIT.ResetBlockCache(); + NDS.JIT.ResetBlockCache(); } JitBlockEntry res = (JitBlockEntry)GetRXPtr(); @@ -723,7 +724,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] CPSRDirty = false; if (hasMemInstr) - MOVP2R(RMemBase, Num == 0 ? JIT.Memory.FastMem9Start : JIT.Memory.FastMem7Start); + MOVP2R(RMemBase, Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start); for (int i = 0; i < instrsCount; i++) { @@ -871,7 +872,7 @@ void Compiler::Reset() void Compiler::Comp_AddCycles_C(bool forceNonConstant) { s32 cycles = Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles); if (forceNonConstant) @@ -885,7 +886,7 @@ void Compiler::Comp_AddCycles_CI(u32 numI) IrregularCycles = true; s32 cycles = (Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + numI; if (Thumb || CurInstr.Cond() == 0xE) @@ -899,7 +900,7 @@ void Compiler::Comp_AddCycles_CI(u32 c, ARM64Reg numI, ArithOption shift) IrregularCycles = true; s32 cycles = (Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + c; ADD(RCycles, RCycles, cycles); @@ -919,7 +920,7 @@ void Compiler::Comp_AddCycles_CDI() s32 cycles; - s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; + s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; s32 numD = CurInstr.DataCycles; if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM @@ -964,7 +965,7 @@ void Compiler::Comp_AddCycles_CD() } else { - s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; + s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; s32 numD = CurInstr.DataCycles; if ((CurInstr.DataRegion >> 24) == 0x02) diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index d1e5c446..72dd7bcb 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -96,7 +96,11 @@ class Compiler : public Arm64Gen::ARM64XEmitter public: typedef void (Compiler::*CompileFunc)(); - Compiler(ARMJIT& jit); +#ifdef JIT_ENABLED + explicit Compiler(melonDS::NDS& nds); +#else + explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {} +#endif ~Compiler(); void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true); @@ -242,7 +246,7 @@ public: OtherCodeRegion = offset; } - ARMJIT& JIT; + melonDS::NDS& NDS; ptrdiff_t OtherCodeRegion; bool Exit; diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp index 93cd75e6..22a410ae 100644 --- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp @@ -21,6 +21,7 @@ #include "../ARMJIT.h" #include "../ARMJIT_Memory.h" +#include "../NDS.h" using namespace Arm64Gen; @@ -62,9 +63,9 @@ u8* Compiler::RewriteMemAccess(u8* pc) bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) { - u32 localAddr = JIT.LocaliseCodeAddress(Num, addr); + u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr); - int invalidLiteralIdx = JIT.InvalidLiterals.Find(localAddr); + int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr); if (invalidLiteralIdx != -1) { return false; @@ -111,7 +112,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) if (size == 16) addressMask = ~1; - if (JIT.LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) + if (NDS.JIT.LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) { u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1); @@ -146,7 +147,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) MOV(W0, rnMapped); } - bool addrIsStatic = JIT.LiteralOptimizations + bool addrIsStatic = NDS.JIT.LiteralOptimizations && RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post)); u32 staticAddress; if (addrIsStatic) @@ -185,18 +186,18 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) MOV(rnMapped, W0); u32 expectedTarget = Num == 0 - ? JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion) - : JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion); + ? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion) + : NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion); - if (JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || JIT.Memory.IsFastmemCompatible(expectedTarget))) + if (NDS.JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget))) { ptrdiff_t memopStart = GetCodeOffset(); LoadStorePatch patch; assert((rdMapped >= W8 && rdMapped <= W15) || (rdMapped >= W19 && rdMapped <= W25) || rdMapped == W4); patch.PatchFunc = flags & memop_Store - ? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped] - : PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped]; + ? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped] + : PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped]; // take a chance at fastmem if (size > 8) @@ -225,7 +226,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) { void* func = NULL; if (addrIsStatic) - func = JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); + func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); PushRegs(false, false); @@ -263,7 +264,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) if (flags & memop_Store) { MOV(W2, rdMapped); - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: QuickCallFunction(X3, SlowWrite9); break; case 33: QuickCallFunction(X3, SlowWrite9); break; @@ -275,7 +276,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) } else { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: QuickCallFunction(X3, SlowRead9); break; case 33: QuickCallFunction(X3, SlowRead9); break; @@ -291,7 +292,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) if (flags & memop_Store) { MOV(W1, rdMapped); - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: QuickCallFunction(X3, SlowWrite7); break; case 33: QuickCallFunction(X3, SlowWrite7); break; @@ -303,7 +304,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) } else { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: QuickCallFunction(X3, SlowRead7); break; case 33: QuickCallFunction(X3, SlowRead7); break; @@ -452,7 +453,7 @@ void Compiler::T_Comp_LoadPCRel() u32 offset = ((CurInstr.Instr & 0xFF) << 2); u32 addr = (R15 & ~0x2) + offset; - if (!JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) + if (!NDS.JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0); } @@ -494,11 +495,11 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc Comp_AddCycles_CDI(); int expectedTarget = Num == 0 - ? JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) - : JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); + ? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); - bool compileFastPath = JIT.FastMemory - && store && !usermode && (CurInstr.Cond() < 0xE || JIT.Memory.IsFastmemCompatible(expectedTarget)); + bool compileFastPath = NDS.JIT.FastMemory + && store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)); { s32 offset = decrement @@ -680,7 +681,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc if (Num == 0) { MOV(X3, RCPU); - switch ((u32)store * 2 | NDS::ConsoleType) + switch ((u32)store * 2 | NDS.ConsoleType) { case 0: QuickCallFunction(X4, SlowBlockTransfer9); break; case 1: QuickCallFunction(X4, SlowBlockTransfer9); break; @@ -690,7 +691,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc } else { - switch ((u32)store * 2 | NDS::ConsoleType) + switch ((u32)store * 2 | NDS.ConsoleType) { case 0: QuickCallFunction(X4, SlowBlockTransfer7); break; case 1: QuickCallFunction(X4, SlowBlockTransfer7); break; diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 7ed9b8d1..bf1fb063 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -159,12 +159,12 @@ LONG ARMJIT_Memory::ExceptionHandler(EXCEPTION_POINTERS* exceptionInfo) return EXCEPTION_CONTINUE_SEARCH; } - u8* curArea = (u8*)(NDS::CurCPU == 0 ? NDS::JIT->Memory.FastMem9Start : NDS::JIT->Memory.FastMem7Start); + u8* curArea = (u8*)(NDS::Current->CurCPU == 0 ? NDS::Current->JIT.Memory.FastMem9Start : NDS::Current->JIT.Memory.FastMem7Start); FaultDescription desc {}; desc.EmulatedFaultAddr = (u8*)exceptionInfo->ExceptionRecord->ExceptionInformation[1] - curArea; desc.FaultPC = (u8*)exceptionInfo->ContextRecord->CONTEXT_PC; - if (FaultHandler(desc, *NDS::JIT)) + if (FaultHandler(desc, *NDS::Current)) { exceptionInfo->ContextRecord->CONTEXT_PC = (u64)desc.FaultPC; return EXCEPTION_CONTINUE_EXECUTION; @@ -194,12 +194,12 @@ void ARMJIT_Memory::SigsegvHandler(int sig, siginfo_t* info, void* rawContext) ucontext_t* context = (ucontext_t*)rawContext; FaultDescription desc {}; - u8* curArea = (u8*)(NDS::CurCPU == 0 ? NDS::JIT->Memory.FastMem9Start : NDS::JIT->Memory.FastMem7Start); + u8* curArea = (u8*)(NDS::Current->CurCPU == 0 ? NDS::Current->JIT.Memory.FastMem9Start : NDS::Current->JIT.Memory.FastMem7Start); desc.EmulatedFaultAddr = (u8*)info->si_addr - curArea; desc.FaultPC = (u8*)context->CONTEXT_PC; - if (FaultHandler(desc, *NDS::JIT)) + if (FaultHandler(desc, *NDS::Current)) { context->CONTEXT_PC = (u64)desc.FaultPC; return; @@ -319,12 +319,12 @@ void ARMJIT_Memory::SetCodeProtectionRange(u32 addr, u32 size, u32 num, int prot } #endif -void ARMJIT_Memory::Mapping::Unmap(int region, ARMJIT_Memory& memory) noexcept +void ARMJIT_Memory::Mapping::Unmap(int region, melonDS::NDS& nds) noexcept { - u32 dtcmStart = NDS::ARM9->DTCMBase; - u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1; + u32 dtcmStart = nds.ARM9.DTCMBase; + u32 dtcmSize = ~nds.ARM9.DTCMMask + 1; bool skipDTCM = Num == 0 && region != memregion_DTCM; - u8* statuses = Num == 0 ? memory.MappingStatus9 : memory.MappingStatus7; + u8* statuses = Num == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7; u32 offset = 0; while (offset < Size) { @@ -367,20 +367,20 @@ void ARMJIT_Memory::Mapping::Unmap(int region, ARMJIT_Memory& memory) noexcept bool success; if (dtcmStart > Addr) { - success = memory.UnmapFromRange(Addr, 0, OffsetsPerRegion[region] + LocalOffset, dtcmStart - Addr); + success = nds.JIT.Memory.UnmapFromRange(Addr, 0, OffsetsPerRegion[region] + LocalOffset, dtcmStart - Addr); assert(success); } if (dtcmEnd < Addr + Size) { u32 offset = dtcmStart - Addr + dtcmSize; - success = memory.UnmapFromRange(dtcmEnd, 0, OffsetsPerRegion[region] + LocalOffset + offset, Size - offset); + success = nds.JIT.Memory.UnmapFromRange(dtcmEnd, 0, OffsetsPerRegion[region] + LocalOffset + offset, Size - offset); assert(success); } } else #endif { - bool succeded = memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size); + bool succeded = nds.JIT.Memory.UnmapFromRange(Addr, Num, OffsetsPerRegion[region] + LocalOffset, Size); assert(succeded); } #endif @@ -401,7 +401,7 @@ void ARMJIT_Memory::SetCodeProtection(int region, u32 offset, bool protect) noex u32 effectiveAddr = mapping.Addr + (offset - mapping.LocalOffset); if (mapping.Num == 0 && region != memregion_DTCM - && (effectiveAddr & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase) + && (effectiveAddr & NDS.ARM9.DTCMMask) == NDS.ARM9.DTCMBase) continue; u8* states = (u8*)(mapping.Num == 0 ? MappingStatus9 : MappingStatus7); @@ -427,9 +427,9 @@ void ARMJIT_Memory::RemapDTCM(u32 newBase, u32 newSize) noexcept { // this first part could be made more efficient // by unmapping DTCM first and then map the holes - u32 oldDTCMBase = NDS::ARM9->DTCMBase; - u32 oldDTCMSize = ~NDS::ARM9->DTCMMask + 1; - u32 oldDTCMEnd = oldDTCMBase + NDS::ARM9->DTCMMask; + u32 oldDTCMBase = NDS.ARM9.DTCMBase; + u32 oldDTCMSize = ~NDS.ARM9.DTCMMask + 1; + u32 oldDTCMEnd = oldDTCMBase + NDS.ARM9.DTCMMask; u32 newEnd = newBase + newSize; @@ -454,7 +454,7 @@ void ARMJIT_Memory::RemapDTCM(u32 newBase, u32 newSize) noexcept if (mapping.Num == 0 && overlap) { - mapping.Unmap(region, *this); + mapping.Unmap(region, NDS); Mappings[region].Remove(i); } else @@ -466,20 +466,22 @@ void ARMJIT_Memory::RemapDTCM(u32 newBase, u32 newSize) noexcept for (int i = 0; i < Mappings[memregion_DTCM].Length; i++) { - Mappings[memregion_DTCM][i].Unmap(memregion_DTCM, *this); + Mappings[memregion_DTCM][i].Unmap(memregion_DTCM, NDS); } Mappings[memregion_DTCM].Clear(); } void ARMJIT_Memory::RemapNWRAM(int num) noexcept { + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); for (int i = 0; i < Mappings[memregion_SharedWRAM].Length;) { Mapping& mapping = Mappings[memregion_SharedWRAM][i]; - if (DSi::NWRAMStart[mapping.Num][num] < mapping.Addr + mapping.Size - && DSi::NWRAMEnd[mapping.Num][num] > mapping.Addr) + if (dsi->NWRAMStart[mapping.Num][num] < mapping.Addr + mapping.Size + && dsi->NWRAMEnd[mapping.Num][num] > mapping.Addr) { - mapping.Unmap(memregion_SharedWRAM, *this); + mapping.Unmap(memregion_SharedWRAM, NDS); Mappings[memregion_SharedWRAM].Remove(i); } else @@ -489,7 +491,7 @@ void ARMJIT_Memory::RemapNWRAM(int num) noexcept } for (int i = 0; i < Mappings[memregion_NewSharedWRAM_A + num].Length; i++) { - Mappings[memregion_NewSharedWRAM_A + num][i].Unmap(memregion_NewSharedWRAM_A + num, *this); + Mappings[memregion_NewSharedWRAM_A + num][i].Unmap(memregion_NewSharedWRAM_A + num, NDS); } Mappings[memregion_NewSharedWRAM_A + num].Clear(); } @@ -502,7 +504,7 @@ void ARMJIT_Memory::RemapSWRAM() noexcept Mapping& mapping = Mappings[memregion_WRAM7][i]; if (mapping.Addr + mapping.Size <= 0x03800000) { - mapping.Unmap(memregion_WRAM7, *this); + mapping.Unmap(memregion_WRAM7, NDS); Mappings[memregion_WRAM7].Remove(i); } else @@ -510,14 +512,14 @@ void ARMJIT_Memory::RemapSWRAM() noexcept } for (int i = 0; i < Mappings[memregion_SharedWRAM].Length; i++) { - Mappings[memregion_SharedWRAM][i].Unmap(memregion_SharedWRAM, *this); + Mappings[memregion_SharedWRAM][i].Unmap(memregion_SharedWRAM, NDS); } Mappings[memregion_SharedWRAM].Clear(); } bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept { - u32 num = NDS::CurCPU; + u32 num = NDS.CurCPU; int region = num == 0 ? ClassifyAddress9(addr) @@ -533,10 +535,10 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept u8* states = num == 0 ? MappingStatus9 : MappingStatus7; //printf("mapping mirror %x, %x %x %d %d\n", mirrorStart, mirrorSize, memoryOffset, region, num); - bool isExecutable = JIT.CodeMemRegions[region]; + bool isExecutable = NDS.JIT.CodeMemRegions[region]; - u32 dtcmStart = NDS::ARM9->DTCMBase; - u32 dtcmSize = ~NDS::ARM9->DTCMMask + 1; + u32 dtcmStart = NDS.ARM9.DTCMBase; + u32 dtcmSize = ~NDS.ARM9.DTCMMask + 1; u32 dtcmEnd = dtcmStart + dtcmSize; #ifndef __SWITCH__ #ifndef _WIN32 @@ -565,7 +567,7 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept } #endif - AddressRange* range = JIT.CodeMemRegions[region] + memoryOffset / 512; + AddressRange* range = NDS.JIT.CodeMemRegions[region] + memoryOffset / 512; // this overcomplicated piece of code basically just finds whole pieces of code memory // which can be mapped/protected @@ -586,7 +588,7 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept bool hasCode = isExecutable && PageContainsCode(&range[offset / 512]); while (offset < mirrorSize && (!isExecutable || PageContainsCode(&range[offset / 512]) == hasCode) - && (!skipDTCM || mirrorStart + offset != NDS::ARM9->DTCMBase)) + && (!skipDTCM || mirrorStart + offset != NDS.ARM9.DTCMBase)) { assert(states[(mirrorStart + offset) >> 12] == memstate_Unmapped); states[(mirrorStart + offset) >> 12] = hasCode ? memstate_MappedProtected : memstate_MappedRW; @@ -620,19 +622,19 @@ bool ARMJIT_Memory::MapAtAddress(u32 addr) noexcept return true; } -bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, ARMJIT& jit) +bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds) { - if (jit.JITCompiler.IsJITFault(faultDesc.FaultPC)) + if (nds.JIT.JITCompiler.IsJITFault(faultDesc.FaultPC)) { bool rewriteToSlowPath = true; - u8* memStatus = NDS::CurCPU == 0 ? jit.Memory.MappingStatus9 : jit.Memory.MappingStatus7; + u8* memStatus = nds.CurCPU == 0 ? nds.JIT.Memory.MappingStatus9 : nds.JIT.Memory.MappingStatus7; if (memStatus[faultDesc.EmulatedFaultAddr >> 12] == memstate_Unmapped) - rewriteToSlowPath = !jit.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr); + rewriteToSlowPath = !nds.JIT.Memory.MapAtAddress(faultDesc.EmulatedFaultAddr); if (rewriteToSlowPath) - faultDesc.FaultPC = jit.JITCompiler.RewriteMemAccess(faultDesc.FaultPC); + faultDesc.FaultPC = nds.JIT.JITCompiler.RewriteMemAccess(faultDesc.FaultPC); return true; } @@ -641,7 +643,7 @@ bool ARMJIT_Memory::FaultHandler(FaultDescription& faultDesc, ARMJIT& jit) const u64 AddrSpaceSize = 0x100000000; -ARMJIT_Memory::ARMJIT_Memory(ARMJIT& jit) noexcept : JIT(jit) +ARMJIT_Memory::ARMJIT_Memory(melonDS::NDS& nds) : NDS(nds) { #if defined(__SWITCH__) MemoryBase = (u8*)aligned_alloc(0x1000, MemoryTotalSize); @@ -815,7 +817,7 @@ void ARMJIT_Memory::Reset() noexcept for (int region = 0; region < memregions_Count; region++) { for (int i = 0; i < Mappings[region].Length; i++) - Mappings[region][i].Unmap(region, *this); + Mappings[region][i].Unmap(region, NDS); Mappings[region].Clear(); } @@ -866,8 +868,8 @@ bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memory } return false; case memregion_MainRAM: - mirrorStart = addr & ~NDS::MainRAMMask; - mirrorSize = NDS::MainRAMMask + 1; + mirrorStart = addr & ~NDS.MainRAMMask; + mirrorSize = NDS.MainRAMMask + 1; return true; case memregion_BIOS9: if (num == 0) @@ -886,26 +888,26 @@ bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memory } return false; case memregion_SharedWRAM: - if (num == 0 && NDS::SWRAM_ARM9.Mem) + if (num == 0 && NDS.SWRAM_ARM9.Mem) { - mirrorStart = addr & ~NDS::SWRAM_ARM9.Mask; - mirrorSize = NDS::SWRAM_ARM9.Mask + 1; - memoryOffset = NDS::SWRAM_ARM9.Mem - GetSharedWRAM(); + mirrorStart = addr & ~NDS.SWRAM_ARM9.Mask; + mirrorSize = NDS.SWRAM_ARM9.Mask + 1; + memoryOffset = NDS.SWRAM_ARM9.Mem - GetSharedWRAM(); return true; } - else if (num == 1 && NDS::SWRAM_ARM7.Mem) + else if (num == 1 && NDS.SWRAM_ARM7.Mem) { - mirrorStart = addr & ~NDS::SWRAM_ARM7.Mask; - mirrorSize = NDS::SWRAM_ARM7.Mask + 1; - memoryOffset = NDS::SWRAM_ARM7.Mem - GetSharedWRAM(); + mirrorStart = addr & ~NDS.SWRAM_ARM7.Mask; + mirrorSize = NDS.SWRAM_ARM7.Mask + 1; + memoryOffset = NDS.SWRAM_ARM7.Mem - GetSharedWRAM(); return true; } return false; case memregion_WRAM7: if (num == 1) { - mirrorStart = addr & ~(NDS::ARM7WRAMSize - 1); - mirrorSize = NDS::ARM7WRAMSize; + mirrorStart = addr & ~(ARM7WRAMSize - 1); + mirrorSize = ARM7WRAMSize; return true; } return false; @@ -927,7 +929,9 @@ bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memory return false; case memregion_NewSharedWRAM_A: { - u8* ptr = DSi::NWRAMMap_A[num][(addr >> 16) & DSi::NWRAMMask[num][0]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_A[num][(addr >> 16) & dsi->NWRAMMask[num][0]]; if (ptr) { memoryOffset = ptr - GetNWRAM_A(); @@ -939,7 +943,9 @@ bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memory } case memregion_NewSharedWRAM_B: { - u8* ptr = DSi::NWRAMMap_B[num][(addr >> 15) & DSi::NWRAMMask[num][1]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_B[num][(addr >> 15) & dsi->NWRAMMask[num][1]]; if (ptr) { memoryOffset = ptr - GetNWRAM_B(); @@ -951,7 +957,9 @@ bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memory } case memregion_NewSharedWRAM_C: { - u8* ptr = DSi::NWRAMMap_C[num][(addr >> 15) & DSi::NWRAMMask[num][2]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_C[num][(addr >> 15) & dsi->NWRAMMask[num][2]]; if (ptr) { memoryOffset = ptr - GetNWRAM_C(); @@ -964,16 +972,20 @@ bool ARMJIT_Memory::GetMirrorLocation(int region, u32 num, u32 addr, u32& memory case memregion_BIOS9DSi: if (num == 0) { + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); mirrorStart = addr & ~0xFFFF; - mirrorSize = DSi::SCFG_BIOS & (1<<0) ? 0x8000 : 0x10000; + mirrorSize = dsi->SCFG_BIOS & (1<<0) ? 0x8000 : 0x10000; return true; } return false; case memregion_BIOS7DSi: if (num == 1) { + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); mirrorStart = addr & ~0xFFFF; - mirrorSize = DSi::SCFG_BIOS & (1<<8) ? 0x8000 : 0x10000; + mirrorSize = dsi->SCFG_BIOS & (1<<8) ? 0x8000 : 0x10000; return true; } return false; @@ -990,18 +1002,18 @@ u32 ARMJIT_Memory::LocaliseAddress(int region, u32 num, u32 addr) const noexcept case memregion_ITCM: return (addr & (ITCMPhysicalSize - 1)) | (memregion_ITCM << 27); case memregion_MainRAM: - return (addr & NDS::MainRAMMask) | (memregion_MainRAM << 27); + return (addr & NDS.MainRAMMask) | (memregion_MainRAM << 27); case memregion_BIOS9: return (addr & 0xFFF) | (memregion_BIOS9 << 27); case memregion_BIOS7: return (addr & 0x3FFF) | (memregion_BIOS7 << 27); case memregion_SharedWRAM: if (num == 0) - return ((addr & NDS::SWRAM_ARM9.Mask) + (NDS::SWRAM_ARM9.Mem - GetSharedWRAM())) | (memregion_SharedWRAM << 27); + return ((addr & NDS.SWRAM_ARM9.Mask) + (NDS.SWRAM_ARM9.Mem - GetSharedWRAM())) | (memregion_SharedWRAM << 27); else - return ((addr & NDS::SWRAM_ARM7.Mask) + (NDS::SWRAM_ARM7.Mem - GetSharedWRAM())) | (memregion_SharedWRAM << 27); + return ((addr & NDS.SWRAM_ARM7.Mask) + (NDS.SWRAM_ARM7.Mem - GetSharedWRAM())) | (memregion_SharedWRAM << 27); case memregion_WRAM7: - return (addr & (NDS::ARM7WRAMSize - 1)) | (memregion_WRAM7 << 27); + return (addr & (melonDS::ARM7WRAMSize - 1)) | (memregion_WRAM7 << 27); case memregion_VRAM: // TODO: take mapping properly into account return (addr & 0xFFFFF) | (memregion_VRAM << 27); @@ -1010,7 +1022,9 @@ u32 ARMJIT_Memory::LocaliseAddress(int region, u32 num, u32 addr) const noexcept return (addr & 0x3FFFF) | (memregion_VWRAM << 27); case memregion_NewSharedWRAM_A: { - u8* ptr = DSi::NWRAMMap_A[num][(addr >> 16) & DSi::NWRAMMask[num][0]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_A[num][(addr >> 16) & dsi->NWRAMMask[num][0]]; if (ptr) return (ptr - GetNWRAM_A() + (addr & 0xFFFF)) | (memregion_NewSharedWRAM_A << 27); else @@ -1018,7 +1032,9 @@ u32 ARMJIT_Memory::LocaliseAddress(int region, u32 num, u32 addr) const noexcept } case memregion_NewSharedWRAM_B: { - u8* ptr = DSi::NWRAMMap_B[num][(addr >> 15) & DSi::NWRAMMask[num][1]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_B[num][(addr >> 15) & dsi->NWRAMMask[num][1]]; if (ptr) return (ptr - GetNWRAM_B() + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_B << 27); else @@ -1026,7 +1042,9 @@ u32 ARMJIT_Memory::LocaliseAddress(int region, u32 num, u32 addr) const noexcept } case memregion_NewSharedWRAM_C: { - u8* ptr = DSi::NWRAMMap_C[num][(addr >> 15) & DSi::NWRAMMask[num][2]]; + auto* dsi = dynamic_cast(&NDS); + assert(dsi != nullptr); + u8* ptr = dsi->NWRAMMap_C[num][(addr >> 15) & dsi->NWRAMMask[num][2]]; if (ptr) return (ptr - GetNWRAM_C() + (addr & 0x7FFF)) | (memregion_NewSharedWRAM_C << 27); else @@ -1043,19 +1061,20 @@ u32 ARMJIT_Memory::LocaliseAddress(int region, u32 num, u32 addr) const noexcept int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept { - if (addr < NDS::ARM9->ITCMSize) + if (addr < NDS.ARM9.ITCMSize) { return memregion_ITCM; } - else if ((addr & NDS::ARM9->DTCMMask) == NDS::ARM9->DTCMBase) + else if ((addr & NDS.ARM9.DTCMMask) == NDS.ARM9.DTCMBase) { return memregion_DTCM; } else { - if (NDS::ConsoleType == 1 && addr >= 0xFFFF0000 && !(DSi::SCFG_BIOS & (1<<1))) + auto& dsi = static_cast(NDS); // ONLY use this if ConsoleType == 1! + if (NDS.ConsoleType == 1 && addr >= 0xFFFF0000 && !(dsi.SCFG_BIOS & (1<<1))) { - if ((addr >= 0xFFFF8000) && (DSi::SCFG_BIOS & (1<<0))) + if ((addr >= 0xFFFF8000) && (dsi.SCFG_BIOS & (1<<0))) return memregion_Other; return memregion_BIOS9DSi; @@ -1070,17 +1089,17 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept case 0x02000000: return memregion_MainRAM; case 0x03000000: - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { - if (addr >= DSi::NWRAMStart[0][0] && addr < DSi::NWRAMEnd[0][0]) + if (addr >= dsi.NWRAMStart[0][0] && addr < dsi.NWRAMEnd[0][0]) return memregion_NewSharedWRAM_A; - if (addr >= DSi::NWRAMStart[0][1] && addr < DSi::NWRAMEnd[0][1]) + if (addr >= dsi.NWRAMStart[0][1] && addr < dsi.NWRAMEnd[0][1]) return memregion_NewSharedWRAM_B; - if (addr >= DSi::NWRAMStart[0][2] && addr < DSi::NWRAMEnd[0][2]) + if (addr >= dsi.NWRAMStart[0][2] && addr < dsi.NWRAMEnd[0][2]) return memregion_NewSharedWRAM_C; } - if (NDS::SWRAM_ARM9.Mem) + if (NDS.SWRAM_ARM9.Mem) return memregion_SharedWRAM; return memregion_Other; case 0x04000000: @@ -1088,7 +1107,7 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept case 0x06000000: return memregion_VRAM; case 0x0C000000: - return (NDS::ConsoleType==1) ? memregion_MainRAM : memregion_Other; + return (NDS.ConsoleType==1) ? memregion_MainRAM : memregion_Other; default: return memregion_Other; } @@ -1097,9 +1116,10 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept { - if (NDS::ConsoleType == 1 && addr < 0x00010000 && !(DSi::SCFG_BIOS & (1<<9))) + auto& dsi = static_cast(NDS); + if (NDS.ConsoleType == 1 && addr < 0x00010000 && !(dsi.SCFG_BIOS & (1<<9))) { - if (addr >= 0x00008000 && DSi::SCFG_BIOS & (1<<8)) + if (addr >= 0x00008000 && dsi.SCFG_BIOS & (1<<8)) return memregion_Other; return memregion_BIOS7DSi; @@ -1116,17 +1136,17 @@ int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept case 0x02800000: return memregion_MainRAM; case 0x03000000: - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { - if (addr >= DSi::NWRAMStart[1][0] && addr < DSi::NWRAMEnd[1][0]) + if (addr >= dsi.NWRAMStart[1][0] && addr < dsi.NWRAMEnd[1][0]) return memregion_NewSharedWRAM_A; - if (addr >= DSi::NWRAMStart[1][1] && addr < DSi::NWRAMEnd[1][1]) + if (addr >= dsi.NWRAMStart[1][1] && addr < dsi.NWRAMEnd[1][1]) return memregion_NewSharedWRAM_B; - if (addr >= DSi::NWRAMStart[1][2] && addr < DSi::NWRAMEnd[1][2]) + if (addr >= dsi.NWRAMStart[1][2] && addr < dsi.NWRAMEnd[1][2]) return memregion_NewSharedWRAM_C; } - if (NDS::SWRAM_ARM7.Mem) + if (NDS.SWRAM_ARM7.Mem) return memregion_SharedWRAM; return memregion_WRAM7; case 0x03800000: @@ -1140,7 +1160,7 @@ int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept return memregion_VWRAM; case 0x0C000000: case 0x0C800000: - return (NDS::ConsoleType==1) ? memregion_MainRAM : memregion_Other; + return (NDS.ConsoleType==1) ? memregion_MainRAM : memregion_Other; default: return memregion_Other; } @@ -1163,11 +1183,11 @@ void VRAMWrite(u32 addr, T val) { switch (addr & 0x00E00000) { - case 0x00000000: NDS::GPU->WriteVRAM_ABG(addr, val); return; - case 0x00200000: NDS::GPU->WriteVRAM_BBG(addr, val); return; - case 0x00400000: NDS::GPU->WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: NDS::GPU->WriteVRAM_BOBJ(addr, val); return; - default: NDS::GPU->WriteVRAM_LCDC(addr, val); return; + case 0x00000000: NDS::Current->GPU.WriteVRAM_ABG(addr, val); return; + case 0x00200000: NDS::Current->GPU.WriteVRAM_BBG(addr, val); return; + case 0x00400000: NDS::Current->GPU.WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: NDS::Current->GPU.WriteVRAM_BOBJ(addr, val); return; + default: NDS::Current->GPU.WriteVRAM_LCDC(addr, val); return; } } template @@ -1175,59 +1195,119 @@ T VRAMRead(u32 addr) { switch (addr & 0x00E00000) { - case 0x00000000: return NDS::GPU->ReadVRAM_ABG(addr); - case 0x00200000: return NDS::GPU->ReadVRAM_BBG(addr); - case 0x00400000: return NDS::GPU->ReadVRAM_AOBJ(addr); - case 0x00600000: return NDS::GPU->ReadVRAM_BOBJ(addr); - default: return NDS::GPU->ReadVRAM_LCDC(addr); + case 0x00000000: return NDS::Current->GPU.ReadVRAM_ABG(addr); + case 0x00200000: return NDS::Current->GPU.ReadVRAM_BBG(addr); + case 0x00400000: return NDS::Current->GPU.ReadVRAM_AOBJ(addr); + case 0x00600000: return NDS::Current->GPU.ReadVRAM_BOBJ(addr); + default: return NDS::Current->GPU.ReadVRAM_LCDC(addr); } } static u8 GPU3D_Read8(u32 addr) noexcept { - return NDS::GPU->GPU3D.Read8(addr); + return NDS::Current->GPU.GPU3D.Read8(addr); } static u16 GPU3D_Read16(u32 addr) noexcept { - return NDS::GPU->GPU3D.Read16(addr); + return NDS::Current->GPU.GPU3D.Read16(addr); } static u32 GPU3D_Read32(u32 addr) noexcept { - return NDS::GPU->GPU3D.Read32(addr); + return NDS::Current->GPU.GPU3D.Read32(addr); } static void GPU3D_Write8(u32 addr, u8 val) noexcept { - NDS::GPU->GPU3D.Write8(addr, val); + NDS::Current->GPU.GPU3D.Write8(addr, val); } static void GPU3D_Write16(u32 addr, u16 val) noexcept { - NDS::GPU->GPU3D.Write16(addr, val); + NDS::Current->GPU.GPU3D.Write16(addr, val); } static void GPU3D_Write32(u32 addr, u32 val) noexcept { - NDS::GPU->GPU3D.Write32(addr, val); + NDS::Current->GPU.GPU3D.Write32(addr, val); } template static T GPU_ReadVRAM_ARM7(u32 addr) noexcept { - return NDS::GPU->ReadVRAM_ARM7(addr); + return NDS::Current->GPU.ReadVRAM_ARM7(addr); } template static void GPU_WriteVRAM_ARM7(u32 addr, T val) noexcept { - NDS::GPU->WriteVRAM_ARM7(addr, val); + NDS::Current->GPU.WriteVRAM_ARM7(addr, val); } u32 NDSCartSlot_ReadROMData() { // TODO: Add a NDS* parameter, when NDS* is eventually implemented - return NDS::NDSCartSlot->ReadROMData(); + return NDS::Current->NDSCartSlot.ReadROMData(); +} + +static u8 NDS_ARM9IORead8(u32 addr) +{ + return NDS::Current->ARM9IORead8(addr); +} + +static u16 NDS_ARM9IORead16(u32 addr) +{ + return NDS::Current->ARM9IORead16(addr); +} + +static u32 NDS_ARM9IORead32(u32 addr) +{ + return NDS::Current->ARM9IORead32(addr); +} + +static void NDS_ARM9IOWrite8(u32 addr, u8 val) +{ + NDS::Current->ARM9IOWrite8(addr, val); +} + +static void NDS_ARM9IOWrite16(u32 addr, u16 val) +{ + NDS::Current->ARM9IOWrite16(addr, val); +} + +static void NDS_ARM9IOWrite32(u32 addr, u32 val) +{ + NDS::Current->ARM9IOWrite32(addr, val); +} + +static u8 NDS_ARM7IORead8(u32 addr) +{ + return NDS::Current->ARM7IORead8(addr); +} + +static u16 NDS_ARM7IORead16(u32 addr) +{ + return NDS::Current->ARM7IORead16(addr); +} + +static u32 NDS_ARM7IORead32(u32 addr) +{ + return NDS::Current->ARM7IORead32(addr); +} + +static void NDS_ARM7IOWrite8(u32 addr, u8 val) +{ + NDS::Current->ARM7IOWrite8(addr, val); +} + +static void NDS_ARM7IOWrite16(u32 addr, u16 val) +{ + NDS::Current->ARM7IOWrite16(addr, val); +} + +static void NDS_ARM7IOWrite32(u32 addr, u32 val) +{ + NDS::Current->ARM7IOWrite32(addr, val); } void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) const noexcept @@ -1237,7 +1317,7 @@ void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) co switch (addr & 0xFF000000) { case 0x04000000: - if (!store && size == 32 && addr == 0x04100010 && NDS::ExMemCnt[0] & (1<<11)) + if (!store && size == 32 && addr == 0x04100010 && NDS.ExMemCnt[0] & (1<<11)) return (void*)NDSCartSlot_ReadROMData; /* @@ -1259,30 +1339,17 @@ void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) co } } - if (NDS::ConsoleType == 0) + switch (size | store) { - switch (size | store) - { - case 8: return (void*)NDS::ARM9IORead8; - case 9: return (void*)NDS::ARM9IOWrite8; - case 16: return (void*)NDS::ARM9IORead16; - case 17: return (void*)NDS::ARM9IOWrite16; - case 32: return (void*)NDS::ARM9IORead32; - case 33: return (void*)NDS::ARM9IOWrite32; - } - } - else - { - switch (size | store) - { - case 8: return (void*)DSi::ARM9IORead8; - case 9: return (void*)DSi::ARM9IOWrite8; - case 16: return (void*)DSi::ARM9IORead16; - case 17: return (void*)DSi::ARM9IOWrite16; - case 32: return (void*)DSi::ARM9IORead32; - case 33: return (void*)DSi::ARM9IOWrite32; - } + case 8: return (void*)NDS_ARM9IORead8; + case 9: return (void*)NDS_ARM9IOWrite8; + case 16: return (void*)NDS_ARM9IORead16; + case 17: return (void*)NDS_ARM9IOWrite16; + case 32: return (void*)NDS_ARM9IORead32; + case 33: return (void*)NDS_ARM9IOWrite32; } + // NDS::Current will delegate to the DSi versions of these methods + // if it's really a DSi break; case 0x06000000: switch (size | store) @@ -1315,29 +1382,14 @@ void* ARMJIT_Memory::GetFuncForAddr(ARM* cpu, u32 addr, bool store, int size) co } }*/ - if (NDS::ConsoleType == 0) + switch (size | store) { - switch (size | store) - { - case 8: return (void*)NDS::ARM7IORead8; - case 9: return (void*)NDS::ARM7IOWrite8; - case 16: return (void*)NDS::ARM7IORead16; - case 17: return (void*)NDS::ARM7IOWrite16; - case 32: return (void*)NDS::ARM7IORead32; - case 33: return (void*)NDS::ARM7IOWrite32; - } - } - else - { - switch (size | store) - { - case 8: return (void*)DSi::ARM7IORead8; - case 9: return (void*)DSi::ARM7IOWrite8; - case 16: return (void*)DSi::ARM7IORead16; - case 17: return (void*)DSi::ARM7IOWrite16; - case 32: return (void*)DSi::ARM7IORead32; - case 33: return (void*)DSi::ARM7IOWrite32; - } + case 8: return (void*)NDS_ARM7IORead8; + case 9: return (void*)NDS_ARM7IOWrite8; + case 16: return (void*)NDS_ARM7IORead16; + case 17: return (void*)NDS_ARM7IOWrite16; + case 32: return (void*)NDS_ARM7IORead32; + case 33: return (void*)NDS_ARM7IOWrite32; } break; // TODO: the wifi funcs also ought to check POWCNT diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index ed484373..487005b2 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -23,7 +23,7 @@ #include "TinyVector.h" #include "ARM.h" -#include "DSi.h" +#include "MemConstants.h" #if defined(__SWITCH__) #include @@ -44,6 +44,7 @@ namespace melonDS { +namespace Platform { struct DynamicLibrary; } class Compiler; class ARMJIT; @@ -57,13 +58,13 @@ constexpr u32 RoundUp(u32 size) noexcept } const u32 MemBlockMainRAMOffset = 0; -const u32 MemBlockSWRAMOffset = RoundUp(NDS::MainRAMMaxSize); -const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(NDS::SharedWRAMSize); -const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(NDS::ARM7WRAMSize); +const u32 MemBlockSWRAMOffset = RoundUp(MainRAMMaxSize); +const u32 MemBlockARM7WRAMOffset = MemBlockSWRAMOffset + RoundUp(SharedWRAMSize); +const u32 MemBlockDTCMOffset = MemBlockARM7WRAMOffset + RoundUp(ARM7WRAMSize); const u32 MemBlockNWRAM_AOffset = MemBlockDTCMOffset + RoundUp(DTCMPhysicalSize); -const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(DSi::NWRAMSize); -const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(DSi::NWRAMSize); -const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(DSi::NWRAMSize); +const u32 MemBlockNWRAM_BOffset = MemBlockNWRAM_AOffset + RoundUp(NWRAMSize); +const u32 MemBlockNWRAM_COffset = MemBlockNWRAM_BOffset + RoundUp(NWRAMSize); +const u32 MemoryTotalSize = MemBlockNWRAM_COffset + RoundUp(NWRAMSize); class ARMJIT_Memory { @@ -96,7 +97,7 @@ public: #ifdef JIT_ENABLED public: - explicit ARMJIT_Memory(ARMJIT& jit) noexcept; + explicit ARMJIT_Memory(melonDS::NDS& nds); ~ARMJIT_Memory() noexcept; ARMJIT_Memory(const ARMJIT_Memory&) = delete; ARMJIT_Memory(ARMJIT_Memory&&) = delete; @@ -144,7 +145,7 @@ private: u32 Size, LocalOffset; u32 Num; - void Unmap(int region, ARMJIT_Memory& memory) noexcept; + void Unmap(int region, NDS& nds) noexcept; }; struct FaultDescription @@ -152,12 +153,12 @@ private: u32 EmulatedFaultAddr; u8* FaultPC; }; - static bool FaultHandler(FaultDescription& faultDesc, ARMJIT& jit); + static bool FaultHandler(FaultDescription& faultDesc, melonDS::NDS& nds); bool MapIntoRange(u32 addr, u32 num, u32 offset, u32 size) noexcept; bool UnmapFromRange(u32 addr, u32 num, u32 offset, u32 size) noexcept; void SetCodeProtectionRange(u32 addr, u32 size, u32 num, int protection) noexcept; - ARMJIT& JIT; + melonDS::NDS& NDS; void* FastMem9Start; void* FastMem7Start; u8* MemoryBase = nullptr; @@ -180,7 +181,7 @@ private: TinyVector Mappings[memregions_Count] {}; #else public: - explicit ARMJIT_Memory(ARMJIT&) {}; + explicit ARMJIT_Memory(melonDS::NDS&) {}; ~ARMJIT_Memory() = default; ARMJIT_Memory(const ARMJIT_Memory&) = delete; ARMJIT_Memory(ARMJIT_Memory&&) = delete; @@ -214,13 +215,13 @@ public: [[nodiscard]] u8* GetNWRAM_C() noexcept { return NWRAM_C.data(); } [[nodiscard]] const u8* GetNWRAM_C() const noexcept { return NWRAM_C.data(); } private: - std::array MainRAM {}; - std::array ARM7WRAM {}; - std::array SharedWRAM {}; + std::array MainRAM {}; + std::array ARM7WRAM {}; + std::array SharedWRAM {}; std::array DTCM {}; - std::array NWRAM_A {}; - std::array NWRAM_B {}; - std::array NWRAM_C {}; + std::array NWRAM_A {}; + std::array NWRAM_B {}; + std::array NWRAM_C {}; #endif }; } diff --git a/src/ARMJIT_x64/ARMJIT_Branch.cpp b/src/ARMJIT_x64/ARMJIT_Branch.cpp index 5d76447e..f7a01f48 100644 --- a/src/ARMJIT_x64/ARMJIT_Branch.cpp +++ b/src/ARMJIT_x64/ARMJIT_Branch.cpp @@ -18,6 +18,7 @@ #include "ARMJIT_Compiler.h" #include "../ARM.h" +#include "../NDS.h" using namespace Gen; @@ -120,7 +121,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) u32 compileTimePC = CurCPU->R[15]; CurCPU->R[15] = newPC; - cycles += NDS::ARM7MemTimings[codeCycles][0] + NDS::ARM7MemTimings[codeCycles][1]; + cycles += NDS.ARM7MemTimings[codeCycles][0] + NDS.ARM7MemTimings[codeCycles][1]; CurCPU->R[15] = compileTimePC; } @@ -132,7 +133,7 @@ void Compiler::Comp_JumpTo(u32 addr, bool forceNonConstantCycles) u32 compileTimePC = CurCPU->R[15]; CurCPU->R[15] = newPC; - cycles += NDS::ARM7MemTimings[codeCycles][2] + NDS::ARM7MemTimings[codeCycles][3]; + cycles += NDS.ARM7MemTimings[codeCycles][2] + NDS.ARM7MemTimings[codeCycles][3]; CurCPU->R[15] = compileTimePC; } diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp index 9fc72370..eec4d7d1 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp @@ -20,6 +20,7 @@ #include "../ARMJIT.h" #include "../ARMInterpreter.h" +#include "../NDS.h" #include #include @@ -234,7 +235,7 @@ void Compiler::A_Comp_MSR() */ u8 CodeMemory[1024 * 1024 * 32]; -Compiler::Compiler(ARMJIT& jit) : XEmitter(), JIT(jit) +Compiler::Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) { { #ifdef _WIN32 @@ -714,12 +715,12 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] if (NearSize - (GetCodePtr() - NearStart) < 1024 * 32) // guess... { Log(LogLevel::Debug, "near reset\n"); - JIT.ResetBlockCache(); + NDS.JIT.ResetBlockCache(); } if (FarSize - (FarCode - FarStart) < 1024 * 32) // guess... { Log(LogLevel::Debug, "far reset\n"); - JIT.ResetBlockCache(); + NDS.JIT.ResetBlockCache(); } ConstantCycles = 0; @@ -863,7 +864,7 @@ JitBlockEntry Compiler::CompileBlock(ARM* cpu, bool thumb, FetchedInstr instrs[] void Compiler::Comp_AddCycles_C(bool forceNonConstant) { s32 cycles = Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 1 : 3] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles); if ((!Thumb && CurInstr.Cond() < 0xE) || forceNonConstant) @@ -875,7 +876,7 @@ void Compiler::Comp_AddCycles_C(bool forceNonConstant) void Compiler::Comp_AddCycles_CI(u32 i) { s32 cycles = (Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles)) + i; if (!Thumb && CurInstr.Cond() < 0xE) @@ -887,7 +888,7 @@ void Compiler::Comp_AddCycles_CI(u32 i) void Compiler::Comp_AddCycles_CI(Gen::X64Reg i, int add) { s32 cycles = Num ? - NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] + NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2] : ((R15 & 0x2) ? 0 : CurInstr.CodeCycles); if (!Thumb && CurInstr.Cond() < 0xE) @@ -912,7 +913,7 @@ void Compiler::Comp_AddCycles_CDI() s32 cycles; - s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; + s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; s32 numD = CurInstr.DataCycles; if ((CurInstr.DataRegion >> 24) == 0x02) // mainRAM @@ -957,7 +958,7 @@ void Compiler::Comp_AddCycles_CD() } else { - s32 numC = NDS::ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; + s32 numC = NDS.ARM7MemTimings[CurInstr.CodeCycles][Thumb ? 0 : 2]; s32 numD = CurInstr.DataCycles; if ((CurInstr.DataRegion >> 4) == 0x02) diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index f38a6c3a..fa6d78a4 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -35,6 +35,7 @@ namespace melonDS { class ARMJIT; class ARMJIT_Memory; +class NDS; const Gen::X64Reg RCPU = Gen::RBP; const Gen::X64Reg RCPSR = Gen::R15; @@ -81,9 +82,9 @@ class Compiler : public Gen::XEmitter { public: #ifdef JIT_ENABLED - explicit Compiler(ARMJIT& jit); + explicit Compiler(melonDS::NDS& nds); #else - explicit Compiler(ARMJIT& jit) : XEmitter(), JIT(jit) {} + explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {} #endif void Reset(); @@ -243,7 +244,7 @@ public: void CreateMethod(const char* namefmt, void* start, ...); #endif - ARMJIT& JIT; + melonDS::NDS& NDS; u8* FarCode {}; u8* NearCode {}; u32 FarSize {}; diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp index 9b7d865f..72a073db 100644 --- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp @@ -18,6 +18,7 @@ #include "ARMJIT_Compiler.h" #include "../ARMJIT.h" +#include "../NDS.h" using namespace Gen; @@ -68,9 +69,9 @@ u8* Compiler::RewriteMemAccess(u8* pc) bool Compiler::Comp_MemLoadLiteral(int size, bool signExtend, int rd, u32 addr) { - u32 localAddr = JIT.LocaliseCodeAddress(Num, addr); + u32 localAddr = NDS.JIT.LocaliseCodeAddress(Num, addr); - int invalidLiteralIdx = JIT.InvalidLiterals.Find(localAddr); + int invalidLiteralIdx = NDS.JIT.InvalidLiterals.Find(localAddr); if (invalidLiteralIdx != -1) { return false; @@ -118,7 +119,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag if (size == 16) addressMask = ~1; - if (JIT.LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) + if (NDS.JIT.LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) { u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1); @@ -135,7 +136,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag Comp_AddCycles_CDI(); } - bool addrIsStatic = JIT.LiteralOptimizations + bool addrIsStatic = NDS.JIT.LiteralOptimizations && RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post)); u32 staticAddress; if (addrIsStatic) @@ -196,10 +197,10 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag MOV(32, rnMapped, R(finalAddr)); u32 expectedTarget = Num == 0 - ? JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) - : JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); + ? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); - if (JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || JIT.Memory.IsFastmemCompatible(expectedTarget))) + if (NDS.JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget))) { if (rdMapped.IsImm()) { @@ -212,12 +213,12 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag assert(rdMapped.GetSimpleReg() >= 0 && rdMapped.GetSimpleReg() < 16); patch.PatchFunc = flags & memop_Store - ? PatchedStoreFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()] - : PatchedLoadFuncs[NDS::ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()]; + ? PatchedStoreFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][rdMapped.GetSimpleReg()] + : PatchedLoadFuncs[NDS.ConsoleType][Num][__builtin_ctz(size) - 3][!!(flags & memop_SignExtend)][rdMapped.GetSimpleReg()]; assert(patch.PatchFunc != NULL); - MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? JIT.Memory.FastMem9Start : JIT.Memory.FastMem7Start)); + MOV(64, R(RSCRATCH), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start)); X64Reg maskedAddr = RSCRATCH3; if (size > 8) @@ -268,7 +269,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag void* func = NULL; if (addrIsStatic) - func = JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); + func = NDS.JIT.Memory.GetFuncForAddr(CurCPU, staticAddress, flags & memop_Store, size); if (func) { @@ -313,7 +314,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag MOV(32, R(ABI_PARAM1), R(RSCRATCH3)); if (flags & memop_Store) { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: CALL((void*)&SlowWrite9); break; case 16: CALL((void*)&SlowWrite9); break; @@ -325,7 +326,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag } else { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: CALL((void*)&SlowRead9); break; case 16: CALL((void*)&SlowRead9); break; @@ -344,7 +345,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag { MOV(32, R(ABI_PARAM2), rdMapped); - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: CALL((void*)&SlowWrite7); break; case 16: CALL((void*)&SlowWrite7); break; @@ -356,7 +357,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag } else { - switch (size | NDS::ConsoleType) + switch (size | NDS.ConsoleType) { case 32: CALL((void*)&SlowRead7); break; case 16: CALL((void*)&SlowRead7); break; @@ -422,16 +423,16 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc s32 offset = (regsCount * 4) * (decrement ? -1 : 1); int expectedTarget = Num == 0 - ? JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) - : JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); + ? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) + : NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); if (!store) Comp_AddCycles_CDI(); else Comp_AddCycles_CD(); - bool compileFastPath = JIT.FastMemory - && !usermode && (CurInstr.Cond() < 0xE || JIT.Memory.IsFastmemCompatible(expectedTarget)); + bool compileFastPath = NDS.JIT.FastMemory + && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)); // we need to make sure that the stack stays aligned to 16 bytes #ifdef _WIN32 @@ -454,7 +455,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc u8* fastPathStart = GetWritableCodePtr(); u8* loadStoreAddr[16]; - MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? JIT.Memory.FastMem9Start : JIT.Memory.FastMem7Start)); + MOV(64, R(RSCRATCH2), ImmPtr(Num == 0 ? NDS.JIT.Memory.FastMem9Start : NDS.JIT.Memory.FastMem7Start)); ADD(64, R(RSCRATCH2), R(RSCRATCH4)); u32 offset = 0; @@ -523,7 +524,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc if (Num == 0) MOV(64, R(ABI_PARAM4), R(RCPU)); - switch (Num * 2 | NDS::ConsoleType) + switch (Num * 2 | NDS.ConsoleType) { case 0: CALL((void*)&SlowBlockTransfer9); break; case 1: CALL((void*)&SlowBlockTransfer9); break; @@ -627,7 +628,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc if (Num == 0) MOV(64, R(ABI_PARAM4), R(RCPU)); - switch (Num * 2 | NDS::ConsoleType) + switch (Num * 2 | NDS.ConsoleType) { case 0: CALL((void*)&SlowBlockTransfer9); break; case 1: CALL((void*)&SlowBlockTransfer9); break; @@ -808,7 +809,7 @@ void Compiler::T_Comp_LoadPCRel() { u32 offset = (CurInstr.Instr & 0xFF) << 2; u32 addr = (R15 & ~0x2) + offset; - if (!JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) + if (!NDS.JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0); } diff --git a/src/CP15.cpp b/src/CP15.cpp index af23da3a..7cea845d 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -124,7 +124,7 @@ void ARMv5::UpdateDTCMSetting() if (newDTCMBase != DTCMBase || newDTCMMask != DTCMMask) { - JIT.Memory.RemapDTCM(newDTCMBase, newDTCMSize); + NDS.JIT.Memory.RemapDTCM(newDTCMBase, newDTCMSize); DTCMBase = newDTCMBase; DTCMMask = newDTCMMask; } @@ -295,7 +295,7 @@ void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend) for (u32 i = addrstart; i < addrend; i++) { u8 pu = PU_Map[i]; - u8* bustimings = NDS::ARM9MemTimings[i >> 2]; + u8* bustimings = NDS.ARM9MemTimings[i >> 2]; if (pu & 0x40) { @@ -303,7 +303,7 @@ void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend) } else { - MemTimings[i][0] = bustimings[2] << NDS::ARM9ClockShift; + MemTimings[i][0] = bustimings[2] << NDS.ARM9ClockShift; } if (pu & 0x10) @@ -314,9 +314,9 @@ void ARMv5::UpdateRegionTimings(u32 addrstart, u32 addrend) } else { - MemTimings[i][1] = bustimings[0] << NDS::ARM9ClockShift; - MemTimings[i][2] = bustimings[2] << NDS::ARM9ClockShift; - MemTimings[i][3] = bustimings[3] << NDS::ARM9ClockShift; + MemTimings[i][1] = bustimings[0] << NDS.ARM9ClockShift; + MemTimings[i][2] = bustimings[2] << NDS.ARM9ClockShift; + MemTimings[i][3] = bustimings[3] << NDS.ARM9ClockShift; } } } @@ -388,14 +388,14 @@ void ARMv5::ICacheLookup(u32 addr) else { for (int i = 0; i < 32; i+=4) - *(u32*)&ptr[i] = NDS::ARM9Read32(addr+i); + *(u32*)&ptr[i] = NDS.ARM9Read32(addr+i); } ICacheTags[line] = tag; // ouch :/ //printf("cache miss %08X: %d/%d\n", addr, NDS::ARM9MemTimings[addr >> 14][2], NDS::ARM9MemTimings[addr >> 14][3]); - CodeCycles = (NDS::ARM9MemTimings[addr >> 14][2] + (NDS::ARM9MemTimings[addr >> 14][3] * 7)) << NDS::ARM9ClockShift; + CodeCycles = (NDS.ARM9MemTimings[addr >> 14][2] + (NDS.ARM9MemTimings[addr >> 14][3] * 7)) << NDS.ARM9ClockShift; CurICacheLine = ptr; } @@ -923,7 +923,7 @@ void ARMv5::DataWrite8(u32 addr, u8 val) { DataCycles = 1; *(u8*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; - JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -953,7 +953,7 @@ void ARMv5::DataWrite16(u32 addr, u16 val) { DataCycles = 1; *(u16*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; - JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -983,7 +983,7 @@ void ARMv5::DataWrite32(u32 addr, u32 val) { DataCycles = 1; *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; - JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); return; } if ((addr & DTCMMask) == DTCMBase) @@ -1006,7 +1006,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val) DataCycles += 1; *(u32*)&ITCM[addr & (ITCMPhysicalSize - 1)] = val; #ifdef JIT_ENABLED - JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); + NDS.JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_ITCM>(addr); #endif return; } @@ -1021,7 +1021,7 @@ void ARMv5::DataWrite32S(u32 addr, u32 val) DataCycles += MemTimings[addr >> 12][3]; } -void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region) +void ARMv5::GetCodeMemRegion(u32 addr, MemRegion* region) { /*if (addr < ITCMSize) { @@ -1030,7 +1030,7 @@ void ARMv5::GetCodeMemRegion(u32 addr, NDS::MemRegion* region) return; }*/ - GetMemRegion(addr, false, &CodeMem); + NDS.ARM9GetMemRegion(addr, false, &CodeMem); } } diff --git a/src/DMA.cpp b/src/DMA.cpp index 8afc8971..717b38fa 100644 --- a/src/DMA.cpp +++ b/src/DMA.cpp @@ -49,10 +49,10 @@ using Platform::LogLevel; // TODO: timings are nonseq when address is fixed/decrementing -DMA::DMA(u32 cpu, u32 num, melonDS::GPU& gpu) : +DMA::DMA(u32 cpu, u32 num, melonDS::NDS& nds) : CPU(cpu), Num(num), - GPU(gpu) + NDS(nds) { if (cpu == 0) CountMask = 0x001FFFFF; @@ -145,7 +145,7 @@ void DMA::WriteCnt(u32 val) if ((StartMode & 0x7) == 0) Start(); else if (StartMode == 0x07) - GPU.GPU3D.CheckFIFODMA(); + NDS.GPU.GPU3D.CheckFIFODMA(); if (StartMode==0x06 || StartMode==0x13) Log(LogLevel::Warn, "UNIMPLEMENTED ARM%d DMA%d START MODE %02X, %08X->%08X\n", CPU?7:9, Num, StartMode, SrcAddr, DstAddr); @@ -192,7 +192,7 @@ void DMA::Start() MRAMBurstTable = DMATiming::MRAMDummy; InProgress = true; - NDS::StopCPU(CPU, 1<> 14; u32 dst_id = CurDstAddr >> 14; - u32 src_rgn = NDS::ARM9Regions[src_id]; - u32 dst_rgn = NDS::ARM9Regions[dst_id]; + u32 src_rgn = NDS.ARM9Regions[src_id]; + u32 dst_rgn = NDS.ARM9Regions[dst_id]; u32 src_n, src_s, dst_n, dst_s; - src_n = NDS::ARM9MemTimings[src_id][4]; - src_s = NDS::ARM9MemTimings[src_id][5]; - dst_n = NDS::ARM9MemTimings[dst_id][4]; - dst_s = NDS::ARM9MemTimings[dst_id][5]; + src_n = NDS.ARM9MemTimings[src_id][4]; + src_s = NDS.ARM9MemTimings[src_id][5]; + dst_n = NDS.ARM9MemTimings[dst_id][4]; + dst_s = NDS.ARM9MemTimings[dst_id][5]; - if (src_rgn == NDS::Mem9_MainRAM) + if (src_rgn == Mem9_MainRAM) { - if (dst_rgn == NDS::Mem9_MainRAM) + if (dst_rgn == Mem9_MainRAM) return 16; if (SrcAddrInc > 0) @@ -220,7 +220,7 @@ u32 DMA::UnitTimings9_16(bool burststart) { MRAMBurstCount = 0; - if (dst_rgn == NDS::Mem9_GBAROM) + if (dst_rgn == Mem9_GBAROM) { if (dst_s == 4) MRAMBurstTable = DMATiming::MRAMRead16Bursts[1]; @@ -241,7 +241,7 @@ u32 DMA::UnitTimings9_16(bool burststart) (burststart ? dst_n : dst_s); } } - else if (dst_rgn == NDS::Mem9_MainRAM) + else if (dst_rgn == Mem9_MainRAM) { if (DstAddrInc > 0) { @@ -249,7 +249,7 @@ u32 DMA::UnitTimings9_16(bool burststart) { MRAMBurstCount = 0; - if (src_rgn == NDS::Mem9_GBAROM) + if (src_rgn == Mem9_GBAROM) { if (src_s == 4) MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1]; @@ -286,18 +286,18 @@ u32 DMA::UnitTimings9_32(bool burststart) u32 src_id = CurSrcAddr >> 14; u32 dst_id = CurDstAddr >> 14; - u32 src_rgn = NDS::ARM9Regions[src_id]; - u32 dst_rgn = NDS::ARM9Regions[dst_id]; + u32 src_rgn = NDS.ARM9Regions[src_id]; + u32 dst_rgn = NDS.ARM9Regions[dst_id]; u32 src_n, src_s, dst_n, dst_s; - src_n = NDS::ARM9MemTimings[src_id][6]; - src_s = NDS::ARM9MemTimings[src_id][7]; - dst_n = NDS::ARM9MemTimings[dst_id][6]; - dst_s = NDS::ARM9MemTimings[dst_id][7]; + src_n = NDS.ARM9MemTimings[src_id][6]; + src_s = NDS.ARM9MemTimings[src_id][7]; + dst_n = NDS.ARM9MemTimings[dst_id][6]; + dst_s = NDS.ARM9MemTimings[dst_id][7]; - if (src_rgn == NDS::Mem9_MainRAM) + if (src_rgn == Mem9_MainRAM) { - if (dst_rgn == NDS::Mem9_MainRAM) + if (dst_rgn == Mem9_MainRAM) return 18; if (SrcAddrInc > 0) @@ -306,7 +306,7 @@ u32 DMA::UnitTimings9_32(bool burststart) { MRAMBurstCount = 0; - if (dst_rgn == NDS::Mem9_GBAROM) + if (dst_rgn == Mem9_GBAROM) { if (dst_s == 8) MRAMBurstTable = DMATiming::MRAMRead32Bursts[2]; @@ -329,7 +329,7 @@ u32 DMA::UnitTimings9_32(bool burststart) (burststart ? dst_n : dst_s); } } - else if (dst_rgn == NDS::Mem9_MainRAM) + else if (dst_rgn == Mem9_MainRAM) { if (DstAddrInc > 0) { @@ -337,7 +337,7 @@ u32 DMA::UnitTimings9_32(bool burststart) { MRAMBurstCount = 0; - if (src_rgn == NDS::Mem9_GBAROM) + if (src_rgn == Mem9_GBAROM) { if (src_s == 8) MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2]; @@ -378,18 +378,18 @@ u32 DMA::UnitTimings7_16(bool burststart) u32 src_id = CurSrcAddr >> 15; u32 dst_id = CurDstAddr >> 15; - u32 src_rgn = NDS::ARM7Regions[src_id]; - u32 dst_rgn = NDS::ARM7Regions[dst_id]; + u32 src_rgn = NDS.ARM7Regions[src_id]; + u32 dst_rgn = NDS.ARM7Regions[dst_id]; u32 src_n, src_s, dst_n, dst_s; - src_n = NDS::ARM7MemTimings[src_id][0]; - src_s = NDS::ARM7MemTimings[src_id][1]; - dst_n = NDS::ARM7MemTimings[dst_id][0]; - dst_s = NDS::ARM7MemTimings[dst_id][1]; + src_n = NDS.ARM7MemTimings[src_id][0]; + src_s = NDS.ARM7MemTimings[src_id][1]; + dst_n = NDS.ARM7MemTimings[dst_id][0]; + dst_s = NDS.ARM7MemTimings[dst_id][1]; - if (src_rgn == NDS::Mem7_MainRAM) + if (src_rgn == Mem7_MainRAM) { - if (dst_rgn == NDS::Mem7_MainRAM) + if (dst_rgn == Mem7_MainRAM) return 16; if (SrcAddrInc > 0) @@ -398,7 +398,7 @@ u32 DMA::UnitTimings7_16(bool burststart) { MRAMBurstCount = 0; - if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1) + if (dst_rgn == Mem7_GBAROM || dst_rgn == Mem7_Wifi0 || dst_rgn == Mem7_Wifi1) { if (dst_s == 4) MRAMBurstTable = DMATiming::MRAMRead16Bursts[1]; @@ -419,7 +419,7 @@ u32 DMA::UnitTimings7_16(bool burststart) (burststart ? dst_n : dst_s); } } - else if (dst_rgn == NDS::Mem7_MainRAM) + else if (dst_rgn == Mem7_MainRAM) { if (DstAddrInc > 0) { @@ -427,7 +427,7 @@ u32 DMA::UnitTimings7_16(bool burststart) { MRAMBurstCount = 0; - if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1) + if (src_rgn == Mem7_GBAROM || src_rgn == Mem7_Wifi0 || src_rgn == Mem7_Wifi1) { if (src_s == 4) MRAMBurstTable = DMATiming::MRAMWrite16Bursts[1]; @@ -464,18 +464,18 @@ u32 DMA::UnitTimings7_32(bool burststart) u32 src_id = CurSrcAddr >> 15; u32 dst_id = CurDstAddr >> 15; - u32 src_rgn = NDS::ARM7Regions[src_id]; - u32 dst_rgn = NDS::ARM7Regions[dst_id]; + u32 src_rgn = NDS.ARM7Regions[src_id]; + u32 dst_rgn = NDS.ARM7Regions[dst_id]; u32 src_n, src_s, dst_n, dst_s; - src_n = NDS::ARM7MemTimings[src_id][2]; - src_s = NDS::ARM7MemTimings[src_id][3]; - dst_n = NDS::ARM7MemTimings[dst_id][2]; - dst_s = NDS::ARM7MemTimings[dst_id][3]; + src_n = NDS.ARM7MemTimings[src_id][2]; + src_s = NDS.ARM7MemTimings[src_id][3]; + dst_n = NDS.ARM7MemTimings[dst_id][2]; + dst_s = NDS.ARM7MemTimings[dst_id][3]; - if (src_rgn == NDS::Mem7_MainRAM) + if (src_rgn == Mem7_MainRAM) { - if (dst_rgn == NDS::Mem7_MainRAM) + if (dst_rgn == Mem7_MainRAM) return 18; if (SrcAddrInc > 0) @@ -484,7 +484,7 @@ u32 DMA::UnitTimings7_32(bool burststart) { MRAMBurstCount = 0; - if (dst_rgn == NDS::Mem7_GBAROM || dst_rgn == NDS::Mem7_Wifi0 || dst_rgn == NDS::Mem7_Wifi1) + if (dst_rgn == Mem7_GBAROM || dst_rgn == Mem7_Wifi0 || dst_rgn == Mem7_Wifi1) { if (dst_s == 8) MRAMBurstTable = DMATiming::MRAMRead32Bursts[2]; @@ -507,7 +507,7 @@ u32 DMA::UnitTimings7_32(bool burststart) (burststart ? dst_n : dst_s); } } - else if (dst_rgn == NDS::Mem7_MainRAM) + else if (dst_rgn == Mem7_MainRAM) { if (DstAddrInc > 0) { @@ -515,7 +515,7 @@ u32 DMA::UnitTimings7_32(bool burststart) { MRAMBurstCount = 0; - if (src_rgn == NDS::Mem7_GBAROM || src_rgn == NDS::Mem7_Wifi0 || src_rgn == NDS::Mem7_Wifi1) + if (src_rgn == Mem7_GBAROM || src_rgn == Mem7_Wifi0 || src_rgn == Mem7_Wifi1) { if (src_s == 8) MRAMBurstTable = DMATiming::MRAMWrite32Bursts[2]; @@ -549,10 +549,9 @@ u32 DMA::UnitTimings7_32(bool burststart) } } -template void DMA::Run9() { - if (NDS::ARM9Timestamp >= NDS::ARM9Target) return; + if (NDS.ARM9Timestamp >= NDS.ARM9Target) return; Executing = true; @@ -564,40 +563,34 @@ void DMA::Run9() { while (IterCount > 0 && !Stall) { - NDS::ARM9Timestamp += (UnitTimings9_16(burststart) << NDS::ARM9ClockShift); + NDS.ARM9Timestamp += (UnitTimings9_16(burststart) << NDS.ARM9ClockShift); burststart = false; - if (ConsoleType == 1) - DSi::ARM9Write16(CurDstAddr, DSi::ARM9Read16(CurSrcAddr)); - else - NDS::ARM9Write16(CurDstAddr, NDS::ARM9Read16(CurSrcAddr)); + NDS.ARM9Write16(CurDstAddr, NDS.ARM9Read16(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<1; CurDstAddr += DstAddrInc<<1; IterCount--; RemCount--; - if (NDS::ARM9Timestamp >= NDS::ARM9Target) break; + if (NDS.ARM9Timestamp >= NDS.ARM9Target) break; } } else { while (IterCount > 0 && !Stall) { - NDS::ARM9Timestamp += (UnitTimings9_32(burststart) << NDS::ARM9ClockShift); + NDS.ARM9Timestamp += (UnitTimings9_32(burststart) << NDS.ARM9ClockShift); burststart = false; - if (ConsoleType == 1) - DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr)); - else - NDS::ARM9Write32(CurDstAddr, NDS::ARM9Read32(CurSrcAddr)); + NDS.ARM9Write32(CurDstAddr, NDS.ARM9Read32(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<2; CurDstAddr += DstAddrInc<<2; IterCount--; RemCount--; - if (NDS::ARM9Timestamp >= NDS::ARM9Target) break; + if (NDS.ARM9Timestamp >= NDS.ARM9Target) break; } } @@ -609,10 +602,10 @@ void DMA::Run9() if (IterCount == 0) { Running = 0; - NDS::ResumeCPU(0, 1< void DMA::Run7() { - if (NDS::ARM7Timestamp >= NDS::ARM7Target) return; + if (NDS.ARM7Timestamp >= NDS.ARM7Target) return; Executing = true; @@ -644,40 +636,34 @@ void DMA::Run7() { while (IterCount > 0 && !Stall) { - NDS::ARM7Timestamp += UnitTimings7_16(burststart); + NDS.ARM7Timestamp += UnitTimings7_16(burststart); burststart = false; - if (ConsoleType == 1) - DSi::ARM7Write16(CurDstAddr, DSi::ARM7Read16(CurSrcAddr)); - else - NDS::ARM7Write16(CurDstAddr, NDS::ARM7Read16(CurSrcAddr)); + NDS.ARM7Write16(CurDstAddr, NDS.ARM7Read16(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<1; CurDstAddr += DstAddrInc<<1; IterCount--; RemCount--; - if (NDS::ARM7Timestamp >= NDS::ARM7Target) break; + if (NDS.ARM7Timestamp >= NDS.ARM7Target) break; } } else { while (IterCount > 0 && !Stall) { - NDS::ARM7Timestamp += UnitTimings7_32(burststart); + NDS.ARM7Timestamp += UnitTimings7_32(burststart); burststart = false; - if (ConsoleType == 1) - DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr)); - else - NDS::ARM7Write32(CurDstAddr, NDS::ARM7Read32(CurSrcAddr)); + NDS.ARM7Write32(CurDstAddr, NDS.ARM7Read32(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<2; CurDstAddr += DstAddrInc<<2; IterCount--; RemCount--; - if (NDS::ARM7Timestamp >= NDS::ARM7Target) break; + if (NDS.ARM7Timestamp >= NDS.ARM7Target) break; } } @@ -689,7 +675,7 @@ void DMA::Run7() if (IterCount == 0) { Running = 0; - NDS::ResumeCPU(1, 1< void DMA::Run() { if (!Running) return; - if (CPU == 0) return Run9(); - else return Run7(); + if (CPU == 0) return Run9(); + else return Run7(); } -template void DMA::Run<0>(); -template void DMA::Run<1>(); - } \ No newline at end of file diff --git a/src/DMA.h b/src/DMA.h index 5535661d..e0e3be15 100644 --- a/src/DMA.h +++ b/src/DMA.h @@ -21,17 +21,16 @@ #include #include "types.h" -#include "Savestate.h" -#include "DMA_Timings.h" namespace melonDS { -class GPU; +class NDS; +class Savestate; class DMA { public: - DMA(u32 cpu, u32 num, GPU& gpu); + DMA(u32 cpu, u32 num, NDS& nds); ~DMA() = default; void Reset(); @@ -46,12 +45,8 @@ public: u32 UnitTimings7_16(bool burststart); u32 UnitTimings7_32(bool burststart); - template void Run(); - - template void Run9(); - template void Run7(); bool IsInMode(u32 mode) const noexcept @@ -83,7 +78,7 @@ public: u32 Cnt {}; private: - melonDS::GPU& GPU; + melonDS::NDS& NDS; u32 CPU {}; u32 Num {}; diff --git a/src/DSi.cpp b/src/DSi.cpp index 351d5ed4..f2781e48 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -45,135 +45,88 @@ namespace melonDS { using namespace Platform; -namespace DSi +// matching NDMA modes for DSi +const u32 NDMAModes[] = { + // ARM9 -u16 SCFG_BIOS; -u16 SCFG_Clock9; -u16 SCFG_Clock7; -u32 SCFG_EXT[2]; -u32 SCFG_MC; -u16 SCFG_RST; + 0x10, // immediate + 0x06, // VBlank + 0x07, // HBlank + 0x08, // scanline start + 0x09, // mainmem FIFO + 0x04, // DS cart slot + 0xFF, // GBA cart slot + 0x0A, // GX FIFO + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -u8 ARM9iBIOS[0x10000] = { 0 }; -u8 ARM7iBIOS[0x10000] = { 0 }; + // ARM7 -u32 MBK[2][9]; + 0x30, // immediate + 0x26, // VBlank + 0x24, // DS cart slot + 0xFF, // wifi / GBA cart slot (TODO) +}; -u8* NWRAM_A; -u8* NWRAM_B; -u8* NWRAM_C; - -u8* NWRAMMap_A[2][4]; -u8* NWRAMMap_B[3][8]; -u8* NWRAMMap_C[3][8]; - -u32 NWRAMStart[2][3]; -u32 NWRAMEnd[2][3]; -u32 NWRAMMask[2][3]; - -u32 NDMACnt[2]; -DSi_NDMA* NDMAs[8]; - -std::unique_ptr NANDImage; -DSi_SDHost* SDMMC; -DSi_SDHost* SDIO; - -DSi_I2CHost* I2C; -DSi_CamModule* CamModule; -DSi_AES* AES; -DSi_DSP* DSP; - -// FIXME: these currently have no effect (and aren't stored in a savestate) -// ... not that they matter all that much -u8 GPIO_Data; -u8 GPIO_Dir; -u8 GPIO_IEdgeSel; -u8 GPIO_IE; -u8 GPIO_WiFi; - - -void Set_SCFG_Clock9(u16 val); -void Set_SCFG_MC(u32 val); - - -bool Init() +DSi::DSi() noexcept : + NDS(1), + NDMAs { + DSi_NDMA(0, 0, *this), + DSi_NDMA(0, 1, *this), + DSi_NDMA(0, 2, *this), + DSi_NDMA(0, 3, *this), + DSi_NDMA(1, 0, *this), + DSi_NDMA(1, 1, *this), + DSi_NDMA(1, 2, *this), + DSi_NDMA(1, 3, *this), + }, + DSP(*this), + SDMMC(*this, 0), + SDIO(*this, 1), + I2C(*this), + CamModule(*this), + AES(*this) { // Memory is owned by ARMJIT_Memory, don't free it - NWRAM_A = NDS::JIT->Memory.GetNWRAM_A(); - NWRAM_B = NDS::JIT->Memory.GetNWRAM_B(); - NWRAM_C = NDS::JIT->Memory.GetNWRAM_C(); - - NDMAs[0] = new DSi_NDMA(0, 0, *NDS::GPU); - NDMAs[1] = new DSi_NDMA(0, 1, *NDS::GPU); - NDMAs[2] = new DSi_NDMA(0, 2, *NDS::GPU); - NDMAs[3] = new DSi_NDMA(0, 3, *NDS::GPU); - NDMAs[4] = new DSi_NDMA(1, 0, *NDS::GPU); - NDMAs[5] = new DSi_NDMA(1, 1, *NDS::GPU); - NDMAs[6] = new DSi_NDMA(1, 2, *NDS::GPU); - NDMAs[7] = new DSi_NDMA(1, 3, *NDS::GPU); - - SDMMC = new DSi_SDHost(0); - SDIO = new DSi_SDHost(1); - - I2C = new DSi_I2CHost(); - CamModule = new DSi_CamModule(); - AES = new DSi_AES(); - DSP = new DSi_DSP(); - - return true; + NWRAM_A = JIT.Memory.GetNWRAM_A(); + NWRAM_B = JIT.Memory.GetNWRAM_B(); + NWRAM_C = NDS::JIT.Memory.GetNWRAM_C(); } -void DeInit() +DSi::~DSi() noexcept { // Memory is owned externally NWRAM_A = nullptr; NWRAM_B = nullptr; NWRAM_C = nullptr; - - for (int i = 0; i < 8; i++) - { - delete NDMAs[i]; - NDMAs[i] = nullptr; - } - - delete SDMMC; SDMMC = nullptr; - delete SDIO; SDIO = nullptr; - - delete I2C; I2C = nullptr; - delete CamModule; CamModule = nullptr; - delete AES; AES = nullptr; - delete DSP; DSP = nullptr; - - NANDImage = nullptr; - // The NANDImage is cleaned up (and its underlying file closed) - // as part of unique_ptr's destructor } -void Reset() +void DSi::Reset() { - //NDS::ARM9->CP15Write(0x910, 0x0D00000A); - //NDS::ARM9->CP15Write(0x911, 0x00000020); - //NDS::ARM9->CP15Write(0x100, NDS::ARM9->CP15Read(0x100) | 0x00050000); + //ARM9.CP15Write(0x910, 0x0D00000A); + //ARM9.CP15Write(0x911, 0x00000020); + //ARM9.CP15Write(0x100, ARM9.CP15Read(0x100) | 0x00050000); + NDS::Reset(); - NDS::MapSharedWRAM(3); + KeyInput &= ~(1 << (16+6)); + MapSharedWRAM(3); NDMACnt[0] = 0; NDMACnt[1] = 0; - for (int i = 0; i < 8; i++) NDMAs[i]->Reset(); + for (int i = 0; i < 8; i++) NDMAs[i].Reset(); - I2C->Reset(); - CamModule->Reset(); - DSP->Reset(); + I2C.Reset(); + CamModule.Reset(); + DSP.Reset(); - SDMMC->CloseHandles(); - SDIO->CloseHandles(); + SDMMC.CloseHandles(); + SDIO.CloseHandles(); LoadNAND(); - SDMMC->Reset(); - SDIO->Reset(); + SDMMC.Reset(); + SDIO.Reset(); - AES->Reset(); + AES.Reset(); if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) { @@ -187,10 +140,10 @@ void Reset() SCFG_Clock7 = 0x0187; SCFG_EXT[0] = 0x8307F100; SCFG_EXT[1] = 0x93FFFB06; - SCFG_MC = 0x0010 | (~((u32)(NDS::NDSCartSlot->GetCart() != nullptr))&1);//0x0011; + SCFG_MC = 0x0010 | (~((u32)(NDSCartSlot.GetCart() != nullptr))&1);//0x0011; SCFG_RST = 0; - DSP->SetRstLine(false); + DSP.SetRstLine(false); GPIO_Data = 0xff; // these actually initialize to high after reset GPIO_Dir = 0x80; // enable sound out, all others input @@ -199,16 +152,44 @@ void Reset() GPIO_WiFi = 0; // LCD init flag - NDS::GPU->DispStat[0] |= (1<<6); - NDS::GPU->DispStat[1] |= (1<<6); + GPU.DispStat[0] |= (1<<6); + GPU.DispStat[1] |= (1<<6); } -void Stop() +void DSi::Stop(Platform::StopReason reason) { - CamModule->Stop(); + NDS::Stop(reason); + CamModule.Stop(); } -void DoSavestate(Savestate* file) +bool DSi::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) +{ + if (NDS::LoadCart(romdata, romlen, savedata, savelen)) + { + SetCartInserted(true); + return true; + } + + return false; +} + + +void DSi::EjectCart() +{ + NDS::EjectCart(); + + SetCartInserted(false); +} +void DSi::CamInputFrame(int cam, u32* data, int width, int height, bool rgb) +{ + switch (cam) + { + case 0: return I2C.GetOuterCamera()->InputFrame(data, width, height, rgb); + case 1: return I2C.GetInnerCamera()->InputFrame(data, width, height, rgb); + } +} + +void DSi::DoSavestateExtra(Savestate* file) { file->Section("DSIG"); @@ -232,7 +213,7 @@ void DoSavestate(Savestate* file) { Set_SCFG_Clock9(SCFG_Clock9); Set_SCFG_MC(SCFG_MC); - DSP->SetRstLine(SCFG_RST & 0x0001); + DSP.SetRstLine(SCFG_RST & 0x0001); MBK[0][8] = 0; MBK[1][8] = 0; @@ -277,17 +258,17 @@ void DoSavestate(Savestate* file) } for (int i = 0; i < 8; i++) - NDMAs[i]->DoSavestate(file); + NDMAs[i].DoSavestate(file); - AES->DoSavestate(file); - CamModule->DoSavestate(file); - DSP->DoSavestate(file); - I2C->DoSavestate(file); - SDMMC->DoSavestate(file); - SDIO->DoSavestate(file); + AES.DoSavestate(file); + CamModule.DoSavestate(file); + DSP.DoSavestate(file); + I2C.DoSavestate(file); + SDMMC.DoSavestate(file); + SDIO.DoSavestate(file); } -void SetCartInserted(bool inserted) +void DSi::SetCartInserted(bool inserted) { if (inserted) SCFG_MC &= ~1; @@ -295,7 +276,7 @@ void SetCartInserted(bool inserted) SCFG_MC |= 1; } -void DecryptModcryptArea(u32 offset, u32 size, u8* iv) +void DSi::DecryptModcryptArea(u32 offset, u32 size, u8* iv) { AES_ctx ctx; u8 key[16]; @@ -304,13 +285,13 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv) if ((offset == 0) || (size == 0)) return; - const NDSHeader& header = NDS::NDSCartSlot->GetCart()->GetHeader(); + const NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); if ((header.DSiCryptoFlags & (1<<4)) || (header.AppFlags & (1<<7))) { // dev key - const u8* cartrom = NDS::NDSCartSlot->GetCart()->GetROM(); + const u8* cartrom = NDSCartSlot.GetCart()->GetROM(); memcpy(key, &cartrom[0], 16); } else @@ -394,13 +375,13 @@ void DecryptModcryptArea(u32 offset, u32 size, u8* iv) } } -void SetupDirectBoot() +void DSi::SetupDirectBoot() { bool dsmode = false; - NDSHeader& header = NDS::NDSCartSlot->GetCart()->GetHeader(); - const u8* cartrom = NDS::NDSCartSlot->GetCart()->GetROM(); - u32 cartid = NDS::NDSCartSlot->GetCart()->ID(); - DSi_TSC* tsc = (DSi_TSC*)NDS::SPI->GetTSC(); + NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); + const u8* cartrom = NDSCartSlot.GetCart()->GetROM(); + u32 cartid = NDSCartSlot.GetCart()->ID(); + DSi_TSC* tsc = (DSi_TSC*)SPI.GetTSC(); // TODO: add controls for forcing DS or DSi mode? if (!(header.UnitCode & 0x02)) @@ -550,7 +531,7 @@ void SetupDirectBoot() } } - Firmware::WifiBoard nwifiver = NDS::SPI->GetFirmware()->GetHeader().WifiBoard; + Firmware::WifiBoard nwifiver = SPI.GetFirmware()->GetHeader().WifiBoard; ARM9Write8(0x020005E0, static_cast(nwifiver)); // TODO: these should be taken from the wifi firmware in NAND @@ -576,7 +557,7 @@ void SetupDirectBoot() ARM9Write32(0x02FFFC00, cartid); ARM9Write16(0x02FFFC40, 0x0001); // boot indicator - ARM9Write8(0x02FFFDFA, I2C->GetBPTWL()->GetBootFlag() | 0x80); + ARM9Write8(0x02FFFDFA, I2C.GetBPTWL()->GetBootFlag() | 0x80); ARM9Write8(0x02FFFDFB, 0x01); } @@ -588,7 +569,7 @@ void SetupDirectBoot() if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000) { u8 securearea[0x800]; - NDS::NDSCartSlot->DecryptSecureArea(securearea); + NDSCartSlot.DecryptSecureArea(securearea); for (u32 i = 0; i < 0x800; i+=4) { @@ -638,37 +619,37 @@ void SetupDirectBoot() } } - NDS::ARM7BIOSProt = 0x20; + ARM7BIOSProt = 0x20; - NDS::SPI->GetFirmwareMem()->SetupDirectBoot(true); + SPI.GetFirmwareMem()->SetupDirectBoot(); - NDS::ARM9->CP15Write(0x100, 0x00056078); - NDS::ARM9->CP15Write(0x200, 0x0000004A); - NDS::ARM9->CP15Write(0x201, 0x0000004A); - NDS::ARM9->CP15Write(0x300, 0x0000000A); - NDS::ARM9->CP15Write(0x502, 0x15111011); - NDS::ARM9->CP15Write(0x503, 0x05101011); - NDS::ARM9->CP15Write(0x600, 0x04000033); - NDS::ARM9->CP15Write(0x601, 0x04000033); - NDS::ARM9->CP15Write(0x610, 0x02000031); - NDS::ARM9->CP15Write(0x611, 0x02000031); - NDS::ARM9->CP15Write(0x620, 0x00000000); - NDS::ARM9->CP15Write(0x621, 0x00000000); - NDS::ARM9->CP15Write(0x630, 0x08000033); - NDS::ARM9->CP15Write(0x631, 0x08000033); - NDS::ARM9->CP15Write(0x640, 0x0E00001B); - NDS::ARM9->CP15Write(0x641, 0x0E00001B); - NDS::ARM9->CP15Write(0x650, 0x00000000); - NDS::ARM9->CP15Write(0x651, 0x00000000); - NDS::ARM9->CP15Write(0x660, 0xFFFF001D); - NDS::ARM9->CP15Write(0x661, 0xFFFF001D); - NDS::ARM9->CP15Write(0x670, 0x02FFC01B); - NDS::ARM9->CP15Write(0x671, 0x02FFC01B); - NDS::ARM9->CP15Write(0x910, 0x0E00000A); - NDS::ARM9->CP15Write(0x911, 0x00000020); + ARM9.CP15Write(0x100, 0x00056078); + ARM9.CP15Write(0x200, 0x0000004A); + ARM9.CP15Write(0x201, 0x0000004A); + ARM9.CP15Write(0x300, 0x0000000A); + ARM9.CP15Write(0x502, 0x15111011); + ARM9.CP15Write(0x503, 0x05101011); + ARM9.CP15Write(0x600, 0x04000033); + ARM9.CP15Write(0x601, 0x04000033); + ARM9.CP15Write(0x610, 0x02000031); + ARM9.CP15Write(0x611, 0x02000031); + ARM9.CP15Write(0x620, 0x00000000); + ARM9.CP15Write(0x621, 0x00000000); + ARM9.CP15Write(0x630, 0x08000033); + ARM9.CP15Write(0x631, 0x08000033); + ARM9.CP15Write(0x640, 0x0E00001B); + ARM9.CP15Write(0x641, 0x0E00001B); + ARM9.CP15Write(0x650, 0x00000000); + ARM9.CP15Write(0x651, 0x00000000); + ARM9.CP15Write(0x660, 0xFFFF001D); + ARM9.CP15Write(0x661, 0xFFFF001D); + ARM9.CP15Write(0x670, 0x02FFC01B); + ARM9.CP15Write(0x671, 0x02FFC01B); + ARM9.CP15Write(0x910, 0x0E00000A); + ARM9.CP15Write(0x911, 0x00000020); } -void SoftReset() +void DSi::SoftReset() { // TODO: check exactly what is reset // presumably, main RAM isn't reset, since the DSi can be told @@ -678,30 +659,30 @@ void SoftReset() // also, BPTWL[0x70] could be abused to quickly boot specific titles - NDS::JIT->Reset(); - NDS::JIT->CheckAndInvalidateITCM(); + JIT.Reset(); + JIT.CheckAndInvalidateITCM(); - NDS::ARM9->Reset(); - NDS::ARM7->Reset(); + ARM9.Reset(); + ARM7.Reset(); - NDS::ARM9->CP15Reset(); + ARM9.CP15Reset(); NDS::MapSharedWRAM(3); // TODO: does the DSP get reset? NWRAM doesn't, so I'm assuming no // *HOWEVER*, the bootrom (which does get rerun) does remap NWRAM, and thus // the DSP most likely gets reset - DSP->Reset(); + DSP.Reset(); - SDMMC->CloseHandles(); - SDIO->CloseHandles(); + SDMMC.CloseHandles(); + SDIO.CloseHandles(); LoadNAND(); - SDMMC->Reset(); - SDIO->Reset(); + SDMMC.Reset(); + SDIO.Reset(); - AES->Reset(); + AES.Reset(); if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) { @@ -718,15 +699,15 @@ void SoftReset() SCFG_MC = 0x0010;//0x0011; // TODO: is this actually reset? SCFG_RST = 0; - DSP->SetRstLine(false); + DSP.SetRstLine(false); // LCD init flag - NDS::GPU->DispStat[0] |= (1<<6); - NDS::GPU->DispStat[1] |= (1<<6); + GPU.DispStat[0] |= (1<<6); + GPU.DispStat[1] |= (1<<6); } -bool LoadNAND() +bool DSi::LoadNAND() { if (!NANDImage) { @@ -905,8 +886,8 @@ bool LoadNAND() if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) { // point CPUs to boot ROM reset vectors - NDS::ARM9->JumpTo(0xFFFF0000); - NDS::ARM7->JumpTo(0x00000000); + ARM9.JumpTo(0xFFFF0000); + ARM7.JumpTo(0x00000000); } else { @@ -921,10 +902,10 @@ bool LoadNAND() ARM7Write16(eaddr+0x3E, 0x40E0); ARM7Write16(eaddr+0x42, 0x0001); - memcpy(&NDS::ARM9->ITCM[0x4400], &ARM9iBIOS[0x87F4], 0x400); - memcpy(&NDS::ARM9->ITCM[0x4800], &ARM9iBIOS[0x9920], 0x80); - memcpy(&NDS::ARM9->ITCM[0x4894], &ARM9iBIOS[0x99A0], 0x1048); - memcpy(&NDS::ARM9->ITCM[0x58DC], &ARM9iBIOS[0xA9E8], 0x1048); + memcpy(&ARM9.ITCM[0x4400], &ARM9iBIOS[0x87F4], 0x400); + memcpy(&ARM9.ITCM[0x4800], &ARM9iBIOS[0x9920], 0x80); + memcpy(&ARM9.ITCM[0x4894], &ARM9iBIOS[0x99A0], 0x1048); + memcpy(&ARM9.ITCM[0x58DC], &ARM9iBIOS[0xA9E8], 0x1048); u8 ARM7Init[0x3C00]; memset(ARM7Init, 0, 0x3C00); @@ -937,8 +918,8 @@ bool LoadNAND() ARM7Write32(0x03FFC400+i, *(u32*)&ARM7Init[i]); // repoint the CPUs to the boot2 binaries - NDS::ARM9->JumpTo(bootparams[2]); - NDS::ARM7->JumpTo(bootparams[6]); + ARM9.JumpTo(bootparams[2]); + ARM7.JumpTo(bootparams[6]); } // user data is now expected to be patched by the frontend @@ -947,78 +928,104 @@ bool LoadNAND() } -void RunNDMAs(u32 cpu) +void DSi::RunNDMAs(u32 cpu) { // TODO: round-robin mode (requires DMA channels to have a subblock delay set) if (cpu == 0) { - if (NDS::ARM9Timestamp >= NDS::ARM9Target) return; + if (ARM9Timestamp >= ARM9Target) return; - if (!(NDS::CPUStop & NDS::CPUStop_GXStall)) NDMAs[0]->Run(); - if (!(NDS::CPUStop & NDS::CPUStop_GXStall)) NDMAs[1]->Run(); - if (!(NDS::CPUStop & NDS::CPUStop_GXStall)) NDMAs[2]->Run(); - if (!(NDS::CPUStop & NDS::CPUStop_GXStall)) NDMAs[3]->Run(); + if (!(CPUStop & CPUStop_GXStall)) NDMAs[0].Run(); + if (!(CPUStop & CPUStop_GXStall)) NDMAs[1].Run(); + if (!(CPUStop & CPUStop_GXStall)) NDMAs[2].Run(); + if (!(CPUStop & CPUStop_GXStall)) NDMAs[3].Run(); } else { - if (NDS::ARM7Timestamp >= NDS::ARM7Target) return; + if (ARM7Timestamp >= ARM7Target) return; - NDMAs[4]->Run(); - NDMAs[5]->Run(); - NDMAs[6]->Run(); - NDMAs[7]->Run(); + NDMAs[4].Run(); + NDMAs[5].Run(); + NDMAs[6].Run(); + NDMAs[7].Run(); } } -void StallNDMAs() +void DSi::StallNDMAs() { // TODO } -bool NDMAsInMode(u32 cpu, u32 mode) + +bool DSi::DMAsInMode(u32 cpu, u32 mode) +{ + if (NDS::DMAsInMode(cpu, mode)) return true; + + return NDMAsInMode(cpu, NDMAModes[mode]); +} + +bool DSi::DMAsRunning(u32 cpu) +{ + if (NDS::DMAsRunning(cpu)) return true; + + return NDMAsRunning(cpu); +} + +bool DSi::NDMAsInMode(u32 cpu, u32 mode) { cpu <<= 2; - if (NDMAs[cpu+0]->IsInMode(mode)) return true; - if (NDMAs[cpu+1]->IsInMode(mode)) return true; - if (NDMAs[cpu+2]->IsInMode(mode)) return true; - if (NDMAs[cpu+3]->IsInMode(mode)) return true; + if (NDMAs[cpu+0].IsInMode(mode)) return true; + if (NDMAs[cpu+1].IsInMode(mode)) return true; + if (NDMAs[cpu+2].IsInMode(mode)) return true; + if (NDMAs[cpu+3].IsInMode(mode)) return true; return false; } -bool NDMAsRunning(u32 cpu) +bool DSi::NDMAsRunning(u32 cpu) { cpu <<= 2; - if (NDMAs[cpu+0]->IsRunning()) return true; - if (NDMAs[cpu+1]->IsRunning()) return true; - if (NDMAs[cpu+2]->IsRunning()) return true; - if (NDMAs[cpu+3]->IsRunning()) return true; + if (NDMAs[cpu+0].IsRunning()) return true; + if (NDMAs[cpu+1].IsRunning()) return true; + if (NDMAs[cpu+2].IsRunning()) return true; + if (NDMAs[cpu+3].IsRunning()) return true; return false; } -void CheckNDMAs(u32 cpu, u32 mode) +void DSi::CheckNDMAs(u32 cpu, u32 mode) { cpu <<= 2; - NDMAs[cpu+0]->StartIfNeeded(mode); - NDMAs[cpu+1]->StartIfNeeded(mode); - NDMAs[cpu+2]->StartIfNeeded(mode); - NDMAs[cpu+3]->StartIfNeeded(mode); + NDMAs[cpu+0].StartIfNeeded(mode); + NDMAs[cpu+1].StartIfNeeded(mode); + NDMAs[cpu+2].StartIfNeeded(mode); + NDMAs[cpu+3].StartIfNeeded(mode); } -void StopNDMAs(u32 cpu, u32 mode) +void DSi::StopNDMAs(u32 cpu, u32 mode) { cpu <<= 2; - NDMAs[cpu+0]->StopIfNeeded(mode); - NDMAs[cpu+1]->StopIfNeeded(mode); - NDMAs[cpu+2]->StopIfNeeded(mode); - NDMAs[cpu+3]->StopIfNeeded(mode); + NDMAs[cpu+0].StopIfNeeded(mode); + NDMAs[cpu+1].StopIfNeeded(mode); + NDMAs[cpu+2].StopIfNeeded(mode); + NDMAs[cpu+3].StopIfNeeded(mode); } +void DSi::StopDMAs(u32 cpu, u32 mode) +{ + NDS::StopDMAs(cpu, mode); + StopNDMAs(cpu, mode); +} +void DSi::CheckDMAs(u32 cpu, u32 mode) +{ + NDS::CheckDMAs(cpu, mode); + + CheckNDMAs(cpu, NDMAModes[mode]); +} // new WRAM mapping // TODO: find out what happens upon overlapping slots!! -void MapNWRAM_A(u32 num, u8 val) +void DSi::MapNWRAM_A(u32 num, u8 val) { // NWRAM Bank A does not allow all bits to be set // possible non working combinations are caught by later code, but these are not set-able at all @@ -1035,7 +1042,7 @@ void MapNWRAM_A(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; - NDS::JIT->Memory.RemapNWRAM(0); + JIT.Memory.RemapNWRAM(0); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1063,7 +1070,7 @@ void MapNWRAM_A(u32 num, u8 val) } } -void MapNWRAM_B(u32 num, u8 val) +void DSi::MapNWRAM_B(u32 num, u8 val) { // NWRAM Bank B does not allow all bits to be set // possible non working combinations are caught by later code, but these are not set-able at all @@ -1080,7 +1087,7 @@ void MapNWRAM_B(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; - NDS::JIT->Memory.RemapNWRAM(1); + JIT.Memory.RemapNWRAM(1); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1110,7 +1117,7 @@ void MapNWRAM_B(u32 num, u8 val) } } -void MapNWRAM_C(u32 num, u8 val) +void DSi::MapNWRAM_C(u32 num, u8 val) { // NWRAM Bank C does not allow all bits to be set // possible non working combinations are caught by later code, but these are not set-able at all @@ -1127,7 +1134,7 @@ void MapNWRAM_C(u32 num, u8 val) u8 oldval = (MBK[0][mbkn] >> mbks) & 0xFF; if (oldval == val) return; - NDS::JIT->Memory.RemapNWRAM(2); + JIT.Memory.RemapNWRAM(2); MBK[0][mbkn] &= ~(0xFF << mbks); MBK[0][mbkn] |= (val << mbks); @@ -1157,7 +1164,7 @@ void MapNWRAM_C(u32 num, u8 val) } } -void MapNWRAMRange(u32 cpu, u32 num, u32 val) +void DSi::MapNWRAMRange(u32 cpu, u32 num, u32 val) { // The windowing registers are not writeable in all bits // We need to do this before the change test, so we do not @@ -1176,7 +1183,7 @@ void MapNWRAMRange(u32 cpu, u32 num, u32 val) u32 oldval = MBK[cpu][5+num]; if (oldval == val) return; - NDS::JIT->Memory.RemapNWRAM(num); + JIT.Memory.RemapNWRAM(num); MBK[cpu][5+num] = val; @@ -1228,41 +1235,41 @@ void MapNWRAMRange(u32 cpu, u32 num, u32 val) } } -void ApplyNewRAMSize(u32 size) +void DSi::ApplyNewRAMSize(u32 size) { switch (size) { case 0: case 1: - NDS::MainRAMMask = 0x3FFFFF; + MainRAMMask = 0x3FFFFF; Log(LogLevel::Debug, "RAM: 4MB\n"); break; case 2: case 3: // TODO: debug console w/ 32MB? - NDS::MainRAMMask = 0xFFFFFF; + MainRAMMask = 0xFFFFFF; Log(LogLevel::Debug, "RAM: 16MB\n"); break; } } -void Set_SCFG_Clock9(u16 val) +void DSi::Set_SCFG_Clock9(u16 val) { - NDS::ARM9Timestamp >>= NDS::ARM9ClockShift; - NDS::ARM9Target >>= NDS::ARM9ClockShift; + ARM9Timestamp >>= ARM9ClockShift; + ARM9Target >>= ARM9ClockShift; Log(LogLevel::Debug, "CLOCK9=%04X\n", val); SCFG_Clock9 = val & 0x0187; - if (SCFG_Clock9 & (1<<0)) NDS::ARM9ClockShift = 2; - else NDS::ARM9ClockShift = 1; + if (SCFG_Clock9 & (1<<0)) ARM9ClockShift = 2; + else ARM9ClockShift = 1; - NDS::ARM9Timestamp <<= NDS::ARM9ClockShift; - NDS::ARM9Target <<= NDS::ARM9ClockShift; - NDS::ARM9->UpdateRegionTimings(0x00000, 0x100000); + ARM9Timestamp <<= ARM9ClockShift; + ARM9Target <<= ARM9ClockShift; + ARM9.UpdateRegionTimings(0x00000, 0x100000); } -void Set_SCFG_MC(u32 val) +void DSi::Set_SCFG_MC(u32 val) { u32 oldslotstatus = SCFG_MC & 0xC; @@ -1273,13 +1280,14 @@ void Set_SCFG_MC(u32 val) if ((oldslotstatus == 0x0) && ((SCFG_MC & 0xC) == 0x4)) { - NDS::NDSCartSlot->ResetCart(); + NDSCartSlot.ResetCart(); } } -u8 ARM9Read8(u32 addr) +u8 DSi::ARM9Read8(u32 addr) { + assert(ConsoleType == 1); if ((addr >= 0xFFFF0000) && (!(SCFG_BIOS & (1<<1)))) { if ((addr >= 0xFFFF8000) && (SCFG_BIOS & (1<<0))) @@ -1317,17 +1325,18 @@ u8 ARM9Read8(u32 addr) case 0x08000000: case 0x09000000: case 0x0A000000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFF; + return (ExMemCnt[0] & (1<<7)) ? 0 : 0xFF; case 0x0C000000: - return *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u8*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read8(addr); } -u16 ARM9Read16(u32 addr) +u16 DSi::ARM9Read16(u32 addr) { + assert(ConsoleType == 1); addr &= ~0x1; if ((addr >= 0xFFFF0000) && (!(SCFG_BIOS & (1<<1)))) @@ -1367,17 +1376,18 @@ u16 ARM9Read16(u32 addr) case 0x08000000: case 0x09000000: case 0x0A000000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFF; + return (ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFF; case 0x0C000000: - return *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u16*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read16(addr); } -u32 ARM9Read32(u32 addr) +u32 DSi::ARM9Read32(u32 addr) { + assert(ConsoleType == 1); addr &= ~0x3; if ((addr >= 0xFFFF0000) && (!(SCFG_BIOS & (1<<1)))) @@ -1422,17 +1432,18 @@ u32 ARM9Read32(u32 addr) case 0x08000000: case 0x09000000: case 0x0A000000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFFFFFF; + return (ExMemCnt[0] & (1<<7)) ? 0 : 0xFFFFFFFF; case 0x0C000000: - return *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u32*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM9Read32(addr); } -void ARM9Write8(u32 addr, u8 val) +void DSi::ARM9Write8(u32 addr, u8 val) { + assert(ConsoleType == 1); switch (addr & 0xFF000000) { case 0x03000000: @@ -1452,7 +1463,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u8*)&ptr[addr & 0xFFFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1470,7 +1481,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1488,7 +1499,7 @@ void ARM9Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1501,14 +1512,14 @@ void ARM9Write8(u32 addr, u8 val) case 0x06000000: if (!(SCFG_EXT[0] & (1<<13))) return; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { - case 0x00000000: NDS::GPU->WriteVRAM_ABG(addr, val); return; - case 0x00200000: NDS::GPU->WriteVRAM_BBG(addr, val); return; - case 0x00400000: NDS::GPU->WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: NDS::GPU->WriteVRAM_BOBJ(addr, val); return; - default: NDS::GPU->WriteVRAM_LCDC(addr, val); return; + case 0x00000000: GPU.WriteVRAM_ABG(addr, val); return; + case 0x00200000: GPU.WriteVRAM_BBG(addr, val); return; + case 0x00400000: GPU.WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: GPU.WriteVRAM_BOBJ(addr, val); return; + default: GPU.WriteVRAM_LCDC(addr, val); return; } case 0x08000000: @@ -1517,16 +1528,17 @@ void ARM9Write8(u32 addr, u8 val) return; case 0x0C000000: - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); - *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u8*)&MainRAM[addr & MainRAMMask] = val; return; } return NDS::ARM9Write8(addr, val); } -void ARM9Write16(u32 addr, u16 val) +void DSi::ARM9Write16(u32 addr, u16 val) { + assert(ConsoleType == 1); addr &= ~0x1; switch (addr & 0xFF000000) @@ -1548,7 +1560,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u16*)&ptr[addr & 0xFFFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1566,7 +1578,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1584,7 +1596,7 @@ void ARM9Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1601,16 +1613,17 @@ void ARM9Write16(u32 addr, u16 val) return; case 0x0C000000: - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); - *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u16*)&MainRAM[addr & MainRAMMask] = val; return; } return NDS::ARM9Write16(addr, val); } -void ARM9Write32(u32 addr, u32 val) +void DSi::ARM9Write32(u32 addr, u32 val) { + assert(ConsoleType == 1); addr &= ~0x3; switch (addr & 0xFF000000) @@ -1632,7 +1645,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u32*)&ptr[addr & 0xFFFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1650,7 +1663,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1668,7 +1681,7 @@ void ARM9Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1685,22 +1698,23 @@ void ARM9Write32(u32 addr, u32 val) return; case 0x0C000000: - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); - *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + *(u32*)&MainRAM[addr & MainRAMMask] = val; return; } return NDS::ARM9Write32(addr, val); } -bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) +bool DSi::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) { + assert(ConsoleType == 1); switch (addr & 0xFF000000) { case 0x02000000: case 0x0C000000: - region->Mem = NDS::MainRAM; - region->Mask = NDS::MainRAMMask; + region->Mem = MainRAM; + region->Mask = MainRAMMask; return true; } @@ -1714,7 +1728,7 @@ bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) return false; } - region->Mem = NDS::ARM9BIOS; + region->Mem = ARM9BIOS; region->Mask = 0xFFF; } else @@ -1731,15 +1745,15 @@ bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) -u8 ARM7Read8(u32 addr) +u8 DSi::ARM7Read8(u32 addr) { if ((addr < 0x00010000) && (!(SCFG_BIOS & (1<<9)))) { if ((addr >= 0x00008000) && (SCFG_BIOS & (1<<8))) return 0xFF; - if (NDS::ARM7->R[15] >= 0x00010000) + if (ARM7.R[15] >= 0x00010000) return 0xFF; - if (addr < NDS::ARM7BIOSProt && NDS::ARM7->R[15] >= NDS::ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFF; return *(u8*)&ARM7iBIOS[addr & 0xFFFF]; @@ -1778,27 +1792,28 @@ u8 ARM7Read8(u32 addr) case 0x09800000: case 0x0A000000: case 0x0A800000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0xFF : 0; + return (ExMemCnt[0] & (1<<7)) ? 0xFF : 0; case 0x0C000000: case 0x0C800000: - return *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u8*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM7Read8(addr); } -u16 ARM7Read16(u32 addr) +u16 DSi::ARM7Read16(u32 addr) { + assert(ConsoleType == 1); addr &= ~0x1; if ((addr < 0x00010000) && (!(SCFG_BIOS & (1<<9)))) { if ((addr >= 0x00008000) && (SCFG_BIOS & (1<<8))) return 0xFFFF; - if (NDS::ARM7->R[15] >= 0x00010000) + if (ARM7.R[15] >= 0x00010000) return 0xFFFF; - if (addr < NDS::ARM7BIOSProt && NDS::ARM7->R[15] >= NDS::ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFFFF; return *(u16*)&ARM7iBIOS[addr & 0xFFFF]; @@ -1837,17 +1852,17 @@ u16 ARM7Read16(u32 addr) case 0x09800000: case 0x0A000000: case 0x0A800000: - return (NDS::ExMemCnt[0] & (1<<7)) ? 0xFFFF : 0; + return (ExMemCnt[0] & (1<<7)) ? 0xFFFF : 0; case 0x0C000000: case 0x0C800000: - return *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask]; + return *(u16*)&MainRAM[addr & MainRAMMask]; } return NDS::ARM7Read16(addr); } -u32 ARM7Read32(u32 addr) +u32 DSi::ARM7Read32(u32 addr) { addr &= ~0x3; @@ -1855,9 +1870,9 @@ u32 ARM7Read32(u32 addr) { if ((addr >= 0x00008000) && (SCFG_BIOS & (1<<8))) return 0xFFFFFFFF; - if (NDS::ARM7->R[15] >= 0x00010000) + if (ARM7.R[15] >= 0x00010000) return 0xFFFFFFFF; - if (addr < NDS::ARM7BIOSProt && NDS::ARM7->R[15] >= NDS::ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFFFFFFFF; return *(u32*)&ARM7iBIOS[addr & 0xFFFF]; @@ -1906,8 +1921,9 @@ u32 ARM7Read32(u32 addr) return NDS::ARM7Read32(addr); } -void ARM7Write8(u32 addr, u8 val) +void DSi::ARM7Write8(u32 addr, u8 val) { + assert(ConsoleType == 1); switch (addr & 0xFF800000) { case 0x03000000: @@ -1928,7 +1944,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u8*)&ptr[addr & 0xFFFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -1946,7 +1962,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -1964,7 +1980,7 @@ void ARM7Write8(u32 addr, u8 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u8*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -1985,7 +2001,7 @@ void ARM7Write8(u32 addr, u8 val) case 0x0C000000: case 0x0C800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -1993,8 +2009,9 @@ void ARM7Write8(u32 addr, u8 val) return NDS::ARM7Write8(addr, val); } -void ARM7Write16(u32 addr, u16 val) +void DSi::ARM7Write16(u32 addr, u16 val) { + assert(ConsoleType == 1); addr &= ~0x1; switch (addr & 0xFF800000) @@ -2017,7 +2034,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u16*)&ptr[addr & 0xFFFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -2035,7 +2052,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -2053,7 +2070,7 @@ void ARM7Write16(u32 addr, u16 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u16*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -2074,7 +2091,7 @@ void ARM7Write16(u32 addr, u16 val) case 0x0C000000: case 0x0C800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -2082,8 +2099,9 @@ void ARM7Write16(u32 addr, u16 val) return NDS::ARM7Write16(addr, val); } -void ARM7Write32(u32 addr, u32 val) +void DSi::ARM7Write32(u32 addr, u32 val) { + assert(ConsoleType == 1); addr &= ~0x3; switch (addr & 0xFF800000) @@ -2106,7 +2124,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_A[page * 0x10000]; *(u32*)&ptr[addr & 0xFFFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_A>(addr); } return; } @@ -2124,7 +2142,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_B[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_B>(addr); } return; } @@ -2142,7 +2160,7 @@ void ARM7Write32(u32 addr, u32 val) continue; u8* ptr = &NWRAM_C[page * 0x8000]; *(u32*)&ptr[addr & 0x7FFF] = val; - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_NewSharedWRAM_C>(addr); } return; } @@ -2163,7 +2181,7 @@ void ARM7Write32(u32 addr, u32 val) case 0x0C000000: case 0x0C800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&NDS::MainRAM[addr & NDS::MainRAMMask] = val; return; } @@ -2171,8 +2189,9 @@ void ARM7Write32(u32 addr, u32 val) return NDS::ARM7Write32(addr, val); } -bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) +bool DSi::ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) { + assert(ConsoleType == 1); switch (addr & 0xFF800000) { case 0x02000000: @@ -2216,7 +2235,7 @@ bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region) case (addr): return (val) & 0xFFFF; \ case (addr+2): return (val) >> 16; -u8 ARM9IORead8(u32 addr) +u8 DSi::ARM9IORead8(u32 addr) { switch (addr) { @@ -2237,20 +2256,21 @@ u8 ARM9IORead8(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return CamModule->Read8(addr); + return CamModule.Read8(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSP->Read8(addr); + return DSP.Read8(addr); } return NDS::ARM9IORead8(addr); } -u16 ARM9IORead16(u32 addr) +u16 DSi::ARM9IORead16(u32 addr) { + assert(ConsoleType == 1); switch (addr) { case 0x04004000: return SCFG_BIOS & 0xFF; @@ -2272,20 +2292,21 @@ u16 ARM9IORead16(u32 addr) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return CamModule->Read16(addr); + return CamModule.Read16(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSP->Read16(addr); + return DSP.Read16(addr); } return NDS::ARM9IORead16(addr); } -u32 ARM9IORead32(u32 addr) +u32 DSi::ARM9IORead32(u32 addr) { + assert(ConsoleType == 1); switch (addr) { case 0x04004000: return SCFG_BIOS & 0xFF; @@ -2304,53 +2325,54 @@ u32 ARM9IORead32(u32 addr) case 0x04004060: return MBK[0][8]; case 0x04004100: return NDMACnt[0]; - case 0x04004104: return NDMAs[0]->SrcAddr; - case 0x04004108: return NDMAs[0]->DstAddr; - case 0x0400410C: return NDMAs[0]->TotalLength; - case 0x04004110: return NDMAs[0]->BlockLength; - case 0x04004114: return NDMAs[0]->SubblockTimer; - case 0x04004118: return NDMAs[0]->FillData; - case 0x0400411C: return NDMAs[0]->Cnt; - case 0x04004120: return NDMAs[1]->SrcAddr; - case 0x04004124: return NDMAs[1]->DstAddr; - case 0x04004128: return NDMAs[1]->TotalLength; - case 0x0400412C: return NDMAs[1]->BlockLength; - case 0x04004130: return NDMAs[1]->SubblockTimer; - case 0x04004134: return NDMAs[1]->FillData; - case 0x04004138: return NDMAs[1]->Cnt; - case 0x0400413C: return NDMAs[2]->SrcAddr; - case 0x04004140: return NDMAs[2]->DstAddr; - case 0x04004144: return NDMAs[2]->TotalLength; - case 0x04004148: return NDMAs[2]->BlockLength; - case 0x0400414C: return NDMAs[2]->SubblockTimer; - case 0x04004150: return NDMAs[2]->FillData; - case 0x04004154: return NDMAs[2]->Cnt; - case 0x04004158: return NDMAs[3]->SrcAddr; - case 0x0400415C: return NDMAs[3]->DstAddr; - case 0x04004160: return NDMAs[3]->TotalLength; - case 0x04004164: return NDMAs[3]->BlockLength; - case 0x04004168: return NDMAs[3]->SubblockTimer; - case 0x0400416C: return NDMAs[3]->FillData; - case 0x04004170: return NDMAs[3]->Cnt; + case 0x04004104: return NDMAs[0].SrcAddr; + case 0x04004108: return NDMAs[0].DstAddr; + case 0x0400410C: return NDMAs[0].TotalLength; + case 0x04004110: return NDMAs[0].BlockLength; + case 0x04004114: return NDMAs[0].SubblockTimer; + case 0x04004118: return NDMAs[0].FillData; + case 0x0400411C: return NDMAs[0].Cnt; + case 0x04004120: return NDMAs[1].SrcAddr; + case 0x04004124: return NDMAs[1].DstAddr; + case 0x04004128: return NDMAs[1].TotalLength; + case 0x0400412C: return NDMAs[1].BlockLength; + case 0x04004130: return NDMAs[1].SubblockTimer; + case 0x04004134: return NDMAs[1].FillData; + case 0x04004138: return NDMAs[1].Cnt; + case 0x0400413C: return NDMAs[2].SrcAddr; + case 0x04004140: return NDMAs[2].DstAddr; + case 0x04004144: return NDMAs[2].TotalLength; + case 0x04004148: return NDMAs[2].BlockLength; + case 0x0400414C: return NDMAs[2].SubblockTimer; + case 0x04004150: return NDMAs[2].FillData; + case 0x04004154: return NDMAs[2].Cnt; + case 0x04004158: return NDMAs[3].SrcAddr; + case 0x0400415C: return NDMAs[3].DstAddr; + case 0x04004160: return NDMAs[3].TotalLength; + case 0x04004164: return NDMAs[3].BlockLength; + case 0x04004168: return NDMAs[3].SubblockTimer; + case 0x0400416C: return NDMAs[3].FillData; + case 0x04004170: return NDMAs[3].Cnt; } if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return 0; - return CamModule->Read32(addr); + return CamModule.Read32(addr); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return 0; - return DSP->Read32(addr); + return DSP.Read32(addr); } return NDS::ARM9IORead32(addr); } -void ARM9IOWrite8(u32 addr, u8 val) +void DSi::ARM9IOWrite8(u32 addr, u8 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04000301: @@ -2367,7 +2389,7 @@ void ARM9IOWrite8(u32 addr, u8 val) if (!(SCFG_EXT[0] & (1 << 31))) /* no access to SCFG Registers if disabled*/ return; SCFG_RST = (SCFG_RST & 0xFF00) | val; - DSP->SetRstLine(val & 1); + DSP.SetRstLine(val & 1); return; case 0x04004040: @@ -2407,20 +2429,21 @@ void ARM9IOWrite8(u32 addr, u8 val) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return CamModule->Write8(addr, val); + return CamModule.Write8(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSP->Write8(addr, val); + return DSP.Write8(addr, val); } return NDS::ARM9IOWrite8(addr, val); } -void ARM9IOWrite16(u32 addr, u16 val) +void DSi::ARM9IOWrite16(u32 addr, u16 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04004004: @@ -2433,7 +2456,7 @@ void ARM9IOWrite16(u32 addr, u16 val) if (!(SCFG_EXT[0] & (1 << 31))) /* no access to SCFG Registers if disabled*/ return; SCFG_RST = val; - DSP->SetRstLine(val & 1); + DSP.SetRstLine(val & 1); return; case 0x04004040: @@ -2467,20 +2490,21 @@ void ARM9IOWrite16(u32 addr, u16 val) if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return CamModule->Write16(addr, val); + return CamModule.Write16(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSP->Write16(addr, val); + return DSP.Write16(addr, val); } return NDS::ARM9IOWrite16(addr, val); } -void ARM9IOWrite32(u32 addr, u32 val) +void DSi::ARM9IOWrite32(u32 addr, u32 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04004004: @@ -2488,7 +2512,7 @@ void ARM9IOWrite32(u32 addr, u32 val) return; Set_SCFG_Clock9(val & 0xFFFF); SCFG_RST = val >> 16; - DSP->SetRstLine((val >> 16) & 1); + DSP.SetRstLine((val >> 16) & 1); break; case 0x04004008: @@ -2523,7 +2547,7 @@ void ARM9IOWrite32(u32 addr, u32 val) // is still busy clearing/relocating shit //if (newram != oldram) // NDS::ScheduleEvent(NDS::Event_DSi_RAMSizeChange, false, 512*512*512, ApplyNewRAMSize, newram); - Log(LogLevel::Debug, "from %08X, ARM7 %08X, %08X\n", NDS::GetPC(0), NDS::GetPC(1), NDS::ARM7->R[1]); + Log(LogLevel::Debug, "from %08X, ARM7 %08X, %08X\n", NDS::GetPC(0), NDS::GetPC(1), ARM7.R[1]); } return; @@ -2584,54 +2608,55 @@ void ARM9IOWrite32(u32 addr, u32 val) return; case 0x04004100: NDMACnt[0] = val & 0x800F0000; return; - case 0x04004104: NDMAs[0]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004108: NDMAs[0]->DstAddr = val & 0xFFFFFFFC; return; - case 0x0400410C: NDMAs[0]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004110: NDMAs[0]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004114: NDMAs[0]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004118: NDMAs[0]->FillData = val; return; - case 0x0400411C: NDMAs[0]->WriteCnt(val); return; - case 0x04004120: NDMAs[1]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004124: NDMAs[1]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004128: NDMAs[1]->TotalLength = val & 0x0FFFFFFF; return; - case 0x0400412C: NDMAs[1]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004130: NDMAs[1]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004134: NDMAs[1]->FillData = val; return; - case 0x04004138: NDMAs[1]->WriteCnt(val); return; - case 0x0400413C: NDMAs[2]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004140: NDMAs[2]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004144: NDMAs[2]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004148: NDMAs[2]->BlockLength = val & 0x00FFFFFF; return; - case 0x0400414C: NDMAs[2]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004150: NDMAs[2]->FillData = val; return; - case 0x04004154: NDMAs[2]->WriteCnt(val); return; - case 0x04004158: NDMAs[3]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x0400415C: NDMAs[3]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004160: NDMAs[3]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004164: NDMAs[3]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004168: NDMAs[3]->SubblockTimer = val & 0x0003FFFF; return; - case 0x0400416C: NDMAs[3]->FillData = val; return; - case 0x04004170: NDMAs[3]->WriteCnt(val); return; + case 0x04004104: NDMAs[0].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004108: NDMAs[0].DstAddr = val & 0xFFFFFFFC; return; + case 0x0400410C: NDMAs[0].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004110: NDMAs[0].BlockLength = val & 0x00FFFFFF; return; + case 0x04004114: NDMAs[0].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004118: NDMAs[0].FillData = val; return; + case 0x0400411C: NDMAs[0].WriteCnt(val); return; + case 0x04004120: NDMAs[1].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004124: NDMAs[1].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004128: NDMAs[1].TotalLength = val & 0x0FFFFFFF; return; + case 0x0400412C: NDMAs[1].BlockLength = val & 0x00FFFFFF; return; + case 0x04004130: NDMAs[1].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004134: NDMAs[1].FillData = val; return; + case 0x04004138: NDMAs[1].WriteCnt(val); return; + case 0x0400413C: NDMAs[2].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004140: NDMAs[2].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004144: NDMAs[2].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004148: NDMAs[2].BlockLength = val & 0x00FFFFFF; return; + case 0x0400414C: NDMAs[2].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004150: NDMAs[2].FillData = val; return; + case 0x04004154: NDMAs[2].WriteCnt(val); return; + case 0x04004158: NDMAs[3].SrcAddr = val & 0xFFFFFFFC; return; + case 0x0400415C: NDMAs[3].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004160: NDMAs[3].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004164: NDMAs[3].BlockLength = val & 0x00FFFFFF; return; + case 0x04004168: NDMAs[3].SubblockTimer = val & 0x0003FFFF; return; + case 0x0400416C: NDMAs[3].FillData = val; return; + case 0x04004170: NDMAs[3].WriteCnt(val); return; } if ((addr & 0xFFFFFF00) == 0x04004200) { if (!(SCFG_EXT[0] & (1<<17))) return; - return CamModule->Write32(addr, val); + return CamModule.Write32(addr, val); } if ((addr & 0xFFFFFF00) == 0x04004300) { if (!(SCFG_EXT[0] & (1<<18))) return; - return DSP->Write32(addr, val); + return DSP.Write32(addr, val); } return NDS::ARM9IOWrite32(addr, val); } -u8 ARM7IORead8(u32 addr) +u8 DSi::ARM7IORead8(u32 addr) { + assert(ConsoleType == 1); switch (addr) { @@ -2650,8 +2675,8 @@ u8 ARM7IORead8(u32 addr) CASE_READ8_32BIT(0x0400405C, MBK[1][7]) CASE_READ8_32BIT(0x04004060, MBK[1][8]) - case 0x04004500: return I2C->ReadData(); - case 0x04004501: return I2C->ReadCnt(); + case 0x04004500: return I2C.ReadData(); + case 0x04004501: return I2C.ReadCnt(); case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF; case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF; @@ -2663,8 +2688,8 @@ u8 ARM7IORead8(u32 addr) case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56; case 0x04004D08: return 0; - case 0x4004700: return DSP->ReadSNDExCnt() & 0xFF; - case 0x4004701: return DSP->ReadSNDExCnt() >> 8; + case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF; + case 0x4004701: return DSP.ReadSNDExCnt() >> 8; case 0x04004C00: return GPIO_Data; case 0x04004C01: return GPIO_Dir; @@ -2677,8 +2702,9 @@ u8 ARM7IORead8(u32 addr) return NDS::ARM7IORead8(addr); } -u16 ARM7IORead16(u32 addr) +u16 DSi::ARM7IORead16(u32 addr) { + assert(ConsoleType == 1); switch (addr) { case 0x04000218: return NDS::IE2; @@ -2706,7 +2732,7 @@ u16 ARM7IORead16(u32 addr) case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48; case 0x04004D08: return 0; - case 0x4004700: return DSP->ReadSNDExCnt(); + case 0x4004700: return DSP.ReadSNDExCnt(); case 0x04004C00: return GPIO_Data | ((u16)GPIO_Dir << 8); case 0x04004C02: return GPIO_IEdgeSel | ((u16)GPIO_IE << 8); @@ -2715,18 +2741,19 @@ u16 ARM7IORead16(u32 addr) if (addr >= 0x04004800 && addr < 0x04004A00) { - return SDMMC->Read(addr); + return SDMMC.Read(addr); } if (addr >= 0x04004A00 && addr < 0x04004C00) { - return SDIO->Read(addr); + return SDIO.Read(addr); } return NDS::ARM7IORead16(addr); } -u32 ARM7IORead32(u32 addr) +u32 DSi::ARM7IORead32(u32 addr) { + assert(ConsoleType == 1); switch (addr) { case 0x04000218: return NDS::IE2; @@ -2747,63 +2774,64 @@ u32 ARM7IORead32(u32 addr) case 0x04004060: return MBK[1][8]; case 0x04004100: return NDMACnt[1]; - case 0x04004104: return NDMAs[4]->SrcAddr; - case 0x04004108: return NDMAs[4]->DstAddr; - case 0x0400410C: return NDMAs[4]->TotalLength; - case 0x04004110: return NDMAs[4]->BlockLength; - case 0x04004114: return NDMAs[4]->SubblockTimer; - case 0x04004118: return NDMAs[4]->FillData; - case 0x0400411C: return NDMAs[4]->Cnt; - case 0x04004120: return NDMAs[5]->SrcAddr; - case 0x04004124: return NDMAs[5]->DstAddr; - case 0x04004128: return NDMAs[5]->TotalLength; - case 0x0400412C: return NDMAs[5]->BlockLength; - case 0x04004130: return NDMAs[5]->SubblockTimer; - case 0x04004134: return NDMAs[5]->FillData; - case 0x04004138: return NDMAs[5]->Cnt; - case 0x0400413C: return NDMAs[6]->SrcAddr; - case 0x04004140: return NDMAs[6]->DstAddr; - case 0x04004144: return NDMAs[6]->TotalLength; - case 0x04004148: return NDMAs[6]->BlockLength; - case 0x0400414C: return NDMAs[6]->SubblockTimer; - case 0x04004150: return NDMAs[6]->FillData; - case 0x04004154: return NDMAs[6]->Cnt; - case 0x04004158: return NDMAs[7]->SrcAddr; - case 0x0400415C: return NDMAs[7]->DstAddr; - case 0x04004160: return NDMAs[7]->TotalLength; - case 0x04004164: return NDMAs[7]->BlockLength; - case 0x04004168: return NDMAs[7]->SubblockTimer; - case 0x0400416C: return NDMAs[7]->FillData; - case 0x04004170: return NDMAs[7]->Cnt; + case 0x04004104: return NDMAs[4].SrcAddr; + case 0x04004108: return NDMAs[4].DstAddr; + case 0x0400410C: return NDMAs[4].TotalLength; + case 0x04004110: return NDMAs[4].BlockLength; + case 0x04004114: return NDMAs[4].SubblockTimer; + case 0x04004118: return NDMAs[4].FillData; + case 0x0400411C: return NDMAs[4].Cnt; + case 0x04004120: return NDMAs[5].SrcAddr; + case 0x04004124: return NDMAs[5].DstAddr; + case 0x04004128: return NDMAs[5].TotalLength; + case 0x0400412C: return NDMAs[5].BlockLength; + case 0x04004130: return NDMAs[5].SubblockTimer; + case 0x04004134: return NDMAs[5].FillData; + case 0x04004138: return NDMAs[5].Cnt; + case 0x0400413C: return NDMAs[6].SrcAddr; + case 0x04004140: return NDMAs[6].DstAddr; + case 0x04004144: return NDMAs[6].TotalLength; + case 0x04004148: return NDMAs[6].BlockLength; + case 0x0400414C: return NDMAs[6].SubblockTimer; + case 0x04004150: return NDMAs[6].FillData; + case 0x04004154: return NDMAs[6].Cnt; + case 0x04004158: return NDMAs[7].SrcAddr; + case 0x0400415C: return NDMAs[7].DstAddr; + case 0x04004160: return NDMAs[7].TotalLength; + case 0x04004164: return NDMAs[7].BlockLength; + case 0x04004168: return NDMAs[7].SubblockTimer; + case 0x0400416C: return NDMAs[7].FillData; + case 0x04004170: return NDMAs[7].Cnt; - case 0x04004400: return AES->ReadCnt(); - case 0x0400440C: return AES->ReadOutputFIFO(); + case 0x04004400: return AES.ReadCnt(); + case 0x0400440C: return AES.ReadOutputFIFO(); case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF; case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32; case 0x04004D08: return 0; case 0x4004700: - Log(LogLevel::Debug, "32-Bit SNDExCnt read? %08X\n", NDS::ARM7->R[15]); - return DSP->ReadSNDExCnt(); + Log(LogLevel::Debug, "32-Bit SNDExCnt read? %08X\n", ARM7.R[15]); + return DSP.ReadSNDExCnt(); } if (addr >= 0x04004800 && addr < 0x04004A00) { - if (addr == 0x0400490C) return SDMMC->ReadFIFO32(); - return SDMMC->Read(addr) | (SDMMC->Read(addr+2) << 16); + if (addr == 0x0400490C) return SDMMC.ReadFIFO32(); + return SDMMC.Read(addr) | (SDMMC.Read(addr+2) << 16); } if (addr >= 0x04004A00 && addr < 0x04004C00) { - if (addr == 0x04004B0C) return SDIO->ReadFIFO32(); - return SDIO->Read(addr) | (SDIO->Read(addr+2) << 16); + if (addr == 0x04004B0C) return SDIO.ReadFIFO32(); + return SDIO.Read(addr) | (SDIO.Read(addr+2) << 16); } return NDS::ARM7IORead32(addr); } -void ARM7IOWrite8(u32 addr, u8 val) +void DSi::ARM7IOWrite8(u32 addr, u8 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04004000: @@ -2834,14 +2862,14 @@ void ARM7IOWrite8(u32 addr, u8 val) return; } - case 0x04004500: I2C->WriteData(val); return; - case 0x04004501: I2C->WriteCnt(val); return; + case 0x04004500: I2C.WriteData(val); return; + case 0x04004501: I2C.WriteCnt(val); return; case 0x4004700: - DSP->WriteSNDExCnt((u16)val, 0xFF); + DSP.WriteSNDExCnt((u16)val, 0xFF); return; case 0x4004701: - DSP->WriteSNDExCnt(((u16)val << 8), 0xFF00); + DSP.WriteSNDExCnt(((u16)val << 8), 0xFF00); return; case 0x04004C00: @@ -2866,7 +2894,7 @@ void ARM7IOWrite8(u32 addr, u8 val) u32 shift = (addr&3)*8; addr -= 0x04004420; addr &= ~3; - AES->WriteIV(addr, (u32)val << shift, 0xFF << shift); + AES.WriteIV(addr, (u32)val << shift, 0xFF << shift); return; } if (addr >= 0x04004430 && addr < 0x04004440) @@ -2874,7 +2902,7 @@ void ARM7IOWrite8(u32 addr, u8 val) u32 shift = (addr&3)*8; addr -= 0x04004430; addr &= ~3; - AES->WriteMAC(addr, (u32)val << shift, 0xFF << shift); + AES.WriteMAC(addr, (u32)val << shift, 0xFF << shift); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -2888,17 +2916,18 @@ void ARM7IOWrite8(u32 addr, u8 val) switch (addr >> 4) { - case 0: AES->WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; - case 1: AES->WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; - case 2: AES->WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 0: AES.WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 1: AES.WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; + case 2: AES.WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFF << shift); return; } } return NDS::ARM7IOWrite8(addr, val); } -void ARM7IOWrite16(u32 addr, u16 val) +void DSi::ARM7IOWrite16(u32 addr, u16 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04000218: NDS::IE2 = (val & 0x7FF7); NDS::UpdateIRQ(1); return; @@ -2936,11 +2965,11 @@ void ARM7IOWrite16(u32 addr, u16 val) return; case 0x04004406: - AES->WriteBlkCnt(val<<16); + AES.WriteBlkCnt(val<<16); return; case 0x4004700: - DSP->WriteSNDExCnt(val, 0xFFFF); + DSP.WriteSNDExCnt(val, 0xFFFF); return; case 0x04004C00: @@ -2961,7 +2990,7 @@ void ARM7IOWrite16(u32 addr, u16 val) u32 shift = (addr&1)*16; addr -= 0x04004420; addr &= ~1; - AES->WriteIV(addr, (u32)val << shift, 0xFFFF << shift); + AES.WriteIV(addr, (u32)val << shift, 0xFFFF << shift); return; } if (addr >= 0x04004430 && addr < 0x04004440) @@ -2969,7 +2998,7 @@ void ARM7IOWrite16(u32 addr, u16 val) u32 shift = (addr&1)*16; addr -= 0x04004430; addr &= ~1; - AES->WriteMAC(addr, (u32)val << shift, 0xFFFF << shift); + AES.WriteMAC(addr, (u32)val << shift, 0xFFFF << shift); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -2983,28 +3012,29 @@ void ARM7IOWrite16(u32 addr, u16 val) switch (addr >> 4) { - case 0: AES->WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; - case 1: AES->WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; - case 2: AES->WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 0: AES.WriteKeyNormal(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 1: AES.WriteKeyX(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; + case 2: AES.WriteKeyY(n, addr&0xF, (u32)val << shift, 0xFFFF << shift); return; } } if (addr >= 0x04004800 && addr < 0x04004A00) { - SDMMC->Write(addr, val); + SDMMC.Write(addr, val); return; } if (addr >= 0x04004A00 && addr < 0x04004C00) { - SDIO->Write(addr, val); + SDIO.Write(addr, val); return; } return NDS::ARM7IOWrite16(addr, val); } -void ARM7IOWrite32(u32 addr, u32 val) +void DSi::ARM7IOWrite32(u32 addr, u32 val) { + assert(ConsoleType == 1); switch (addr) { case 0x04000218: NDS::IE2 = (val & 0x7FF7); NDS::UpdateIRQ(1); return; @@ -3054,55 +3084,55 @@ void ARM7IOWrite32(u32 addr, u32 val) return; case 0x04004100: NDMACnt[1] = val & 0x800F0000; return; - case 0x04004104: NDMAs[4]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004108: NDMAs[4]->DstAddr = val & 0xFFFFFFFC; return; - case 0x0400410C: NDMAs[4]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004110: NDMAs[4]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004114: NDMAs[4]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004118: NDMAs[4]->FillData = val; return; - case 0x0400411C: NDMAs[4]->WriteCnt(val); return; - case 0x04004120: NDMAs[5]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004124: NDMAs[5]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004128: NDMAs[5]->TotalLength = val & 0x0FFFFFFF; return; - case 0x0400412C: NDMAs[5]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004130: NDMAs[5]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004134: NDMAs[5]->FillData = val; return; - case 0x04004138: NDMAs[5]->WriteCnt(val); return; - case 0x0400413C: NDMAs[6]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x04004140: NDMAs[6]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004144: NDMAs[6]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004148: NDMAs[6]->BlockLength = val & 0x00FFFFFF; return; - case 0x0400414C: NDMAs[6]->SubblockTimer = val & 0x0003FFFF; return; - case 0x04004150: NDMAs[6]->FillData = val; return; - case 0x04004154: NDMAs[6]->WriteCnt(val); return; - case 0x04004158: NDMAs[7]->SrcAddr = val & 0xFFFFFFFC; return; - case 0x0400415C: NDMAs[7]->DstAddr = val & 0xFFFFFFFC; return; - case 0x04004160: NDMAs[7]->TotalLength = val & 0x0FFFFFFF; return; - case 0x04004164: NDMAs[7]->BlockLength = val & 0x00FFFFFF; return; - case 0x04004168: NDMAs[7]->SubblockTimer = val & 0x0003FFFF; return; - case 0x0400416C: NDMAs[7]->FillData = val; return; - case 0x04004170: NDMAs[7]->WriteCnt(val); return; + case 0x04004104: NDMAs[4].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004108: NDMAs[4].DstAddr = val & 0xFFFFFFFC; return; + case 0x0400410C: NDMAs[4].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004110: NDMAs[4].BlockLength = val & 0x00FFFFFF; return; + case 0x04004114: NDMAs[4].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004118: NDMAs[4].FillData = val; return; + case 0x0400411C: NDMAs[4].WriteCnt(val); return; + case 0x04004120: NDMAs[5].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004124: NDMAs[5].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004128: NDMAs[5].TotalLength = val & 0x0FFFFFFF; return; + case 0x0400412C: NDMAs[5].BlockLength = val & 0x00FFFFFF; return; + case 0x04004130: NDMAs[5].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004134: NDMAs[5].FillData = val; return; + case 0x04004138: NDMAs[5].WriteCnt(val); return; + case 0x0400413C: NDMAs[6].SrcAddr = val & 0xFFFFFFFC; return; + case 0x04004140: NDMAs[6].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004144: NDMAs[6].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004148: NDMAs[6].BlockLength = val & 0x00FFFFFF; return; + case 0x0400414C: NDMAs[6].SubblockTimer = val & 0x0003FFFF; return; + case 0x04004150: NDMAs[6].FillData = val; return; + case 0x04004154: NDMAs[6].WriteCnt(val); return; + case 0x04004158: NDMAs[7].SrcAddr = val & 0xFFFFFFFC; return; + case 0x0400415C: NDMAs[7].DstAddr = val & 0xFFFFFFFC; return; + case 0x04004160: NDMAs[7].TotalLength = val & 0x0FFFFFFF; return; + case 0x04004164: NDMAs[7].BlockLength = val & 0x00FFFFFF; return; + case 0x04004168: NDMAs[7].SubblockTimer = val & 0x0003FFFF; return; + case 0x0400416C: NDMAs[7].FillData = val; return; + case 0x04004170: NDMAs[7].WriteCnt(val); return; - case 0x04004400: AES->WriteCnt(val); return; - case 0x04004404: AES->WriteBlkCnt(val); return; - case 0x04004408: AES->WriteInputFIFO(val); return; + case 0x04004400: AES.WriteCnt(val); return; + case 0x04004404: AES.WriteBlkCnt(val); return; + case 0x04004408: AES.WriteInputFIFO(val); return; case 0x4004700: - Log(LogLevel::Debug, "32-Bit SNDExCnt write? %08X %08X\n", val, NDS::ARM7->R[15]); - DSP->WriteSNDExCnt(val, 0xFFFF); + Log(LogLevel::Debug, "32-Bit SNDExCnt write? %08X %08X\n", val, ARM7.R[15]); + DSP.WriteSNDExCnt(val, 0xFFFF); return; } if (addr >= 0x04004420 && addr < 0x04004430) { addr -= 0x04004420; - AES->WriteIV(addr, val, 0xFFFFFFFF); + AES.WriteIV(addr, val, 0xFFFFFFFF); return; } if (addr >= 0x04004430 && addr < 0x04004440) { addr -= 0x04004430; - AES->WriteMAC(addr, val, 0xFFFFFFFF); + AES.WriteMAC(addr, val, 0xFFFFFFFF); return; } if (addr >= 0x04004440 && addr < 0x04004500) @@ -3113,31 +3143,31 @@ void ARM7IOWrite32(u32 addr, u32 val) switch (addr >> 4) { - case 0: AES->WriteKeyNormal(n, addr&0xF, val, 0xFFFFFFFF); return; - case 1: AES->WriteKeyX(n, addr&0xF, val, 0xFFFFFFFF); return; - case 2: AES->WriteKeyY(n, addr&0xF, val, 0xFFFFFFFF); return; + case 0: AES.WriteKeyNormal(n, addr&0xF, val, 0xFFFFFFFF); return; + case 1: AES.WriteKeyX(n, addr&0xF, val, 0xFFFFFFFF); return; + case 2: AES.WriteKeyY(n, addr&0xF, val, 0xFFFFFFFF); return; } } if (addr >= 0x04004800 && addr < 0x04004A00) { - if (addr == 0x0400490C) { SDMMC->WriteFIFO32(val); return; } - SDMMC->Write(addr, val & 0xFFFF); - SDMMC->Write(addr+2, val >> 16); + if (addr == 0x0400490C) { SDMMC.WriteFIFO32(val); return; } + SDMMC.Write(addr, val & 0xFFFF); + SDMMC.Write(addr+2, val >> 16); return; } if (addr >= 0x04004A00 && addr < 0x04004C00) { - if (addr == 0x04004B0C) { SDIO->WriteFIFO32(val); return; } - SDIO->Write(addr, val & 0xFFFF); - SDIO->Write(addr+2, val >> 16); + if (addr == 0x04004B0C) { SDIO.WriteFIFO32(val); return; } + SDIO.Write(addr, val & 0xFFFF); + SDIO.Write(addr+2, val >> 16); return; } if (addr >= 0x04004300 && addr <= 0x04004400) { - DSP->Write32(addr, val); + DSP.Write32(addr, val); return; } @@ -3145,5 +3175,3 @@ void ARM7IOWrite32(u32 addr, u32 val) } } - -} \ No newline at end of file diff --git a/src/DSi.h b/src/DSi.h index ac6bf772..a221b5d7 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -20,7 +20,12 @@ #define DSI_H #include "NDS.h" +#include "DSi_NDMA.h" #include "DSi_SD.h" +#include "DSi_DSP.h" +#include "DSi_AES.h" +#include "DSi_Camera.h" +#include "DSi_NAND.h" namespace melonDS { @@ -34,99 +39,135 @@ namespace DSi_NAND class NANDImage; } -namespace DSi +class DSi final : public NDS { +protected: + void DoSavestateExtra(Savestate* file) override; +public: + u16 SCFG_BIOS; + u16 SCFG_Clock9; + u32 SCFG_EXT[2]; -extern u16 SCFG_BIOS; -extern u16 SCFG_Clock9; -extern u32 SCFG_EXT[2]; + u8 ARM9iBIOS[0x10000]; + u8 ARM7iBIOS[0x10000]; + std::unique_ptr NANDImage; + DSi_SDHost SDMMC; + DSi_SDHost SDIO; + const u32 NWRAMSize = 0x40000; -extern u8 ARM9iBIOS[0x10000]; -extern u8 ARM7iBIOS[0x10000]; + u8* NWRAM_A; + u8* NWRAM_B; + u8* NWRAM_C; -extern std::unique_ptr NANDImage; -extern DSi_SDHost* SDMMC; -extern DSi_SDHost* SDIO; + u8* NWRAMMap_A[2][4]; + u8* NWRAMMap_B[3][8]; + u8* NWRAMMap_C[3][8]; -const u32 NWRAMSize = 0x40000; + u32 NWRAMStart[2][3]; + u32 NWRAMEnd[2][3]; + u32 NWRAMMask[2][3]; -extern u8* NWRAM_A; -extern u8* NWRAM_B; -extern u8* NWRAM_C; + DSi_I2CHost I2C; + DSi_CamModule CamModule; + DSi_AES AES; + DSi_DSP DSP; -extern u8* NWRAMMap_A[2][4]; -extern u8* NWRAMMap_B[3][8]; -extern u8* NWRAMMap_C[3][8]; + void Reset() override; + void Stop(Platform::StopReason reason) override; -extern u32 NWRAMStart[2][3]; -extern u32 NWRAMEnd[2][3]; -extern u32 NWRAMMask[2][3]; + bool DoSavestate(Savestate* file); -extern DSi_I2CHost* I2C; -extern DSi_CamModule* CamModule; -extern DSi_AES* AES; -extern DSi_DSP* DSP; + void SetCartInserted(bool inserted); -bool Init(); -void DeInit(); -void Reset(); -void Stop(); + void SetupDirectBoot() override; + void SoftReset(); -void DoSavestate(Savestate* file); + bool LoadNAND(); -void SetCartInserted(bool inserted); + void RunNDMAs(u32 cpu); + void StallNDMAs(); + bool NDMAsInMode(u32 cpu, u32 mode); + bool NDMAsRunning(u32 cpu); + void CheckNDMAs(u32 cpu, u32 mode); + void StopNDMAs(u32 cpu, u32 mode); -void SetupDirectBoot(); -void SoftReset(); + void MapNWRAM_A(u32 num, u8 val); + void MapNWRAM_B(u32 num, u8 val); + void MapNWRAM_C(u32 num, u8 val); + void MapNWRAMRange(u32 cpu, u32 num, u32 val); -bool LoadNAND(); + u8 ARM9Read8(u32 addr) override; + u16 ARM9Read16(u32 addr) override; + u32 ARM9Read32(u32 addr) override; + void ARM9Write8(u32 addr, u8 val) override; + void ARM9Write16(u32 addr, u16 val) override; + void ARM9Write32(u32 addr, u32 val) override; -void RunNDMAs(u32 cpu); -void StallNDMAs(); -bool NDMAsInMode(u32 cpu, u32 mode); -bool NDMAsRunning(u32 cpu); -void CheckNDMAs(u32 cpu, u32 mode); -void StopNDMAs(u32 cpu, u32 mode); + bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) override; -void MapNWRAM_A(u32 num, u8 val); -void MapNWRAM_B(u32 num, u8 val); -void MapNWRAM_C(u32 num, u8 val); -void MapNWRAMRange(u32 cpu, u32 num, u32 val); + u8 ARM7Read8(u32 addr) override; + u16 ARM7Read16(u32 addr) override; + u32 ARM7Read32(u32 addr) override; + void ARM7Write8(u32 addr, u8 val) override; + void ARM7Write16(u32 addr, u16 val) override; + void ARM7Write32(u32 addr, u32 val) override; -u8 ARM9Read8(u32 addr); -u16 ARM9Read16(u32 addr); -u32 ARM9Read32(u32 addr); -void ARM9Write8(u32 addr, u8 val); -void ARM9Write16(u32 addr, u16 val); -void ARM9Write32(u32 addr, u32 val); + bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) override; -bool ARM9GetMemRegion(u32 addr, bool write, NDS::MemRegion* region); + u8 ARM9IORead8(u32 addr) override; + u16 ARM9IORead16(u32 addr) override; + u32 ARM9IORead32(u32 addr) override; + void ARM9IOWrite8(u32 addr, u8 val) override; + void ARM9IOWrite16(u32 addr, u16 val) override; + void ARM9IOWrite32(u32 addr, u32 val) override; -u8 ARM7Read8(u32 addr); -u16 ARM7Read16(u32 addr); -u32 ARM7Read32(u32 addr); -void ARM7Write8(u32 addr, u8 val); -void ARM7Write16(u32 addr, u16 val); -void ARM7Write32(u32 addr, u32 val); + u8 ARM7IORead8(u32 addr) override; + u16 ARM7IORead16(u32 addr) override; + u32 ARM7IORead32(u32 addr) override; + void ARM7IOWrite8(u32 addr, u8 val) override; + void ARM7IOWrite16(u32 addr, u16 val) override; + void ARM7IOWrite32(u32 addr, u32 val) override; -bool ARM7GetMemRegion(u32 addr, bool write, NDS::MemRegion* region); +public: + DSi() noexcept; + ~DSi() noexcept override; + DSi(const DSi&) = delete; + DSi& operator=(const DSi&) = delete; + DSi(DSi&&) = delete; + DSi& operator=(DSi&&) = delete; + bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) override; + void EjectCart() override; + bool NeedsDirectBoot() override + { + // for now, DSi mode requires original BIOS/NAND + return false; + } + void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) override; + bool DMAsInMode(u32 cpu, u32 mode) override; + bool DMAsRunning(u32 cpu) override; + void StopDMAs(u32 cpu, u32 mode) override; + void CheckDMAs(u32 cpu, u32 mode) override; + u16 SCFG_Clock7; + u32 SCFG_MC; + u16 SCFG_RST; + u32 MBK[2][9]; + u32 NDMACnt[2]; + std::array NDMAs; + // FIXME: these currently have no effect (and aren't stored in a savestate) + // ... not that they matter all that much + u8 GPIO_Data; + u8 GPIO_Dir; + u8 GPIO_IEdgeSel; + u8 GPIO_IE; + u8 GPIO_WiFi; -u8 ARM9IORead8(u32 addr); -u16 ARM9IORead16(u32 addr); -u32 ARM9IORead32(u32 addr); -void ARM9IOWrite8(u32 addr, u8 val); -void ARM9IOWrite16(u32 addr, u16 val); -void ARM9IOWrite32(u32 addr, u32 val); - -u8 ARM7IORead8(u32 addr); -u16 ARM7IORead16(u32 addr); -u32 ARM7IORead32(u32 addr); -void ARM7IOWrite8(u32 addr, u8 val); -void ARM7IOWrite16(u32 addr, u16 val); -void ARM7IOWrite32(u32 addr, u32 val); - -} +private: + void Set_SCFG_Clock9(u16 val); + void Set_SCFG_MC(u32 val); + void DecryptModcryptArea(u32 offset, u32 size, u8* iv); + void ApplyNewRAMSize(u32 size); +}; } #endif // DSI_H diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index a29e43f1..8e04586a 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -36,7 +36,7 @@ using Platform::LogLevel; #define _printhex2R(str, size) { for (int z = 0; z < (size); z++) printf("%02X", (str)[((size)-1)-z]); } -DSi_AES::DSi_AES() +DSi_AES::DSi_AES(melonDS::DSi& dsi) : DSi(dsi) { const u8 zero[16] = {0}; AES_init_ctx_iv(&Ctx, zero, zero); @@ -78,7 +78,7 @@ void DSi_AES::Reset() OutputMACDue = false; // initialize keys - u64 consoleid = DSi::NANDImage->GetConsoleID(); + u64 consoleid = DSi.NANDImage->GetConsoleID(); // slot 0: modcrypt *(u32*)&KeyX[0][0] = 0x746E694E; @@ -313,7 +313,7 @@ void DSi_AES::WriteCnt(u32 val) AES_init_ctx_iv(&Ctx, key, iv); } - DSi::CheckNDMAs(1, 0x2A); + DSi.CheckNDMAs(1, 0x2A); } else { @@ -347,9 +347,9 @@ u32 DSi_AES::ReadOutputFIFO() else { if (OutputFIFO.Level() > 0) - DSi::CheckNDMAs(1, 0x2B); + DSi.CheckNDMAs(1, 0x2B); else - DSi::StopNDMAs(1, 0x2B); + DSi.StopNDMAs(1, 0x2B); if (OutputMACDue && OutputFIFO.Level() <= 12) { @@ -384,7 +384,7 @@ void DSi_AES::CheckInputDMA() if (InputFIFO.Level() <= InputDMASize) { // trigger input DMA - DSi::CheckNDMAs(1, 0x2A); + DSi.CheckNDMAs(1, 0x2A); } Update(); @@ -395,7 +395,7 @@ void DSi_AES::CheckOutputDMA() if (OutputFIFO.Level() >= OutputDMASize) { // trigger output DMA - DSi::CheckNDMAs(1, 0x2B); + DSi.CheckNDMAs(1, 0x2B); } } @@ -475,13 +475,13 @@ void DSi_AES::Update() } Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ2(NDS::IRQ2_DSi_AES); - DSi::StopNDMAs(1, 0x2A); + if (Cnt & (1<<30)) DSi.SetIRQ2(IRQ2_DSi_AES); + DSi.StopNDMAs(1, 0x2A); if (!OutputFIFO.IsEmpty()) - DSi::CheckNDMAs(1, 0x2B); + DSi.CheckNDMAs(1, 0x2B); else - DSi::StopNDMAs(1, 0x2B); + DSi.StopNDMAs(1, 0x2B); OutputFlush = false; } } diff --git a/src/DSi_AES.h b/src/DSi_AES.h index 9c0a63fe..4df82695 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -45,10 +45,11 @@ __attribute((always_inline)) static void Bswap128(void* Dst, const void* Src) #endif #pragma GCC diagnostic pop +class DSi; class DSi_AES { public: - DSi_AES(); + DSi_AES(melonDS::DSi& dsi); ~DSi_AES(); void Reset(); void DoSavestate(Savestate* file); @@ -73,6 +74,7 @@ public: static void DeriveNormalKey(u8* keyX, u8* keyY, u8* normalkey); private: + melonDS::DSi& DSi; u32 Cnt; u32 BlkCnt; diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 708875c1..225bea8b 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -38,13 +38,13 @@ const u32 DSi_CamModule::kIRQInterval = 1120000; // ~30 FPS const u32 DSi_CamModule::kTransferStart = 60000; -DSi_CamModule::DSi_CamModule() +DSi_CamModule::DSi_CamModule(melonDS::DSi& dsi) : DSi(dsi) { - NDS::RegisterEventFunc(NDS::Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ)); - NDS::RegisterEventFunc(NDS::Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline)); + DSi.RegisterEventFunc(Event_DSi_CamIRQ, 0, MemberEventFunc(DSi_CamModule, IRQ)); + DSi.RegisterEventFunc(Event_DSi_CamTransfer, 0, MemberEventFunc(DSi_CamModule, TransferScanline)); - Camera0 = DSi::I2C->GetOuterCamera(); - Camera1 = DSi::I2C->GetInnerCamera(); + Camera0 = DSi.I2C.GetOuterCamera(); + Camera1 = DSi.I2C.GetInnerCamera(); } DSi_CamModule::~DSi_CamModule() @@ -52,8 +52,8 @@ DSi_CamModule::~DSi_CamModule() Camera0 = nullptr; Camera1 = nullptr; - NDS::UnregisterEventFunc(NDS::Event_DSi_CamIRQ, 0); - NDS::UnregisterEventFunc(NDS::Event_DSi_CamTransfer, 0); + DSi.UnregisterEventFunc(Event_DSi_CamIRQ, 0); + DSi.UnregisterEventFunc(Event_DSi_CamTransfer, 0); } void DSi_CamModule::Reset() @@ -70,7 +70,7 @@ void DSi_CamModule::Reset() BufferNumLines = 0; CurCamera = nullptr; - NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, false, kIRQInterval, 0, 0); + DSi.ScheduleEvent(Event_DSi_CamIRQ, false, kIRQInterval, 0, 0); } void DSi_CamModule::Stop() @@ -106,7 +106,7 @@ void DSi_CamModule::IRQ(u32 param) activecam->StartTransfer(); if (Cnt & (1<<11)) - NDS::SetIRQ(0, NDS::IRQ_DSi_Camera); + DSi.SetIRQ(0, IRQ_DSi_Camera); if (Cnt & (1<<15)) { @@ -114,11 +114,11 @@ void DSi_CamModule::IRQ(u32 param) BufferWritePos = 0; BufferNumLines = 0; CurCamera = activecam; - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, kTransferStart, 0, 0); + DSi.ScheduleEvent(Event_DSi_CamTransfer, false, kTransferStart, 0, 0); } } - NDS::ScheduleEvent(NDS::Event_DSi_CamIRQ, true, kIRQInterval, 0, 0); + DSi.ScheduleEvent(Event_DSi_CamIRQ, true, kIRQInterval, 0, 0); } void DSi_CamModule::TransferScanline(u32 line) @@ -144,7 +144,7 @@ void DSi_CamModule::TransferScanline(u32 line) if (line < ystart || line > yend) { if (!CurCamera->TransferDone()) - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, 0, line+1); + DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1); return; } @@ -212,7 +212,7 @@ void DSi_CamModule::TransferScanline(u32 line) BufferReadPos = 0; // checkme BufferWritePos = 0; BufferNumLines = 0; - DSi::CheckNDMAs(0, 0x0B); + DSi.CheckNDMAs(0, 0x0B); } else { @@ -224,7 +224,7 @@ void DSi_CamModule::TransferScanline(u32 line) if (CurCamera->TransferDone()) return; - NDS::ScheduleEvent(NDS::Event_DSi_CamTransfer, false, delay, 0, line+1); + DSi.ScheduleEvent(Event_DSi_CamTransfer, false, delay, 0, line+1); } @@ -379,7 +379,7 @@ void DSi_CamModule::Write32(u32 addr, u32 val) -DSi_Camera::DSi_Camera(DSi_I2CHost* host, u32 num) : DSi_I2CDevice(host), Num(num) +DSi_Camera::DSi_Camera(melonDS::DSi& dsi, DSi_I2CHost* host, u32 num) : DSi_I2CDevice(dsi, host), Num(num) { } diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index 8a62e47d..ec409223 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -25,17 +25,18 @@ namespace melonDS { +class DSi; class DSi_CamModule; class DSi_Camera : public DSi_I2CDevice { public: - DSi_Camera(DSi_I2CHost* host, u32 num); + DSi_Camera(melonDS::DSi& dsi, DSi_I2CHost* host, u32 num); ~DSi_Camera(); - void DoSavestate(Savestate* file); + void DoSavestate(Savestate* file) override; - void Reset(); + void Reset() override; void Stop(); bool IsActivated(); @@ -45,9 +46,9 @@ public: // lengths in words int TransferScanline(u32* buffer, int maxlen); - void Acquire(); - u8 Read(bool last); - void Write(u8 val, bool last); + void Acquire() override; + u8 Read(bool last) override; + void Write(u8 val, bool last) override; void InputFrame(u32* data, int width, int height, bool rgb); @@ -84,7 +85,7 @@ private: class DSi_CamModule { public: - DSi_CamModule(); + DSi_CamModule(melonDS::DSi& dsi); ~DSi_CamModule(); void Reset(); void Stop(); @@ -105,6 +106,7 @@ public: void Write32(u32 addr, u32 val); private: + melonDS::DSi& DSi; DSi_Camera* Camera0; // 78 / facing outside DSi_Camera* Camera1; // 7A / selfie cam diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index 61d2b162..088943a9 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -55,33 +55,33 @@ u16 DSi_DSP::GetPSTS() void DSi_DSP::IrqRep0() { - if (DSP_PCFG & (1<< 9)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + if (DSP_PCFG & (1<< 9)) DSi.SetIRQ(0, IRQ_DSi_DSP); } void DSi_DSP::IrqRep1() { - if (DSP_PCFG & (1<<10)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + if (DSP_PCFG & (1<<10)) DSi.SetIRQ(0, IRQ_DSi_DSP); } void DSi_DSP::IrqRep2() { - if (DSP_PCFG & (1<<11)) NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + if (DSP_PCFG & (1<<11)) DSi.SetIRQ(0, IRQ_DSi_DSP); } void DSi_DSP::IrqSem() { DSP_PSTS |= 1<<9; // apparently these are always fired? - NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + DSi.SetIRQ(0, IRQ_DSi_DSP); } u16 DSi_DSP::DSPRead16(u32 addr) { if (!(addr & 0x40000)) { - u8* ptr = DSi::NWRAMMap_B[2][(addr >> 15) & 0x7]; + u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7]; return ptr ? *(u16*)&ptr[addr & 0x7FFF] : 0; } else { - u8* ptr = DSi::NWRAMMap_C[2][(addr >> 15) & 0x7]; + u8* ptr = DSi.NWRAMMap_C[2][(addr >> 15) & 0x7]; return ptr ? *(u16*)&ptr[addr & 0x7FFF] : 0; } } @@ -92,12 +92,12 @@ void DSi_DSP::DSPWrite16(u32 addr, u16 val) if (!(addr & 0x40000)) { - u8* ptr = DSi::NWRAMMap_B[2][(addr >> 15) & 0x7]; + u8* ptr = DSi.NWRAMMap_B[2][(addr >> 15) & 0x7]; if (ptr) *(u16*)&ptr[addr & 0x7FFF] = val; } else { - u8* ptr = DSi::NWRAMMap_C[2][(addr >> 15) & 0x7]; + u8* ptr = DSi.NWRAMMap_C[2][(addr >> 15) & 0x7]; if (ptr) *(u16*)&ptr[addr & 0x7FFF] = val; } } @@ -107,9 +107,9 @@ void DSi_DSP::AudioCb(std::array frame) // TODO } -DSi_DSP::DSi_DSP() +DSi_DSP::DSi_DSP(melonDS::DSi& dsi) : DSi(dsi) { - NDS::RegisterEventFunc(NDS::Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32)); + DSi.RegisterEventFunc(Event_DSi_DSP, 0, MemberEventFunc(DSi_DSP, DSPCatchUpU32)); TeakraCore = new Teakra::Teakra(); SCFG_RST = false; @@ -133,12 +133,12 @@ DSi_DSP::DSi_DSP() // these happen instantaneously and without too much regard for bus aribtration // rules, so, this might have to be changed later on Teakra::AHBMCallback cb; - cb.read8 = DSi::ARM9Read8; - cb.write8 = DSi::ARM9Write8; - cb.read16 = DSi::ARM9Read16; - cb.write16 = DSi::ARM9Write16; - cb.read32 = DSi::ARM9Read32; - cb.write32 = DSi::ARM9Write32; + cb.read8 = [this](auto addr) { return DSi.ARM9Read8(addr); }; + cb.write8 = [this](auto addr, auto val) { DSi.ARM9Write8(addr, val); }; + cb.read16 = [this](auto addr) { return DSi.ARM9Read16(addr); }; + cb.write16 = [this](auto addr, auto val) { DSi.ARM9Write16(addr, val); }; + cb.read32 = [this](auto addr) { return DSi.ARM9Read32(addr); }; + cb.write32 = [this](auto addr, auto val) { DSi.ARM9Write32(addr, val); }; TeakraCore->SetAHBMCallback(cb); TeakraCore->SetAudioCallback(std::bind(&DSi_DSP::AudioCb, this, _1)); @@ -156,7 +156,7 @@ DSi_DSP::~DSi_DSP() //PDATAWriteFifo = NULL; TeakraCore = NULL; - NDS::UnregisterEventFunc(NDS::Event_DSi_DSP, 0); + DSi.UnregisterEventFunc(Event_DSi_DSP, 0); } void DSi_DSP::Reset() @@ -177,7 +177,7 @@ void DSi_DSP::Reset() //PDATAWriteFifo->Clear(); TeakraCore->Reset(); - NDS::CancelEvent(NDS::Event_DSi_DSP); + DSi.CancelEvent(Event_DSi_DSP); SNDExCnt = 0; } @@ -190,17 +190,17 @@ void DSi_DSP::SetRstLine(bool release) { SCFG_RST = release; Reset(); - DSPTimestamp = NDS::ARM9Timestamp; // only start now! + DSPTimestamp = DSi.ARM9Timestamp; // only start now! } inline bool DSi_DSP::IsDSPCoreEnabled() { - return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0))); + return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0))); } inline bool DSi_DSP::IsDSPIOEnabled() { - return (DSi::SCFG_Clock9 & (1<<1)) && SCFG_RST; + return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST; } bool DSi_DSP::DSPCatchUp() @@ -210,13 +210,13 @@ bool DSi_DSP::DSPCatchUp() { // nothing to do, but advance the current time so that we don't do an // unreasonable amount of cycles when rst is released - if (DSPTimestamp < NDS::ARM9Timestamp) - DSPTimestamp = NDS::ARM9Timestamp; + if (DSPTimestamp < DSi.ARM9Timestamp) + DSPTimestamp = DSi.ARM9Timestamp; return false; } - u64 curtime = NDS::ARM9Timestamp; + u64 curtime = DSi.ARM9Timestamp; if (DSPTimestamp >= curtime) return true; // ummmm?! @@ -257,7 +257,7 @@ void DSi_DSP::PDataDMAWrite(u16 wrval) { switch (TeakraCore->AHBMGetUnitSize(0)) { - case 0: /* 8bit */ DSi::ARM9Write8 (addr, (u8)wrval); break; + case 0: /* 8bit */ DSi.ARM9Write8 (addr, (u8)wrval); break; case 1: /* 16 b */ TeakraCore->AHBMWrite16(addr, wrval); break; // does it work like this, or should it first buffer two u16's // until it has enough data to write to the actual destination? @@ -272,7 +272,7 @@ void DSi_DSP::PDataDMAWrite(u16 wrval) if (DSP_PCFG & (1<<1)) // auto-increment ++DSP_PADR; // overflows and stays within a 64k 'page' // TODO: is this +1 or +2? - NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); // wrfifo empty + DSi.SetIRQ(0, IRQ_DSi_DSP); // wrfifo empty } // TODO: FIFO interrupts! (rd full, nonempty) u16 DSi_DSP::PDataDMARead() @@ -299,7 +299,7 @@ u16 DSi_DSP::PDataDMARead() { switch (TeakraCore->AHBMGetUnitSize(0)) { - case 0: /* 8bit */ r = DSi::ARM9Read8 (addr); break; + case 0: /* 8bit */ r = DSi.ARM9Read8 (addr); break; case 1: /* 16 b */ r = TeakraCore->AHBMRead16(addr); break; case 2: /* 32 b */ r = (u16)TeakraCore->AHBMRead32(addr); break; } @@ -337,7 +337,7 @@ void DSi_DSP::PDataDMAStart() for (int i = 0; i < amt; ++i) PDataDMAFetch(); - NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + DSi.SetIRQ(0, IRQ_DSi_DSP); } void DSi_DSP::PDataDMACancel() @@ -372,7 +372,7 @@ u16 DSi_DSP::PDataDMAReadMMIO() } if (!PDATAReadFifo.IsEmpty() || PDATAReadFifo.IsFull()) - NDS::SetIRQ(0, NDS::IRQ_DSi_DSP); + DSi.SetIRQ(0, IRQ_DSi_DSP); return ret; } @@ -477,7 +477,7 @@ void DSi_DSP::Write8(u32 addr, u8 val) } void DSi_DSP::Write16(u32 addr, u16 val) { - Log(LogLevel::Debug,"DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, NDS::GetPC(0)); + Log(LogLevel::Debug,"DSP WRITE16 %d %08X %08X %08X\n", IsDSPCoreEnabled(), addr, val, DSi.GetPC(0)); //if (!IsDSPIOEnabled()) return; DSPCatchUp(); @@ -565,8 +565,8 @@ void DSi_DSP::Run(u32 cycles) DSPTimestamp += cycles; - NDS::CancelEvent(NDS::Event_DSi_DSP); - NDS::ScheduleEvent(NDS::Event_DSi_DSP, false, + DSi.CancelEvent(Event_DSi_DSP); + DSi.ScheduleEvent(Event_DSi_DSP, false, 16384/*from citra (TeakraSlice)*/, 0, 0); } diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h index 18ad3d1d..a18dabf1 100644 --- a/src/DSi_DSP.h +++ b/src/DSi_DSP.h @@ -29,10 +29,11 @@ namespace Teakra { class Teakra; } namespace melonDS { +class DSi; class DSi_DSP { public: - DSi_DSP(); + DSi_DSP(melonDS::DSi& dsi); ~DSi_DSP(); void Reset(); void DoSavestate(Savestate* file); @@ -68,6 +69,7 @@ public: void AudioCb(std::array frame); private: + melonDS::DSi& DSi; // not sure whether to not rather put it somewhere else u16 SNDExCnt; diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index 678c8be6..d5ea60c6 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -57,7 +57,7 @@ const u8 DSi_BPTWL::VolumeUpTable[32] = }; -DSi_BPTWL::DSi_BPTWL(DSi_I2CHost* host) : DSi_I2CDevice(host) +DSi_BPTWL::DSi_BPTWL(melonDS::DSi& dsi, DSi_I2CHost* host) : DSi_I2CDevice(dsi, host) { } @@ -177,19 +177,19 @@ void DSi_BPTWL::DoHardwareReset(bool direct) if (direct) { // TODO: This doesn't seem to stop the SPU - DSi::SoftReset(); + DSi.SoftReset(); return; } // TODO: soft-reset might need to be scheduled later! // TODO: this has been moved for the JIT to work, nothing is confirmed here - NDS::ARM7->Halt(4); + DSi.ARM7.Halt(4); } void DSi_BPTWL::DoShutdown() { ResetButtonState(); - NDS::Stop(Platform::StopReason::PowerOff); + DSi.Stop(Platform::StopReason::PowerOff); } @@ -369,7 +369,7 @@ void DSi_BPTWL::SetIRQ(u8 irqFlag) if (GetIRQMode()) { - NDS::SetIRQ2(NDS::IRQ2_DSi_BPTWL); + DSi.SetIRQ2(IRQ2_DSi_BPTWL); } } @@ -451,11 +451,11 @@ void DSi_BPTWL::Write(u8 val, bool last) } -DSi_I2CHost::DSi_I2CHost() +DSi_I2CHost::DSi_I2CHost(melonDS::DSi& dsi) : DSi(dsi) { - BPTWL = new DSi_BPTWL(this); - Camera0 = new DSi_Camera(this, 0); - Camera1 = new DSi_Camera(this, 1); + BPTWL = new DSi_BPTWL(dsi, this); + Camera0 = new DSi_Camera(dsi, this, 0); + Camera1 = new DSi_Camera(dsi, this, 1); } DSi_I2CHost::~DSi_I2CHost() diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index 83d7e028..51fe78e6 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -26,11 +26,11 @@ namespace melonDS { class DSi_I2CHost; class DSi_Camera; - +class DSi; class DSi_I2CDevice { public: - DSi_I2CDevice(DSi_I2CHost* host) : Host(host) {} + DSi_I2CDevice(melonDS::DSi& dsi, DSi_I2CHost* host) : DSi(dsi), Host(host) {} virtual ~DSi_I2CDevice() {} virtual void Reset() = 0; virtual void DoSavestate(Savestate* file) = 0; @@ -40,6 +40,7 @@ public: virtual void Write(u8 val, bool last) = 0; protected: + melonDS::DSi& DSi; DSi_I2CHost* Host; }; @@ -80,7 +81,7 @@ public: IRQ_ValidMask = 0x7B, }; - DSi_BPTWL(DSi_I2CHost* host); + DSi_BPTWL(melonDS::DSi& dsi, DSi_I2CHost* host); ~DSi_BPTWL() override; void Reset() override; void DoSavestate(Savestate* file) override; @@ -153,7 +154,7 @@ private: class DSi_I2CHost { public: - DSi_I2CHost(); + DSi_I2CHost(melonDS::DSi& dsi); ~DSi_I2CHost(); void Reset(); void DoSavestate(Savestate* file); @@ -169,6 +170,7 @@ public: void WriteData(u8 val); private: + melonDS::DSi& DSi; u8 Cnt; u8 Data; diff --git a/src/DSi_NDMA.cpp b/src/DSi_NDMA.cpp index 4f248eae..fe1f0ba7 100644 --- a/src/DSi_NDMA.cpp +++ b/src/DSi_NDMA.cpp @@ -28,7 +28,7 @@ namespace melonDS using Platform::Log; using Platform::LogLevel; -DSi_NDMA::DSi_NDMA(u32 cpu, u32 num, melonDS::GPU& gpu) : GPU(gpu) +DSi_NDMA::DSi_NDMA(u32 cpu, u32 num, melonDS::DSi& dsi) : DSi(dsi), CPU(cpu), Num(num) { CPU = cpu; Num = num; @@ -127,7 +127,7 @@ void DSi_NDMA::WriteCnt(u32 val) if ((StartMode & 0x1F) == 0x10) Start(); else if (StartMode == 0x0A) - GPU.GPU3D.CheckFIFODMA(); + DSi.GPU.GPU3D.CheckFIFODMA(); // TODO: unsupported start modes: // * timers (00-03) @@ -181,13 +181,13 @@ void DSi_NDMA::Start() //if (SubblockTimer & 0xFFFF) // printf("TODO! NDMA SUBBLOCK TIMER: %08X\n", SubblockTimer); - if (NDS::DMAsRunning(CPU)) + if (DSi.DMAsRunning(CPU)) Running = 1; else Running = 2; InProgress = true; - NDS::StopCPU(CPU, 1<<(Num+4)); + DSi.StopCPU(CPU, 1<<(Num+4)); } void DSi_NDMA::Run() @@ -199,7 +199,7 @@ void DSi_NDMA::Run() void DSi_NDMA::Run9() { - if (NDS::ARM9Timestamp >= NDS::ARM9Target) return; + if (DSi.ARM9Timestamp >= DSi.ARM9Target) return; Executing = true; @@ -214,11 +214,11 @@ void DSi_NDMA::Run9() if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02) { - unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][2] + NDS::ARM9MemTimings[CurDstAddr >> 14][2]; + unitcycles = DSi.ARM9MemTimings[CurSrcAddr >> 14][2] + DSi.ARM9MemTimings[CurDstAddr >> 14][2]; } else { - unitcycles = NDS::ARM9MemTimings[CurSrcAddr >> 14][3] + NDS::ARM9MemTimings[CurDstAddr >> 14][3]; + unitcycles = DSi.ARM9MemTimings[CurSrcAddr >> 14][3] + DSi.ARM9MemTimings[CurDstAddr >> 14][3]; if ((CurSrcAddr >> 24) == (CurDstAddr >> 24)) unitcycles++; else if ((CurSrcAddr >> 24) == 0x02) @@ -234,12 +234,12 @@ void DSi_NDMA::Run9() while (IterCount > 0 && !Stall) { - NDS::ARM9Timestamp += (unitcycles << NDS::ARM9ClockShift); + DSi.ARM9Timestamp += (unitcycles << DSi.ARM9ClockShift); if (dofill) - DSi::ARM9Write32(CurDstAddr, FillData); + DSi.ARM9Write32(CurDstAddr, FillData); else - DSi::ARM9Write32(CurDstAddr, DSi::ARM9Read32(CurSrcAddr)); + DSi.ARM9Write32(CurDstAddr, DSi.ARM9Read32(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<2; CurDstAddr += DstAddrInc<<2; @@ -247,7 +247,7 @@ void DSi_NDMA::Run9() RemCount--; TotalRemCount--; - if (NDS::ARM9Timestamp >= NDS::ARM9Target) break; + if (DSi.ARM9Timestamp >= DSi.ARM9Target) break; } Executing = false; @@ -258,10 +258,10 @@ void DSi_NDMA::Run9() if (IterCount == 0) { Running = 0; - NDS::ResumeCPU(0, 1<<(Num+4)); + DSi.ResumeCPU(0, 1<<(Num+4)); if (StartMode == 0x0A) - GPU.GPU3D.CheckFIFODMA(); + DSi.GPU.GPU3D.CheckFIFODMA(); } return; @@ -270,25 +270,25 @@ void DSi_NDMA::Run9() if ((StartMode & 0x1F) == 0x10) // CHECKME { Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num); + if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num); } else if (!(Cnt & (1<<29))) { if (TotalRemCount == 0) { Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ(0, NDS::IRQ_DSi_NDMA0 + Num); + if (Cnt & (1<<30)) DSi.SetIRQ(0, IRQ_DSi_NDMA0 + Num); } } Running = 0; InProgress = false; - NDS::ResumeCPU(0, 1<<(Num+4)); + DSi.ResumeCPU(0, 1<<(Num+4)); } void DSi_NDMA::Run7() { - if (NDS::ARM7Timestamp >= NDS::ARM7Target) return; + if (DSi.ARM7Timestamp >= DSi.ARM7Target) return; Executing = true; @@ -303,11 +303,11 @@ void DSi_NDMA::Run7() if ((CurSrcAddr >> 24) == 0x02 && (CurDstAddr >> 24) == 0x02) { - unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][2] + NDS::ARM7MemTimings[CurDstAddr >> 15][2]; + unitcycles = DSi.ARM7MemTimings[CurSrcAddr >> 15][2] + DSi.ARM7MemTimings[CurDstAddr >> 15][2]; } else { - unitcycles = NDS::ARM7MemTimings[CurSrcAddr >> 15][3] + NDS::ARM7MemTimings[CurDstAddr >> 15][3]; + unitcycles = DSi.ARM7MemTimings[CurSrcAddr >> 15][3] + DSi.ARM7MemTimings[CurDstAddr >> 15][3]; if ((CurSrcAddr >> 23) == (CurDstAddr >> 23)) unitcycles++; else if ((CurSrcAddr >> 24) == 0x02) @@ -323,12 +323,12 @@ void DSi_NDMA::Run7() while (IterCount > 0 && !Stall) { - NDS::ARM7Timestamp += unitcycles; + DSi.ARM7Timestamp += unitcycles; if (dofill) - DSi::ARM7Write32(CurDstAddr, FillData); + DSi.ARM7Write32(CurDstAddr, FillData); else - DSi::ARM7Write32(CurDstAddr, DSi::ARM7Read32(CurSrcAddr)); + DSi.ARM7Write32(CurDstAddr, DSi.ARM7Read32(CurSrcAddr)); CurSrcAddr += SrcAddrInc<<2; CurDstAddr += DstAddrInc<<2; @@ -336,7 +336,7 @@ void DSi_NDMA::Run7() RemCount--; TotalRemCount--; - if (NDS::ARM7Timestamp >= NDS::ARM7Target) break; + if (DSi.ARM7Timestamp >= DSi.ARM7Target) break; } Executing = false; @@ -347,10 +347,10 @@ void DSi_NDMA::Run7() if (IterCount == 0) { Running = 0; - NDS::ResumeCPU(1, 1<<(Num+4)); + DSi.ResumeCPU(1, 1<<(Num+4)); - DSi::AES->CheckInputDMA(); - DSi::AES->CheckOutputDMA(); + DSi.AES.CheckInputDMA(); + DSi.AES.CheckOutputDMA(); } return; @@ -359,23 +359,23 @@ void DSi_NDMA::Run7() if ((StartMode & 0x1F) == 0x10) // CHECKME { Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num); + if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num); } else if (!(Cnt & (1<<29))) { if (TotalRemCount == 0) { Cnt &= ~(1<<31); - if (Cnt & (1<<30)) NDS::SetIRQ(1, NDS::IRQ_DSi_NDMA0 + Num); + if (Cnt & (1<<30)) DSi.SetIRQ(1, IRQ_DSi_NDMA0 + Num); } } Running = 0; InProgress = false; - NDS::ResumeCPU(1, 1<<(Num+4)); + DSi.ResumeCPU(1, 1<<(Num+4)); - DSi::AES->CheckInputDMA(); - DSi::AES->CheckOutputDMA(); + DSi.AES.CheckInputDMA(); + DSi.AES.CheckOutputDMA(); } } \ No newline at end of file diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h index 4479341b..7e87da7b 100644 --- a/src/DSi_NDMA.h +++ b/src/DSi_NDMA.h @@ -24,12 +24,12 @@ namespace melonDS { -class GPU; +class DSi; class DSi_NDMA { public: - DSi_NDMA(u32 cpu, u32 num, GPU& gpu); + DSi_NDMA(u32 cpu, u32 num, melonDS::DSi& dsi); ~DSi_NDMA(); void Reset(); @@ -77,7 +77,7 @@ public: u32 Cnt; private: - melonDS::GPU& GPU; + melonDS::DSi& DSi; u32 CPU, Num; u32 StartMode; diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 66320ba2..9e006e2c 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -119,21 +119,22 @@ const u8 CIS1[256] = }; -DSi_NWifi::DSi_NWifi(DSi_SDHost* host) - : DSi_SDDevice(host), - Mailbox - { - // HACK - // the mailboxes are supposed to be 0x80 bytes - // however, as we do things instantly, emulating this is meaningless - // and only adds complication - DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), - DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), - // mailbox 8: extra mailbox acting as a bigger RX buffer - DynamicFIFO(0x8000) - } +DSi_NWifi::DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host) : + DSi_SDDevice(host), + Mailbox + { + // HACK + // the mailboxes are supposed to be 0x80 bytes + // however, as we do things instantly, emulating this is meaningless + // and only adds complication + DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), + DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), DynamicFIFO(0x600), + // mailbox 8: extra mailbox acting as a bigger RX buffer + DynamicFIFO(0x8000) + }, + DSi(dsi) { - NDS::RegisterEventFunc(NDS::Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer)); + DSi.RegisterEventFunc(Event_DSi_NWifi, 0, MemberEventFunc(DSi_NWifi, MSTimer)); // this seems to control whether the firmware upload is done EEPROMReady = 0; @@ -141,9 +142,9 @@ DSi_NWifi::DSi_NWifi(DSi_SDHost* host) DSi_NWifi::~DSi_NWifi() { - NDS::CancelEvent(NDS::Event_DSi_NWifi); + DSi.CancelEvent(Event_DSi_NWifi); - NDS::UnregisterEventFunc(NDS::Event_DSi_NWifi, 0); + DSi.UnregisterEventFunc(Event_DSi_NWifi, 0); } void DSi_NWifi::Reset() @@ -164,7 +165,7 @@ void DSi_NWifi::Reset() for (int i = 0; i < 9; i++) Mailbox[i].Clear(); - const Firmware* fw = NDS::SPI->GetFirmware(); + const Firmware* fw = DSi.SPI.GetFirmware(); MacAddress mac = fw->GetHeader().MacAddr; Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", @@ -226,7 +227,7 @@ void DSi_NWifi::Reset() BeaconTimer = 0x10A2220ULL; ConnectionStatus = 0; - NDS::CancelEvent(NDS::Event_DSi_NWifi); + DSi.CancelEvent(Event_DSi_NWifi); } void DSi_NWifi::DoSavestate(Savestate* file) @@ -909,7 +910,7 @@ void DSi_NWifi::HTC_Command() SendWMIEvent(1, 0x1006, regdomain_evt, 4); BootPhase = 2; - NDS::ScheduleEvent(NDS::Event_DSi_NWifi, false, 33611, 0, 0); + DSi.ScheduleEvent(Event_DSi_NWifi, false, 33611, 0, 0); } break; @@ -1608,7 +1609,7 @@ void DSi_NWifi::MSTimer(u32 param) CheckRX(); } - NDS::ScheduleEvent(NDS::Event_DSi_NWifi, true, 33611, 0, 0); + DSi.ScheduleEvent(Event_DSi_NWifi, true, 33611, 0, 0); } } \ No newline at end of file diff --git a/src/DSi_NWifi.h b/src/DSi_NWifi.h index f3e5e150..39e9459c 100644 --- a/src/DSi_NWifi.h +++ b/src/DSi_NWifi.h @@ -28,7 +28,7 @@ namespace melonDS class DSi_NWifi : public DSi_SDDevice { public: - DSi_NWifi(DSi_SDHost* host); + DSi_NWifi(melonDS::DSi& dsi, DSi_SDHost* host); ~DSi_NWifi(); void Reset(); @@ -45,6 +45,7 @@ public: void MSTimer(u32 param); private: + melonDS::DSi& DSi; u32 TransferCmd; u32 TransferAddr; u32 RemSize; diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 54f03cea..4cbf595d 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -57,13 +57,13 @@ enum }; -DSi_SDHost::DSi_SDHost(u32 num) +DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, u32 num) : DSi(dsi) { Num = num; - NDS::RegisterEventFunc(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); - NDS::RegisterEventFunc(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); Ports[0] = nullptr; @@ -75,9 +75,9 @@ DSi_SDHost::~DSi_SDHost() if (Ports[0]) delete Ports[0]; if (Ports[1]) delete Ports[1]; - NDS::UnregisterEventFunc(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, Transfer_TX); - NDS::UnregisterEventFunc(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, Transfer_RX); } @@ -156,15 +156,15 @@ void DSi_SDHost::Reset() else sd = nullptr; - mmc = new DSi_MMCStorage(this, *DSi::NANDImage); - mmc->SetCID(DSi::NANDImage->GetEMMCID().data()); + mmc = new DSi_MMCStorage(this, *DSi.NANDImage); + mmc->SetCID(DSi.NANDImage->GetEMMCID().data()); Ports[0] = sd; Ports[1] = mmc; } else { - DSi_NWifi* nwifi = new DSi_NWifi(this); + DSi_NWifi* nwifi = new DSi_NWifi(DSi, this); Ports[0] = nwifi; } @@ -228,7 +228,7 @@ void DSi_SDHost::UpdateData32IRQ() newflags &= (Data32IRQ >> 11); if ((oldflags == 0) && (newflags != 0)) - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); } void DSi_SDHost::ClearIRQ(u32 irq) @@ -244,7 +244,7 @@ void DSi_SDHost::SetIRQ(u32 irq) u32 newflags = IRQStatus & ~IRQMask; if ((oldflags == 0) && (newflags != 0)) - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); } void DSi_SDHost::UpdateIRQ(u32 oldmask) @@ -253,7 +253,7 @@ void DSi_SDHost::UpdateIRQ(u32 oldmask) u32 newflags = IRQStatus & ~IRQMask; if ((oldflags == 0) && (newflags != 0)) - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); } void DSi_SDHost::SetCardIRQ() @@ -270,8 +270,8 @@ void DSi_SDHost::SetCardIRQ() if ((oldflags == 0) && (newflags != 0)) // checkme { - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO_Data1 : IRQ2_DSi_SD_Data1); } } @@ -282,8 +282,8 @@ void DSi_SDHost::UpdateCardIRQ(u16 oldmask) if ((oldflags == 0) && (newflags != 0)) // checkme { - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO : NDS::IRQ2_DSi_SDMMC); - NDS::SetIRQ2(Num ? NDS::IRQ2_DSi_SDIO_Data1 : NDS::IRQ2_DSi_SD_Data1); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO : IRQ2_DSi_SDMMC); + DSi.SetIRQ2(Num ? IRQ2_DSi_SDIO_Data1 : IRQ2_DSi_SD_Data1); } } @@ -324,7 +324,7 @@ u32 DSi_SDHost::DataRX(u8* data, u32 len) // we need a delay because DSi boot2 will send a command and then wait for IRQ0 // but if IRQ24 is thrown instantly, the handler clears IRQ0 before the // send-command function starts polling IRQ status - NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + DSi.ScheduleEvent(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, false, 512, Transfer_RX, 0); return len; @@ -366,7 +366,7 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len) if (DataFIFO32.IsEmpty()) { SetIRQ(25); - DSi::CheckNDMAs(1, Num ? 0x29 : 0x28); + DSi.CheckNDMAs(1, Num ? 0x29 : 0x28); } return 0; } @@ -405,7 +405,7 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len) CurFIFO ^= 1; BlockCountInternal--; - NDS::ScheduleEvent(Num ? NDS::Event_DSi_SDIOTransfer : NDS::Event_DSi_SDMMCTransfer, + DSi.ScheduleEvent(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, false, 512, Transfer_TX, 0); return len; @@ -536,7 +536,7 @@ u16 DSi_SDHost::Read(u32 addr) case 0x10A: return 0; } - Log(LogLevel::Warn, "unknown %s read %08X @ %08X\n", SD_DESC, addr, NDS::GetPC(1)); + Log(LogLevel::Warn, "unknown %s read %08X @ %08X\n", SD_DESC, addr, DSi.GetPC(1)); return 0; } @@ -761,7 +761,7 @@ void DSi_SDHost::UpdateFIFO32() if ((DataFIFO32.Level() << 2) >= BlockLen32) { - DSi::CheckNDMAs(1, Num ? 0x29 : 0x28); + DSi.CheckNDMAs(1, Num ? 0x29 : 0x28); } } diff --git a/src/DSi_SD.h b/src/DSi_SD.h index faa42964..17ba8d30 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -32,12 +32,13 @@ namespace DSi_NAND } class DSi_SDDevice; +class DSi; class DSi_SDHost { public: - DSi_SDHost(u32 num); + DSi_SDHost(melonDS::DSi& dsi, u32 num); ~DSi_SDHost(); void CloseHandles(); @@ -69,6 +70,7 @@ public: void CheckSwapFIFO(); private: + melonDS::DSi& DSi; u32 Num; u16 PortSelect; diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index 520e57ba..6c7a15c7 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -28,7 +28,7 @@ using Platform::Log; using Platform::LogLevel; -DSi_TSC::DSi_TSC(SPIHost* host) : TSC(host) +DSi_TSC::DSi_TSC(melonDS::DSi& dsi) : TSC(dsi) { } @@ -198,7 +198,7 @@ void DSi_TSC::Write(u8 val) { Log(LogLevel::Debug, "DSi_SPI_TSC: DS-compatibility mode\n"); DataPos = 0; - NDS::KeyInput |= (1 << (16+6)); + NDS.KeyInput |= (1 << (16+6)); return; } } diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h index 8e9ff912..47777da5 100644 --- a/src/DSi_SPI_TSC.h +++ b/src/DSi_SPI_TSC.h @@ -25,10 +25,11 @@ namespace melonDS { +class DSi; class DSi_TSC : public TSC { public: - DSi_TSC(SPIHost* host); + DSi_TSC(melonDS::DSi& dsi); ~DSi_TSC() override; void Reset() override; diff --git a/src/GBACart.cpp b/src/GBACart.cpp index a0c5467e..f5e320f9 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -850,7 +850,7 @@ void GBACartSlot::LoadAddon(int type) noexcept { switch (type) { - case NDS::GBAAddon_RAMExpansion: + case GBAAddon_RAMExpansion: Cart = std::make_unique(); break; diff --git a/src/GPU.cpp b/src/GPU.cpp index 27dcbfbb..7a81f7dd 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -64,12 +64,12 @@ enum VRAMDirty need to be reset for the respective VRAM bank. */ -GPU::GPU(ARMJIT& jit) noexcept : GPU2D_A(0, *this), GPU2D_B(1, *this), JIT(jit) +GPU::GPU(melonDS::NDS& nds) noexcept : NDS(nds), GPU2D_A(0, *this), GPU2D_B(1, *this), GPU3D(nds) { - NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); - NDS::RegisterEventFunc(NDS::Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); - NDS::RegisterEventFunc(NDS::Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); - NDS::RegisterEventFunc(NDS::Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); + NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); + NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); + NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); + NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); GPU2D_Renderer = std::make_unique(*this); @@ -92,10 +92,10 @@ GPU::~GPU() noexcept Framebuffer[1][0] = nullptr; Framebuffer[1][1] = nullptr; - NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_StartHBlank); - NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_StartScanline); - NDS::UnregisterEventFunc(NDS::Event_LCD, LCD_FinishFrame); - NDS::UnregisterEventFunc(NDS::Event_DisplayFIFO, 0); + NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank); + NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline); + NDS.UnregisterEventFunc(Event_LCD, LCD_FinishFrame); + NDS.UnregisterEventFunc(Event_DisplayFIFO, 0); } void GPU::ResetVRAMCache() noexcept @@ -298,7 +298,7 @@ void GPU::DoSavestate(Savestate* file) noexcept void GPU::AssignFramebuffers() noexcept { int backbuf = FrontBuffer ? 0 : 1; - if (NDS::PowerControl9 & (1<<15)) + if (NDS.PowerControl9 & (1<<15)) { GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0], Framebuffer[backbuf][1]); } @@ -588,7 +588,7 @@ void GPU::MapVRAM_CD(u32 bank, u8 cnt) noexcept VRAMMap_ARM7[ofs] |= bankmask; memset(VRAMDirty[bank].Data, 0xFF, sizeof(VRAMDirty[bank].Data)); VRAMSTAT |= (1 << (bank-2)); - JIT.CheckAndInvalidateWVRAM(ofs); + NDS.JIT.CheckAndInvalidateWVRAM(ofs); break; case 3: // texture @@ -942,8 +942,8 @@ void GPU::DisplayFIFO(u32 x) noexcept if (x < 256) { // transfer the next 8 pixels - NDS::CheckDMAs(0, 0x04); - NDS::ScheduleEvent(NDS::Event_DisplayFIFO, true, 6*8, 0, x+8); + NDS.CheckDMAs(0, 0x04); + NDS.ScheduleEvent(Event_DisplayFIFO, true, 6*8, 0, x+8); } else GPU2D_A.SampleFIFO(253, 3); // sample the remaining pixels @@ -954,7 +954,7 @@ void GPU::StartFrame() noexcept // only run the display FIFO if needed: // * if it is used for display or capture // * if we have display FIFO DMA - RunFIFO = GPU2D_A.UsesFIFO() || NDS::DMAsInMode(0, 0x04); + RunFIFO = GPU2D_A.UsesFIFO() || NDS.DMAsInMode(0, 0x04); TotalScanlines = 0; StartScanline(0); @@ -982,7 +982,7 @@ void GPU::StartHBlank(u32 line) noexcept GPU2D_Renderer->DrawSprites(line+1, &GPU2D_B); } - NDS::CheckDMAs(0, 0x02); + NDS.CheckDMAs(0, 0x02); } else if (VCount == 215) { @@ -994,13 +994,13 @@ void GPU::StartHBlank(u32 line) noexcept GPU2D_Renderer->DrawSprites(0, &GPU2D_B); } - if (DispStat[0] & (1<<4)) NDS::SetIRQ(0, NDS::IRQ_HBlank); - if (DispStat[1] & (1<<4)) NDS::SetIRQ(1, NDS::IRQ_HBlank); + if (DispStat[0] & (1<<4)) NDS.SetIRQ(0, IRQ_HBlank); + if (DispStat[1] & (1<<4)) NDS.SetIRQ(1, IRQ_HBlank); if (VCount < 262) - NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_StartScanline, line+1); + NDS.ScheduleEvent(Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_StartScanline, line+1); else - NDS::ScheduleEvent(NDS::Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_FinishFrame, line+1); + NDS.ScheduleEvent(Event_LCD, true, (LINE_CYCLES - HBLANK_CYCLES), LCD_FinishFrame, line+1); } void GPU::FinishFrame(u32 lines) noexcept @@ -1053,7 +1053,7 @@ void GPU::StartScanline(u32 line) noexcept { DispStat[0] |= (1<<2); - if (DispStat[0] & (1<<5)) NDS::SetIRQ(0, NDS::IRQ_VCount); + if (DispStat[0] & (1<<5)) NDS.SetIRQ(0, IRQ_VCount); } else DispStat[0] &= ~(1<<2); @@ -1062,7 +1062,7 @@ void GPU::StartScanline(u32 line) noexcept { DispStat[1] |= (1<<2); - if (DispStat[1] & (1<<5)) NDS::SetIRQ(1, NDS::IRQ_VCount); + if (DispStat[1] & (1<<5)) NDS.SetIRQ(1, IRQ_VCount); } else DispStat[1] &= ~(1<<2); @@ -1071,9 +1071,9 @@ void GPU::StartScanline(u32 line) noexcept GPU2D_B.CheckWindows(VCount); if (VCount >= 2 && VCount < 194) - NDS::CheckDMAs(0, 0x03); + NDS.CheckDMAs(0, 0x03); else if (VCount == 194) - NDS::StopDMAs(0, 0x03); + NDS.StopDMAs(0, 0x03); if (line < 192) { @@ -1085,7 +1085,7 @@ void GPU::StartScanline(u32 line) noexcept } if (RunFIFO) - NDS::ScheduleEvent(NDS::Event_DisplayFIFO, false, 32, 0, 0); + NDS.ScheduleEvent(Event_DisplayFIFO, false, 32, 0, 0); } if (VCount == 262) @@ -1111,13 +1111,13 @@ void GPU::StartScanline(u32 line) noexcept DispStat[0] |= (1<<0); DispStat[1] |= (1<<0); - NDS::StopDMAs(0, 0x04); + NDS.StopDMAs(0, 0x04); - NDS::CheckDMAs(0, 0x01); - NDS::CheckDMAs(1, 0x11); + NDS.CheckDMAs(0, 0x01); + NDS.CheckDMAs(1, 0x11); - if (DispStat[0] & (1<<3)) NDS::SetIRQ(0, NDS::IRQ_VBlank); - if (DispStat[1] & (1<<3)) NDS::SetIRQ(1, NDS::IRQ_VBlank); + if (DispStat[0] & (1<<3)) NDS.SetIRQ(0, IRQ_VBlank); + if (DispStat[1] & (1<<3)) NDS.SetIRQ(1, IRQ_VBlank); GPU2D_A.VBlank(); GPU2D_B.VBlank(); @@ -1131,7 +1131,7 @@ void GPU::StartScanline(u32 line) noexcept } } - NDS::ScheduleEvent(NDS::Event_LCD, true, HBLANK_CYCLES, LCD_StartHBlank, line); + NDS.ScheduleEvent(Event_LCD, true, HBLANK_CYCLES, LCD_StartHBlank, line); } diff --git a/src/GPU.h b/src/GPU.h index dbeec528..84210c9d 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -67,7 +67,7 @@ struct RenderSettings class GPU { public: - GPU(ARMJIT& jit) noexcept; + GPU(melonDS::NDS& nds) noexcept; ~GPU() noexcept; void Reset() noexcept; void Stop() noexcept; @@ -536,7 +536,7 @@ public: void SyncDirtyFlags() noexcept; - ARMJIT& JIT; + melonDS::NDS& NDS; u16 VCount = 0; u16 TotalScanlines = 0; u16 DispStat[2] {}; @@ -582,7 +582,7 @@ public: GPU2D::Unit GPU2D_A; GPU2D::Unit GPU2D_B; - melonDS::GPU3D GPU3D {}; + melonDS::GPU3D GPU3D; NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMDirty[9] {}; VRAMTrackingSet<512*1024, 16*1024> VRAMDirty_ABG {}; diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index b00e6fd2..5603a136 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -139,6 +139,10 @@ const u8 CmdNumParams[256] = void MatrixLoadIdentity(s32* m); +GPU3D::GPU3D(melonDS::NDS& nds) noexcept : NDS(nds) +{ +} + void GPU3D::ResetRenderingState() noexcept { RenderNumPolygons = 0; @@ -1596,7 +1600,7 @@ void GPU3D::CmdFIFOWrite(CmdFIFOEntry& entry) noexcept // has 64 entries. this is less complicated than trying to make STMxx stall-able. CmdStallQueue.Write(entry); - NDS::GXFIFOStall(); + NDS.GXFIFOStall(); return; } @@ -1640,7 +1644,7 @@ GPU3D::CmdFIFOEntry GPU3D::CmdFIFORead() noexcept } if (CmdStallQueue.IsEmpty()) - NDS::GXFIFOUnstall(); + NDS.GXFIFOUnstall(); } CheckFIFODMA(); @@ -2273,13 +2277,13 @@ void GPU3D::Run() noexcept if (!GeometryEnabled || FlushRequest || (CmdPIPE.IsEmpty() && !(GXStat & (1<<27)))) { - Timestamp = NDS::ARM9Timestamp >> NDS::ARM9ClockShift; + Timestamp = NDS.ARM9Timestamp >> NDS.ARM9ClockShift; return; } - s32 cycles = (NDS::ARM9Timestamp >> NDS::ARM9ClockShift) - Timestamp; + s32 cycles = (NDS.ARM9Timestamp >> NDS.ARM9ClockShift) - Timestamp; CycleCount -= cycles; - Timestamp = NDS::ARM9Timestamp >> NDS::ARM9ClockShift; + Timestamp = NDS.ARM9Timestamp >> NDS.ARM9ClockShift; if (CycleCount <= 0) { @@ -2312,14 +2316,14 @@ void GPU3D::CheckFIFOIRQ() noexcept case 2: irq = CmdFIFO.IsEmpty(); break; } - if (irq) NDS::SetIRQ(0, NDS::IRQ_GXFIFO); - else NDS::ClearIRQ(0, NDS::IRQ_GXFIFO); + if (irq) NDS.SetIRQ(0, IRQ_GXFIFO); + else NDS.ClearIRQ(0, IRQ_GXFIFO); } void GPU3D::CheckFIFODMA() noexcept { if (CmdFIFO.Level() < 128) - NDS::CheckDMAs(0, 0x07); + NDS.CheckDMAs(0, 0x07); } void GPU3D::VCount144() noexcept diff --git a/src/GPU3D.h b/src/GPU3D.h index f543ff86..1d3e1265 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -81,11 +81,12 @@ struct Polygon }; class Renderer3D; +class NDS; class GPU3D { public: - GPU3D() noexcept = default; + GPU3D(melonDS::NDS& nds) noexcept; ~GPU3D() noexcept = default; void Reset() noexcept; @@ -124,6 +125,7 @@ public: void Write16(u32 addr, u16 val) noexcept; void Write32(u32 addr, u32 val) noexcept; private: + melonDS::NDS& NDS; typedef union { u64 _contents; diff --git a/src/MemConstants.h b/src/MemConstants.h new file mode 100644 index 00000000..ab80faba --- /dev/null +++ b/src/MemConstants.h @@ -0,0 +1,35 @@ +/* + 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_MEMCONSTANTS_H +#define MELONDS_MEMCONSTANTS_H + +#include "types.h" + +namespace melonDS +{ +constexpr u32 MainRAMMaxSize = 0x1000000; +constexpr u32 SharedWRAMSize = 0x8000; +constexpr u32 ARM7WRAMSize = 0x10000; +constexpr u32 NWRAMSize = 0x40000; +constexpr u32 ARM9BIOSSize = 0x1000; +constexpr u32 ARM7BIOSSize = 0x4000; +constexpr u32 DSiBIOSSize = 0x10000; +} + +#endif // MELONDS_MEMCONSTANTS_H \ No newline at end of file diff --git a/src/MemRegion.h b/src/MemRegion.h new file mode 100644 index 00000000..11b3d1da --- /dev/null +++ b/src/MemRegion.h @@ -0,0 +1,33 @@ +/* + 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_MEMREGION_H +#define MELONDS_MEMREGION_H + +#include "types.h" + +// this file exists to break #include cycle loops +namespace melonDS +{ +struct MemRegion +{ + u8* Mem; + u32 Mask; +}; +} +#endif //MELONDS_MEMREGION_H diff --git a/src/NDS.cpp b/src/NDS.cpp index 576eb9cd..3aa4a52a 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -47,8 +47,8 @@ namespace melonDS { using namespace Platform; -namespace NDS -{ +const s32 kMaxIterationCycles = 64; +const s32 kIterationCycleMargin = 8; // timing notes // @@ -72,204 +72,49 @@ namespace NDS // // timings for GBA slot and wifi are set up at runtime -int ConsoleType; +NDS* NDS::Current = nullptr; -u8 ARM9MemTimings[0x40000][8]; -u32 ARM9Regions[0x40000]; -u8 ARM7MemTimings[0x20000][4]; -u32 ARM7Regions[0x20000]; - -ARMv5* ARM9; -ARMv4* ARM7; - -#ifdef JIT_ENABLED -bool EnableJIT; -#endif - -u32 NumFrames; -u32 NumLagFrames; -bool LagFrameFlag; -u64 LastSysClockCycles; -u64 FrameStartTimestamp; - -int CurCPU; - -const s32 kMaxIterationCycles = 64; -const s32 kIterationCycleMargin = 8; - -u32 ARM9ClockShift; - -// no need to worry about those overflowing, they can keep going for atleast 4350 years -u64 ARM9Timestamp, ARM9Target; -u64 ARM7Timestamp, ARM7Target; -u64 SysTimestamp; - -struct SchedEvent +NDS::NDS(int type) noexcept : + ConsoleType(type), + JIT(*this), + SPU(*this), + GPU(*this), + SPI(*this), + RTC(*this), + Wifi(*this), + NDSCartSlot(*this), + GBACartSlot(), + AREngine(*this), + ARM9(*this), + ARM7(*this), + DMAs { + DMA(0, 0, *this), + DMA(0, 1, *this), + DMA(0, 2, *this), + DMA(0, 3, *this), + DMA(1, 0, *this), + DMA(1, 1, *this), + DMA(1, 2, *this), + DMA(1, 3, *this), + } { - std::map Funcs; - u64 Timestamp; - u32 FuncID; - u32 Param; -}; + RegisterEventFunc(Event_Div, 0, MemberEventFunc(NDS, DivDone)); + RegisterEventFunc(Event_Sqrt, 0, MemberEventFunc(NDS, SqrtDone)); -SchedEvent SchedList[Event_MAX]; -u32 SchedListMask; - -u32 CPUStop; - -u8 ARM9BIOS[0x1000]; -u8 ARM7BIOS[0x4000]; - -u8* MainRAM; -u32 MainRAMMask; - -u8* SharedWRAM; -u8 WRAMCnt; - -// putting them together so they're always next to each other -MemRegion SWRAM_ARM9; -MemRegion SWRAM_ARM7; - -u8* ARM7WRAM; - -u16 ExMemCnt[2]; - -// TODO: these belong in NDSCart! -u8 ROMSeed0[2*8]; -u8 ROMSeed1[2*8]; - -// IO shit -u32 IME[2]; -u32 IE[2], IF[2]; -u32 IE2, IF2; - -u8 PostFlag9; -u8 PostFlag7; -u16 PowerControl9; -u16 PowerControl7; - -u16 WifiWaitCnt; - -u16 ARM7BIOSProt; - -Timer Timers[8]; -u8 TimerCheckMask[2]; -u64 TimerTimestamp[2]; - -DMA* DMAs[8]; -u32 DMA9Fill[4]; - -u16 IPCSync9, IPCSync7; -u16 IPCFIFOCnt9, IPCFIFOCnt7; -FIFO IPCFIFO9; // FIFO in which the ARM9 writes -FIFO IPCFIFO7; - -u16 DivCnt; -u32 DivNumerator[2]; -u32 DivDenominator[2]; -u32 DivQuotient[2]; -u32 DivRemainder[2]; - -u16 SqrtCnt; -u32 SqrtVal[2]; -u32 SqrtRes; - -u32 KeyInput; -u16 KeyCnt[2]; -u16 RCnt; - -class SPU* SPU; -class SPIHost* SPI; -class RTC* RTC; -class Wifi* Wifi; -std::unique_ptr NDSCartSlot; -std::unique_ptr GBACartSlot; -std::unique_ptr GPU; -std::unique_ptr JIT; -class AREngine* AREngine; - -bool Running; - -bool RunningGame; - -void DivDone(u32 param); -void SqrtDone(u32 param); -void RunTimer(u32 tid, s32 cycles); -void UpdateWifiTimings(); -void SetWifiWaitCnt(u16 val); -void SetGBASlotTimings(); - - -bool Init() -{ - RegisterEventFunc(Event_Div, 0, DivDone); - RegisterEventFunc(Event_Sqrt, 0, SqrtDone); - - JIT = std::make_unique(); - GPU = std::make_unique(*JIT); - - MainRAM = JIT->Memory.GetMainRAM(); - SharedWRAM = JIT->Memory.GetSharedWRAM(); - ARM7WRAM = JIT->Memory.GetARM7WRAM(); - - ARM9 = new ARMv5(*JIT, *GPU); - ARM7 = new ARMv4(*JIT, *GPU); - - DMAs[0] = new DMA(0, 0, *GPU); - DMAs[1] = new DMA(0, 1, *GPU); - DMAs[2] = new DMA(0, 2, *GPU); - DMAs[3] = new DMA(0, 3, *GPU); - DMAs[4] = new DMA(1, 0, *GPU); - DMAs[5] = new DMA(1, 1, *GPU); - DMAs[6] = new DMA(1, 2, *GPU); - DMAs[7] = new DMA(1, 3, *GPU); - - SPU = new class SPU; - SPI = new class SPIHost(); - RTC = new class RTC(); - Wifi = new class Wifi(); - NDSCartSlot = std::make_unique(); - GBACartSlot = std::make_unique(); - - if (!DSi::Init()) return false; - - AREngine = new class AREngine(); - - return true; + MainRAM = JIT.Memory.GetMainRAM(); + SharedWRAM = JIT.Memory.GetSharedWRAM(); + ARM7WRAM = JIT.Memory.GetARM7WRAM(); } -void DeInit() +NDS::~NDS() noexcept { - delete ARM9; ARM9 = nullptr; - delete ARM7; ARM7 = nullptr; - - for (int i = 0; i < 8; i++) - { - delete DMAs[i]; - DMAs[i] = nullptr; - } - - delete SPU; SPU = nullptr; - delete SPI; SPI = nullptr; - delete RTC; RTC = nullptr; - delete Wifi; Wifi = nullptr; - - NDSCartSlot = nullptr; - GBACartSlot = nullptr; - GPU = nullptr; - - DSi::DeInit(); - - delete AREngine; AREngine = nullptr; - UnregisterEventFunc(Event_Div, 0); UnregisterEventFunc(Event_Sqrt, 0); - - JIT = nullptr; + // The destructor for each component is automatically called by the compiler } -void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq) +void NDS::SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq) { addrstart >>= 2; addrend >>= 2; @@ -308,10 +153,10 @@ void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, ARM9Regions[i] = region; } - ARM9->UpdateRegionTimings(addrstart<<2, addrend<<2); + ARM9.UpdateRegionTimings(addrstart<<2, addrend<<2); } -void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq) +void NDS::SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq) { addrstart >>= 3; addrend >>= 3; @@ -342,7 +187,7 @@ void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, } } -void InitTimings() +void NDS::InitTimings() { // TODO, eventually: // VRAM is initially unmapped. The timings should be those of void regions. @@ -379,7 +224,7 @@ void InitTimings() // handled later: GBA slot, wifi } -bool NeedsDirectBoot() +bool NDS::NeedsDirectBoot() { if (ConsoleType == 1) { @@ -393,144 +238,142 @@ bool NeedsDirectBoot() return true; // DSi/3DS firmwares aren't bootable - if (!SPI->GetFirmware()->IsBootable()) + if (!SPI.GetFirmware()->IsBootable()) return true; return false; } } -void SetupDirectBoot(const std::string& romname) +void NDS::SetupDirectBoot() { - const NDSHeader& header = NDSCartSlot->GetCart()->GetHeader(); + const NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); + u32 cartid = NDSCartSlot.GetCart()->ID(); + const u8* cartrom = NDSCartSlot.GetCart()->GetROM(); + MapSharedWRAM(3); - if (ConsoleType == 1) + // setup main RAM data + + for (u32 i = 0; i < 0x170; i+=4) { - DSi::SetupDirectBoot(); - } - else - { - u32 cartid = NDSCartSlot->GetCart()->ID(); - const u8* cartrom = NDSCartSlot->GetCart()->GetROM(); - MapSharedWRAM(3); - - // setup main RAM data - - for (u32 i = 0; i < 0x170; i+=4) - { - u32 tmp = *(u32*)&cartrom[i]; - ARM9Write32(0x027FFE00+i, tmp); - } - - ARM9Write32(0x027FF800, cartid); - ARM9Write32(0x027FF804, cartid); - ARM9Write16(0x027FF808, header.HeaderCRC16); - ARM9Write16(0x027FF80A, header.SecureAreaCRC16); - - ARM9Write16(0x027FF850, 0x5835); - - ARM9Write32(0x027FFC00, cartid); - ARM9Write32(0x027FFC04, cartid); - ARM9Write16(0x027FFC08, header.HeaderCRC16); - ARM9Write16(0x027FFC0A, header.SecureAreaCRC16); - - ARM9Write16(0x027FFC10, 0x5835); - ARM9Write16(0x027FFC30, 0xFFFF); - ARM9Write16(0x027FFC40, 0x0001); - - u32 arm9start = 0; - - // load the ARM9 secure area - if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000) - { - u8 securearea[0x800]; - NDSCartSlot->DecryptSecureArea(securearea); - - for (u32 i = 0; i < 0x800; i+=4) - { - ARM9Write32(header.ARM9RAMAddress+i, *(u32*)&securearea[i]); - arm9start += 4; - } - } - - // CHECKME: firmware seems to load this in 0x200 byte chunks - - for (u32 i = arm9start; i < header.ARM9Size; i+=4) - { - u32 tmp = *(u32*)&cartrom[header.ARM9ROMOffset+i]; - ARM9Write32(header.ARM9RAMAddress+i, tmp); - } - - for (u32 i = 0; i < header.ARM7Size; i+=4) - { - u32 tmp = *(u32*)&cartrom[header.ARM7ROMOffset+i]; - ARM7Write32(header.ARM7RAMAddress+i, tmp); - } - - ARM7BIOSProt = 0x1204; - - SPI->GetFirmwareMem()->SetupDirectBoot(false); - - ARM9->CP15Write(0x100, 0x00012078); - ARM9->CP15Write(0x200, 0x00000042); - ARM9->CP15Write(0x201, 0x00000042); - ARM9->CP15Write(0x300, 0x00000002); - ARM9->CP15Write(0x502, 0x15111011); - ARM9->CP15Write(0x503, 0x05100011); - ARM9->CP15Write(0x600, 0x04000033); - ARM9->CP15Write(0x601, 0x04000033); - ARM9->CP15Write(0x610, 0x0200002B); - ARM9->CP15Write(0x611, 0x0200002B); - ARM9->CP15Write(0x620, 0x00000000); - ARM9->CP15Write(0x621, 0x00000000); - ARM9->CP15Write(0x630, 0x08000035); - ARM9->CP15Write(0x631, 0x08000035); - ARM9->CP15Write(0x640, 0x0300001B); - ARM9->CP15Write(0x641, 0x0300001B); - ARM9->CP15Write(0x650, 0x00000000); - ARM9->CP15Write(0x651, 0x00000000); - ARM9->CP15Write(0x660, 0xFFFF001D); - ARM9->CP15Write(0x661, 0xFFFF001D); - ARM9->CP15Write(0x670, 0x027FF017); - ARM9->CP15Write(0x671, 0x027FF017); - ARM9->CP15Write(0x910, 0x0300000A); - ARM9->CP15Write(0x911, 0x00000020); + u32 tmp = *(u32*)&cartrom[i]; + NDS::ARM9Write32(0x027FFE00+i, tmp); } - NDSCartSlot->SetupDirectBoot(romname); + NDS::ARM9Write32(0x027FF800, cartid); + NDS::ARM9Write32(0x027FF804, cartid); + NDS::ARM9Write16(0x027FF808, header.HeaderCRC16); + NDS::ARM9Write16(0x027FF80A, header.SecureAreaCRC16); - ARM9->R[12] = header.ARM9EntryAddress; - ARM9->R[13] = 0x03002F7C; - ARM9->R[14] = header.ARM9EntryAddress; - ARM9->R_IRQ[0] = 0x03003F80; - ARM9->R_SVC[0] = 0x03003FC0; + NDS::ARM9Write16(0x027FF850, 0x5835); - ARM7->R[12] = header.ARM7EntryAddress; - ARM7->R[13] = 0x0380FD80; - ARM7->R[14] = header.ARM7EntryAddress; - ARM7->R_IRQ[0] = 0x0380FF80; - ARM7->R_SVC[0] = 0x0380FFC0; + NDS::ARM9Write32(0x027FFC00, cartid); + NDS::ARM9Write32(0x027FFC04, cartid); + NDS::ARM9Write16(0x027FFC08, header.HeaderCRC16); + NDS::ARM9Write16(0x027FFC0A, header.SecureAreaCRC16); - ARM9->JumpTo(header.ARM9EntryAddress); - ARM7->JumpTo(header.ARM7EntryAddress); + NDS::ARM9Write16(0x027FFC10, 0x5835); + NDS::ARM9Write16(0x027FFC30, 0xFFFF); + NDS::ARM9Write16(0x027FFC40, 0x0001); + + u32 arm9start = 0; + + // load the ARM9 secure area + if (header.ARM9ROMOffset >= 0x4000 && header.ARM9ROMOffset < 0x8000) + { + u8 securearea[0x800]; + NDSCartSlot.DecryptSecureArea(securearea); + + for (u32 i = 0; i < 0x800; i+=4) + { + NDS::ARM9Write32(header.ARM9RAMAddress+i, *(u32*)&securearea[i]); + arm9start += 4; + } + } + + // CHECKME: firmware seems to load this in 0x200 byte chunks + + for (u32 i = arm9start; i < header.ARM9Size; i+=4) + { + u32 tmp = *(u32*)&cartrom[header.ARM9ROMOffset+i]; + NDS::ARM9Write32(header.ARM9RAMAddress+i, tmp); + } + + for (u32 i = 0; i < header.ARM7Size; i+=4) + { + u32 tmp = *(u32*)&cartrom[header.ARM7ROMOffset+i]; + NDS::ARM7Write32(header.ARM7RAMAddress+i, tmp); + } + + ARM7BIOSProt = 0x1204; + + SPI.GetFirmwareMem()->SetupDirectBoot(); + + ARM9.CP15Write(0x100, 0x00012078); + ARM9.CP15Write(0x200, 0x00000042); + ARM9.CP15Write(0x201, 0x00000042); + ARM9.CP15Write(0x300, 0x00000002); + ARM9.CP15Write(0x502, 0x15111011); + ARM9.CP15Write(0x503, 0x05100011); + ARM9.CP15Write(0x600, 0x04000033); + ARM9.CP15Write(0x601, 0x04000033); + ARM9.CP15Write(0x610, 0x0200002B); + ARM9.CP15Write(0x611, 0x0200002B); + ARM9.CP15Write(0x620, 0x00000000); + ARM9.CP15Write(0x621, 0x00000000); + ARM9.CP15Write(0x630, 0x08000035); + ARM9.CP15Write(0x631, 0x08000035); + ARM9.CP15Write(0x640, 0x0300001B); + ARM9.CP15Write(0x641, 0x0300001B); + ARM9.CP15Write(0x650, 0x00000000); + ARM9.CP15Write(0x651, 0x00000000); + ARM9.CP15Write(0x660, 0xFFFF001D); + ARM9.CP15Write(0x661, 0xFFFF001D); + ARM9.CP15Write(0x670, 0x027FF017); + ARM9.CP15Write(0x671, 0x027FF017); + ARM9.CP15Write(0x910, 0x0300000A); + ARM9.CP15Write(0x911, 0x00000020); +} + +void NDS::SetupDirectBoot(const std::string& romname) +{ + const NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); + SetupDirectBoot(); + + NDSCartSlot.SetupDirectBoot(romname); + + ARM9.R[12] = header.ARM9EntryAddress; + ARM9.R[13] = 0x03002F7C; + ARM9.R[14] = header.ARM9EntryAddress; + ARM9.R_IRQ[0] = 0x03003F80; + ARM9.R_SVC[0] = 0x03003FC0; + + ARM7.R[12] = header.ARM7EntryAddress; + ARM7.R[13] = 0x0380FD80; + ARM7.R[14] = header.ARM7EntryAddress; + ARM7.R_IRQ[0] = 0x0380FF80; + ARM7.R_SVC[0] = 0x0380FFC0; + + ARM9.JumpTo(header.ARM9EntryAddress); + ARM7.JumpTo(header.ARM7EntryAddress); PostFlag9 = 0x01; PostFlag7 = 0x01; PowerControl9 = 0x820F; - GPU->SetPowerCnt(PowerControl9); + GPU.SetPowerCnt(PowerControl9); // checkme RCnt = 0x8000; - NDSCartSlot->SetSPICnt(0x8000); + NDSCartSlot.SetSPICnt(0x8000); - SPU->SetBias(0x200); + SPU.SetBias(0x200); SetWifiWaitCnt(0x0030); } -void Reset() +void NDS::Reset() { Platform::FileHandle* f; u32 i; @@ -544,7 +387,7 @@ void Reset() // BIOS files are now loaded by the frontend - JIT->Reset(); + JIT.Reset(); if (ConsoleType == 1) { @@ -561,7 +404,7 @@ void Reset() // has to be called before InitTimings // otherwise some PU settings are completely // unitialised on the first run - ARM9->CP15Reset(); + ARM9.CP15Reset(); ARM9Timestamp = 0; ARM9Target = 0; ARM7Timestamp = 0; ARM7Target = 0; @@ -610,8 +453,8 @@ void Reset() DivCnt = 0; SqrtCnt = 0; - ARM9->Reset(); - ARM7->Reset(); + ARM9.Reset(); + ARM7.Reset(); CPUStop = 0; @@ -621,7 +464,7 @@ void Reset() TimerTimestamp[0] = 0; TimerTimestamp[1] = 0; - for (i = 0; i < 8; i++) DMAs[i]->Reset(); + for (i = 0; i < 8; i++) DMAs[i].Reset(); memset(DMA9Fill, 0, 4*4); for (i = 0; i < Event_MAX; i++) @@ -639,24 +482,24 @@ void Reset() KeyCnt[1] = 0; RCnt = 0; - GPU->Reset(); - NDSCartSlot->Reset(); - GBACartSlot->Reset(); - SPU->Reset(); - SPI->Reset(); - RTC->Reset(); - Wifi->Reset(); + GPU.Reset(); + NDSCartSlot.Reset(); + GBACartSlot.Reset(); + SPU.Reset(); + SPI.Reset(); + RTC.Reset(); + Wifi.Reset(); // TODO: move the SOUNDBIAS/degrade logic to SPU? // The SOUNDBIAS register does nothing on DSi - SPU->SetApplyBias(ConsoleType == 0); + SPU.SetApplyBias(ConsoleType == 0); bool degradeAudio = true; if (ConsoleType == 1) { - DSi::Reset(); + //DSi::Reset(); KeyInput &= ~(1 << (16+6)); degradeAudio = false; } @@ -667,12 +510,10 @@ void Reset() else if (bitDepth == 2) // Always 16-bit degradeAudio = false; - SPU->SetDegrade10Bit(degradeAudio); - - AREngine->Reset(); + SPU.SetDegrade10Bit(degradeAudio); } -void Start() +void NDS::Start() { Running = true; } @@ -694,7 +535,7 @@ static const char* StopReasonName(Platform::StopReason reason) } } -void Stop(Platform::StopReason reason) +void NDS::Stop(Platform::StopReason reason) { Platform::LogLevel level; switch (reason) @@ -715,14 +556,11 @@ void Stop(Platform::StopReason reason) Log(level, "Stopping emulated console (Reason: %s)\n", StopReasonName(reason)); Running = false; Platform::SignalStop(reason); - GPU->Stop(); - SPU->Stop(); - - if (ConsoleType == 1) - DSi::Stop(); + GPU.Stop(); + SPU.Stop(); } -bool DoSavestate(Savestate* file) +bool NDS::DoSavestate(Savestate* file) { file->Section("NDSG"); @@ -835,36 +673,35 @@ bool DoSavestate(Savestate* file) } for (int i = 0; i < 8; i++) - DMAs[i]->DoSavestate(file); + DMAs[i].DoSavestate(file); - ARM9->DoSavestate(file); - ARM7->DoSavestate(file); + ARM9.DoSavestate(file); + ARM7.DoSavestate(file); - NDSCartSlot->DoSavestate(file); + NDSCartSlot.DoSavestate(file); if (ConsoleType == 0) - GBACartSlot->DoSavestate(file); - GPU->DoSavestate(file); - SPU->DoSavestate(file); - SPI->DoSavestate(file); - RTC->DoSavestate(file); - Wifi->DoSavestate(file); + GBACartSlot.DoSavestate(file); + GPU.DoSavestate(file); + SPU.DoSavestate(file); + SPI.DoSavestate(file); + RTC.DoSavestate(file); + Wifi.DoSavestate(file); - if (ConsoleType == 1) - DSi::DoSavestate(file); + DoSavestateExtra(file); // Handles DSi state if applicable if (!file->Saving) { - GPU->SetPowerCnt(PowerControl9); + GPU.SetPowerCnt(PowerControl9); - SPU->SetPowerCnt(PowerControl7 & 0x0001); - Wifi->SetPowerCnt(PowerControl7 & 0x0002); + SPU.SetPowerCnt(PowerControl7 & 0x0001); + Wifi.SetPowerCnt(PowerControl7 & 0x0002); } #ifdef JIT_ENABLED if (!file->Saving) { - JIT->ResetBlockCache(); - JIT->Memory.Reset(); + JIT.ResetBlockCache(); + JIT.Memory.Reset(); } #endif @@ -873,80 +710,70 @@ bool DoSavestate(Savestate* file) return true; } -void SetConsoleType(int type) +bool NDS::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) { - ConsoleType = type; -} - -bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) -{ - if (!NDSCartSlot->LoadROM(romdata, romlen)) + if (!NDSCartSlot.LoadROM(romdata, romlen)) return false; if (savedata && savelen) - NDSCartSlot->LoadSave(savedata, savelen); + NDSCartSlot.LoadSave(savedata, savelen); return true; } -void LoadSave(const u8* savedata, u32 savelen) +void NDS::LoadSave(const u8* savedata, u32 savelen) { if (savedata && savelen) - NDSCartSlot->LoadSave(savedata, savelen); + NDSCartSlot.LoadSave(savedata, savelen); } -void EjectCart() +void NDS::EjectCart() { - NDSCartSlot->EjectCart(); + NDSCartSlot.EjectCart(); } -bool CartInserted() +bool NDS::CartInserted() { - return NDSCartSlot->GetCart() != nullptr; + return NDSCartSlot.GetCart() != nullptr; } -bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) +bool NDS::LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) { - if (!GBACartSlot) - return false; - - if (!GBACartSlot->LoadROM(romdata, romlen)) + if (!GBACartSlot.LoadROM(romdata, romlen)) return false; if (savedata && savelen) - GBACartSlot->LoadSave(savedata, savelen); + GBACartSlot.LoadSave(savedata, savelen); return true; } -void LoadGBAAddon(int type) +void NDS::LoadGBAAddon(int type) { - if (GBACartSlot) - GBACartSlot->LoadAddon(type); + GBACartSlot.LoadAddon(type); } -void EjectGBACart() +void NDS::EjectGBACart() { - if (GBACartSlot) - GBACartSlot->EjectCart(); + GBACartSlot.EjectCart(); } -void LoadBIOS() +void NDS::LoadBIOS() { Reset(); } -bool IsLoadedARM9BIOSBuiltIn() +bool NDS::IsLoadedARM9BIOSBuiltIn() { - return memcmp(NDS::ARM9BIOS, bios_arm9_bin, sizeof(NDS::ARM9BIOS)) == 0; + return memcmp(ARM9BIOS, bios_arm9_bin, sizeof(NDS::ARM9BIOS)) == 0; } -bool IsLoadedARM7BIOSBuiltIn() +bool NDS::IsLoadedARM7BIOSBuiltIn() { - return memcmp(NDS::ARM7BIOS, bios_arm7_bin, sizeof(NDS::ARM7BIOS)) == 0; + return memcmp(ARM7BIOS, bios_arm7_bin, sizeof(NDS::ARM7BIOS)) == 0; } -u64 NextTarget() +u64 NDS::NextTarget() { u64 minEvent = UINT64_MAX; @@ -971,7 +798,7 @@ u64 NextTarget() return max; } -void RunSystem(u64 timestamp) +void NDS::RunSystem(u64 timestamp) { SysTimestamp = timestamp; @@ -996,7 +823,7 @@ void RunSystem(u64 timestamp) } } -u64 NextTargetSleep() +u64 NDS::NextTargetSleep() { u64 minEvent = UINT64_MAX; @@ -1019,7 +846,7 @@ u64 NextTargetSleep() return minEvent; } -void RunSystemSleep(u64 timestamp) +void NDS::RunSystemSleep(u64 timestamp) { u64 offset = timestamp - SysTimestamp; SysTimestamp = timestamp; @@ -1061,12 +888,12 @@ void RunSystemSleep(u64 timestamp) } } -template -u32 RunFrame() +template +u32 NDS::RunFrame() { FrameStartTimestamp = SysTimestamp; - GPU->TotalScanlines = 0; + GPU.TotalScanlines = 0; LagFrameFlag = true; bool runFrame = Running && !(CPUStop & CPUStop_Sleep); @@ -1090,7 +917,7 @@ u32 RunFrame() ARM7Timestamp = target; TimerTimestamp[0] = target; TimerTimestamp[1] = target; - GPU->GPU3D.Timestamp = target; + GPU.GPU3D.Timestamp = target; RunSystemSleep(target); if (!(CPUStop & CPUStop_Sleep)) @@ -1098,20 +925,20 @@ u32 RunFrame() } if (SysTimestamp >= frametarget) - GPU->BlankFrame(); + GPU.BlankFrame(); } else { - ARM9->CheckGdbIncoming(); - ARM7->CheckGdbIncoming(); + ARM9.CheckGdbIncoming(); + ARM7.CheckGdbIncoming(); if (!(CPUStop & CPUStop_Wakeup)) { - GPU->StartFrame(); + GPU.StartFrame(); } CPUStop &= ~CPUStop_Wakeup; - while (Running && GPU->TotalScanlines==0) + while (Running && GPU.TotalScanlines==0) { u64 target = NextTarget(); ARM9Target = target << ARM9ClockShift; @@ -1120,30 +947,34 @@ u32 RunFrame() if (CPUStop & CPUStop_GXStall) { // GXFIFO stall - s32 cycles = GPU->GPU3D.CyclesToRunFor(); + s32 cycles = GPU.GPU3D.CyclesToRunFor(); ARM9Timestamp = std::min(ARM9Target, ARM9Timestamp+(cycles<Run(); - if (!(CPUStop & CPUStop_GXStall)) DMAs[1]->Run(); - if (!(CPUStop & CPUStop_GXStall)) DMAs[2]->Run(); - if (!(CPUStop & CPUStop_GXStall)) DMAs[3]->Run(); - if (ConsoleType == 1) DSi::RunNDMAs(0); + DMAs[0].Run(); + if (!(CPUStop & CPUStop_GXStall)) DMAs[1].Run(); + if (!(CPUStop & CPUStop_GXStall)) DMAs[2].Run(); + if (!(CPUStop & CPUStop_GXStall)) DMAs[3].Run(); + if (ConsoleType == 1) + { + auto& dsi = dynamic_cast(*this); + dsi.RunNDMAs(0); + } } else { #ifdef JIT_ENABLED if (EnableJIT) - ARM9->ExecuteJIT(); + ARM9.ExecuteJIT(); else #endif - ARM9->Execute(); + ARM9.Execute(); } RunTimers(0); - GPU->GPU3D.Run(); + GPU.GPU3D.Run(); target = ARM9Timestamp >> ARM9ClockShift; CurCPU = 1; @@ -1154,20 +985,24 @@ u32 RunFrame() if (CPUStop & CPUStop_DMA7) { - DMAs[4]->Run(); - DMAs[5]->Run(); - DMAs[6]->Run(); - DMAs[7]->Run(); - if (ConsoleType == 1) DSi::RunNDMAs(1); + DMAs[4].Run(); + DMAs[5].Run(); + DMAs[6].Run(); + DMAs[7].Run(); + if (ConsoleType == 1) + { + auto& dsi = dynamic_cast(*this); + dsi.RunNDMAs(1); + } } else { #ifdef JIT_ENABLED if (EnableJIT) - ARM7->ExecuteJIT(); + ARM7.ExecuteJIT(); else #endif - ARM7->Execute(); + ARM7.Execute(); } RunTimers(1); @@ -1182,7 +1017,7 @@ u32 RunFrame() } } - if (GPU->TotalScanlines == 0) + if (GPU.TotalScanlines == 0) continue; #ifdef DEBUG_CHECK_DESYNC @@ -1190,9 +1025,9 @@ u32 RunFrame() (u32)(SysTimestamp>>32), (u32)SysTimestamp, (ARM9Timestamp>>1)-SysTimestamp, ARM7Timestamp-SysTimestamp, - GPU3D::Timestamp-SysTimestamp); + GPU.GPU3D.Timestamp-SysTimestamp); #endif - SPU->TransferOutput(); + SPU.TransferOutput(); break; } @@ -1203,26 +1038,22 @@ u32 RunFrame() NumLagFrames++; if (Running) - return GPU->TotalScanlines; + return GPU.TotalScanlines; else return 263; } -u32 RunFrame() +u32 NDS::RunFrame() { #ifdef JIT_ENABLED if (EnableJIT) - return NDS::ConsoleType == 1 - ? RunFrame() - : RunFrame(); + return RunFrame(); else #endif - return NDS::ConsoleType == 1 - ? RunFrame() - : RunFrame(); + return RunFrame(); } -void Reschedule(u64 target) +void NDS::Reschedule(u64 target) { if (CurCPU == 0) { @@ -1236,21 +1067,21 @@ void Reschedule(u64 target) } } -void RegisterEventFunc(u32 id, u32 funcid, EventFunc func) +void NDS::RegisterEventFunc(u32 id, u32 funcid, EventFunc func) { SchedEvent& evt = SchedList[id]; evt.Funcs[funcid] = func; } -void UnregisterEventFunc(u32 id, u32 funcid) +void NDS::UnregisterEventFunc(u32 id, u32 funcid) { SchedEvent& evt = SchedList[id]; evt.Funcs.erase(funcid); } -void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param) +void NDS::ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param) { if (SchedListMask & (1<GetTSC()->SetTouchCoords(x, y); + SPI.GetTSC()->SetTouchCoords(x, y); } -void ReleaseScreen() +void NDS::ReleaseScreen() { - SPI->GetTSC()->SetTouchCoords(0x000, 0xFFF); + SPI.GetTSC()->SetTouchCoords(0x000, 0xFFF); } -void CheckKeyIRQ(u32 cpu, u32 oldkey, u32 newkey) +void NDS::CheckKeyIRQ(u32 cpu, u32 oldkey, u32 newkey) { u16 cnt = KeyCnt[cpu]; if (!(cnt & (1<<14))) // IRQ disabled @@ -1325,7 +1156,7 @@ void CheckKeyIRQ(u32 cpu, u32 oldkey, u32 newkey) SetIRQ(cpu, IRQ_Keypad); } -void SetKeyMask(u32 mask) +void NDS::SetKeyMask(u32 mask) { u32 key_lo = mask & 0x3FF; u32 key_hi = (mask >> 10) & 0x3; @@ -1338,13 +1169,13 @@ void SetKeyMask(u32 mask) CheckKeyIRQ(1, oldkey, KeyInput); } -bool IsLidClosed() +bool NDS::IsLidClosed() { if (KeyInput & (1<<23)) return true; return false; } -void SetLidClosed(bool closed) +void NDS::SetLidClosed(bool closed) { if (closed) { @@ -1357,24 +1188,9 @@ void SetLidClosed(bool closed) } } -void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) +void NDS::MicInputFrame(s16* data, int samples) { - // TODO: support things like the GBA-slot camera addon - // whenever these are emulated - - if (ConsoleType == 1) - { - switch (cam) - { - case 0: return DSi::CamModule->GetOuterCamera()->InputFrame(data, width, height, rgb); - case 1: return DSi::CamModule->GetInnerCamera()->InputFrame(data, width, height, rgb); - } - } -} - -void MicInputFrame(s16* data, int samples) -{ - return SPI->GetTSC()->MicInputFrame(data, samples); + return SPI.GetTSC()->MicInputFrame(data, samples); } /*int ImportSRAM(u8* data, u32 length) @@ -1383,19 +1199,19 @@ void MicInputFrame(s16* data, int samples) }*/ -void Halt() +void NDS::Halt() { Log(LogLevel::Info, "Halt()\n"); Running = false; } -void MapSharedWRAM(u8 val) +void NDS::MapSharedWRAM(u8 val) { if (val == WRAMCnt) return; - NDS::JIT->Memory.RemapSWRAM(); + JIT.Memory.RemapSWRAM(); WRAMCnt = val; @@ -1432,7 +1248,7 @@ void MapSharedWRAM(u8 val) } -void UpdateWifiTimings() +void NDS::UpdateWifiTimings() { if (PowerControl7 & 0x0002) { @@ -1449,7 +1265,7 @@ void UpdateWifiTimings() } } -void SetWifiWaitCnt(u16 val) +void NDS::SetWifiWaitCnt(u16 val) { if (WifiWaitCnt == val) return; @@ -1457,7 +1273,7 @@ void SetWifiWaitCnt(u16 val) UpdateWifiTimings(); } -void SetGBASlotTimings() +void NDS::SetGBASlotTimings() { const int ntimings[4] = {10, 8, 6, 18}; const u16 openbus[4] = {0xFE08, 0x0000, 0x0000, 0xFFFF}; @@ -1492,28 +1308,27 @@ void SetGBASlotTimings() // for example, the Cartridge Construction Kit relies on this to determine that // the GBA slot is empty - assert(GBACartSlot != nullptr); - GBACartSlot->SetOpenBusDecay(openbus[(curcnt>>2) & 0x3]); + GBACartSlot.SetOpenBusDecay(openbus[(curcnt>>2) & 0x3]); } -void UpdateIRQ(u32 cpu) +void NDS::UpdateIRQ(u32 cpu) { - ARM* arm = cpu ? (ARM*)ARM7 : (ARM*)ARM9; + ARM& arm = cpu ? (ARM&)ARM7 : (ARM&)ARM9; if (IME[cpu] & 0x1) { - arm->IRQ = !!(IE[cpu] & IF[cpu]); + arm.IRQ = !!(IE[cpu] & IF[cpu]); if ((ConsoleType == 1) && cpu) - arm->IRQ |= !!(IE2 & IF2); + arm.IRQ |= !!(IE2 & IF2); } else { - arm->IRQ = 0; + arm.IRQ = 0; } } -void SetIRQ(u32 cpu, u32 irq) +void NDS::SetIRQ(u32 cpu, u32 irq) { IF[cpu] |= (1 << irq); UpdateIRQ(cpu); @@ -1524,30 +1339,30 @@ void SetIRQ(u32 cpu, u32 irq) { CPUStop &= ~CPUStop_Sleep; CPUStop |= CPUStop_Wakeup; - GPU->GPU3D.RestartFrame(); + GPU.GPU3D.RestartFrame(); } } } -void ClearIRQ(u32 cpu, u32 irq) +void NDS::ClearIRQ(u32 cpu, u32 irq) { IF[cpu] &= ~(1 << irq); UpdateIRQ(cpu); } -void SetIRQ2(u32 irq) +void NDS::SetIRQ2(u32 irq) { IF2 |= (1 << irq); UpdateIRQ(1); } -void ClearIRQ2(u32 irq) +void NDS::ClearIRQ2(u32 irq) { IF2 &= ~(1 << irq); UpdateIRQ(1); } -bool HaltInterrupted(u32 cpu) +bool NDS::HaltInterrupted(u32 cpu) { if (cpu == 0) { @@ -1564,62 +1379,66 @@ bool HaltInterrupted(u32 cpu) return false; } -void StopCPU(u32 cpu, u32 mask) +void NDS::StopCPU(u32 cpu, u32 mask) { if (cpu) { CPUStop |= (mask << 16); - ARM7->Halt(2); + ARM7.Halt(2); } else { CPUStop |= mask; - ARM9->Halt(2); + ARM9.Halt(2); } } -void ResumeCPU(u32 cpu, u32 mask) +void NDS::ResumeCPU(u32 cpu, u32 mask) { if (cpu) mask <<= 16; CPUStop &= ~mask; } -void GXFIFOStall() +void NDS::GXFIFOStall() { if (CPUStop & CPUStop_GXStall) return; CPUStop |= CPUStop_GXStall; - if (CurCPU == 1) ARM9->Halt(2); + if (CurCPU == 1) ARM9.Halt(2); else { - DMAs[0]->StallIfRunning(); - DMAs[1]->StallIfRunning(); - DMAs[2]->StallIfRunning(); - DMAs[3]->StallIfRunning(); - if (ConsoleType == 1) DSi::StallNDMAs(); + DMAs[0].StallIfRunning(); + DMAs[1].StallIfRunning(); + DMAs[2].StallIfRunning(); + DMAs[3].StallIfRunning(); + if (ConsoleType == 1) + { + auto& dsi = dynamic_cast(*this); + dsi.StallNDMAs(); + } } } -void GXFIFOUnstall() +void NDS::GXFIFOUnstall() { CPUStop &= ~CPUStop_GXStall; } -void EnterSleepMode() +void NDS::EnterSleepMode() { if (CPUStop & CPUStop_Sleep) return; CPUStop |= CPUStop_Sleep; - ARM7->Halt(2); + ARM7.Halt(2); } -u32 GetPC(u32 cpu) +u32 NDS::GetPC(u32 cpu) { - return cpu ? ARM7->R[15] : ARM9->R[15]; + return cpu ? ARM7.R[15] : ARM9.R[15]; } -u64 GetSysClockCycles(int num) +u64 NDS::GetSysClockCycles(int num) { u64 ret; @@ -1646,19 +1465,19 @@ u64 GetSysClockCycles(int num) return ret; } -void NocashPrint(u32 ncpu, u32 addr) +void NDS::NocashPrint(u32 ncpu, u32 addr) { // addr: debug string - ARM* cpu = ncpu ? (ARM*)ARM7 : (ARM*)ARM9; - u8 (*readfn)(u32) = ncpu ? NDS::ARM7Read8 : NDS::ARM9Read8; + ARM* cpu = ncpu ? (ARM*)&ARM7 : (ARM*)&ARM9; + u8 (NDS::*readfn)(u32) = ncpu ? &NDS::ARM7Read8 : &NDS::ARM9Read8; char output[1024]; int ptr = 0; for (int i = 0; i < 120 && ptr < 1023; ) { - char ch = readfn(addr++); + char ch = (this->*readfn)(addr++); i++; if (ch == '%') @@ -1666,7 +1485,7 @@ void NocashPrint(u32 ncpu, u32 addr) char cmd[16]; int j; for (j = 0; j < 15; ) { - char ch2 = readfn(addr++); + char ch2 = (this->*readfn)(addr++); i++; if (i >= 120) break; if (ch2 == '%') break; @@ -1701,7 +1520,7 @@ void NocashPrint(u32 ncpu, u32 addr) else if (!strcmp(cmd, "lr")) sprintf(subs, "%08X", cpu->R[14]); else if (!strcmp(cmd, "pc")) sprintf(subs, "%08X", cpu->R[15]); else if (!strcmp(cmd, "frame")) sprintf(subs, "%u", NumFrames); - else if (!strcmp(cmd, "scanline")) sprintf(subs, "%u", GPU->VCount); + else if (!strcmp(cmd, "scanline")) sprintf(subs, "%u", GPU.VCount); else if (!strcmp(cmd, "totalclks")) sprintf(subs, "%" PRIu64, GetSysClockCycles(0)); else if (!strcmp(cmd, "lastclks")) sprintf(subs, "%" PRIu64, GetSysClockCycles(1)); else if (!strcmp(cmd, "zeroclks")) @@ -1727,16 +1546,14 @@ void NocashPrint(u32 ncpu, u32 addr) Log(LogLevel::Debug, "%s", output); } - - -void MonitorARM9Jump(u32 addr) +void NDS::MonitorARM9Jump(u32 addr) { // checkme: can the entrypoint addr be THUMB? // also TODO: make it work in DSi mode - if ((!RunningGame) && NDSCartSlot->GetCart()) + if ((!RunningGame) && NDSCartSlot.GetCart()) { - const NDSHeader& header = NDSCartSlot->GetCart()->GetHeader(); + const NDSHeader& header = NDSCartSlot.GetCart()->GetHeader(); if (addr == header.ARM9EntryAddress) { Log(LogLevel::Info, "Game is now booting\n"); @@ -1747,7 +1564,7 @@ void MonitorARM9Jump(u32 addr) -void HandleTimerOverflow(u32 tid) +void NDS::HandleTimerOverflow(u32 tid) { Timer* timer = &Timers[tid]; @@ -1780,7 +1597,7 @@ void HandleTimerOverflow(u32 tid) } } -void RunTimer(u32 tid, s32 cycles) +void NDS::RunTimer(u32 tid, s32 cycles) { Timer* timer = &Timers[tid]; @@ -1792,7 +1609,7 @@ void RunTimer(u32 tid, s32 cycles) } } -void RunTimers(u32 cpu) +void NDS::RunTimers(u32 cpu) { u32 timermask = TimerCheckMask[cpu]; s32 cycles; @@ -1812,7 +1629,7 @@ void RunTimers(u32 cpu) const s32 TimerPrescaler[4] = {0, 6, 8, 10}; -u16 TimerGetCounter(u32 timer) +u16 NDS::TimerGetCounter(u32 timer) { RunTimers(timer>>2); u32 ret = Timers[timer].Counter; @@ -1820,7 +1637,7 @@ u16 TimerGetCounter(u32 timer) return ret >> 10; } -void TimerStart(u32 id, u16 cnt) +void NDS::TimerStart(u32 id, u16 cnt) { Timer* timer = &Timers[id]; u16 curstart = timer->Cnt & (1<<7); @@ -1844,93 +1661,49 @@ void TimerStart(u32 id, u16 cnt) -// matching NDMA modes for DSi -const u32 NDMAModes[] = -{ - // ARM9 - - 0x10, // immediate - 0x06, // VBlank - 0x07, // HBlank - 0x08, // scanline start - 0x09, // mainmem FIFO - 0x04, // DS cart slot - 0xFF, // GBA cart slot - 0x0A, // GX FIFO - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - - // ARM7 - - 0x30, // immediate - 0x26, // VBlank - 0x24, // DS cart slot - 0xFF, // wifi / GBA cart slot (TODO) -}; - -bool DMAsInMode(u32 cpu, u32 mode) +bool NDS::DMAsInMode(u32 cpu, u32 mode) { cpu <<= 2; - if (DMAs[cpu+0]->IsInMode(mode)) return true; - if (DMAs[cpu+1]->IsInMode(mode)) return true; - if (DMAs[cpu+2]->IsInMode(mode)) return true; - if (DMAs[cpu+3]->IsInMode(mode)) return true; - - if (ConsoleType == 1) - { - cpu >>= 2; - return DSi::NDMAsInMode(cpu, NDMAModes[mode]); - } + if (DMAs[cpu+0].IsInMode(mode)) return true; + if (DMAs[cpu+1].IsInMode(mode)) return true; + if (DMAs[cpu+2].IsInMode(mode)) return true; + if (DMAs[cpu+3].IsInMode(mode)) return true; return false; } -bool DMAsRunning(u32 cpu) +bool NDS::DMAsRunning(u32 cpu) { cpu <<= 2; - if (DMAs[cpu+0]->IsRunning()) return true; - if (DMAs[cpu+1]->IsRunning()) return true; - if (DMAs[cpu+2]->IsRunning()) return true; - if (DMAs[cpu+3]->IsRunning()) return true; - if (ConsoleType == 1) - { - if (DSi::NDMAsRunning(cpu>>2)) return true; - } + if (DMAs[cpu+0].IsRunning()) return true; + if (DMAs[cpu+1].IsRunning()) return true; + if (DMAs[cpu+2].IsRunning()) return true; + if (DMAs[cpu+3].IsRunning()) return true; + return false; } -void CheckDMAs(u32 cpu, u32 mode) +void NDS::CheckDMAs(u32 cpu, u32 mode) { cpu <<= 2; - DMAs[cpu+0]->StartIfNeeded(mode); - DMAs[cpu+1]->StartIfNeeded(mode); - DMAs[cpu+2]->StartIfNeeded(mode); - DMAs[cpu+3]->StartIfNeeded(mode); - - if (ConsoleType == 1) - { - cpu >>= 2; - DSi::CheckNDMAs(cpu, NDMAModes[mode]); - } + DMAs[cpu+0].StartIfNeeded(mode); + DMAs[cpu+1].StartIfNeeded(mode); + DMAs[cpu+2].StartIfNeeded(mode); + DMAs[cpu+3].StartIfNeeded(mode); } -void StopDMAs(u32 cpu, u32 mode) +void NDS::StopDMAs(u32 cpu, u32 mode) { cpu <<= 2; - DMAs[cpu+0]->StopIfNeeded(mode); - DMAs[cpu+1]->StopIfNeeded(mode); - DMAs[cpu+2]->StopIfNeeded(mode); - DMAs[cpu+3]->StopIfNeeded(mode); - - if (ConsoleType == 1) - { - cpu >>= 2; - DSi::StopNDMAs(cpu, NDMAModes[mode]); - } + DMAs[cpu+0].StopIfNeeded(mode); + DMAs[cpu+1].StopIfNeeded(mode); + DMAs[cpu+2].StopIfNeeded(mode); + DMAs[cpu+3].StopIfNeeded(mode); } -void DivDone(u32 param) +void NDS::DivDone(u32 param) { DivCnt &= ~0xC000; @@ -2008,15 +1781,15 @@ void DivDone(u32 param) DivCnt |= 0x4000; } -void StartDiv() +void NDS::StartDiv() { - NDS::CancelEvent(NDS::Event_Div); + CancelEvent(Event_Div); DivCnt |= 0x8000; - NDS::ScheduleEvent(NDS::Event_Div, false, ((DivCnt&0x3)==0) ? 18:34, 0, 0); + ScheduleEvent(Event_Div, false, ((DivCnt&0x3)==0) ? 18:34, 0, 0); } // http://stackoverflow.com/questions/1100090/looking-for-an-efficient-integer-square-root-algorithm-for-arm-thumb2 -void SqrtDone(u32 param) +void NDS::SqrtDone(u32 param) { u64 val; u32 res = 0; @@ -2056,19 +1829,19 @@ void SqrtDone(u32 param) SqrtRes = res; } -void StartSqrt() +void NDS::StartSqrt() { - NDS::CancelEvent(NDS::Event_Sqrt); + CancelEvent(Event_Sqrt); SqrtCnt |= 0x8000; - NDS::ScheduleEvent(NDS::Event_Sqrt, false, 13, 0, 0); + ScheduleEvent(Event_Sqrt, false, 13, 0, 0); } -void debug(u32 param) +void NDS::debug(u32 param) { - Log(LogLevel::Debug, "ARM9 PC=%08X LR=%08X %08X\n", ARM9->R[15], ARM9->R[14], ARM9->R_IRQ[1]); - Log(LogLevel::Debug, "ARM7 PC=%08X LR=%08X %08X\n", ARM7->R[15], ARM7->R[14], ARM7->R_IRQ[1]); + Log(LogLevel::Debug, "ARM9 PC=%08X LR=%08X %08X\n", ARM9.R[15], ARM9.R[14], ARM9.R_IRQ[1]); + Log(LogLevel::Debug, "ARM7 PC=%08X LR=%08X %08X\n", ARM7.R[15], ARM7.R[14], ARM7.R_IRQ[1]); Log(LogLevel::Debug, "ARM9 IME=%08X IE=%08X IF=%08X\n", IME[0], IE[0], IF[0]); Log(LogLevel::Debug, "ARM7 IME=%08X IE=%08X IF=%08X IE2=%04X IF2=%04X\n", IME[1], IE[1], IF[1], IE2, IF2); @@ -2077,20 +1850,20 @@ void debug(u32 param) // printf("VRAM %c: %02X\n", 'A'+i, GPU->VRAMCNT[i]); Platform::FileHandle* shit = Platform::OpenFile("debug/DSfirmware.bin", FileMode::Write); - Platform::FileWrite(ARM9->ITCM, 0x8000, 1, shit); + Platform::FileWrite(ARM9.ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x02400000; i+=4) { - u32 val = ARM7Read32(i); + u32 val = NDS::ARM7Read32(i); Platform::FileWrite(&val, 4, 1, shit); } for (u32 i = 0x037F0000; i < 0x03810000; i+=4) { - u32 val = ARM7Read32(i); + u32 val = NDS::ARM7Read32(i); Platform::FileWrite(&val, 4, 1, shit); } for (u32 i = 0x06000000; i < 0x06040000; i+=4) { - u32 val = ARM7Read32(i); + u32 val = NDS::ARM7Read32(i); Platform::FileWrite(&val, 4, 1, shit); } Platform::CloseFile(shit); @@ -2114,7 +1887,7 @@ void debug(u32 param) -u8 ARM9Read8(u32 addr) +u8 NDS::ARM9Read8(u32 addr) { if ((addr & 0xFFFFF000) == 0xFFFF0000) { @@ -2137,44 +1910,43 @@ u8 ARM9Read8(u32 addr) } case 0x04000000: - return ARM9IORead8(addr); + // Specifically want to call the NDS version, not a subclass + return NDS::ARM9IORead8(addr); case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU->ReadPalette(addr); + return GPU.ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU->ReadVRAM_ABG(addr); - case 0x00200000: return GPU->ReadVRAM_BBG(addr); - case 0x00400000: return GPU->ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU->ReadVRAM_BOBJ(addr); - default: return GPU->ReadVRAM_LCDC(addr); + case 0x00000000: return GPU.ReadVRAM_ABG(addr); + case 0x00200000: return GPU.ReadVRAM_BBG(addr); + case 0x00400000: return GPU.ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU.ReadVRAM_BOBJ(addr); + default: return GPU.ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU->ReadOAM(addr); + return GPU.ReadOAM(addr); case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - if (addr & 0x1) return GBACartSlot->ROMRead(addr-1) >> 8; - return GBACartSlot->ROMRead(addr) & 0xFF; + if (addr & 0x1) return GBACartSlot.ROMRead(addr-1) >> 8; + return GBACartSlot.ROMRead(addr) & 0xFF; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x00; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->SRAMRead(addr); + return GBACartSlot.SRAMRead(addr); } Log(LogLevel::Debug, "unknown arm9 read8 %08X\n", addr); return 0; } -u16 ARM9Read16(u32 addr) +u16 NDS::ARM9Read16(u32 addr) { addr &= ~0x1; @@ -2199,44 +1971,42 @@ u16 ARM9Read16(u32 addr) } case 0x04000000: - return ARM9IORead16(addr); + return NDS::ARM9IORead16(addr); case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU->ReadPalette(addr); + return GPU.ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU->ReadVRAM_ABG(addr); - case 0x00200000: return GPU->ReadVRAM_BBG(addr); - case 0x00400000: return GPU->ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU->ReadVRAM_BOBJ(addr); - default: return GPU->ReadVRAM_LCDC(addr); + case 0x00000000: return GPU.ReadVRAM_ABG(addr); + case 0x00200000: return GPU.ReadVRAM_BBG(addr); + case 0x00400000: return GPU.ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU.ReadVRAM_BOBJ(addr); + default: return GPU.ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU->ReadOAM(addr); + return GPU.ReadOAM(addr); case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->ROMRead(addr); + return GBACartSlot.ROMRead(addr); case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x0000; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->SRAMRead(addr) | - (GBACartSlot->SRAMRead(addr+1) << 8); + return GBACartSlot.SRAMRead(addr) | + (GBACartSlot.SRAMRead(addr+1) << 8); } - //if (addr) Log(LogLevel::Warn, "unknown arm9 read16 %08X %08X\n", addr, ARM9->R[15]); + //if (addr) Log(LogLevel::Warn, "unknown arm9 read16 %08X %08X\n", addr, ARM9.R[15]); return 0; } -u32 ARM9Read32(u32 addr) +u32 NDS::ARM9Read32(u32 addr) { addr &= ~0x3; @@ -2261,65 +2031,63 @@ u32 ARM9Read32(u32 addr) } case 0x04000000: - return ARM9IORead32(addr); + return NDS::ARM9IORead32(addr); case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU->ReadPalette(addr); + return GPU.ReadPalette(addr); case 0x06000000: switch (addr & 0x00E00000) { - case 0x00000000: return GPU->ReadVRAM_ABG(addr); - case 0x00200000: return GPU->ReadVRAM_BBG(addr); - case 0x00400000: return GPU->ReadVRAM_AOBJ(addr); - case 0x00600000: return GPU->ReadVRAM_BOBJ(addr); - default: return GPU->ReadVRAM_LCDC(addr); + case 0x00000000: return GPU.ReadVRAM_ABG(addr); + case 0x00200000: return GPU.ReadVRAM_BBG(addr); + case 0x00400000: return GPU.ReadVRAM_AOBJ(addr); + case 0x00600000: return GPU.ReadVRAM_BOBJ(addr); + default: return GPU.ReadVRAM_LCDC(addr); } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return 0; - return GPU->ReadOAM(addr & 0x7FF); + return GPU.ReadOAM(addr & 0x7FF); case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->ROMRead(addr) | - (GBACartSlot->ROMRead(addr+2) << 16); + return GBACartSlot.ROMRead(addr) | + (GBACartSlot.ROMRead(addr+2) << 16); case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return 0x00000000; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->SRAMRead(addr) | - (GBACartSlot->SRAMRead(addr+1) << 8) | - (GBACartSlot->SRAMRead(addr+2) << 16) | - (GBACartSlot->SRAMRead(addr+3) << 24); + return GBACartSlot.SRAMRead(addr) | + (GBACartSlot.SRAMRead(addr+1) << 8) | + (GBACartSlot.SRAMRead(addr+2) << 16) | + (GBACartSlot.SRAMRead(addr+3) << 24); } - //Log(LogLevel::Warn, "unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9->R[15], ARM9->R[12]); + //Log(LogLevel::Warn, "unknown arm9 read32 %08X | %08X %08X\n", addr, ARM9.R[15], ARM9.R[12]); return 0; } -void ARM9Write8(u32 addr, u8 val) +void NDS::ARM9Write8(u32 addr, u8 val) { switch (addr & 0xFF000000) { case 0x02000000: - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM9.Mem) { - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u8*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; case 0x04000000: - ARM9IOWrite8(addr, val); + NDS::ARM9IOWrite8(addr, val); return; case 0x05000000: @@ -2333,142 +2101,137 @@ void ARM9Write8(u32 addr, u8 val) case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->SRAMWrite(addr, val); + GBACartSlot.SRAMWrite(addr, val); return; } Log(LogLevel::Debug, "unknown arm9 write8 %08X %02X\n", addr, val); } -void ARM9Write16(u32 addr, u16 val) +void NDS::ARM9Write16(u32 addr, u16 val) { addr &= ~0x1; switch (addr & 0xFF000000) { case 0x02000000: - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM9.Mem) { - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u16*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; case 0x04000000: - ARM9IOWrite16(addr, val); + NDS::ARM9IOWrite16(addr, val); return; case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU->WritePalette(addr, val); + GPU.WritePalette(addr, val); return; case 0x06000000: - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { - case 0x00000000: GPU->WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU->WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU->WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU->WriteVRAM_BOBJ(addr, val); return; - default: GPU->WriteVRAM_LCDC(addr, val); return; + case 0x00000000: GPU.WriteVRAM_ABG(addr, val); return; + case 0x00200000: GPU.WriteVRAM_BBG(addr, val); return; + case 0x00400000: GPU.WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: GPU.WriteVRAM_BOBJ(addr, val); return; + default: GPU.WriteVRAM_LCDC(addr, val); return; } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU->WriteOAM(addr, val); + GPU.WriteOAM(addr, val); return; case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->ROMWrite(addr, val); + GBACartSlot.ROMWrite(addr, val); return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->SRAMWrite(addr, val & 0xFF); - GBACartSlot->SRAMWrite(addr+1, val >> 8); + GBACartSlot.SRAMWrite(addr, val & 0xFF); + GBACartSlot.SRAMWrite(addr+1, val >> 8); return; } //if (addr) Log(LogLevel::Warn, "unknown arm9 write16 %08X %04X\n", addr, val); } -void ARM9Write32(u32 addr, u32 val) +void NDS::ARM9Write32(u32 addr, u32 val) { addr &= ~0x3; switch (addr & 0xFF000000) { case 0x02000000: - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&MainRAM[addr & MainRAMMask] = val; return ; case 0x03000000: if (SWRAM_ARM9.Mem) { - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u32*)&SWRAM_ARM9.Mem[addr & SWRAM_ARM9.Mask] = val; } return; case 0x04000000: - ARM9IOWrite32(addr, val); + NDS::ARM9IOWrite32(addr, val); return; case 0x05000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU->WritePalette(addr, val); + GPU.WritePalette(addr, val); return; case 0x06000000: - NDS::JIT->CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); + JIT.CheckAndInvalidate<0, ARMJIT_Memory::memregion_VRAM>(addr); switch (addr & 0x00E00000) { - case 0x00000000: GPU->WriteVRAM_ABG(addr, val); return; - case 0x00200000: GPU->WriteVRAM_BBG(addr, val); return; - case 0x00400000: GPU->WriteVRAM_AOBJ(addr, val); return; - case 0x00600000: GPU->WriteVRAM_BOBJ(addr, val); return; - default: GPU->WriteVRAM_LCDC(addr, val); return; + case 0x00000000: GPU.WriteVRAM_ABG(addr, val); return; + case 0x00200000: GPU.WriteVRAM_BBG(addr, val); return; + case 0x00400000: GPU.WriteVRAM_AOBJ(addr, val); return; + case 0x00600000: GPU.WriteVRAM_BOBJ(addr, val); return; + default: GPU.WriteVRAM_LCDC(addr, val); return; } case 0x07000000: if (!(PowerControl9 & ((addr & 0x400) ? (1<<9) : (1<<1)))) return; - GPU->WriteOAM(addr, val); + GPU.WriteOAM(addr, val); return; case 0x08000000: case 0x09000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->ROMWrite(addr, val & 0xFFFF); - GBACartSlot->ROMWrite(addr+2, val >> 16); + GBACartSlot.ROMWrite(addr, val & 0xFFFF); + GBACartSlot.ROMWrite(addr+2, val >> 16); return; case 0x0A000000: if (ExMemCnt[0] & (1<<7)) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->SRAMWrite(addr, val & 0xFF); - GBACartSlot->SRAMWrite(addr+1, (val >> 8) & 0xFF); - GBACartSlot->SRAMWrite(addr+2, (val >> 16) & 0xFF); - GBACartSlot->SRAMWrite(addr+3, val >> 24); + GBACartSlot.SRAMWrite(addr, val & 0xFF); + GBACartSlot.SRAMWrite(addr+1, (val >> 8) & 0xFF); + GBACartSlot.SRAMWrite(addr+2, (val >> 16) & 0xFF); + GBACartSlot.SRAMWrite(addr+3, val >> 24); return; } - //Log(LogLevel::Warn, "unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9->R[15]); + //Log(LogLevel::Warn, "unknown arm9 write32 %08X %08X | %08X\n", addr, val, ARM9.R[15]); } -bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) +bool NDS::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) { switch (addr & 0xFF000000) { @@ -2500,14 +2263,14 @@ bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) -u8 ARM7Read8(u32 addr) +u8 NDS::ARM7Read8(u32 addr) { if (addr < 0x00004000) { // TODO: check the boundary? is it 4000 or higher on regular DS? - if (ARM7->R[15] >= 0x00004000) + if (ARM7.R[15] >= 0x00004000) return 0xFF; - if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFF; return *(u8*)&ARM7BIOS[addr]; @@ -2533,50 +2296,48 @@ u8 ARM7Read8(u32 addr) return *(u8*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)]; case 0x04000000: - return ARM7IORead8(addr); + return NDS::ARM7IORead8(addr); case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - if (addr & 0x1) return Wifi->Read(addr-1) >> 8; - return Wifi->Read(addr) & 0xFF; + if (addr & 0x1) return Wifi.Read(addr-1) >> 8; + return Wifi.Read(addr) & 0xFF; } break; case 0x06000000: case 0x06800000: - return GPU->ReadVRAM_ARM7(addr); + return GPU.ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - if (addr & 0x1) return GBACartSlot->ROMRead(addr-1) >> 8; - return GBACartSlot->ROMRead(addr) & 0xFF; + if (addr & 0x1) return GBACartSlot.ROMRead(addr-1) >> 8; + return GBACartSlot.ROMRead(addr) & 0xFF; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->SRAMRead(addr); + return GBACartSlot.SRAMRead(addr); } - Log(LogLevel::Debug, "unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7->R[15], ARM7->R[0], ARM7->R[1]); + Log(LogLevel::Debug, "unknown arm7 read8 %08X %08X %08X/%08X\n", addr, ARM7.R[15], ARM7.R[0], ARM7.R[1]); return 0; } -u16 ARM7Read16(u32 addr) +u16 NDS::ARM7Read16(u32 addr) { addr &= ~0x1; if (addr < 0x00004000) { - if (ARM7->R[15] >= 0x00004000) + if (ARM7.R[15] >= 0x00004000) return 0xFFFF; - if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFFFF; return *(u16*)&ARM7BIOS[addr]; @@ -2602,49 +2363,47 @@ u16 ARM7Read16(u32 addr) return *(u16*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)]; case 0x04000000: - return ARM7IORead16(addr); + return NDS::ARM7IORead16(addr); case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - return Wifi->Read(addr); + return Wifi.Read(addr); } break; case 0x06000000: case 0x06800000: - return GPU->ReadVRAM_ARM7(addr); + return GPU.ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->ROMRead(addr); + return GBACartSlot.ROMRead(addr); case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x0000; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->SRAMRead(addr) | - (GBACartSlot->SRAMRead(addr+1) << 8); + return GBACartSlot.SRAMRead(addr) | + (GBACartSlot.SRAMRead(addr+1) << 8); } - Log(LogLevel::Debug, "unknown arm7 read16 %08X %08X\n", addr, ARM7->R[15]); + Log(LogLevel::Debug, "unknown arm7 read16 %08X %08X\n", addr, ARM7.R[15]); return 0; } -u32 ARM7Read32(u32 addr) +u32 NDS::ARM7Read32(u32 addr) { addr &= ~0x3; if (addr < 0x00004000) { - if (ARM7->R[15] >= 0x00004000) + if (ARM7.R[15] >= 0x00004000) return 0xFFFFFFFF; - if (addr < ARM7BIOSProt && ARM7->R[15] >= ARM7BIOSProt) + if (addr < ARM7BIOSProt && ARM7.R[15] >= ARM7BIOSProt) return 0xFFFFFFFF; return *(u32*)&ARM7BIOS[addr]; @@ -2670,80 +2429,78 @@ u32 ARM7Read32(u32 addr) return *(u32*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)]; case 0x04000000: - return ARM7IORead32(addr); + return NDS::ARM7IORead32(addr); case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return 0; - return Wifi->Read(addr) | (Wifi->Read(addr+2) << 16); + return Wifi.Read(addr) | (Wifi.Read(addr+2) << 16); } break; case 0x06000000: case 0x06800000: - return GPU->ReadVRAM_ARM7(addr); + return GPU.ReadVRAM_ARM7(addr); case 0x08000000: case 0x08800000: case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->ROMRead(addr) | - (GBACartSlot->ROMRead(addr+2) << 16); + return GBACartSlot.ROMRead(addr) | + (GBACartSlot.ROMRead(addr+2) << 16); case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return 0x00000000; // deselected CPU is 00h-filled - assert(GBACartSlot != nullptr); - return GBACartSlot->SRAMRead(addr) | - (GBACartSlot->SRAMRead(addr+1) << 8) | - (GBACartSlot->SRAMRead(addr+2) << 16) | - (GBACartSlot->SRAMRead(addr+3) << 24); + return GBACartSlot.SRAMRead(addr) | + (GBACartSlot.SRAMRead(addr+1) << 8) | + (GBACartSlot.SRAMRead(addr+2) << 16) | + (GBACartSlot.SRAMRead(addr+3) << 24); } - //Log(LogLevel::Warn, "unknown arm7 read32 %08X | %08X\n", addr, ARM7->R[15]); + //Log(LogLevel::Warn, "unknown arm7 read32 %08X | %08X\n", addr, ARM7.R[15]); return 0; } -void ARM7Write8(u32 addr, u8 val) +void NDS::ARM7Write8(u32 addr, u8 val) { switch (addr & 0xFF800000) { case 0x02000000: case 0x02800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u8*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u8*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u8*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u8*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; case 0x04000000: - ARM7IOWrite8(addr, val); + NDS::ARM7IOWrite8(addr, val); return; case 0x06000000: case 0x06800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); - GPU->WriteVRAM_ARM7(addr, val); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); + GPU.WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -2755,17 +2512,16 @@ void ARM7Write8(u32 addr, u8 val) case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->SRAMWrite(addr, val); + GBACartSlot.SRAMWrite(addr, val); return; } - //if (ARM7->R[15] > 0x00002F30) // ARM7 BIOS bug + //if (ARM7.R[15] > 0x00002F30) // ARM7 BIOS bug if (addr >= 0x01000000) - Log(LogLevel::Debug, "unknown arm7 write8 %08X %02X @ %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown arm7 write8 %08X %02X @ %08X\n", addr, val, ARM7.R[15]); } -void ARM7Write16(u32 addr, u16 val) +void NDS::ARM7Write16(u32 addr, u16 val) { addr &= ~0x1; @@ -2773,46 +2529,46 @@ void ARM7Write16(u32 addr, u16 val) { case 0x02000000: case 0x02800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u16*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u16*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u16*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u16*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; case 0x04000000: - ARM7IOWrite16(addr, val); + NDS::ARM7IOWrite16(addr, val); return; case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return; - Wifi->Write(addr, val); + Wifi.Write(addr, val); return; } break; case 0x06000000: case 0x06800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); - GPU->WriteVRAM_ARM7(addr, val); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); + GPU.WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -2820,23 +2576,22 @@ void ARM7Write16(u32 addr, u16 val) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->ROMWrite(addr, val); + GBACartSlot.ROMWrite(addr, val); return; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - GBACartSlot->SRAMWrite(addr, val & 0xFF); - GBACartSlot->SRAMWrite(addr+1, val >> 8); + GBACartSlot.SRAMWrite(addr, val & 0xFF); + GBACartSlot.SRAMWrite(addr+1, val >> 8); return; } if (addr >= 0x01000000) - Log(LogLevel::Debug, "unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown arm7 write16 %08X %04X @ %08X\n", addr, val, ARM7.R[15]); } -void ARM7Write32(u32 addr, u32 val) +void NDS::ARM7Write32(u32 addr, u32 val) { addr &= ~0x3; @@ -2844,47 +2599,47 @@ void ARM7Write32(u32 addr, u32 val) { case 0x02000000: case 0x02800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_MainRAM>(addr); *(u32*)&MainRAM[addr & MainRAMMask] = val; return; case 0x03000000: if (SWRAM_ARM7.Mem) { - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_SharedWRAM>(addr); *(u32*)&SWRAM_ARM7.Mem[addr & SWRAM_ARM7.Mask] = val; return; } else { - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u32*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; } case 0x03800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_WRAM7>(addr); *(u32*)&ARM7WRAM[addr & (ARM7WRAMSize - 1)] = val; return; case 0x04000000: - ARM7IOWrite32(addr, val); + NDS::ARM7IOWrite32(addr, val); return; case 0x04800000: if (addr < 0x04810000) { if (!(PowerControl7 & (1<<1))) return; - Wifi->Write(addr, val & 0xFFFF); - Wifi->Write(addr+2, val >> 16); + Wifi.Write(addr, val & 0xFFFF); + Wifi.Write(addr+2, val >> 16); return; } break; case 0x06000000: case 0x06800000: - NDS::JIT->CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); - GPU->WriteVRAM_ARM7(addr, val); + JIT.CheckAndInvalidate<1, ARMJIT_Memory::memregion_VWRAM>(addr); + GPU.WriteVRAM_ARM7(addr, val); return; case 0x08000000: @@ -2892,27 +2647,25 @@ void ARM7Write32(u32 addr, u32 val) case 0x09000000: case 0x09800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->ROMWrite(addr, val & 0xFFFF); - GBACartSlot->ROMWrite(addr+2, val >> 16); + GBACartSlot.ROMWrite(addr, val & 0xFFFF); + GBACartSlot.ROMWrite(addr+2, val >> 16); return; case 0x0A000000: case 0x0A800000: if (!(ExMemCnt[0] & (1<<7))) return; // deselected CPU, skip the write - assert(GBACartSlot != nullptr); - GBACartSlot->SRAMWrite(addr, val & 0xFF); - GBACartSlot->SRAMWrite(addr+1, (val >> 8) & 0xFF); - GBACartSlot->SRAMWrite(addr+2, (val >> 16) & 0xFF); - GBACartSlot->SRAMWrite(addr+3, val >> 24); + GBACartSlot.SRAMWrite(addr, val & 0xFF); + GBACartSlot.SRAMWrite(addr+1, (val >> 8) & 0xFF); + GBACartSlot.SRAMWrite(addr+2, (val >> 16) & 0xFF); + GBACartSlot.SRAMWrite(addr+3, val >> 24); return; } if (addr >= 0x01000000) - Log(LogLevel::Debug, "unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown arm7 write32 %08X %08X @ %08X\n", addr, val, ARM7.R[15]); } -bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) +bool NDS::ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) { switch (addr & 0xFF800000) { @@ -2945,7 +2698,7 @@ bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) // BIOS. ARM7 PC has to be within range. if (addr < 0x00004000 && !write) { - if (ARM7->R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7->R[15] < ARM7BIOSProt)) + if (ARM7.R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7.R[15] < ARM7BIOSProt)) { region->Mem = ARM7BIOS; region->Mask = 0x3FFF; @@ -2970,7 +2723,7 @@ bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) case (addr+2): return ((val) >> 16) & 0xFF; \ case (addr+3): return (val) >> 24; -u8 ARM9IORead8(u32 addr) +u8 NDS::ARM9IORead8(u32 addr) { switch (addr) { @@ -2981,54 +2734,54 @@ u8 ARM9IORead8(u32 addr) case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->ReadSPIData(); + return NDSCartSlot.ReadSPIData(); return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(0); + return NDSCartSlot.GetROMCommand(0); return 0; case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(1); + return NDSCartSlot.GetROMCommand(1); return 0; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(2); + return NDSCartSlot.GetROMCommand(2); return 0; case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(3); + return NDSCartSlot.GetROMCommand(3); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(4); + return NDSCartSlot.GetROMCommand(4); return 0; case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(5); + return NDSCartSlot.GetROMCommand(5); return 0; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(6); + return NDSCartSlot.GetROMCommand(6); return 0; case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(7); + return NDSCartSlot.GetROMCommand(7); return 0; case 0x04000208: return IME[0]; - case 0x04000240: return GPU->VRAMCNT[0]; - case 0x04000241: return GPU->VRAMCNT[1]; - case 0x04000242: return GPU->VRAMCNT[2]; - case 0x04000243: return GPU->VRAMCNT[3]; - case 0x04000244: return GPU->VRAMCNT[4]; - case 0x04000245: return GPU->VRAMCNT[5]; - case 0x04000246: return GPU->VRAMCNT[6]; + case 0x04000240: return GPU.VRAMCNT[0]; + case 0x04000241: return GPU.VRAMCNT[1]; + case 0x04000242: return GPU.VRAMCNT[2]; + case 0x04000243: return GPU.VRAMCNT[3]; + case 0x04000244: return GPU.VRAMCNT[4]; + case 0x04000245: return GPU.VRAMCNT[5]; + case 0x04000246: return GPU.VRAMCNT[6]; case 0x04000247: return WRAMCnt; - case 0x04000248: return GPU->VRAMCNT[7]; - case 0x04000249: return GPU->VRAMCNT[8]; + case 0x04000248: return GPU.VRAMCNT[7]; + case 0x04000249: return GPU.VRAMCNT[8]; CASE_READ8_16BIT(0x04000280, DivCnt) CASE_READ8_32BIT(0x04000290, DivNumerator[0]) @@ -3050,15 +2803,15 @@ u8 ARM9IORead8(u32 addr) if (addr >= 0x04000000 && addr < 0x04000060) { - return GPU->GPU2D_A.Read8(addr); + return GPU.GPU2D_A.Read8(addr); } if (addr >= 0x04001000 && addr < 0x04001060) { - return GPU->GPU2D_B.Read8(addr); + return GPU.GPU2D_B.Read8(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU->GPU3D.Read8(addr); + return GPU.GPU3D.Read8(addr); } // NO$GBA debug register "Emulation ID" if(addr >= 0x04FFFA00 && addr < 0x04FFFA10) @@ -3070,29 +2823,29 @@ u8 ARM9IORead8(u32 addr) } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM9 IO read8 %08X %08X\n", addr, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO read8 %08X %08X\n", addr, ARM9.R[15]); return 0; } -u16 ARM9IORead16(u32 addr) +u16 NDS::ARM9IORead16(u32 addr) { switch (addr) { - case 0x04000004: return GPU->DispStat[0]; - case 0x04000006: return GPU->VCount; + case 0x04000004: return GPU.DispStat[0]; + case 0x04000006: return GPU.VCount; - case 0x04000060: return GPU->GPU3D.Read16(addr); + case 0x04000060: return GPU.GPU3D.Read16(addr); case 0x04000064: - case 0x04000066: return GPU->GPU2D_A.Read16(addr); + case 0x04000066: return GPU.GPU2D_A.Read16(addr); - case 0x040000B8: return DMAs[0]->Cnt & 0xFFFF; - case 0x040000BA: return DMAs[0]->Cnt >> 16; - case 0x040000C4: return DMAs[1]->Cnt & 0xFFFF; - case 0x040000C6: return DMAs[1]->Cnt >> 16; - case 0x040000D0: return DMAs[2]->Cnt & 0xFFFF; - case 0x040000D2: return DMAs[2]->Cnt >> 16; - case 0x040000DC: return DMAs[3]->Cnt & 0xFFFF; - case 0x040000DE: return DMAs[3]->Cnt >> 16; + case 0x040000B8: return DMAs[0].Cnt & 0xFFFF; + case 0x040000BA: return DMAs[0].Cnt >> 16; + case 0x040000C4: return DMAs[1].Cnt & 0xFFFF; + case 0x040000C6: return DMAs[1].Cnt >> 16; + case 0x040000D0: return DMAs[2].Cnt & 0xFFFF; + case 0x040000D2: return DMAs[2].Cnt >> 16; + case 0x040000DC: return DMAs[3].Cnt & 0xFFFF; + case 0x040000DE: return DMAs[3].Cnt >> 16; case 0x040000E0: return ((u16*)DMA9Fill)[0]; case 0x040000E2: return ((u16*)DMA9Fill)[1]; @@ -3128,32 +2881,32 @@ u16 ARM9IORead16(u32 addr) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetSPICnt(); + return NDSCartSlot.GetSPICnt(); return 0; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->ReadSPIData(); + return NDSCartSlot.ReadSPIData(); return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(0) | - (NDSCartSlot->GetROMCommand(1) << 8); + return NDSCartSlot.GetROMCommand(0) | + (NDSCartSlot.GetROMCommand(1) << 8); return 0; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(2) | - (NDSCartSlot->GetROMCommand(3) << 8); + return NDSCartSlot.GetROMCommand(2) | + (NDSCartSlot.GetROMCommand(3) << 8); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(4) | - (NDSCartSlot->GetROMCommand(5) << 8); + return NDSCartSlot.GetROMCommand(4) | + (NDSCartSlot.GetROMCommand(5) << 8); return 0; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(6) | - (NDSCartSlot->GetROMCommand(7) << 8); + return NDSCartSlot.GetROMCommand(6) | + (NDSCartSlot.GetROMCommand(7) << 8); return 0; case 0x04000204: return ExMemCnt[0]; @@ -3161,11 +2914,11 @@ u16 ARM9IORead16(u32 addr) case 0x04000210: return IE[0] & 0xFFFF; case 0x04000212: return IE[0] >> 16; - case 0x04000240: return GPU->VRAMCNT[0] | (GPU->VRAMCNT[1] << 8); - case 0x04000242: return GPU->VRAMCNT[2] | (GPU->VRAMCNT[3] << 8); - case 0x04000244: return GPU->VRAMCNT[4] | (GPU->VRAMCNT[5] << 8); - case 0x04000246: return GPU->VRAMCNT[6] | (WRAMCnt << 8); - case 0x04000248: return GPU->VRAMCNT[7] | (GPU->VRAMCNT[8] << 8); + case 0x04000240: return GPU.VRAMCNT[0] | (GPU.VRAMCNT[1] << 8); + case 0x04000242: return GPU.VRAMCNT[2] | (GPU.VRAMCNT[3] << 8); + case 0x04000244: return GPU.VRAMCNT[4] | (GPU.VRAMCNT[5] << 8); + case 0x04000246: return GPU.VRAMCNT[6] | (WRAMCnt << 8); + case 0x04000248: return GPU.VRAMCNT[7] | (GPU.VRAMCNT[8] << 8); case 0x04000280: return DivCnt; case 0x04000290: return DivNumerator[0] & 0xFFFF; @@ -3205,43 +2958,43 @@ u16 ARM9IORead16(u32 addr) if ((addr >= 0x04000000 && addr < 0x04000060) || (addr == 0x0400006C)) { - return GPU->GPU2D_A.Read16(addr); + return GPU.GPU2D_A.Read16(addr); } if ((addr >= 0x04001000 && addr < 0x04001060) || (addr == 0x0400106C)) { - return GPU->GPU2D_B.Read16(addr); + return GPU.GPU2D_B.Read16(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU->GPU3D.Read16(addr); + return GPU.GPU3D.Read16(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM9 IO read16 %08X %08X\n", addr, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO read16 %08X %08X\n", addr, ARM9.R[15]); return 0; } -u32 ARM9IORead32(u32 addr) +u32 NDS::ARM9IORead32(u32 addr) { switch (addr) { - case 0x04000004: return GPU->DispStat[0] | (GPU->VCount << 16); + case 0x04000004: return GPU.DispStat[0] | (GPU.VCount << 16); - case 0x04000060: return GPU->GPU3D.Read32(addr); - case 0x04000064: return GPU->GPU2D_A.Read32(addr); + case 0x04000060: return GPU.GPU3D.Read32(addr); + case 0x04000064: return GPU.GPU2D_A.Read32(addr); - case 0x040000B0: return DMAs[0]->SrcAddr; - case 0x040000B4: return DMAs[0]->DstAddr; - case 0x040000B8: return DMAs[0]->Cnt; - case 0x040000BC: return DMAs[1]->SrcAddr; - case 0x040000C0: return DMAs[1]->DstAddr; - case 0x040000C4: return DMAs[1]->Cnt; - case 0x040000C8: return DMAs[2]->SrcAddr; - case 0x040000CC: return DMAs[2]->DstAddr; - case 0x040000D0: return DMAs[2]->Cnt; - case 0x040000D4: return DMAs[3]->SrcAddr; - case 0x040000D8: return DMAs[3]->DstAddr; - case 0x040000DC: return DMAs[3]->Cnt; + case 0x040000B0: return DMAs[0].SrcAddr; + case 0x040000B4: return DMAs[0].DstAddr; + case 0x040000B8: return DMAs[0].Cnt; + case 0x040000BC: return DMAs[1].SrcAddr; + case 0x040000C0: return DMAs[1].DstAddr; + case 0x040000C4: return DMAs[1].Cnt; + case 0x040000C8: return DMAs[2].SrcAddr; + case 0x040000CC: return DMAs[2].DstAddr; + case 0x040000D0: return DMAs[2].Cnt; + case 0x040000D4: return DMAs[3].SrcAddr; + case 0x040000D8: return DMAs[3].DstAddr; + case 0x040000DC: return DMAs[3].Cnt; case 0x040000E0: return DMA9Fill[0]; case 0x040000E4: return DMA9Fill[1]; @@ -3258,39 +3011,39 @@ u32 ARM9IORead32(u32 addr) case 0x04000130: LagFrameFlag = false; return (KeyInput & 0xFFFF) | (KeyCnt[0] << 16); case 0x04000180: return IPCSync9; - case 0x04000184: return ARM9IORead16(addr); + case 0x04000184: return NDS::ARM9IORead16(addr); case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetSPICnt() | (NDSCartSlot->ReadSPIData() << 16); + return NDSCartSlot.GetSPICnt() | (NDSCartSlot.ReadSPIData() << 16); return 0; case 0x040001A4: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCnt(); + return NDSCartSlot.GetROMCnt(); return 0; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(0) | - (NDSCartSlot->GetROMCommand(1) << 8) | - (NDSCartSlot->GetROMCommand(2) << 16) | - (NDSCartSlot->GetROMCommand(3) << 24); + return NDSCartSlot.GetROMCommand(0) | + (NDSCartSlot.GetROMCommand(1) << 8) | + (NDSCartSlot.GetROMCommand(2) << 16) | + (NDSCartSlot.GetROMCommand(3) << 24); return 0; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) - return NDSCartSlot->GetROMCommand(4) | - (NDSCartSlot->GetROMCommand(5) << 8) | - (NDSCartSlot->GetROMCommand(6) << 16) | - (NDSCartSlot->GetROMCommand(7) << 24); + return NDSCartSlot.GetROMCommand(4) | + (NDSCartSlot.GetROMCommand(5) << 8) | + (NDSCartSlot.GetROMCommand(6) << 16) | + (NDSCartSlot.GetROMCommand(7) << 24); return 0; case 0x04000208: return IME[0]; case 0x04000210: return IE[0]; case 0x04000214: return IF[0]; - case 0x04000240: return GPU->VRAMCNT[0] | (GPU->VRAMCNT[1] << 8) | (GPU->VRAMCNT[2] << 16) | (GPU->VRAMCNT[3] << 24); - case 0x04000244: return GPU->VRAMCNT[4] | (GPU->VRAMCNT[5] << 8) | (GPU->VRAMCNT[6] << 16) | (WRAMCnt << 24); - case 0x04000248: return GPU->VRAMCNT[7] | (GPU->VRAMCNT[8] << 8); + case 0x04000240: return GPU.VRAMCNT[0] | (GPU.VRAMCNT[1] << 8) | (GPU.VRAMCNT[2] << 16) | (GPU.VRAMCNT[3] << 24); + case 0x04000244: return GPU.VRAMCNT[4] | (GPU.VRAMCNT[5] << 8) | (GPU.VRAMCNT[6] << 16) | (WRAMCnt << 24); + case 0x04000248: return GPU.VRAMCNT[7] | (GPU.VRAMCNT[8] << 8); case 0x04000280: return DivCnt; case 0x04000290: return DivNumerator[0]; @@ -3332,7 +3085,7 @@ u32 ARM9IORead32(u32 addr) return IPCFIFO7.Peek(); case 0x04100010: - if (!(ExMemCnt[0] & (1<<11))) return NDSCartSlot->ReadROMData(); + if (!(ExMemCnt[0] & (1<<11))) return NDSCartSlot.ReadROMData(); return 0; case 0x04004000: @@ -3349,30 +3102,30 @@ u32 ARM9IORead32(u32 addr) if ((addr >= 0x04000000 && addr < 0x04000060) || (addr == 0x0400006C)) { - return GPU->GPU2D_A.Read32(addr); + return GPU.GPU2D_A.Read32(addr); } if ((addr >= 0x04001000 && addr < 0x04001060) || (addr == 0x0400106C)) { - return GPU->GPU2D_B.Read32(addr); + return GPU.GPU2D_B.Read32(addr); } if (addr >= 0x04000320 && addr < 0x040006A4) { - return GPU->GPU3D.Read32(addr); + return GPU.GPU3D.Read32(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM9 IO read32 %08X %08X\n", addr, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO read32 %08X %08X\n", addr, ARM9.R[15]); return 0; } -void ARM9IOWrite8(u32 addr, u8 val) +void NDS::ARM9IOWrite8(u32 addr, u8 val) { switch (addr) { case 0x0400006C: - case 0x0400006D: GPU->GPU2D_A.Write8(addr, val); return; + case 0x0400006D: GPU.GPU2D_A.Write8(addr, val); return; case 0x0400106C: - case 0x0400106D: GPU->GPU2D_B.Write8(addr, val); return; + case 0x0400106D: GPU.GPU2D_B.Write8(addr, val); return; case 0x04000132: KeyCnt[0] = (KeyCnt[0] & 0xFF00) | val; @@ -3382,43 +3135,43 @@ void ARM9IOWrite8(u32 addr, u8 val) return; case 0x04000188: - ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); + NDS::ARM9IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); return; case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - NDSCartSlot->WriteSPICnt((NDSCartSlot->GetSPICnt() & 0xFF00) | val); + NDSCartSlot.WriteSPICnt((NDSCartSlot.GetSPICnt() & 0xFF00) | val); return; case 0x040001A1: if (!(ExMemCnt[0] & (1<<11))) - NDSCartSlot->WriteSPICnt((NDSCartSlot->GetSPICnt() & 0x00FF) | (val << 8)); + NDSCartSlot.WriteSPICnt((NDSCartSlot.GetSPICnt() & 0x00FF) | (val << 8)); return; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - NDSCartSlot->WriteSPIData(val); + NDSCartSlot.WriteSPIData(val); return; - case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(0, val); return; - case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(1, val); return; - case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(2, val); return; - case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(3, val); return; - case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(4, val); return; - case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(5, val); return; - case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(6, val); return; - case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->SetROMCommand(7, val); return; + case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(0, val); return; + case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(1, val); return; + case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(2, val); return; + case 0x040001AB: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(3, val); return; + case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(4, val); return; + case 0x040001AD: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(5, val); return; + case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(6, val); return; + case 0x040001AF: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(7, val); return; case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return; - case 0x04000240: GPU->MapVRAM_AB(0, val); return; - case 0x04000241: GPU->MapVRAM_AB(1, val); return; - case 0x04000242: GPU->MapVRAM_CD(2, val); return; - case 0x04000243: GPU->MapVRAM_CD(3, val); return; - case 0x04000244: GPU->MapVRAM_E(4, val); return; - case 0x04000245: GPU->MapVRAM_FG(5, val); return; - case 0x04000246: GPU->MapVRAM_FG(6, val); return; + case 0x04000240: GPU.MapVRAM_AB(0, val); return; + case 0x04000241: GPU.MapVRAM_AB(1, val); return; + case 0x04000242: GPU.MapVRAM_CD(2, val); return; + case 0x04000243: GPU.MapVRAM_CD(3, val); return; + case 0x04000244: GPU.MapVRAM_E(4, val); return; + case 0x04000245: GPU.MapVRAM_FG(5, val); return; + case 0x04000246: GPU.MapVRAM_FG(6, val); return; case 0x04000247: MapSharedWRAM(val); return; - case 0x04000248: GPU->MapVRAM_H(7, val); return; - case 0x04000249: GPU->MapVRAM_I(8, val); return; + case 0x04000248: GPU.MapVRAM_H(7, val); return; + case 0x04000249: GPU.MapVRAM_I(8, val); return; case 0x04000300: if (PostFlag9 & 0x01) val |= 0x01; @@ -3428,46 +3181,46 @@ void ARM9IOWrite8(u32 addr, u8 val) if (addr >= 0x04000000 && addr < 0x04000060) { - GPU->GPU2D_A.Write8(addr, val); + GPU.GPU2D_A.Write8(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU->GPU2D_B.Write8(addr, val); + GPU.GPU2D_B.Write8(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU->GPU3D.Write8(addr, val); + GPU.GPU3D.Write8(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM9 IO write8 %08X %02X %08X\n", addr, val, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO write8 %08X %02X %08X\n", addr, val, ARM9.R[15]); } -void ARM9IOWrite16(u32 addr, u16 val) +void NDS::ARM9IOWrite16(u32 addr, u16 val) { switch (addr) { - case 0x04000004: GPU->SetDispStat(0, val); return; - case 0x04000006: GPU->SetVCount(val); return; + case 0x04000004: GPU.SetDispStat(0, val); return; + case 0x04000006: GPU.SetVCount(val); return; - case 0x04000060: GPU->GPU3D.Write16(addr, val); return; + case 0x04000060: GPU.GPU3D.Write16(addr, val); return; case 0x04000068: - case 0x0400006A: GPU->GPU2D_A.Write16(addr, val); return; + case 0x0400006A: GPU.GPU2D_A.Write16(addr, val); return; - case 0x0400006C: GPU->GPU2D_A.Write16(addr, val); return; - case 0x0400106C: GPU->GPU2D_B.Write16(addr, val); return; + case 0x0400006C: GPU.GPU2D_A.Write16(addr, val); return; + case 0x0400106C: GPU.GPU2D_B.Write16(addr, val); return; - case 0x040000B8: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0xFFFF0000) | val); return; - case 0x040000BA: DMAs[0]->WriteCnt((DMAs[0]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000C4: DMAs[1]->WriteCnt((DMAs[1]->Cnt & 0xFFFF0000) | val); return; - case 0x040000C6: DMAs[1]->WriteCnt((DMAs[1]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000D0: DMAs[2]->WriteCnt((DMAs[2]->Cnt & 0xFFFF0000) | val); return; - case 0x040000D2: DMAs[2]->WriteCnt((DMAs[2]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000DC: DMAs[3]->WriteCnt((DMAs[3]->Cnt & 0xFFFF0000) | val); return; - case 0x040000DE: DMAs[3]->WriteCnt((DMAs[3]->Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000B8: DMAs[0].WriteCnt((DMAs[0].Cnt & 0xFFFF0000) | val); return; + case 0x040000BA: DMAs[0].WriteCnt((DMAs[0].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000C4: DMAs[1].WriteCnt((DMAs[1].Cnt & 0xFFFF0000) | val); return; + case 0x040000C6: DMAs[1].WriteCnt((DMAs[1].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000D0: DMAs[2].WriteCnt((DMAs[2].Cnt & 0xFFFF0000) | val); return; + case 0x040000D2: DMAs[2].WriteCnt((DMAs[2].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000DC: DMAs[3].WriteCnt((DMAs[3].Cnt & 0xFFFF0000) | val); return; + case 0x040000DE: DMAs[3].WriteCnt((DMAs[3].Cnt & 0x0000FFFF) | (val << 16)); return; case 0x040000E0: DMA9Fill[0] = (DMA9Fill[0] & 0xFFFF0000) | val; return; case 0x040000E2: DMA9Fill[0] = (DMA9Fill[0] & 0x0000FFFF) | (val << 16); return; @@ -3515,44 +3268,44 @@ void ARM9IOWrite16(u32 addr, u16 val) return; case 0x04000188: - ARM9IOWrite32(addr, val | (val << 16)); + NDS::ARM9IOWrite32(addr, val | (val << 16)); return; case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) - NDSCartSlot->WriteSPICnt(val); + NDSCartSlot.WriteSPICnt(val); return; case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) - NDSCartSlot->WriteSPIData(val & 0xFF); + NDSCartSlot.WriteSPIData(val & 0xFF); return; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) { - NDSCartSlot->SetROMCommand(0, val & 0xFF); - NDSCartSlot->SetROMCommand(1, val >> 8); + NDSCartSlot.SetROMCommand(0, val & 0xFF); + NDSCartSlot.SetROMCommand(1, val >> 8); } return; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) { - NDSCartSlot->SetROMCommand(2, val & 0xFF); - NDSCartSlot->SetROMCommand(3, val >> 8); + NDSCartSlot.SetROMCommand(2, val & 0xFF); + NDSCartSlot.SetROMCommand(3, val >> 8); } return; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) { - NDSCartSlot->SetROMCommand(4, val & 0xFF); - NDSCartSlot->SetROMCommand(5, val >> 8); + NDSCartSlot.SetROMCommand(4, val & 0xFF); + NDSCartSlot.SetROMCommand(5, val >> 8); } return; case 0x040001AE: if (!(ExMemCnt[0] & (1<<11))) { - NDSCartSlot->SetROMCommand(6, val & 0xFF); - NDSCartSlot->SetROMCommand(7, val >> 8); + NDSCartSlot.SetROMCommand(6, val & 0xFF); + NDSCartSlot.SetROMCommand(7, val >> 8); } return; @@ -3575,24 +3328,24 @@ void ARM9IOWrite16(u32 addr, u16 val) // TODO: what happens when writing to IF this way?? case 0x04000240: - GPU->MapVRAM_AB(0, val & 0xFF); - GPU->MapVRAM_AB(1, val >> 8); + GPU.MapVRAM_AB(0, val & 0xFF); + GPU.MapVRAM_AB(1, val >> 8); return; case 0x04000242: - GPU->MapVRAM_CD(2, val & 0xFF); - GPU->MapVRAM_CD(3, val >> 8); + GPU.MapVRAM_CD(2, val & 0xFF); + GPU.MapVRAM_CD(3, val >> 8); return; case 0x04000244: - GPU->MapVRAM_E(4, val & 0xFF); - GPU->MapVRAM_FG(5, val >> 8); + GPU.MapVRAM_E(4, val & 0xFF); + GPU.MapVRAM_FG(5, val >> 8); return; case 0x04000246: - GPU->MapVRAM_FG(6, val & 0xFF); + GPU.MapVRAM_FG(6, val & 0xFF); MapSharedWRAM(val >> 8); return; case 0x04000248: - GPU->MapVRAM_H(7, val & 0xFF); - GPU->MapVRAM_I(8, val >> 8); + GPU.MapVRAM_H(7, val & 0xFF); + GPU.MapVRAM_I(8, val >> 8); return; case 0x04000280: DivCnt = val; StartDiv(); return; @@ -3606,57 +3359,57 @@ void ARM9IOWrite16(u32 addr, u16 val) case 0x04000304: PowerControl9 = val & 0x820F; - GPU->SetPowerCnt(PowerControl9); + GPU.SetPowerCnt(PowerControl9); return; } if (addr >= 0x04000000 && addr < 0x04000060) { - GPU->GPU2D_A.Write16(addr, val); + GPU.GPU2D_A.Write16(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU->GPU2D_B.Write16(addr, val); + GPU.GPU2D_B.Write16(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU->GPU3D.Write16(addr, val); + GPU.GPU3D.Write16(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM9 IO write16 %08X %04X %08X\n", addr, val, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO write16 %08X %04X %08X\n", addr, val, ARM9.R[15]); } -void ARM9IOWrite32(u32 addr, u32 val) +void NDS::ARM9IOWrite32(u32 addr, u32 val) { switch (addr) { case 0x04000004: - GPU->SetDispStat(0, val & 0xFFFF); - GPU->SetVCount(val >> 16); + GPU.SetDispStat(0, val & 0xFFFF); + GPU.SetVCount(val >> 16); return; - case 0x04000060: GPU->GPU3D.Write32(addr, val); return; + case 0x04000060: GPU.GPU3D.Write32(addr, val); return; case 0x04000064: - case 0x04000068: GPU->GPU2D_A.Write32(addr, val); return; + case 0x04000068: GPU.GPU2D_A.Write32(addr, val); return; - case 0x0400006C: GPU->GPU2D_A.Write16(addr, val&0xFFFF); return; - case 0x0400106C: GPU->GPU2D_B.Write16(addr, val&0xFFFF); return; + case 0x0400006C: GPU.GPU2D_A.Write16(addr, val&0xFFFF); return; + case 0x0400106C: GPU.GPU2D_B.Write16(addr, val&0xFFFF); return; - case 0x040000B0: DMAs[0]->SrcAddr = val; return; - case 0x040000B4: DMAs[0]->DstAddr = val; return; - case 0x040000B8: DMAs[0]->WriteCnt(val); return; - case 0x040000BC: DMAs[1]->SrcAddr = val; return; - case 0x040000C0: DMAs[1]->DstAddr = val; return; - case 0x040000C4: DMAs[1]->WriteCnt(val); return; - case 0x040000C8: DMAs[2]->SrcAddr = val; return; - case 0x040000CC: DMAs[2]->DstAddr = val; return; - case 0x040000D0: DMAs[2]->WriteCnt(val); return; - case 0x040000D4: DMAs[3]->SrcAddr = val; return; - case 0x040000D8: DMAs[3]->DstAddr = val; return; - case 0x040000DC: DMAs[3]->WriteCnt(val); return; + case 0x040000B0: DMAs[0].SrcAddr = val; return; + case 0x040000B4: DMAs[0].DstAddr = val; return; + case 0x040000B8: DMAs[0].WriteCnt(val); return; + case 0x040000BC: DMAs[1].SrcAddr = val; return; + case 0x040000C0: DMAs[1].DstAddr = val; return; + case 0x040000C4: DMAs[1].WriteCnt(val); return; + case 0x040000C8: DMAs[2].SrcAddr = val; return; + case 0x040000CC: DMAs[2].DstAddr = val; return; + case 0x040000D0: DMAs[2].WriteCnt(val); return; + case 0x040000D4: DMAs[3].SrcAddr = val; return; + case 0x040000D8: DMAs[3].DstAddr = val; return; + case 0x040000DC: DMAs[3].WriteCnt(val); return; case 0x040000E0: DMA9Fill[0] = val; return; case 0x040000E4: DMA9Fill[1] = val; return; @@ -3686,7 +3439,7 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x04000180: case 0x04000184: - ARM9IOWrite16(addr, val); + NDS::ARM9IOWrite16(addr, val); return; case 0x04000188: if (IPCFIFOCnt9 & 0x8000) @@ -3706,31 +3459,31 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x040001A0: if (!(ExMemCnt[0] & (1<<11))) { - NDSCartSlot->WriteSPICnt(val & 0xFFFF); - NDSCartSlot->WriteSPIData((val >> 16) & 0xFF); + NDSCartSlot.WriteSPICnt(val & 0xFFFF); + NDSCartSlot.WriteSPIData((val >> 16) & 0xFF); } return; case 0x040001A4: if (!(ExMemCnt[0] & (1<<11))) - NDSCartSlot->WriteROMCnt(val); + NDSCartSlot.WriteROMCnt(val); return; case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) { - NDSCartSlot->SetROMCommand(0, val & 0xFF); - NDSCartSlot->SetROMCommand(1, (val >> 8) & 0xFF); - NDSCartSlot->SetROMCommand(2, (val >> 16) & 0xFF); - NDSCartSlot->SetROMCommand(3, val >> 24); + NDSCartSlot.SetROMCommand(0, val & 0xFF); + NDSCartSlot.SetROMCommand(1, (val >> 8) & 0xFF); + NDSCartSlot.SetROMCommand(2, (val >> 16) & 0xFF); + NDSCartSlot.SetROMCommand(3, val >> 24); } return; case 0x040001AC: if (!(ExMemCnt[0] & (1<<11))) { - NDSCartSlot->SetROMCommand(4, val & 0xFF); - NDSCartSlot->SetROMCommand(5, (val >> 8) & 0xFF); - NDSCartSlot->SetROMCommand(6, (val >> 16) & 0xFF); - NDSCartSlot->SetROMCommand(7, val >> 24); + NDSCartSlot.SetROMCommand(4, val & 0xFF); + NDSCartSlot.SetROMCommand(5, (val >> 8) & 0xFF); + NDSCartSlot.SetROMCommand(6, (val >> 16) & 0xFF); + NDSCartSlot.SetROMCommand(7, val >> 24); } return; @@ -3739,23 +3492,23 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x04000208: IME[0] = val & 0x1; UpdateIRQ(0); return; case 0x04000210: IE[0] = val; UpdateIRQ(0); return; - case 0x04000214: IF[0] &= ~val; GPU->GPU3D.CheckFIFOIRQ(); UpdateIRQ(0); return; + case 0x04000214: IF[0] &= ~val; GPU.GPU3D.CheckFIFOIRQ(); UpdateIRQ(0); return; case 0x04000240: - GPU->MapVRAM_AB(0, val & 0xFF); - GPU->MapVRAM_AB(1, (val >> 8) & 0xFF); - GPU->MapVRAM_CD(2, (val >> 16) & 0xFF); - GPU->MapVRAM_CD(3, val >> 24); + GPU.MapVRAM_AB(0, val & 0xFF); + GPU.MapVRAM_AB(1, (val >> 8) & 0xFF); + GPU.MapVRAM_CD(2, (val >> 16) & 0xFF); + GPU.MapVRAM_CD(3, val >> 24); return; case 0x04000244: - GPU->MapVRAM_E(4, val & 0xFF); - GPU->MapVRAM_FG(5, (val >> 8) & 0xFF); - GPU->MapVRAM_FG(6, (val >> 16) & 0xFF); + GPU.MapVRAM_E(4, val & 0xFF); + GPU.MapVRAM_FG(5, (val >> 8) & 0xFF); + GPU.MapVRAM_FG(6, (val >> 16) & 0xFF); MapSharedWRAM(val >> 24); return; case 0x04000248: - GPU->MapVRAM_H(7, val & 0xFF); - GPU->MapVRAM_I(8, (val >> 8) & 0xFF); + GPU.MapVRAM_H(7, val & 0xFF); + GPU.MapVRAM_I(8, (val >> 8) & 0xFF); return; case 0x04000280: DivCnt = val; StartDiv(); return; @@ -3772,11 +3525,11 @@ void ARM9IOWrite32(u32 addr, u32 val) case 0x04000304: PowerControl9 = val & 0x820F; - GPU->SetPowerCnt(PowerControl9); + GPU.SetPowerCnt(PowerControl9); return; case 0x04100010: - if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot->WriteROMData(val); + if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.WriteROMData(val); return; // NO$GBA debug register "String Out (raw)" @@ -3810,25 +3563,25 @@ void ARM9IOWrite32(u32 addr, u32 val) if (addr >= 0x04000000 && addr < 0x04000060) { - GPU->GPU2D_A.Write32(addr, val); + GPU.GPU2D_A.Write32(addr, val); return; } if (addr >= 0x04001000 && addr < 0x04001060) { - GPU->GPU2D_B.Write32(addr, val); + GPU.GPU2D_B.Write32(addr, val); return; } if (addr >= 0x04000320 && addr < 0x040006A4) { - GPU->GPU3D.Write32(addr, val); + GPU.GPU3D.Write32(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM9 IO write32 %08X %08X %08X\n", addr, val, ARM9->R[15]); + Log(LogLevel::Debug, "unknown ARM9 IO write32 %08X %08X %08X\n", addr, val, ARM9.R[15]); } -u8 ARM7IORead8(u32 addr) +u8 NDS::ARM7IORead8(u32 addr) { switch (addr) { @@ -3841,51 +3594,51 @@ u8 ARM7IORead8(u32 addr) case 0x04000136: return (KeyInput >> 16) & 0xFF; case 0x04000137: return KeyInput >> 24; - case 0x04000138: return RTC->Read() & 0xFF; + case 0x04000138: return RTC.Read() & 0xFF; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->ReadSPIData(); + return NDSCartSlot.ReadSPIData(); return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(0); + return NDSCartSlot.GetROMCommand(0); return 0; case 0x040001A9: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(1); + return NDSCartSlot.GetROMCommand(1); return 0; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(2); + return NDSCartSlot.GetROMCommand(2); return 0; case 0x040001AB: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(3); + return NDSCartSlot.GetROMCommand(3); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(4); + return NDSCartSlot.GetROMCommand(4); return 0; case 0x040001AD: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(5); + return NDSCartSlot.GetROMCommand(5); return 0; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(6); + return NDSCartSlot.GetROMCommand(6); return 0; case 0x040001AF: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(7); + return NDSCartSlot.GetROMCommand(7); return 0; - case 0x040001C2: return SPI->ReadData(); + case 0x040001C2: return SPI.ReadData(); case 0x04000208: return IME[1]; - case 0x04000240: return GPU->VRAMSTAT; + case 0x04000240: return GPU.VRAMSTAT; case 0x04000241: return WRAMCnt; case 0x04000300: return PostFlag7; @@ -3894,29 +3647,29 @@ u8 ARM7IORead8(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU->Read8(addr); + return SPU.Read8(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM7 IO read8 %08X %08X\n", addr, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO read8 %08X %08X\n", addr, ARM7.R[15]); return 0; } -u16 ARM7IORead16(u32 addr) +u16 NDS::ARM7IORead16(u32 addr) { switch (addr) { - case 0x04000004: return GPU->DispStat[1]; - case 0x04000006: return GPU->VCount; + case 0x04000004: return GPU.DispStat[1]; + case 0x04000006: return GPU.VCount; - case 0x040000B8: return DMAs[4]->Cnt & 0xFFFF; - case 0x040000BA: return DMAs[4]->Cnt >> 16; - case 0x040000C4: return DMAs[5]->Cnt & 0xFFFF; - case 0x040000C6: return DMAs[5]->Cnt >> 16; - case 0x040000D0: return DMAs[6]->Cnt & 0xFFFF; - case 0x040000D2: return DMAs[6]->Cnt >> 16; - case 0x040000DC: return DMAs[7]->Cnt & 0xFFFF; - case 0x040000DE: return DMAs[7]->Cnt >> 16; + case 0x040000B8: return DMAs[4].Cnt & 0xFFFF; + case 0x040000BA: return DMAs[4].Cnt >> 16; + case 0x040000C4: return DMAs[5].Cnt & 0xFFFF; + case 0x040000C6: return DMAs[5].Cnt >> 16; + case 0x040000D0: return DMAs[6].Cnt & 0xFFFF; + case 0x040000D2: return DMAs[6].Cnt >> 16; + case 0x040000DC: return DMAs[7].Cnt & 0xFFFF; + case 0x040000DE: return DMAs[7].Cnt >> 16; case 0x04000100: return TimerGetCounter(4); case 0x04000102: return Timers[4].Cnt; @@ -3932,7 +3685,7 @@ u16 ARM7IORead16(u32 addr) case 0x04000134: return RCnt; case 0x04000136: return KeyInput >> 16; - case 0x04000138: return RTC->Read(); + case 0x04000138: return RTC.Read(); case 0x04000180: return IPCSync7; case 0x04000184: @@ -3945,32 +3698,32 @@ u16 ARM7IORead16(u32 addr) return val; } - case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot->GetSPICnt(); return 0; - case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot->ReadSPIData(); return 0; + case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.GetSPICnt(); return 0; + case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.ReadSPIData(); return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(0) | - (NDSCartSlot->GetROMCommand(1) << 8); + return NDSCartSlot.GetROMCommand(0) | + (NDSCartSlot.GetROMCommand(1) << 8); return 0; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(2) | - (NDSCartSlot->GetROMCommand(3) << 8); + return NDSCartSlot.GetROMCommand(2) | + (NDSCartSlot.GetROMCommand(3) << 8); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(4) | - (NDSCartSlot->GetROMCommand(5) << 8); + return NDSCartSlot.GetROMCommand(4) | + (NDSCartSlot.GetROMCommand(5) << 8); return 0; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(6) | - (NDSCartSlot->GetROMCommand(7) << 8); + return NDSCartSlot.GetROMCommand(6) | + (NDSCartSlot.GetROMCommand(7) << 8); return 0; - case 0x040001C0: return SPI->ReadCnt(); - case 0x040001C2: return SPI->ReadData(); + case 0x040001C0: return SPI.ReadCnt(); + case 0x040001C2: return SPI.ReadData(); case 0x04000204: return ExMemCnt[1]; case 0x04000206: @@ -3988,32 +3741,32 @@ u16 ARM7IORead16(u32 addr) if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU->Read16(addr); + return SPU.Read16(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM7 IO read16 %08X %08X\n", addr, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO read16 %08X %08X\n", addr, ARM7.R[15]); return 0; } -u32 ARM7IORead32(u32 addr) +u32 NDS::ARM7IORead32(u32 addr) { switch (addr) { - case 0x04000004: return GPU->DispStat[1] | (GPU->VCount << 16); + case 0x04000004: return GPU.DispStat[1] | (GPU.VCount << 16); - case 0x040000B0: return DMAs[4]->SrcAddr; - case 0x040000B4: return DMAs[4]->DstAddr; - case 0x040000B8: return DMAs[4]->Cnt; - case 0x040000BC: return DMAs[5]->SrcAddr; - case 0x040000C0: return DMAs[5]->DstAddr; - case 0x040000C4: return DMAs[5]->Cnt; - case 0x040000C8: return DMAs[6]->SrcAddr; - case 0x040000CC: return DMAs[6]->DstAddr; - case 0x040000D0: return DMAs[6]->Cnt; - case 0x040000D4: return DMAs[7]->SrcAddr; - case 0x040000D8: return DMAs[7]->DstAddr; - case 0x040000DC: return DMAs[7]->Cnt; + case 0x040000B0: return DMAs[4].SrcAddr; + case 0x040000B4: return DMAs[4].DstAddr; + case 0x040000B8: return DMAs[4].Cnt; + case 0x040000BC: return DMAs[5].SrcAddr; + case 0x040000C0: return DMAs[5].DstAddr; + case 0x040000C4: return DMAs[5].Cnt; + case 0x040000C8: return DMAs[6].SrcAddr; + case 0x040000CC: return DMAs[6].DstAddr; + case 0x040000D0: return DMAs[6].Cnt; + case 0x040000D4: return DMAs[7].SrcAddr; + case 0x040000D8: return DMAs[7].DstAddr; + case 0x040000DC: return DMAs[7].Cnt; case 0x04000100: return TimerGetCounter(4) | (Timers[4].Cnt << 16); case 0x04000104: return TimerGetCounter(5) | (Timers[5].Cnt << 16); @@ -4022,37 +3775,37 @@ u32 ARM7IORead32(u32 addr) case 0x04000130: return (KeyInput & 0xFFFF) | (KeyCnt[1] << 16); case 0x04000134: return RCnt | (KeyInput & 0xFFFF0000); - case 0x04000138: return RTC->Read(); + case 0x04000138: return RTC.Read(); case 0x04000180: return IPCSync7; - case 0x04000184: return ARM7IORead16(addr); + case 0x04000184: return NDS::ARM7IORead16(addr); case 0x040001A0: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetSPICnt() | (NDSCartSlot->ReadSPIData() << 16); + return NDSCartSlot.GetSPICnt() | (NDSCartSlot.ReadSPIData() << 16); return 0; case 0x040001A4: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCnt(); + return NDSCartSlot.GetROMCnt(); return 0; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(0) | - (NDSCartSlot->GetROMCommand(1) << 8) | - (NDSCartSlot->GetROMCommand(2) << 16) | - (NDSCartSlot->GetROMCommand(3) << 24); + return NDSCartSlot.GetROMCommand(0) | + (NDSCartSlot.GetROMCommand(1) << 8) | + (NDSCartSlot.GetROMCommand(2) << 16) | + (NDSCartSlot.GetROMCommand(3) << 24); return 0; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) - return NDSCartSlot->GetROMCommand(4) | - (NDSCartSlot->GetROMCommand(5) << 8) | - (NDSCartSlot->GetROMCommand(6) << 16) | - (NDSCartSlot->GetROMCommand(7) << 24); + return NDSCartSlot.GetROMCommand(4) | + (NDSCartSlot.GetROMCommand(5) << 8) | + (NDSCartSlot.GetROMCommand(6) << 16) | + (NDSCartSlot.GetROMCommand(7) << 24); return 0; case 0x040001C0: - return SPI->ReadCnt() | (SPI->ReadData() << 16); + return SPI.ReadCnt() | (SPI.ReadData() << 16); case 0x04000208: return IME[1]; case 0x04000210: return IE[1]; @@ -4083,21 +3836,21 @@ u32 ARM7IORead32(u32 addr) return IPCFIFO9.Peek(); case 0x04100010: - if (ExMemCnt[0] & (1<<11)) return NDSCartSlot->ReadROMData(); + if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.ReadROMData(); return 0; } if (addr >= 0x04000400 && addr < 0x04000520) { - return SPU->Read32(addr); + return SPU.Read32(addr); } if ((addr & 0xFFFFF000) != 0x04004000) - Log(LogLevel::Debug, "unknown ARM7 IO read32 %08X %08X\n", addr, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO read32 %08X %08X\n", addr, ARM7.R[15]); return 0; } -void ARM7IOWrite8(u32 addr, u8 val) +void NDS::ARM7IOWrite8(u32 addr, u8 val) { switch (addr) { @@ -4114,44 +3867,44 @@ void ARM7IOWrite8(u32 addr, u8 val) RCnt = (RCnt & 0x00FF) | (val << 8); return; - case 0x04000138: RTC->Write(val, true); return; + case 0x04000138: RTC.Write(val, true); return; case 0x04000188: - ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); + NDS::ARM7IOWrite32(addr, val | (val << 8) | (val << 16) | (val << 24)); return; case 0x040001A0: if (ExMemCnt[0] & (1<<11)) { - NDSCartSlot->WriteSPICnt((NDSCartSlot->GetSPICnt() & 0xFF00) | val); + NDSCartSlot.WriteSPICnt((NDSCartSlot.GetSPICnt() & 0xFF00) | val); } return; case 0x040001A1: if (ExMemCnt[0] & (1<<11)) - NDSCartSlot->WriteSPICnt((NDSCartSlot->GetSPICnt() & 0x00FF) | (val << 8)); + NDSCartSlot.WriteSPICnt((NDSCartSlot.GetSPICnt() & 0x00FF) | (val << 8)); return; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - NDSCartSlot->WriteSPIData(val); + NDSCartSlot.WriteSPIData(val); return; - case 0x040001A8: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(0, val); return; - case 0x040001A9: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(1, val); return; - case 0x040001AA: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(2, val); return; - case 0x040001AB: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(3, val); return; - case 0x040001AC: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(4, val); return; - case 0x040001AD: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(5, val); return; - case 0x040001AE: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(6, val); return; - case 0x040001AF: if (ExMemCnt[0] & (1<<11)) NDSCartSlot->SetROMCommand(7, val); return; + case 0x040001A8: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(0, val); return; + case 0x040001A9: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(1, val); return; + case 0x040001AA: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(2, val); return; + case 0x040001AB: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(3, val); return; + case 0x040001AC: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(4, val); return; + case 0x040001AD: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(5, val); return; + case 0x040001AE: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(6, val); return; + case 0x040001AF: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(7, val); return; case 0x040001C2: - SPI->WriteData(val); + SPI.WriteData(val); return; case 0x04000208: IME[1] = val & 0x1; UpdateIRQ(1); return; case 0x04000300: - if (ARM7->R[15] >= 0x4000) + if (ARM7.R[15] >= 0x4000) return; if (!(PostFlag7 & 0x01)) PostFlag7 = val & 0x01; @@ -4160,35 +3913,35 @@ void ARM7IOWrite8(u32 addr, u8 val) case 0x04000301: val &= 0xC0; if (val == 0x40) Stop(StopReason::GBAModeNotSupported); - else if (val == 0x80) ARM7->Halt(1); + else if (val == 0x80) ARM7.Halt(1); else if (val == 0xC0) EnterSleepMode(); return; } if (addr >= 0x04000400 && addr < 0x04000520) { - SPU->Write8(addr, val); + SPU.Write8(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM7 IO write8 %08X %02X %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO write8 %08X %02X %08X\n", addr, val, ARM7.R[15]); } -void ARM7IOWrite16(u32 addr, u16 val) +void NDS::ARM7IOWrite16(u32 addr, u16 val) { switch (addr) { - case 0x04000004: GPU->SetDispStat(1, val); return; - case 0x04000006: GPU->SetVCount(val); return; + case 0x04000004: GPU.SetDispStat(1, val); return; + case 0x04000006: GPU.SetVCount(val); return; - case 0x040000B8: DMAs[4]->WriteCnt((DMAs[4]->Cnt & 0xFFFF0000) | val); return; - case 0x040000BA: DMAs[4]->WriteCnt((DMAs[4]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000C4: DMAs[5]->WriteCnt((DMAs[5]->Cnt & 0xFFFF0000) | val); return; - case 0x040000C6: DMAs[5]->WriteCnt((DMAs[5]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000D0: DMAs[6]->WriteCnt((DMAs[6]->Cnt & 0xFFFF0000) | val); return; - case 0x040000D2: DMAs[6]->WriteCnt((DMAs[6]->Cnt & 0x0000FFFF) | (val << 16)); return; - case 0x040000DC: DMAs[7]->WriteCnt((DMAs[7]->Cnt & 0xFFFF0000) | val); return; - case 0x040000DE: DMAs[7]->WriteCnt((DMAs[7]->Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000B8: DMAs[4].WriteCnt((DMAs[4].Cnt & 0xFFFF0000) | val); return; + case 0x040000BA: DMAs[4].WriteCnt((DMAs[4].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000C4: DMAs[5].WriteCnt((DMAs[5].Cnt & 0xFFFF0000) | val); return; + case 0x040000C6: DMAs[5].WriteCnt((DMAs[5].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000D0: DMAs[6].WriteCnt((DMAs[6].Cnt & 0xFFFF0000) | val); return; + case 0x040000D2: DMAs[6].WriteCnt((DMAs[6].Cnt & 0x0000FFFF) | (val << 16)); return; + case 0x040000DC: DMAs[7].WriteCnt((DMAs[7].Cnt & 0xFFFF0000) | val); return; + case 0x040000DE: DMAs[7].WriteCnt((DMAs[7].Cnt & 0x0000FFFF) | (val << 16)); return; case 0x04000100: Timers[4].Reload = val; return; case 0x04000102: TimerStart(4, val); return; @@ -4202,7 +3955,7 @@ void ARM7IOWrite16(u32 addr, u16 val) case 0x04000132: KeyCnt[1] = val; return; case 0x04000134: RCnt = val; return; - case 0x04000138: RTC->Write(val, false); return; + case 0x04000138: RTC.Write(val, false); return; case 0x04000180: IPCSync9 &= 0xFFF0; @@ -4228,44 +3981,44 @@ void ARM7IOWrite16(u32 addr, u16 val) return; case 0x04000188: - ARM7IOWrite32(addr, val | (val << 16)); + NDS::ARM7IOWrite32(addr, val | (val << 16)); return; case 0x040001A0: if (ExMemCnt[0] & (1<<11)) - NDSCartSlot->WriteSPICnt(val); + NDSCartSlot.WriteSPICnt(val); return; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) - NDSCartSlot->WriteSPIData(val & 0xFF); + NDSCartSlot.WriteSPIData(val & 0xFF); return; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) { - NDSCartSlot->SetROMCommand(0, val & 0xFF); - NDSCartSlot->SetROMCommand(1, val >> 8); + NDSCartSlot.SetROMCommand(0, val & 0xFF); + NDSCartSlot.SetROMCommand(1, val >> 8); } return; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) { - NDSCartSlot->SetROMCommand(2, val & 0xFF); - NDSCartSlot->SetROMCommand(3, val >> 8); + NDSCartSlot.SetROMCommand(2, val & 0xFF); + NDSCartSlot.SetROMCommand(3, val >> 8); } return; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) { - NDSCartSlot->SetROMCommand(4, val & 0xFF); - NDSCartSlot->SetROMCommand(5, val >> 8); + NDSCartSlot.SetROMCommand(4, val & 0xFF); + NDSCartSlot.SetROMCommand(5, val >> 8); } return; case 0x040001AE: if (ExMemCnt[0] & (1<<11)) { - NDSCartSlot->SetROMCommand(6, val & 0xFF); - NDSCartSlot->SetROMCommand(7, val >> 8); + NDSCartSlot.SetROMCommand(6, val & 0xFF); + NDSCartSlot.SetROMCommand(7, val >> 8); } return; @@ -4273,10 +4026,10 @@ void ARM7IOWrite16(u32 addr, u16 val) case 0x040001BA: ROMSeed1[12] = val & 0x7F; return; case 0x040001C0: - SPI->WriteCnt(val); + SPI.WriteCnt(val); return; case 0x040001C2: - SPI->WriteData(val & 0xFF); + SPI.WriteData(val & 0xFF); return; case 0x04000204: @@ -4298,7 +4051,7 @@ void ARM7IOWrite16(u32 addr, u16 val) // TODO: what happens when writing to IF this way?? case 0x04000300: - if (ARM7->R[15] >= 0x4000) + if (ARM7.R[15] >= 0x4000) return; if (!(PostFlag7 & 0x01)) PostFlag7 = val & 0x01; @@ -4308,8 +4061,8 @@ void ARM7IOWrite16(u32 addr, u16 val) { u16 change = PowerControl7 ^ val; PowerControl7 = val & 0x0003; - SPU->SetPowerCnt(val & 0x0001); - Wifi->SetPowerCnt(val & 0x0002); + SPU.SetPowerCnt(val & 0x0001); + Wifi.SetPowerCnt(val & 0x0002); if (change & 0x0002) UpdateWifiTimings(); } return; @@ -4322,34 +4075,34 @@ void ARM7IOWrite16(u32 addr, u16 val) if (addr >= 0x04000400 && addr < 0x04000520) { - SPU->Write16(addr, val); + SPU.Write16(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM7 IO write16 %08X %04X %08X\n", addr, val, ARM7->R[15]); + Log(LogLevel::Debug, "unknown ARM7 IO write16 %08X %04X %08X\n", addr, val, ARM7.R[15]); } -void ARM7IOWrite32(u32 addr, u32 val) +void NDS::ARM7IOWrite32(u32 addr, u32 val) { switch (addr) { case 0x04000004: - GPU->SetDispStat(1, val & 0xFFFF); - GPU->SetVCount(val >> 16); + GPU.SetDispStat(1, val & 0xFFFF); + GPU.SetVCount(val >> 16); return; - case 0x040000B0: DMAs[4]->SrcAddr = val; return; - case 0x040000B4: DMAs[4]->DstAddr = val; return; - case 0x040000B8: DMAs[4]->WriteCnt(val); return; - case 0x040000BC: DMAs[5]->SrcAddr = val; return; - case 0x040000C0: DMAs[5]->DstAddr = val; return; - case 0x040000C4: DMAs[5]->WriteCnt(val); return; - case 0x040000C8: DMAs[6]->SrcAddr = val; return; - case 0x040000CC: DMAs[6]->DstAddr = val; return; - case 0x040000D0: DMAs[6]->WriteCnt(val); return; - case 0x040000D4: DMAs[7]->SrcAddr = val; return; - case 0x040000D8: DMAs[7]->DstAddr = val; return; - case 0x040000DC: DMAs[7]->WriteCnt(val); return; + case 0x040000B0: DMAs[4].SrcAddr = val; return; + case 0x040000B4: DMAs[4].DstAddr = val; return; + case 0x040000B8: DMAs[4].WriteCnt(val); return; + case 0x040000BC: DMAs[5].SrcAddr = val; return; + case 0x040000C0: DMAs[5].DstAddr = val; return; + case 0x040000C4: DMAs[5].WriteCnt(val); return; + case 0x040000C8: DMAs[6].SrcAddr = val; return; + case 0x040000CC: DMAs[6].DstAddr = val; return; + case 0x040000D0: DMAs[6].WriteCnt(val); return; + case 0x040000D4: DMAs[7].SrcAddr = val; return; + case 0x040000D8: DMAs[7].DstAddr = val; return; + case 0x040000DC: DMAs[7].WriteCnt(val); return; case 0x04000100: Timers[4].Reload = val & 0xFFFF; @@ -4370,11 +4123,11 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x04000130: KeyCnt[1] = val >> 16; return; case 0x04000134: RCnt = val & 0xFFFF; return; - case 0x04000138: RTC->Write(val & 0xFFFF, false); return; + case 0x04000138: RTC.Write(val & 0xFFFF, false); return; case 0x04000180: case 0x04000184: - ARM7IOWrite16(addr, val); + NDS::ARM7IOWrite16(addr, val); return; case 0x04000188: if (IPCFIFOCnt7 & 0x8000) @@ -4394,31 +4147,31 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x040001A0: if (ExMemCnt[0] & (1<<11)) { - NDSCartSlot->WriteSPICnt(val & 0xFFFF); - NDSCartSlot->WriteSPIData((val >> 16) & 0xFF); + NDSCartSlot.WriteSPICnt(val & 0xFFFF); + NDSCartSlot.WriteSPIData((val >> 16) & 0xFF); } return; case 0x040001A4: if (ExMemCnt[0] & (1<<11)) - NDSCartSlot->WriteROMCnt(val); + NDSCartSlot.WriteROMCnt(val); return; case 0x040001A8: if (ExMemCnt[0] & (1<<11)) { - NDSCartSlot->SetROMCommand(0, val & 0xFF); - NDSCartSlot->SetROMCommand(1, (val >> 8) & 0xFF); - NDSCartSlot->SetROMCommand(2, (val >> 16) & 0xFF); - NDSCartSlot->SetROMCommand(3, val >> 24); + NDSCartSlot.SetROMCommand(0, val & 0xFF); + NDSCartSlot.SetROMCommand(1, (val >> 8) & 0xFF); + NDSCartSlot.SetROMCommand(2, (val >> 16) & 0xFF); + NDSCartSlot.SetROMCommand(3, val >> 24); } return; case 0x040001AC: if (ExMemCnt[0] & (1<<11)) { - NDSCartSlot->SetROMCommand(4, val & 0xFF); - NDSCartSlot->SetROMCommand(5, (val >> 8) & 0xFF); - NDSCartSlot->SetROMCommand(6, (val >> 16) & 0xFF); - NDSCartSlot->SetROMCommand(7, val >> 24); + NDSCartSlot.SetROMCommand(4, val & 0xFF); + NDSCartSlot.SetROMCommand(5, (val >> 8) & 0xFF); + NDSCartSlot.SetROMCommand(6, (val >> 16) & 0xFF); + NDSCartSlot.SetROMCommand(7, val >> 24); } return; @@ -4426,8 +4179,8 @@ void ARM7IOWrite32(u32 addr, u32 val) case 0x040001B4: *(u32*)&ROMSeed1[8] = val; return; case 0x040001C0: - SPI->WriteCnt(val & 0xFFFF); - SPI->WriteData((val >> 16) & 0xFF); + SPI.WriteCnt(val & 0xFFFF); + SPI.WriteData((val >> 16) & 0xFF); return; case 0x04000208: IME[1] = val & 0x1; UpdateIRQ(1); return; @@ -4438,8 +4191,8 @@ void ARM7IOWrite32(u32 addr, u32 val) { u16 change = PowerControl7 ^ val; PowerControl7 = val & 0x0003; - SPU->SetPowerCnt(val & 0x0001); - Wifi->SetPowerCnt(val & 0x0002); + SPU.SetPowerCnt(val & 0x0001); + Wifi.SetPowerCnt(val & 0x0002); if (change & 0x0002) UpdateWifiTimings(); } return; @@ -4450,19 +4203,17 @@ void ARM7IOWrite32(u32 addr, u32 val) return; case 0x04100010: - if (ExMemCnt[0] & (1<<11)) NDSCartSlot->WriteROMData(val); + if (ExMemCnt[0] & (1<<11)) NDSCartSlot.WriteROMData(val); return; } if (addr >= 0x04000400 && addr < 0x04000520) { - SPU->Write32(addr, val); + SPU.Write32(addr, val); return; } - Log(LogLevel::Debug, "unknown ARM7 IO write32 %08X %08X %08X\n", addr, val, ARM7->R[15]); -} - + Log(LogLevel::Debug, "unknown ARM7 IO write32 %08X %08X %08X\n", addr, val, ARM7.R[15]); } } \ No newline at end of file diff --git a/src/NDS.h b/src/NDS.h index 7bfad625..0979b2fa 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -29,6 +29,14 @@ #include "types.h" #include "NDSCart.h" #include "GBACart.h" +#include "SPU.h" +#include "SPI.h" +#include "RTC.h" +#include "Wifi.h" +#include "AREngine.h" +#include "GPU.h" +#include "ARMJIT.h" +#include "DMA.h" // when touching the main loop/timing code, pls test a lot of shit // with this enabled, to make sure it doesn't desync @@ -36,17 +44,6 @@ namespace melonDS { -class SPU; -class SPIHost; -class RTC; -class Wifi; - -class AREngine; -class GPU; -class ARMJIT; - -namespace NDS -{ enum { @@ -75,7 +72,13 @@ enum typedef std::function EventFunc; #define MemberEventFunc(cls,func) std::bind(&cls::func,this,std::placeholders::_1) - +struct SchedEvent +{ + std::map Funcs; + u64 Timestamp; + u32 FuncID; + u32 Param; +}; enum { IRQ_VBlank = 0, @@ -197,196 +200,274 @@ enum // TODO: add DSi regions! }; -struct MemRegion -{ - u8* Mem; - u32 Mask; -}; - // supported GBA slot addon types enum { GBAAddon_RAMExpansion = 1, }; +class SPU; +class SPIHost; +class RTC; +class Wifi; + +class AREngine; +class GPU; +class ARMJIT; + +class NDS +{ +public: + #ifdef JIT_ENABLED -extern bool EnableJIT; + bool EnableJIT; #endif -extern int ConsoleType; -extern int CurCPU; + int ConsoleType; + int CurCPU; -extern u8 ARM9MemTimings[0x40000][8]; -extern u32 ARM9Regions[0x40000]; -extern u8 ARM7MemTimings[0x20000][4]; -extern u32 ARM7Regions[0x20000]; + SchedEvent SchedList[Event_MAX] {}; + u8 ARM9MemTimings[0x40000][8]; + u32 ARM9Regions[0x40000]; + u8 ARM7MemTimings[0x20000][4]; + u32 ARM7Regions[0x20000]; -extern u32 NumFrames; -extern u32 NumLagFrames; -extern bool LagFrameFlag; + u32 NumFrames; + u32 NumLagFrames; + bool LagFrameFlag; -extern u64 ARM9Timestamp, ARM9Target; -extern u64 ARM7Timestamp, ARM7Target; -extern u32 ARM9ClockShift; + // no need to worry about those overflowing, they can keep going for atleast 4350 years + u64 ARM9Timestamp, ARM9Target; + u64 ARM7Timestamp, ARM7Target; + u32 ARM9ClockShift; -extern u32 IME[2]; -extern u32 IE[2]; -extern u32 IF[2]; -extern u32 IE2; -extern u32 IF2; -extern Timer Timers[8]; + u32 IME[2]; + u32 IE[2]; + u32 IF[2]; + u32 IE2; + u32 IF2; + Timer Timers[8]; -extern u32 CPUStop; + u32 CPUStop; -extern u16 PowerControl9; + u16 PowerControl9; -extern u16 ExMemCnt[2]; -extern u8 ROMSeed0[2*8]; -extern u8 ROMSeed1[2*8]; + u16 ExMemCnt[2]; + u8 ROMSeed0[2*8]; + u8 ROMSeed1[2*8]; -extern u8 ARM9BIOS[0x1000]; -extern u8 ARM7BIOS[0x4000]; -extern u16 ARM7BIOSProt; + u8 ARM9BIOS[0x1000]; + u8 ARM7BIOS[0x4000]; + u16 ARM7BIOSProt; -extern u8* MainRAM; -extern u32 MainRAMMask; + u8* MainRAM; + u32 MainRAMMask; -const u32 MainRAMMaxSize = 0x1000000; + const u32 MainRAMMaxSize = 0x1000000; -const u32 SharedWRAMSize = 0x8000; -extern u8* SharedWRAM; + const u32 SharedWRAMSize = 0x8000; + u8* SharedWRAM; -extern MemRegion SWRAM_ARM9; -extern MemRegion SWRAM_ARM7; + MemRegion SWRAM_ARM9; + MemRegion SWRAM_ARM7; -extern u32 KeyInput; -extern u16 RCnt; + u32 KeyInput; + u16 RCnt; -extern class SPU* SPU; -extern class SPIHost* SPI; -extern class RTC* RTC; -extern class Wifi* Wifi; -extern std::unique_ptr NDSCartSlot; -extern std::unique_ptr GBACartSlot; -extern std::unique_ptr GPU; -extern std::unique_ptr JIT; -extern class AREngine* AREngine; + // JIT MUST be declared before all other component objects, + // as they'll need the memory that it allocates in its constructor! + // (Reminder: C++ fields are initialized in the order they're declared, + // regardless of what the constructor's initializer list says.) + melonDS::ARMJIT JIT; + ARMv5 ARM9; + ARMv4 ARM7; + melonDS::SPU SPU; + SPIHost SPI; + melonDS::RTC RTC; + melonDS::Wifi Wifi; + NDSCart::NDSCartSlot NDSCartSlot; + GBACart::GBACartSlot GBACartSlot; + melonDS::GPU GPU; + melonDS::AREngine AREngine; -const u32 ARM7WRAMSize = 0x10000; -extern u8* ARM7WRAM; + const u32 ARM7WRAMSize = 0x10000; + u8* ARM7WRAM; -bool Init(); -void DeInit(); -void Reset(); -void Start(); + virtual void Reset(); + void Start(); -/// Stop the emulator. -void Stop(Platform::StopReason reason = Platform::StopReason::External); + /// Stop the emulator. + virtual void Stop(Platform::StopReason reason = Platform::StopReason::External); -bool DoSavestate(Savestate* file); + bool DoSavestate(Savestate* file); -void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); -void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); + void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); + void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); -// 0=DS 1=DSi -void SetConsoleType(int type); + // 0=DS 1=DSi + void SetConsoleType(int type); -void LoadBIOS(); -bool IsLoadedARM9BIOSBuiltIn(); -bool IsLoadedARM7BIOSBuiltIn(); + 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); -void EjectCart(); -bool CartInserted(); + virtual bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); + void LoadSave(const u8* savedata, u32 savelen); + virtual void EjectCart(); + bool CartInserted(); -bool NeedsDirectBoot(); -void SetupDirectBoot(const std::string& romname); + virtual bool NeedsDirectBoot(); + void SetupDirectBoot(const std::string& romname); + virtual void SetupDirectBoot(); -bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); -void LoadGBAAddon(int type); -void EjectGBACart(); + bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); + void LoadGBAAddon(int type); + void EjectGBACart(); -u32 RunFrame(); + u32 RunFrame(); -void TouchScreen(u16 x, u16 y); -void ReleaseScreen(); + bool IsRunning() const noexcept { return Running; } -void SetKeyMask(u32 mask); + void TouchScreen(u16 x, u16 y); + void ReleaseScreen(); -bool IsLidClosed(); -void SetLidClosed(bool closed); + void SetKeyMask(u32 mask); -void CamInputFrame(int cam, u32* data, int width, int height, bool rgb); -void MicInputFrame(s16* data, int samples); + bool IsLidClosed(); + void SetLidClosed(bool closed); -void RegisterEventFunc(u32 id, u32 funcid, EventFunc func); -void UnregisterEventFunc(u32 id, u32 funcid); -void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param); -void CancelEvent(u32 id); + virtual void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) {} + void MicInputFrame(s16* data, int samples); -void debug(u32 p); + void RegisterEventFunc(u32 id, u32 funcid, EventFunc func); + void UnregisterEventFunc(u32 id, u32 funcid); + void ScheduleEvent(u32 id, bool periodic, s32 delay, u32 funcid, u32 param); + void CancelEvent(u32 id); -void Halt(); + void debug(u32 p); -void MapSharedWRAM(u8 val); + void Halt(); -void UpdateIRQ(u32 cpu); -void SetIRQ(u32 cpu, u32 irq); -void ClearIRQ(u32 cpu, u32 irq); -void SetIRQ2(u32 irq); -void ClearIRQ2(u32 irq); -bool HaltInterrupted(u32 cpu); -void StopCPU(u32 cpu, u32 mask); -void ResumeCPU(u32 cpu, u32 mask); -void GXFIFOStall(); -void GXFIFOUnstall(); + void MapSharedWRAM(u8 val); -u32 GetPC(u32 cpu); -u64 GetSysClockCycles(int num); -void NocashPrint(u32 cpu, u32 addr); + void UpdateIRQ(u32 cpu); + void SetIRQ(u32 cpu, u32 irq); + void ClearIRQ(u32 cpu, u32 irq); + void SetIRQ2(u32 irq); + void ClearIRQ2(u32 irq); + bool HaltInterrupted(u32 cpu); + void StopCPU(u32 cpu, u32 mask); + void ResumeCPU(u32 cpu, u32 mask); + void GXFIFOStall(); + void GXFIFOUnstall(); -void MonitorARM9Jump(u32 addr); + u32 GetPC(u32 cpu); + u64 GetSysClockCycles(int num); + void NocashPrint(u32 cpu, u32 addr); -bool DMAsInMode(u32 cpu, u32 mode); -bool DMAsRunning(u32 cpu); -void CheckDMAs(u32 cpu, u32 mode); -void StopDMAs(u32 cpu, u32 mode); + void MonitorARM9Jump(u32 addr); -void RunTimers(u32 cpu); + virtual bool DMAsInMode(u32 cpu, u32 mode); + virtual bool DMAsRunning(u32 cpu); + virtual void CheckDMAs(u32 cpu, u32 mode); + virtual void StopDMAs(u32 cpu, u32 mode); -u8 ARM9Read8(u32 addr); -u16 ARM9Read16(u32 addr); -u32 ARM9Read32(u32 addr); -void ARM9Write8(u32 addr, u8 val); -void ARM9Write16(u32 addr, u16 val); -void ARM9Write32(u32 addr, u32 val); + void RunTimers(u32 cpu); -bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region); + virtual u8 ARM9Read8(u32 addr); + virtual u16 ARM9Read16(u32 addr); + virtual u32 ARM9Read32(u32 addr); + virtual void ARM9Write8(u32 addr, u8 val); + virtual void ARM9Write16(u32 addr, u16 val); + virtual void ARM9Write32(u32 addr, u32 val); -u8 ARM7Read8(u32 addr); -u16 ARM7Read16(u32 addr); -u32 ARM7Read32(u32 addr); -void ARM7Write8(u32 addr, u8 val); -void ARM7Write16(u32 addr, u16 val); -void ARM7Write32(u32 addr, u32 val); + virtual bool ARM9GetMemRegion(u32 addr, bool write, MemRegion* region); -bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region); + virtual u8 ARM7Read8(u32 addr); + virtual u16 ARM7Read16(u32 addr); + virtual u32 ARM7Read32(u32 addr); + virtual void ARM7Write8(u32 addr, u8 val); + virtual void ARM7Write16(u32 addr, u16 val); + virtual void ARM7Write32(u32 addr, u32 val); -u8 ARM9IORead8(u32 addr); -u16 ARM9IORead16(u32 addr); -u32 ARM9IORead32(u32 addr); -void ARM9IOWrite8(u32 addr, u8 val); -void ARM9IOWrite16(u32 addr, u16 val); -void ARM9IOWrite32(u32 addr, u32 val); + virtual bool ARM7GetMemRegion(u32 addr, bool write, MemRegion* region); -u8 ARM7IORead8(u32 addr); -u16 ARM7IORead16(u32 addr); -u32 ARM7IORead32(u32 addr); -void ARM7IOWrite8(u32 addr, u8 val); -void ARM7IOWrite16(u32 addr, u16 val); -void ARM7IOWrite32(u32 addr, u32 val); + virtual u8 ARM9IORead8(u32 addr); + virtual u16 ARM9IORead16(u32 addr); + virtual u32 ARM9IORead32(u32 addr); + virtual void ARM9IOWrite8(u32 addr, u8 val); + virtual void ARM9IOWrite16(u32 addr, u16 val); + virtual void ARM9IOWrite32(u32 addr, u32 val); -} + virtual u8 ARM7IORead8(u32 addr); + virtual u16 ARM7IORead16(u32 addr); + virtual u32 ARM7IORead32(u32 addr); + virtual void ARM7IOWrite8(u32 addr, u8 val); + virtual void ARM7IOWrite16(u32 addr, u16 val); + virtual void ARM7IOWrite32(u32 addr, u32 val); + +private: + void InitTimings(); + u32 SchedListMask; + u64 SysTimestamp; + u8 WRAMCnt; + u8 PostFlag9; + u8 PostFlag7; + u16 PowerControl7; + u16 WifiWaitCnt; + u8 TimerCheckMask[2]; + u64 TimerTimestamp[2]; + DMA DMAs[8]; + u32 DMA9Fill[4]; + u16 IPCSync9, IPCSync7; + u16 IPCFIFOCnt9, IPCFIFOCnt7; + FIFO IPCFIFO9; // FIFO in which the ARM9 writes + FIFO IPCFIFO7; + u16 DivCnt; + u32 DivNumerator[2]; + u32 DivDenominator[2]; + u32 DivQuotient[2]; + u32 DivRemainder[2]; + u16 SqrtCnt; + u32 SqrtVal[2]; + u32 SqrtRes; + u16 KeyCnt[2]; + bool Running; + bool RunningGame; + u64 LastSysClockCycles; + u64 FrameStartTimestamp; + u64 NextTarget(); + u64 NextTargetSleep(); + void CheckKeyIRQ(u32 cpu, u32 oldkey, u32 newkey); + void Reschedule(u64 target); + void RunSystemSleep(u64 timestamp); + void RunSystem(u64 timestamp); + void HandleTimerOverflow(u32 tid); + u16 TimerGetCounter(u32 timer); + void TimerStart(u32 id, u16 cnt); + void StartDiv(); + void DivDone(u32 param); + void SqrtDone(u32 param); + void StartSqrt(); + void RunTimer(u32 tid, s32 cycles); + void UpdateWifiTimings(); + void SetWifiWaitCnt(u16 val); + void SetGBASlotTimings(); + void EnterSleepMode(); + template + u32 RunFrame(); +public: + NDS() noexcept : NDS(0) {} + virtual ~NDS() noexcept; + NDS(const NDS&) = delete; + NDS& operator=(const NDS&) = delete; + NDS(NDS&&) = delete; + NDS& operator=(NDS&&) = delete; + // The frontend should set and unset this manually after creating and destroying the NDS object. + [[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current; +protected: + explicit NDS(int type) noexcept; + virtual void DoSavestateExtra(Savestate* file) {} +}; } #endif // NDS_H diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 77262a70..95306fc5 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -214,11 +214,11 @@ void CartCommon::Reset() DSiMode = false; } -void CartCommon::SetupDirectBoot(const std::string& romname) +void CartCommon::SetupDirectBoot(const std::string& romname, NDS& nds) { CmdEncMode = 2; DataEncMode = 2; - DSiMode = IsDSi && (NDS::ConsoleType==1); + DSiMode = IsDSi && (nds.ConsoleType==1); } void CartCommon::DoSavestate(Savestate* file) @@ -238,7 +238,7 @@ void CartCommon::LoadSave(const u8* savedata, u32 savelen) { } -int CartCommon::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) +int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { if (CmdEncMode == 0) { @@ -267,15 +267,16 @@ int CartCommon::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* dat case 0x3C: CmdEncMode = 1; - cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.ARM7BIOS, sizeof(NDS::ARM7BIOS)); DSiMode = false; return 0; case 0x3D: if (IsDSi) { + auto& dsi = static_cast(nds); CmdEncMode = 1; - cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, DSi::ARM7iBIOS, sizeof(DSi::ARM7iBIOS)); + cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, dsi.ARM7iBIOS, sizeof(DSi::ARM7iBIOS)); DSiMode = true; } return 0; @@ -359,12 +360,6 @@ u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last) return 0xFF; } -void CartCommon::SetIRQ() -{ - NDS::SetIRQ(0, NDS::IRQ_CartIREQMC); - NDS::SetIRQ(1, NDS::IRQ_CartIREQMC); -} - u8 *CartCommon::GetSaveMemory() const { return nullptr; @@ -496,9 +491,9 @@ void CartRetail::LoadSave(const u8* savedata, u32 savelen) Platform::WriteNDSSave(savedata, len, 0, len); } -int CartRetail::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) +int CartRetail::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cartslot, cmd, data, len); + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); switch (cmd[0]) { @@ -519,7 +514,7 @@ int CartRetail::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* dat return 0; default: - return CartCommon::ROMCommandStart(cartslot, cmd, data, len); + return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); } } @@ -900,9 +895,9 @@ void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen) BuildSRAMID(); } -int CartRetailNAND::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) +int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cartslot, cmd, data, len); + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); switch (cmd[0]) { @@ -1032,7 +1027,7 @@ int CartRetailNAND::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* return 0; default: - return CartRetail::ROMCommandStart(cartslot, cmd, data, len); + return CartRetail::ROMCommandStart(nds, cartslot, cmd, data, len); } } @@ -1152,7 +1147,7 @@ void CartRetailBT::DoSavestate(Savestate* file) u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) { - Log(LogLevel::Debug,"POKETYPE SPI: %02X %d %d - %08X\n", val, pos, last, NDS::GetPC(0)); + //Log(LogLevel::Debug,"POKETYPE SPI: %02X %d %d - %08X\n", val, pos, last, NDS::GetPC(0)); /*if (pos == 0) { @@ -1210,9 +1205,9 @@ void CartHomebrew::Reset() SD = nullptr; } -void CartHomebrew::SetupDirectBoot(const std::string& romname) +void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) { - CartCommon::SetupDirectBoot(romname); + CartCommon::SetupDirectBoot(romname, nds); if (SD) { @@ -1231,17 +1226,17 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname) argvlen = strlen(argv); const NDSHeader& header = GetHeader(); - void (*writefn)(u32,u32) = (NDS::ConsoleType==1) ? DSi::ARM9Write32 : NDS::ARM9Write32; u32 argvbase = header.ARM9RAMAddress + header.ARM9Size; argvbase = (argvbase + 0xF) & ~0xF; for (u32 i = 0; i <= argvlen; i+=4) - writefn(argvbase+i, *(u32*)&argv[i]); + nds.ARM9Write32(argvbase+i, *(u32*)&argv[i]); - writefn(0x02FFFE70, 0x5F617267); - writefn(0x02FFFE74, argvbase); - writefn(0x02FFFE78, argvlen+1); + nds.ARM9Write32(0x02FFFE70, 0x5F617267); + nds.ARM9Write32(0x02FFFE74, argvbase); + nds.ARM9Write32(0x02FFFE78, argvlen+1); + // The DSi version of ARM9Write32 will be called if nds is really a DSi } } @@ -1250,9 +1245,9 @@ void CartHomebrew::DoSavestate(Savestate* file) CartCommon::DoSavestate(file); } -int CartHomebrew::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) +int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(cartslot, cmd, data, len); + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); switch (cmd[0]) { @@ -1283,7 +1278,7 @@ int CartHomebrew::ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* d return 1; default: - return CartCommon::ROMCommandStart(cartslot, cmd, data, len); + return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); } } @@ -1449,19 +1444,19 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) -NDSCartSlot::NDSCartSlot() noexcept +NDSCartSlot::NDSCartSlot(melonDS::NDS& nds) noexcept : NDS(nds) { - NDS::RegisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData)); - NDS::RegisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer)); - NDS::RegisterEventFunc(NDS::Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone)); + NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData)); + NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer)); + NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone)); // All fields are default-constructed because they're listed as such in the class declaration } NDSCartSlot::~NDSCartSlot() noexcept { - NDS::UnregisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_PrepareData); - NDS::UnregisterEventFunc(NDS::Event_ROMTransfer, ROMTransfer_End); - NDS::UnregisterEventFunc(NDS::Event_ROMSPITransfer, 0); + NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData); + NDS.UnregisterEventFunc(Event_ROMTransfer, ROMTransfer_End); + NDS.UnregisterEventFunc(Event_ROMSPITransfer, 0); // Cart is cleaned up automatically because it's a unique_ptr } @@ -1574,10 +1569,10 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept memcpy(out, &cartrom[arm9base], 0x800); - Key1_InitKeycode(false, gamecode, 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, gamecode, 2, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); Key1_Decrypt((u32*)&out[0]); - Key1_InitKeycode(false, gamecode, 3, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, gamecode, 3, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); for (u32 i = 0; i < 0x800; i += 8) Key1_Decrypt((u32*)&out[i]); @@ -1744,11 +1739,11 @@ bool NDSCartSlot::InsertROM(std::unique_ptr&& cart) noexcept strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8); - Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); for (u32 i = 0; i < 0x800; i += 8) Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]); - Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]); Log(LogLevel::Debug, "Re-encrypted cart secure area\n"); @@ -1763,8 +1758,6 @@ bool NDSCartSlot::InsertROM(std::unique_ptr&& cart) noexcept Log(LogLevel::Info, "Inserted cart with ID: %08X\n", Cart->ID()); Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType); - DSi::SetCartInserted(true); - return true; } @@ -1784,7 +1777,7 @@ void NDSCartSlot::LoadSave(const u8* savedata, u32 savelen) noexcept void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept { if (Cart) - Cart->SetupDirectBoot(romname); + Cart->SetupDirectBoot(romname, NDS); } void NDSCartSlot::EjectCart() noexcept @@ -1792,13 +1785,11 @@ void NDSCartSlot::EjectCart() noexcept if (!Cart) return; // ejecting the cart triggers the gamecard IRQ - NDS::SetIRQ(0, NDS::IRQ_CartIREQMC); - NDS::SetIRQ(1, NDS::IRQ_CartIREQMC); + NDS.SetIRQ(0, IRQ_CartIREQMC); + NDS.SetIRQ(1, IRQ_CartIREQMC); Cart = nullptr; - DSi::SetCartInserted(false); - // CHECKME: does an eject imply anything for the ROM/SPI transfer registers? } @@ -1835,7 +1826,7 @@ void NDSCartSlot::ROMEndTransfer(u32 param) noexcept ROMCnt &= ~(1<<31); if (SPICnt & (1<<14)) - NDS::SetIRQ((NDS::ExMemCnt[0]>>11)&0x1, NDS::IRQ_CartXferDone); + NDS.SetIRQ((NDS.ExMemCnt[0]>>11)&0x1, IRQ_CartXferDone); if (Cart) Cart->ROMCommandFinish(TransferCmd.data(), TransferData.data(), TransferLen); @@ -1855,10 +1846,10 @@ void NDSCartSlot::ROMPrepareData(u32 param) noexcept ROMCnt |= (1<<23); - if (NDS::ExMemCnt[0] & (1<<11)) - NDS::CheckDMAs(1, 0x12); + if (NDS.ExMemCnt[0] & (1<<11)) + NDS.CheckDMAs(1, 0x12); else - NDS::CheckDMAs(0, 0x05); + NDS.CheckDMAs(0, 0x05); } void NDSCartSlot::WriteROMCnt(u32 val) noexcept @@ -1870,9 +1861,9 @@ void NDSCartSlot::WriteROMCnt(u32 val) noexcept // a DS cart reader if (val & (1<<15)) { - u32 snum = (NDS::ExMemCnt[0]>>8)&0x8; - u64 seed0 = *(u32*)&NDS::ROMSeed0[snum] | ((u64)NDS::ROMSeed0[snum+4] << 32); - u64 seed1 = *(u32*)&NDS::ROMSeed1[snum] | ((u64)NDS::ROMSeed1[snum+4] << 32); + u32 snum = (NDS.ExMemCnt[0]>>8)&0x8; + u64 seed0 = *(u32*)&NDS.ROMSeed0[snum] | ((u64)NDS.ROMSeed0[snum+4] << 32); + u64 seed1 = *(u32*)&NDS.ROMSeed1[snum] | ((u64)NDS.ROMSeed1[snum+4] << 32); Key2_X = 0; Key2_Y = 0; @@ -1919,7 +1910,7 @@ void NDSCartSlot::WriteROMCnt(u32 val) noexcept TransferDir = 0; if (Cart) - TransferDir = Cart->ROMCommandStart(*this, TransferCmd.data(), TransferData.data(), TransferLen); + TransferDir = Cart->ROMCommandStart(NDS, *this, TransferCmd.data(), TransferData.data(), TransferLen); if ((datasize > 0) && (((ROMCnt >> 30) & 0x1) != TransferDir)) Log(LogLevel::Debug, "NDSCART: !! BAD TRANSFER DIRECTION FOR CMD %02X, DIR=%d, ROMCNT=%08X\n", ROMCommand[0], TransferDir, ROMCnt); @@ -1945,9 +1936,9 @@ void NDSCartSlot::WriteROMCnt(u32 val) noexcept } if (datasize == 0) - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*cmddelay, ROMTransfer_End, 0); + NDS.ScheduleEvent(Event_ROMTransfer, false, xfercycle*cmddelay, ROMTransfer_End, 0); else - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*(cmddelay+4), ROMTransfer_PrepareData, 0); + NDS.ScheduleEvent(Event_ROMTransfer, false, xfercycle*(cmddelay+4), ROMTransfer_PrepareData, 0); } void NDSCartSlot::AdvanceROMTransfer() noexcept @@ -1964,7 +1955,7 @@ void NDSCartSlot::AdvanceROMTransfer() noexcept delay += ((ROMCnt >> 16) & 0x3F); } - NDS::ScheduleEvent(NDS::Event_ROMTransfer, false, xfercycle*delay, ROMTransfer_PrepareData, 0); + NDS.ScheduleEvent(Event_ROMTransfer, false, xfercycle*delay, ROMTransfer_PrepareData, 0); } else ROMEndTransfer(0); @@ -2066,7 +2057,7 @@ void NDSCartSlot::WriteSPIData(u8 val) noexcept // SPI transfers one bit per cycle -> 8 cycles per byte u32 delay = 8 * (8 << (SPICnt & 0x3)); - NDS::ScheduleEvent(NDS::Event_ROMSPITransfer, false, delay, 0, 0); + NDS.ScheduleEvent(Event_ROMSPITransfer, false, delay, 0, 0); } } diff --git a/src/NDSCart.h b/src/NDSCart.h index 18d1fb6d..24f9f9ee 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -30,6 +30,10 @@ #include "FATStorage.h" #include "ROMList.h" +namespace melonDS +{ +class NDS; +} namespace melonDS::NDSCart { @@ -56,14 +60,14 @@ public: [[nodiscard]] u32 Checksum() const; virtual void Reset(); - virtual void SetupDirectBoot(const std::string& romname); + virtual void SetupDirectBoot(const std::string& romname, NDS& nds); virtual void DoSavestate(Savestate* file); virtual void SetupSave(u32 type); virtual void LoadSave(const u8* savedata, u32 savelen); - virtual int ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len); + virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len); virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len); virtual u8 SPIWrite(u8 val, u32 pos, bool last); @@ -83,8 +87,6 @@ public: protected: void ReadROM(u32 addr, u32 len, u8* data, u32 offset); - void SetIRQ(); - u8* ROM; u32 ROMLength; u32 ChipID; @@ -116,7 +118,7 @@ public: virtual void SetupSave(u32 type) override; virtual void LoadSave(const u8* savedata, u32 savelen) override; - virtual int ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; + virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; virtual u8 SPIWrite(u8 val, u32 pos, bool last) override; @@ -155,7 +157,7 @@ public: void LoadSave(const u8* savedata, u32 savelen) override; - int ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; u8 SPIWrite(u8 val, u32 pos, bool last) override; @@ -216,11 +218,11 @@ public: virtual u32 Type() const override { return CartType::Homebrew; } void Reset() override; - void SetupDirectBoot(const std::string& romname) override; + void SetupDirectBoot(const std::string& romname, NDS& nds) override; void DoSavestate(Savestate* file) override; - int ROMCommandStart(NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; private: @@ -235,7 +237,7 @@ private: class NDSCartSlot { public: - NDSCartSlot() noexcept; + NDSCartSlot(melonDS::NDS& nds) noexcept; ~NDSCartSlot() noexcept; void Reset() noexcept; void ResetCart() noexcept; @@ -303,6 +305,7 @@ public: void SetSPICnt(u16 val) noexcept { SPICnt = val; } private: friend class CartCommon; + melonDS::NDS& NDS; u16 SPICnt {}; u32 ROMCnt {}; std::array ROMCommand {}; diff --git a/src/RTC.cpp b/src/RTC.cpp index 747348ce..b5e497a2 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -32,9 +32,9 @@ using Platform::LogLevel; void WriteDateTime(int num, u8 val); -RTC::RTC() +RTC::RTC(melonDS::NDS& nds) : NDS(nds) { - NDS::RegisterEventFunc(NDS::Event_RTC, 0, MemberEventFunc(RTC, ClockTimer)); + NDS.RegisterEventFunc(Event_RTC, 0, MemberEventFunc(RTC, ClockTimer)); ResetState(); @@ -45,7 +45,7 @@ RTC::RTC() RTC::~RTC() { - NDS::UnregisterEventFunc(NDS::Event_RTC, 0); + NDS.UnregisterEventFunc(Event_RTC, 0); } void RTC::Reset() @@ -221,10 +221,10 @@ void RTC::SetIRQ(u8 irq) if ((!(oldstat & 0x30)) && (State.IRQFlag & 0x30)) { - if ((NDS::RCnt & 0xC100) == 0x8100) + if ((NDS.RCnt & 0xC100) == 0x8100) { // CHECKME: is the IRQ status readable in RCNT? - NDS::SetIRQ(1, NDS::IRQ_RTC); + NDS.SetIRQ(1, IRQ_RTC); } } } @@ -306,7 +306,7 @@ void RTC::ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write if (State.Alarm1[2] & (1<<7)) cond = cond && ((State.Alarm1[2] & 0x7F) == State.DateTime[5]); - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { if (State.AlarmDate1[1] & (1<<6)) cond = cond && (State.AlarmDate1[0] == State.DateTime[0]); @@ -348,7 +348,7 @@ void RTC::ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write if (State.Alarm2[2] & (1<<7)) cond = cond && ((State.Alarm2[2] & 0x7F) == State.DateTime[5]); - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { if (State.AlarmDate2[1] & (1<<6)) cond = cond && (State.AlarmDate2[0] == State.DateTime[0]); @@ -520,7 +520,7 @@ void RTC::ScheduleTimer(bool first) s32 delay = sysclock >> 15; TimerError = sysclock & 0x7FFF; - NDS::ScheduleEvent(NDS::Event_RTC, !first, delay, 0, 0); + NDS.ScheduleEvent(Event_RTC, !first, delay, 0, 0); } void RTC::ClockTimer(u32 param) @@ -647,7 +647,7 @@ void RTC::CmdRead() } else if ((CurCmd & 0x0F) == 0x0E) { - if (NDS::ConsoleType != 1) + if (NDS.ConsoleType != 1) { Log(LogLevel::Debug, "RTC: unknown read command %02X\n", CurCmd); return; @@ -797,7 +797,7 @@ void RTC::CmdWrite(u8 val) } else if ((CurCmd & 0x0F) == 0x0E) { - if (NDS::ConsoleType != 1) + if (NDS.ConsoleType != 1) { Log(LogLevel::Debug, "RTC: unknown write command %02X\n", CurCmd); return; @@ -852,7 +852,7 @@ void RTC::ByteIn(u8 val) else CurCmd = val; - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { // for DSi: handle extra commands diff --git a/src/RTC.h b/src/RTC.h index 45d0f26b..0caf5ee5 100644 --- a/src/RTC.h +++ b/src/RTC.h @@ -48,7 +48,7 @@ public: u8 AlarmDate2[3]; }; - RTC(); + RTC(melonDS::NDS& nds); ~RTC(); void Reset(); @@ -66,9 +66,7 @@ public: void Write(u16 val, bool byte); private: - /// This value represents the Nintendo DS IO register, - /// \em not the value of the system's clock. - /// The actual system time is taken directly from the host. + melonDS::NDS& NDS; u16 IO; u8 Input; diff --git a/src/SPI.cpp b/src/SPI.cpp index 3ded7bcc..b3e5b4e1 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -65,7 +65,7 @@ bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) } -FirmwareMem::FirmwareMem(SPIHost* host) : SPIDevice(host) +FirmwareMem::FirmwareMem(melonDS::NDS& nds) : SPIDevice(nds) { } @@ -79,7 +79,7 @@ void FirmwareMem::Reset() if (!Firmware) { Log(LogLevel::Warn, "SPI firmware: no firmware loaded! Using default\n"); - Firmware = std::make_unique(NDS::ConsoleType); + Firmware = std::make_unique(NDS.ConsoleType); } // fix touchscreen coords @@ -134,31 +134,32 @@ void FirmwareMem::DoSavestate(Savestate* file) file->Var32(&Addr); } -void FirmwareMem::SetupDirectBoot(bool dsi) +void FirmwareMem::SetupDirectBoot() { const auto& header = Firmware->GetHeader(); const auto& userdata = Firmware->GetEffectiveUserData(); - if (dsi) + if (NDS.ConsoleType == 1) { + // The ARMWrite methods are virtual, they'll delegate to DSi if necessary for (u32 i = 0; i < 6; i += 2) - DSi::ARM9Write16(0x02FFFCF4, *(u16*)&header.MacAddr[i]); // MAC address + NDS.ARM9Write16(0x02FFFCF4, *(u16*)&header.MacAddr[i]); // MAC address // checkme - DSi::ARM9Write16(0x02FFFCFA, header.EnabledChannels); // enabled channels + NDS.ARM9Write16(0x02FFFCFA, header.EnabledChannels); // enabled channels for (u32 i = 0; i < 0x70; i += 4) - DSi::ARM9Write32(0x02FFFC80+i, *(u32*)&userdata.Bytes[i]); + NDS.ARM9Write32(0x02FFFC80+i, *(u32*)&userdata.Bytes[i]); } else { - NDS::ARM9Write32(0x027FF864, 0); - NDS::ARM9Write32(0x027FF868, header.UserSettingsOffset << 3); // user settings offset + NDS.ARM9Write32(0x027FF864, 0); + NDS.ARM9Write32(0x027FF868, header.UserSettingsOffset << 3); // user settings offset - NDS::ARM9Write16(0x027FF874, header.DataGfxChecksum); // CRC16 for data/gfx - NDS::ARM9Write16(0x027FF876, header.GUIWifiCodeChecksum); // 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*)&userdata.Bytes[i]); + NDS.ARM9Write32(0x027FFC80+i, *(u32*)&userdata.Bytes[i]); } } @@ -326,7 +327,7 @@ void FirmwareMem::Release() -PowerMan::PowerMan(SPIHost* host) : SPIDevice(host) +PowerMan::PowerMan(melonDS::NDS& nds) : SPIDevice(nds) { } @@ -395,7 +396,7 @@ void PowerMan::Write(u8 val) switch (regid) { case 0: - if (val & 0x40) NDS::Stop(StopReason::PowerOff); // shutdown + if (val & 0x40) NDS.Stop(StopReason::PowerOff); // shutdown //printf("power %02X\n", val); break; case 4: @@ -410,7 +411,7 @@ void PowerMan::Write(u8 val) -TSC::TSC(SPIHost* host) : SPIDevice(host) +TSC::TSC(melonDS::NDS& nds) : SPIDevice(nds) { } @@ -452,13 +453,13 @@ void TSC::SetTouchCoords(u16 x, u16 y) if (y == 0xFFF) { // released - NDS::KeyInput |= (1 << (16+6)); + NDS.KeyInput |= (1 << (16+6)); return; } TouchX <<= 4; TouchY <<= 4; - NDS::KeyInput &= ~(1 << (16+6)); + NDS.KeyInput &= ~(1 << (16+6)); } void TSC::MicInputFrame(s16* data, int samples) @@ -500,7 +501,7 @@ void TSC::Write(u8 val) else { // 560190 cycles per frame - u32 cyclepos = (u32)NDS::GetSysClockCycles(2); + u32 cyclepos = (u32)NDS.GetSysClockCycles(2); u32 samplepos = (cyclepos * MicBufferLen) / 560190; if (samplepos >= MicBufferLen) samplepos = MicBufferLen-1; s16 sample = MicBuffer[samplepos]; @@ -529,17 +530,17 @@ void TSC::Write(u8 val) -SPIHost::SPIHost() +SPIHost::SPIHost(melonDS::NDS& nds) : NDS(nds) { - NDS::RegisterEventFunc(NDS::Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone)); + NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone)); - Devices[SPIDevice_FirmwareMem] = new FirmwareMem(this); - Devices[SPIDevice_PowerMan] = new PowerMan(this); + Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS); + Devices[SPIDevice_PowerMan] = new PowerMan(NDS); - if (NDS::ConsoleType == 1) - Devices[SPIDevice_TSC] = new DSi_TSC(this); + if (NDS.ConsoleType == 1) + Devices[SPIDevice_TSC] = new DSi_TSC(static_cast(NDS)); else - Devices[SPIDevice_TSC] = new TSC(this); + Devices[SPIDevice_TSC] = new TSC(NDS); } SPIHost::~SPIHost() @@ -552,7 +553,7 @@ SPIHost::~SPIHost() Devices[i] = nullptr; } - NDS::UnregisterEventFunc(NDS::Event_SPITransfer, 0); + NDS.UnregisterEventFunc(Event_SPITransfer, 0); } void SPIHost::Reset() @@ -603,7 +604,7 @@ void SPIHost::TransferDone(u32 param) Cnt &= ~(1<<7); if (Cnt & (1<<14)) - NDS::SetIRQ(1, NDS::IRQ_SPI); + NDS.SetIRQ(1, IRQ_SPI); } u8 SPIHost::ReadData() @@ -641,7 +642,7 @@ void SPIHost::WriteData(u8 val) // SPI transfers one bit per cycle -> 8 cycles per byte u32 delay = 8 * (8 << (Cnt & 0x3)); - NDS::ScheduleEvent(NDS::Event_SPITransfer, false, delay, 0, 0); + NDS.ScheduleEvent(Event_SPITransfer, false, delay, 0, 0); } } \ No newline at end of file diff --git a/src/SPI.h b/src/SPI.h index c889fb5e..f27f0c3e 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -42,11 +42,11 @@ enum u16 CRC16(const u8* data, u32 len, u32 start); class SPIHost; - +class NDS; class SPIDevice { public: - SPIDevice(SPIHost* host) : Host(host), Hold(false), DataPos(0) {} + SPIDevice(melonDS::NDS& nds) : NDS(nds), Hold(false), DataPos(0) {} virtual ~SPIDevice() {} virtual void Reset() = 0; virtual void DoSavestate(Savestate* file) = 0; @@ -56,7 +56,7 @@ public: virtual void Release() { Hold = false; DataPos = 0; } protected: - SPIHost* Host; + melonDS::NDS& NDS; bool Hold; u32 DataPos; @@ -66,12 +66,12 @@ protected: class FirmwareMem : public SPIDevice { public: - FirmwareMem(SPIHost* host); + FirmwareMem(melonDS::NDS& nds); ~FirmwareMem() override; void Reset() override; void DoSavestate(Savestate* file) override; - void SetupDirectBoot(bool dsi); + void SetupDirectBoot(); const class Firmware* GetFirmware(); bool IsLoadedFirmwareBuiltIn(); @@ -96,7 +96,7 @@ private: class PowerMan : public SPIDevice { public: - PowerMan(SPIHost* host); + PowerMan(melonDS::NDS& nds); ~PowerMan() override; void Reset() override; void DoSavestate(Savestate* file) override; @@ -116,7 +116,7 @@ private: class TSC : public SPIDevice { public: - TSC(SPIHost* host); + TSC(melonDS::NDS& nds); virtual ~TSC() override; virtual void Reset() override; virtual void DoSavestate(Savestate* file) override; @@ -141,7 +141,7 @@ protected: class SPIHost { public: - SPIHost(); + SPIHost(melonDS::NDS& nds); ~SPIHost(); void Reset(); void DoSavestate(Savestate* file); @@ -161,6 +161,7 @@ public: void TransferDone(u32 param); private: + melonDS::NDS& NDS; u16 Cnt; SPIDevice* Devices[3]; diff --git a/src/SPU.cpp b/src/SPU.cpp index a5570d66..00db3691 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -70,15 +70,15 @@ s16 SPUChannel::InterpCubic[0x100][4]; bool SPUChannel::InterpInited = false; -SPU::SPU() +SPU::SPU(melonDS::NDS& nds) : NDS(nds) { - NDS::RegisterEventFunc(NDS::Event_SPU, 0, MemberEventFunc(SPU, Mix)); + NDS.RegisterEventFunc(Event_SPU, 0, MemberEventFunc(SPU, Mix)); for (int i = 0; i < 16; i++) - Channels[i] = new SPUChannel(i); + Channels[i] = new SPUChannel(i, NDS); - Capture[0] = new SPUCaptureUnit(0); - Capture[1] = new SPUCaptureUnit(1); + Capture[0] = new SPUCaptureUnit(0, NDS); + Capture[1] = new SPUCaptureUnit(1, NDS); AudioLock = Platform::Mutex_Create(); @@ -137,7 +137,7 @@ SPU::~SPU() Platform::Mutex_Free(AudioLock); AudioLock = nullptr; - NDS::UnregisterEventFunc(NDS::Event_SPU, 0); + NDS.UnregisterEventFunc(Event_SPU, 0); } void SPU::Reset() @@ -154,7 +154,7 @@ void SPU::Reset() Capture[0]->Reset(); Capture[1]->Reset(); - NDS::ScheduleEvent(NDS::Event_SPU, false, 1024, 0, 0); + NDS.ScheduleEvent(Event_SPU, false, 1024, 0, 0); } void SPU::Stop() @@ -212,7 +212,7 @@ void SPU::SetDegrade10Bit(bool enable) } -SPUChannel::SPUChannel(u32 num) +SPUChannel::SPUChannel(u32 num, melonDS::NDS& nds) : NDS(nds) { Num = num; @@ -225,11 +225,6 @@ SPUChannel::~SPUChannel() void SPUChannel::Reset() { - if (NDS::ConsoleType == 1) - BusRead32 = DSi::ARM7Read32; - else - BusRead32 = NDS::ARM7Read32; - KeyOn = false; SetCnt(0); @@ -299,7 +294,7 @@ void SPUChannel::FIFO_BufferData() { for (u32 i = 0; i < burstlen; i += 4) { - FIFO[FIFOWritePos] = BusRead32(SrcAddr + FIFOReadOffset); + FIFO[FIFOWritePos] = NDS.ARM7Read32(SrcAddr + FIFOReadOffset); FIFOReadOffset += 4; FIFOWritePos++; FIFOWritePos &= 0x7; @@ -583,7 +578,7 @@ void SPUChannel::PanOutput(s32 in, s32& left, s32& right) } -SPUCaptureUnit::SPUCaptureUnit(u32 num) +SPUCaptureUnit::SPUCaptureUnit(u32 num, melonDS::NDS& nds) : NDS(nds), Num(num) { Num = num; } @@ -594,11 +589,6 @@ SPUCaptureUnit::~SPUCaptureUnit() void SPUCaptureUnit::Reset() { - if (NDS::ConsoleType == 1) - BusWrite32 = DSi::ARM7Write32; - else - BusWrite32 = NDS::ARM7Write32; - SetCnt(0); DstAddr = 0; TimerReload = 0; @@ -634,7 +624,8 @@ void SPUCaptureUnit::FIFO_FlushData() { for (u32 i = 0; i < 4; i++) { - BusWrite32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]); + NDS.ARM7Write32(DstAddr + FIFOWriteOffset, FIFO[FIFOReadPos]); + // Calls the NDS or DSi version, depending on the class FIFOReadPos++; FIFOReadPos &= 0x3; @@ -858,7 +849,7 @@ void SPU::Mix(u32 dummy) OutputBackbufferWritePosition += 2; } - NDS::ScheduleEvent(NDS::Event_SPU, true, 1024, 0, 0); + NDS.ScheduleEvent(Event_SPU, true, 1024, 0, 0); } void SPU::TransferOutput() diff --git a/src/SPU.h b/src/SPU.h index 144c19ba..bf0c658b 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -24,12 +24,13 @@ namespace melonDS { +class NDS; class SPU; class SPUChannel { public: - SPUChannel(u32 num); + SPUChannel(u32 num, melonDS::NDS& nds); ~SPUChannel(); void Reset(); void DoSavestate(Savestate* file); @@ -142,13 +143,13 @@ public: void PanOutput(s32 in, s32& left, s32& right); private: - u32 (*BusRead32)(u32 addr); + melonDS::NDS& NDS; }; class SPUCaptureUnit { public: - SPUCaptureUnit(u32 num); + SPUCaptureUnit(u32 num, melonDS::NDS&); ~SPUCaptureUnit(); void Reset(); void DoSavestate(Savestate* file); @@ -199,13 +200,13 @@ public: void Run(s32 sample); private: - void (*BusWrite32)(u32 addr, u32 val); + melonDS::NDS& NDS; }; class SPU { public: - SPU(); + SPU(melonDS::NDS& nds); ~SPU(); void Reset(); void DoSavestate(Savestate* file); @@ -240,6 +241,7 @@ public: private: static const u32 OutputBufferSize = 2*2048; + melonDS::NDS& NDS; s16 OutputBackbuffer[2 * OutputBufferSize]; u32 OutputBackbufferWritePosition; diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 1441d1dd..50275236 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -89,9 +89,9 @@ bool MACIsBroadcast(u8* a) } -Wifi::Wifi() +Wifi::Wifi(melonDS::NDS& nds) : NDS(nds) { - NDS::RegisterEventFunc(NDS::Event_Wifi, 0, MemberEventFunc(Wifi, USTimer)); + NDS.RegisterEventFunc(Event_Wifi, 0, MemberEventFunc(Wifi, USTimer)); //MPInited = false; //LANInited = false; @@ -114,7 +114,7 @@ Wifi::~Wifi() delete WifiAP; WifiAP = nullptr; - NDS::UnregisterEventFunc(NDS::Event_Wifi, 0); + NDS.UnregisterEventFunc(Event_Wifi, 0); } void Wifi::Reset() @@ -158,7 +158,7 @@ void Wifi::Reset() } #undef BBREG_FIXED - const Firmware* fw = NDS::SPI->GetFirmware(); + const Firmware* fw = NDS.SPI.GetFirmware(); RFVersion = fw->GetHeader().RFChipType; memset(RFRegs, 0, 4*0x40); @@ -168,7 +168,7 @@ void Wifi::Reset() IOPORT(0x000) = 0x1440; else if (console == Firmware::FirmwareConsoleType::DSLite) IOPORT(0x000) = 0xC340; - else if (NDS::ConsoleType == 1 && console == Firmware::FirmwareConsoleType::DSi) + else if (NDS.ConsoleType == 1 && console == Firmware::FirmwareConsoleType::DSi) IOPORT(0x000) = 0xC340; // DSi has the modern DS-wifi variant else { @@ -303,14 +303,14 @@ void Wifi::ScheduleTimer(bool first) s32 delay = (cycles + 999999) / 1000000; TimerError = (delay * 1000000) - cycles; - NDS::ScheduleEvent(NDS::Event_Wifi, !first, delay, 0, 0); + NDS.ScheduleEvent(Event_Wifi, !first, delay, 0, 0); } void Wifi::UpdatePowerOn() { bool on = Enabled; - if (NDS::ConsoleType == 1) + if (NDS.ConsoleType == 1) { // TODO for DSi: // * W_POWER_US doesn't work (atleast on DWM-W024) @@ -338,7 +338,7 @@ void Wifi::UpdatePowerOn() { Log(LogLevel::Debug, "WIFI: OFF\n"); - NDS::CancelEvent(NDS::Event_Wifi); + NDS.CancelEvent(Event_Wifi); Platform::MP_End(); } @@ -359,7 +359,7 @@ void Wifi::SetIRQ(u32 irq) u32 newflags = IOPORT(W_IF) & IOPORT(W_IE); if ((oldflags == 0) && (newflags != 0)) - NDS::SetIRQ(1, NDS::IRQ_Wifi); + NDS.SetIRQ(1, IRQ_Wifi); } void Wifi::SetIRQ13() diff --git a/src/Wifi.h b/src/Wifi.h index c47a8833..76fa1463 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -24,7 +24,7 @@ namespace melonDS { class WifiAP; - +class NDS; class Wifi { public: @@ -157,7 +157,7 @@ public: W_RXTXAddr = 0x268, }; - Wifi(); + Wifi(melonDS::NDS& nds); ~Wifi(); void Reset(); void DoSavestate(Savestate* file); @@ -173,6 +173,7 @@ public: u8* GetBSSID(); private: + melonDS::NDS& NDS; u8 RAM[0x2000]; u16 IO[0x1000>>1]; diff --git a/src/frontend/FrontendUtil.h b/src/frontend/FrontendUtil.h index 8d94b37d..6f09d4b7 100644 --- a/src/frontend/FrontendUtil.h +++ b/src/frontend/FrontendUtil.h @@ -24,6 +24,10 @@ #include #include +namespace melonDS +{ +class NDS; +} namespace Frontend { using namespace melonDS; @@ -105,14 +109,14 @@ int AudioOut_GetNumSamples(int outlen); void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume); // feed silence to the microphone input -void Mic_FeedSilence(); +void Mic_FeedSilence(NDS& nds); // feed random noise to the microphone input -void Mic_FeedNoise(); +void Mic_FeedNoise(NDS& nds); // feed an external buffer to the microphone input // buffer should be mono -void Mic_FeedExternalBuffer(); +void Mic_FeedExternalBuffer(NDS& nds); void Mic_SetExternalBuffer(s16* buffer, u32 len); } diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index 4a1d089e..02b3026e 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -82,13 +82,13 @@ void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volum } -void Mic_FeedSilence() +void Mic_FeedSilence(NDS& nds) { MicBufferReadPos = 0; - NDS::MicInputFrame(NULL, 0); + nds.MicInputFrame(NULL, 0); } -void Mic_FeedNoise() +void Mic_FeedNoise(NDS& nds) { int sample_len = sizeof(mic_blow) / sizeof(u16); static int sample_pos = 0; @@ -102,12 +102,12 @@ void Mic_FeedNoise() if (sample_pos >= sample_len) sample_pos = 0; } - NDS::MicInputFrame(tmp, 735); + nds.MicInputFrame(tmp, 735); } -void Mic_FeedExternalBuffer() +void Mic_FeedExternalBuffer(NDS& nds) { - if (!MicBuffer) return Mic_FeedSilence(); + if (!MicBuffer) return Mic_FeedSilence(nds); if ((MicBufferReadPos + 735) > MicBufferLength) { @@ -116,12 +116,12 @@ void Mic_FeedExternalBuffer() memcpy(&tmp[0], &MicBuffer[MicBufferReadPos], len1*sizeof(s16)); memcpy(&tmp[len1], &MicBuffer[0], (735 - len1)*sizeof(s16)); - NDS::MicInputFrame(tmp, 735); + nds.MicInputFrame(tmp, 735); MicBufferReadPos = 735 - len1; } else { - NDS::MicInputFrame(&MicBuffer[MicBufferReadPos], 735); + nds.MicInputFrame(&MicBuffer[MicBufferReadPos], 735); MicBufferReadPos += 735; } } diff --git a/src/frontend/qt_sdl/AudioInOut.cpp b/src/frontend/qt_sdl/AudioInOut.cpp index 90708b20..ae5529d9 100644 --- a/src/frontend/qt_sdl/AudioInOut.cpp +++ b/src/frontend/qt_sdl/AudioInOut.cpp @@ -55,8 +55,9 @@ void AudioCallback(void* data, Uint8* stream, int len) s16 buf_in[1024*2]; int num_in; + EmuThread* emuThread = (EmuThread*)data; SDL_LockMutex(audioSyncLock); - num_in = NDS::SPU->ReadOutput(buf_in, len_in); + num_in = emuThread->NDS->SPU.ReadOutput(buf_in, len_in); SDL_CondSignal(audioSync); SDL_UnlockMutex(audioSyncLock); @@ -244,7 +245,7 @@ void MicLoadWav(const std::string& name) SDL_FreeWAV(buf); } -void MicProcess() +void MicProcess(melonDS::NDS& nds) { int type = Config::MicInputType; bool cmd = Input::HotkeyDown(HK_Mic); @@ -257,16 +258,16 @@ void MicProcess() switch (type) { case micInputType_Silence: // no mic - Frontend::Mic_FeedSilence(); + Frontend::Mic_FeedSilence(nds); break; case micInputType_External: // host mic case micInputType_Wav: // WAV - Frontend::Mic_FeedExternalBuffer(); + Frontend::Mic_FeedExternalBuffer(nds); break; case micInputType_Noise: // blowing noise - Frontend::Mic_FeedNoise(); + Frontend::Mic_FeedNoise(nds); break; } } @@ -296,7 +297,7 @@ void SetupMicInputData() } } -void Init() +void Init(EmuThread* thread) { audioMuted = false; audioSync = SDL_CreateCond(); @@ -310,6 +311,7 @@ void Init() whatIwant.channels = 2; whatIwant.samples = 1024; whatIwant.callback = AudioCallback; + whatIwant.userdata = thread; audioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE); if (!audioDevice) { @@ -349,12 +351,12 @@ void DeInit() micWavBuffer = nullptr; } -void AudioSync() +void AudioSync(NDS& nds) { if (audioDevice) { SDL_LockMutex(audioSyncLock); - while (NDS::SPU->GetOutputSize() > 1024) + while (nds.SPU.GetOutputSize() > 1024) { int ret = SDL_CondWaitTimeout(audioSync, audioSyncLock, 500); if (ret == SDL_MUTEX_TIMEDOUT) break; @@ -363,11 +365,11 @@ void AudioSync() } } -void UpdateSettings() +void UpdateSettings(NDS& nds) { MicClose(); - NDS::SPU->SetInterpolation(Config::AudioInterp); + nds.SPU.SetInterpolation(Config::AudioInterp); SetupMicInputData(); MicOpen(); diff --git a/src/frontend/qt_sdl/AudioInOut.h b/src/frontend/qt_sdl/AudioInOut.h index cc386251..0bf36540 100644 --- a/src/frontend/qt_sdl/AudioInOut.h +++ b/src/frontend/qt_sdl/AudioInOut.h @@ -23,18 +23,23 @@ #include +class EmuThread; +namespace melonDS +{ +class NDS; +} namespace AudioInOut { -void Init(); +void Init(EmuThread* thread); void DeInit(); -void MicProcess(); +void MicProcess(melonDS::NDS& nds); void AudioMute(QMainWindow* mainWindow); -void AudioSync(); +void AudioSync(melonDS::NDS& nds); -void UpdateSettings(); +void UpdateSettings(melonDS::NDS& nds); void Enable(); void Disable(); diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index cb42e641..5e8812e9 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -29,7 +29,7 @@ #include "AudioSettingsDialog.h" #include "ui_AudioSettingsDialog.h" - +#include "main.h" using namespace melonDS; AudioSettingsDialog* AudioSettingsDialog::currentDlg = nullptr; @@ -37,7 +37,7 @@ AudioSettingsDialog* AudioSettingsDialog::currentDlg = nullptr; extern std::string EmuDirectory; -AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDialog(parent), ui(new Ui::AudioSettingsDialog) +AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive, EmuThread* emuThread) : QDialog(parent), ui(new Ui::AudioSettingsDialog), emuThread(emuThread) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -65,7 +65,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive) : QDia ui->chkSyncDSiVolume->setChecked(Config::DSiVolumeSync); // Setup volume slider accordingly - if (emuActive && NDS::ConsoleType == 1) + if (emuActive && emuThread->NDS->ConsoleType == 1) { on_chkSyncDSiVolume_clicked(Config::DSiVolumeSync); } @@ -125,10 +125,11 @@ AudioSettingsDialog::~AudioSettingsDialog() void AudioSettingsDialog::onSyncVolumeLevel() { - if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + if (Config::DSiVolumeSync && emuThread->NDS->ConsoleType == 1) { + auto& dsi = static_cast(*emuThread->NDS); bool state = ui->slVolume->blockSignals(true); - ui->slVolume->setValue(DSi::I2C->GetBPTWL()->GetVolumeLevel()); + ui->slVolume->setValue(dsi.I2C.GetBPTWL()->GetVolumeLevel()); ui->slVolume->blockSignals(state); } } @@ -136,7 +137,7 @@ void AudioSettingsDialog::onSyncVolumeLevel() void AudioSettingsDialog::onConsoleReset() { on_chkSyncDSiVolume_clicked(Config::DSiVolumeSync); - ui->chkSyncDSiVolume->setEnabled(NDS::ConsoleType == 1); + ui->chkSyncDSiVolume->setEnabled(emuThread->NDS->ConsoleType == 1); } void AudioSettingsDialog::on_AudioSettingsDialog_accepted() @@ -181,9 +182,10 @@ void AudioSettingsDialog::on_cbInterpolation_currentIndexChanged(int idx) void AudioSettingsDialog::on_slVolume_valueChanged(int val) { - if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + if (Config::DSiVolumeSync && emuThread->NDS->ConsoleType == 1) { - DSi::I2C->GetBPTWL()->SetVolumeLevel(val); + auto& dsi = static_cast(*emuThread->NDS); + dsi.I2C.GetBPTWL()->SetVolumeLevel(val); return; } @@ -195,10 +197,11 @@ void AudioSettingsDialog::on_chkSyncDSiVolume_clicked(bool checked) Config::DSiVolumeSync = checked; bool state = ui->slVolume->blockSignals(true); - if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + if (Config::DSiVolumeSync && emuThread->NDS->ConsoleType == 1) { + auto& dsi = static_cast(*emuThread->NDS); ui->slVolume->setMaximum(31); - ui->slVolume->setValue(DSi::I2C->GetBPTWL()->GetVolumeLevel()); + ui->slVolume->setValue(dsi.I2C.GetBPTWL()->GetVolumeLevel()); ui->slVolume->setPageStep(4); ui->slVolume->setTickPosition(QSlider::TicksBelow); } diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.h b/src/frontend/qt_sdl/AudioSettingsDialog.h index 98060b2a..ced9bae9 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.h +++ b/src/frontend/qt_sdl/AudioSettingsDialog.h @@ -24,17 +24,18 @@ namespace Ui { class AudioSettingsDialog; } class AudioSettingsDialog; +class EmuThread; class AudioSettingsDialog : public QDialog { Q_OBJECT public: - explicit AudioSettingsDialog(QWidget* parent, bool emuActive); + explicit AudioSettingsDialog(QWidget* parent, bool emuActive, EmuThread* emuThread); ~AudioSettingsDialog(); static AudioSettingsDialog* currentDlg; - static AudioSettingsDialog* openDlg(QWidget* parent, bool emuActive) + static AudioSettingsDialog* openDlg(QWidget* parent, bool emuActive, EmuThread* emuThread) { if (currentDlg) { @@ -42,7 +43,7 @@ public: return currentDlg; } - currentDlg = new AudioSettingsDialog(parent, emuActive); + currentDlg = new AudioSettingsDialog(parent, emuActive, emuThread); currentDlg->show(); return currentDlg; } @@ -69,6 +70,7 @@ private slots: void on_btnMicWavBrowse_clicked(); private: + EmuThread* emuThread; Ui::AudioSettingsDialog* ui; int oldInterp; diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp index 14ccd51c..3d47c45a 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.cpp @@ -29,45 +29,59 @@ #include "types.h" #include +#include "main.h" using namespace melonDS; PowerManagementDialog* PowerManagementDialog::currentDlg = nullptr; -PowerManagementDialog::PowerManagementDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PowerManagementDialog) +PowerManagementDialog::PowerManagementDialog(QWidget* parent, EmuThread* emuThread) : QDialog(parent), emuThread(emuThread), ui(new Ui::PowerManagementDialog) { inited = false; ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - if (NDS::ConsoleType == 1) + if (emuThread->NDS->ConsoleType == 1) { ui->grpDSBattery->setEnabled(false); - oldDSiBatteryLevel = DSi::I2C->GetBPTWL()->GetBatteryLevel(); - oldDSiBatteryCharging = DSi::I2C->GetBPTWL()->GetBatteryCharging(); + auto& dsi = static_cast(*emuThread->NDS); + oldDSiBatteryLevel = dsi.I2C.GetBPTWL()->GetBatteryLevel(); + oldDSiBatteryCharging = dsi.I2C.GetBPTWL()->GetBatteryCharging(); } else { ui->grpDSiBattery->setEnabled(false); - oldDSBatteryLevel = NDS::SPI->GetPowerMan()->GetBatteryLevelOkay(); + oldDSBatteryLevel = emuThread->NDS->SPI.GetPowerMan()->GetBatteryLevelOkay(); } updateDSBatteryLevelControls(); - ui->cbDSiBatteryCharging->setChecked(DSi::I2C->GetBPTWL()->GetBatteryCharging()); - int dsiBatterySliderPos; - switch (DSi::I2C->GetBPTWL()->GetBatteryLevel()) + bool defaultDSiBatteryCharging = (emuThread->NDS->ConsoleType == 1) ? Config::DSiBatteryCharging : false; + + if (emuThread->NDS->ConsoleType == 1) { + auto& dsi = static_cast(*emuThread->NDS); + ui->cbDSiBatteryCharging->setChecked(dsi.I2C.GetBPTWL()->GetBatteryCharging()); + int dsiBatterySliderPos = 4; + switch (dsi.I2C.GetBPTWL()->GetBatteryLevel()) + { case DSi_BPTWL::batteryLevel_AlmostEmpty: dsiBatterySliderPos = 0; break; case DSi_BPTWL::batteryLevel_Low: dsiBatterySliderPos = 1; break; case DSi_BPTWL::batteryLevel_Half: dsiBatterySliderPos = 2; break; case DSi_BPTWL::batteryLevel_ThreeQuarters: dsiBatterySliderPos = 3; break; case DSi_BPTWL::batteryLevel_Full: dsiBatterySliderPos = 4; break; + } + ui->sliderDSiBatteryLevel->setValue(dsiBatterySliderPos); } - ui->sliderDSiBatteryLevel->setValue(dsiBatterySliderPos); + else + { + ui->cbDSiBatteryCharging->setChecked(Config::DSiBatteryCharging); + ui->sliderDSiBatteryLevel->setValue(Config::DSiBatteryLevel); + } + int inst = Platform::InstanceID(); if (inst > 0) @@ -87,26 +101,28 @@ void PowerManagementDialog::done(int r) { if (r == QDialog::Accepted) { - if (NDS::ConsoleType == 1) + if (emuThread->NDS->ConsoleType == 1) { - Config::DSiBatteryLevel = DSi::I2C->GetBPTWL()->GetBatteryLevel(); - Config::DSiBatteryCharging = DSi::I2C->GetBPTWL()->GetBatteryCharging(); + auto& dsi = static_cast(*emuThread->NDS); + Config::DSiBatteryLevel = dsi.I2C.GetBPTWL()->GetBatteryLevel(); + Config::DSiBatteryCharging = dsi.I2C.GetBPTWL()->GetBatteryCharging(); } else { - Config::DSBatteryLevelOkay = NDS::SPI->GetPowerMan()->GetBatteryLevelOkay(); + Config::DSBatteryLevelOkay = emuThread->NDS->SPI.GetPowerMan()->GetBatteryLevelOkay(); } } else { - if (NDS::ConsoleType == 1) + if (emuThread->NDS->ConsoleType == 1) { - DSi::I2C->GetBPTWL()->SetBatteryLevel(oldDSiBatteryLevel); - DSi::I2C->GetBPTWL()->SetBatteryCharging(oldDSiBatteryCharging); + auto& dsi = static_cast(*emuThread->NDS); + dsi.I2C.GetBPTWL()->SetBatteryLevel(oldDSiBatteryLevel); + dsi.I2C.GetBPTWL()->SetBatteryCharging(oldDSiBatteryCharging); } else { - NDS::SPI->GetPowerMan()->SetBatteryLevelOkay(oldDSBatteryLevel); + emuThread->NDS->SPI.GetPowerMan()->SetBatteryLevelOkay(oldDSBatteryLevel); } } @@ -117,17 +133,17 @@ void PowerManagementDialog::done(int r) void PowerManagementDialog::on_rbDSBatteryLow_clicked() { - NDS::SPI->GetPowerMan()->SetBatteryLevelOkay(false); + emuThread->NDS->SPI.GetPowerMan()->SetBatteryLevelOkay(false); } void PowerManagementDialog::on_rbDSBatteryOkay_clicked() { - NDS::SPI->GetPowerMan()->SetBatteryLevelOkay(true); + emuThread->NDS->SPI.GetPowerMan()->SetBatteryLevelOkay(true); } void PowerManagementDialog::updateDSBatteryLevelControls() { - if (NDS::SPI->GetPowerMan()->GetBatteryLevelOkay()) + if (emuThread->NDS->SPI.GetPowerMan()->GetBatteryLevelOkay()) ui->rbDSBatteryOkay->setChecked(true); else ui->rbDSBatteryLow->setChecked(true); @@ -135,23 +151,32 @@ void PowerManagementDialog::updateDSBatteryLevelControls() void PowerManagementDialog::on_cbDSiBatteryCharging_toggled() { - DSi::I2C->GetBPTWL()->SetBatteryCharging(ui->cbDSiBatteryCharging->isChecked()); + if (emuThread->NDS->ConsoleType == 1) + { + auto& dsi = static_cast(*emuThread->NDS); + dsi.I2C.GetBPTWL()->SetBatteryCharging(ui->cbDSiBatteryCharging->isChecked()); + } } void PowerManagementDialog::on_sliderDSiBatteryLevel_valueChanged(int value) { if (!inited) return; - u8 newBatteryLevel; - switch (value) + if (emuThread->NDS->ConsoleType == 1) { - case 0: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_AlmostEmpty; break; - case 1: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_Low; break; - case 2: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_Half; break; - case 3: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_ThreeQuarters; break; - case 4: newBatteryLevel = DSi::I2C->GetBPTWL()->batteryLevel_Full; break; + auto& dsi = static_cast(*emuThread->NDS); + u8 newBatteryLevel = DSi_BPTWL::batteryLevel_Full; + switch (value) + { + case 0: newBatteryLevel = DSi_BPTWL::batteryLevel_AlmostEmpty; break; + case 1: newBatteryLevel = DSi_BPTWL::batteryLevel_Low; break; + case 2: newBatteryLevel = DSi_BPTWL::batteryLevel_Half; break; + case 3: newBatteryLevel = DSi_BPTWL::batteryLevel_ThreeQuarters; break; + case 4: newBatteryLevel = DSi_BPTWL::batteryLevel_Full; break; + } + dsi.I2C.GetBPTWL()->SetBatteryLevel(newBatteryLevel); } - DSi::I2C->GetBPTWL()->SetBatteryLevel(newBatteryLevel); + updateDSBatteryLevelControls(); } diff --git a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h index cd2954a1..bc2abc3d 100644 --- a/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h +++ b/src/frontend/qt_sdl/PowerManagement/PowerManagementDialog.h @@ -25,6 +25,7 @@ #include "types.h" namespace Ui { class PowerManagementDialog; } +class EmuThread; class PowerManagementDialog; class PowerManagementDialog : public QDialog @@ -32,11 +33,11 @@ class PowerManagementDialog : public QDialog Q_OBJECT public: - explicit PowerManagementDialog(QWidget* parent); + explicit PowerManagementDialog(QWidget* parent, EmuThread* emu_thread); ~PowerManagementDialog(); static PowerManagementDialog* currentDlg; - static PowerManagementDialog* openDlg(QWidget* parent) + static PowerManagementDialog* openDlg(QWidget* parent, EmuThread* emu_thread) { if (currentDlg) { @@ -44,7 +45,7 @@ public: return currentDlg; } - currentDlg = new PowerManagementDialog(parent); + currentDlg = new PowerManagementDialog(parent, emu_thread); currentDlg->open(); return currentDlg; } @@ -64,6 +65,7 @@ private slots: private: Ui::PowerManagementDialog* ui; + EmuThread* emuThread; bool inited; bool oldDSBatteryLevel; diff --git a/src/frontend/qt_sdl/RAMInfoDialog.cpp b/src/frontend/qt_sdl/RAMInfoDialog.cpp index b58662c8..5bff99ad 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.cpp +++ b/src/frontend/qt_sdl/RAMInfoDialog.cpp @@ -24,16 +24,16 @@ using namespace melonDS; extern EmuThread* emuThread; -s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType) +s32 GetMainRAMValue(NDS& nds, const u32& addr, const ramInfo_ByteType& byteType) { switch (byteType) { case ramInfo_OneByte: - return *(s8*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + return *(s8*)(nds.MainRAM + (addr&nds.MainRAMMask)); case ramInfo_TwoBytes: - return *(s16*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + return *(s16*)(nds.MainRAM + (addr&nds.MainRAMMask)); case ramInfo_FourBytes: - return *(s32*)(NDS::MainRAM + (addr&NDS::MainRAMMask)); + return *(s32*)(nds.MainRAM + (addr&nds.MainRAMMask)); default: return 0; } @@ -41,7 +41,7 @@ s32 GetMainRAMValue(const u32& addr, const ramInfo_ByteType& byteType) RAMInfoDialog* RAMInfoDialog::currentDlg = nullptr; -RAMInfoDialog::RAMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::RAMInfoDialog) +RAMInfoDialog::RAMInfoDialog(QWidget* parent, EmuThread* emuThread) : QDialog(parent), emuThread(emuThread), ui(new Ui::RAMInfoDialog) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); @@ -91,7 +91,7 @@ void RAMInfoDialog::ShowRowsInTable() for (u32 row = scrollValue; row < std::min(scrollValue+25, RowDataVector->size()); row++) { ramInfo_RowData& rowData = RowDataVector->at(row); - rowData.Update(SearchThread->GetSearchByteType()); + rowData.Update(*emuThread->NDS, SearchThread->GetSearchByteType()); if (ui->ramTable->item(row, ramInfo_Address) == nullptr) { @@ -186,7 +186,7 @@ void RAMInfoDialog::on_ramTable_itemChanged(QTableWidgetItem *item) s32 itemValue = item->text().toInt(); if (rowData.Value != itemValue) - rowData.SetValue(itemValue); + rowData.SetValue(*emuThread->NDS, itemValue); } /** @@ -241,14 +241,14 @@ void RAMSearchThread::run() if (SearchMode == ramInfoSTh_SearchAll || RowDataVector->size() == 0) { // First search mode - for (u32 addr = 0x02000000; SearchRunning && addr < 0x02000000+NDS::MainRAMMaxSize; addr += SearchByteType) + for (u32 addr = 0x02000000; SearchRunning && addr < 0x02000000+MainRAMMaxSize; addr += SearchByteType) { - const s32& value = GetMainRAMValue(addr, SearchByteType); + const s32& value = GetMainRAMValue(*emuThread->NDS, addr, SearchByteType); RowDataVector->push_back({ addr, value, value }); // A solution to prevent to call too many slot. - u32 newProgress = (int)((addr-0x02000000) / (NDS::MainRAMMaxSize-1.0f) * 100); + u32 newProgress = (int)((addr-0x02000000) / (MainRAMMaxSize-1.0f) * 100); if (progress < newProgress) { progress = newProgress; @@ -264,7 +264,7 @@ void RAMSearchThread::run() for (u32 row = 0; SearchRunning && row < RowDataVector->size(); row++) { const u32& addr = RowDataVector->at(row).Address; - const s32& value = GetMainRAMValue(addr, SearchByteType); + const s32& value = GetMainRAMValue(*emuThread->NDS, addr, SearchByteType); if (SearchValue == value) newRowDataVector->push_back({ addr, value, value }); diff --git a/src/frontend/qt_sdl/RAMInfoDialog.h b/src/frontend/qt_sdl/RAMInfoDialog.h index adc9b284..2a5b1620 100644 --- a/src/frontend/qt_sdl/RAMInfoDialog.h +++ b/src/frontend/qt_sdl/RAMInfoDialog.h @@ -32,6 +32,7 @@ namespace Ui { class RAMInfoDialog; } class RAMInfoDialog; class RAMSearchThread; class RAMUpdateThread; +class EmuThread; enum ramInfo_ByteType { @@ -53,7 +54,7 @@ enum ramInfo_Previous }; -melonDS::s32 GetMainRAMValue(const melonDS::u32& addr, const ramInfo_ByteType& byteType); +melonDS::s32 GetMainRAMValue(melonDS::NDS& nds, const melonDS::u32& addr, const ramInfo_ByteType& byteType); struct ramInfo_RowData { @@ -61,14 +62,14 @@ struct ramInfo_RowData melonDS::s32 Value; melonDS::s32 Previous; - void Update(const ramInfo_ByteType& byteType) + void Update(melonDS::NDS& nds, const ramInfo_ByteType& byteType) { - Value = GetMainRAMValue(Address, byteType); + Value = GetMainRAMValue(nds, Address, byteType); } - void SetValue(const melonDS::s32& value) + void SetValue(melonDS::NDS& nds, const melonDS::s32& value) { - melonDS::NDS::MainRAM[Address&melonDS::NDS::MainRAMMask] = (melonDS::u32)value; + nds.MainRAM[Address&nds.MainRAMMask] = (melonDS::u32)value; Value = value; } }; @@ -78,11 +79,11 @@ class RAMInfoDialog : public QDialog Q_OBJECT public: - explicit RAMInfoDialog(QWidget* parent); + explicit RAMInfoDialog(QWidget* parent, EmuThread* emuThread); ~RAMInfoDialog(); static RAMInfoDialog* currentDlg; - static RAMInfoDialog* openDlg(QWidget* parent) + static RAMInfoDialog* openDlg(QWidget* parent, EmuThread* emuThread) { if (currentDlg) { @@ -90,7 +91,7 @@ public: return currentDlg; } - currentDlg = new RAMInfoDialog(parent); + currentDlg = new RAMInfoDialog(parent, emuThread); currentDlg->show(); return currentDlg; } @@ -118,6 +119,7 @@ private slots: void SetProgressbarValue(const melonDS::u32& value); private: + EmuThread* emuThread; Ui::RAMInfoDialog* ui; RAMSearchThread* SearchThread; diff --git a/src/frontend/qt_sdl/ROMInfoDialog.cpp b/src/frontend/qt_sdl/ROMInfoDialog.cpp index 6a46b18e..0f10b2f5 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.cpp +++ b/src/frontend/qt_sdl/ROMInfoDialog.cpp @@ -42,14 +42,13 @@ QString QStringBytes(u64 num) ROMInfoDialog* ROMInfoDialog::currentDlg = nullptr; -ROMInfoDialog::ROMInfoDialog(QWidget* parent) : QDialog(parent), ui(new Ui::ROMInfoDialog) +ROMInfoDialog::ROMInfoDialog(QWidget* parent, const melonDS::NDSCart::CartCommon& rom) : QDialog(parent), ui(new Ui::ROMInfoDialog) { ui->setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - const NDSBanner* banner = NDS::NDSCartSlot->GetCart()->Banner(); - const NDSHeader& header = NDS::NDSCartSlot->GetCart()->GetHeader(); - + const NDSBanner* banner = rom.Banner(); + const NDSHeader& header = rom.GetHeader(); u32 iconData[32 * 32]; ROMManager::ROMIcon(banner->Icon, banner->Palette, iconData); iconImage = QImage(reinterpret_cast(iconData), 32, 32, QImage::Format_RGBA8888).copy(); diff --git a/src/frontend/qt_sdl/ROMInfoDialog.h b/src/frontend/qt_sdl/ROMInfoDialog.h index 7316e98a..f7e3b5f5 100644 --- a/src/frontend/qt_sdl/ROMInfoDialog.h +++ b/src/frontend/qt_sdl/ROMInfoDialog.h @@ -29,17 +29,17 @@ namespace Ui { class ROMInfoDialog; } class ROMInfoDialog; - +namespace melonDS::NDSCart { class CartCommon; } class ROMInfoDialog : public QDialog { Q_OBJECT public: - explicit ROMInfoDialog(QWidget* parent); + explicit ROMInfoDialog(QWidget* parent, const melonDS::NDSCart::CartCommon& rom); ~ROMInfoDialog(); static ROMInfoDialog* currentDlg; - static ROMInfoDialog* openDlg(QWidget* parent) + static ROMInfoDialog* openDlg(QWidget* parent, const melonDS::NDSCart::CartCommon& rom) { if (currentDlg) { @@ -47,7 +47,7 @@ public: return currentDlg; } - currentDlg = new ROMInfoDialog(parent); + currentDlg = new ROMInfoDialog(parent, rom); currentDlg->open(); return currentDlg; } diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 9584c381..2c0cee2c 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -44,6 +44,7 @@ #include "RTC.h" #include "DSi_I2C.h" #include "FreeBIOS.h" +#include "main.h" using std::make_unique; using std::pair; @@ -316,7 +317,7 @@ bool SavestateExists(int slot) return Platform::FileExists(ssfile); } -bool LoadState(const std::string& filename) +bool LoadState(NDS& nds, const std::string& filename) { FILE* file = fopen(filename.c_str(), "rb"); if (file == nullptr) @@ -333,7 +334,7 @@ bool LoadState(const std::string& filename) return false; } - if (!NDS::DoSavestate(backup.get()) || backup->Error) + if (!nds.DoSavestate(backup.get()) || backup->Error) { // Back up the emulator's state. If that failed... Platform::Log(Platform::LogLevel::Error, "Failed to back up state, aborting load (from \"%s\")\n", filename.c_str()); fclose(file); @@ -365,7 +366,7 @@ bool LoadState(const std::string& filename) // Get ready to load the state from the buffer into the emulator std::unique_ptr state = std::make_unique(buffer.data(), size, false); - if (!NDS::DoSavestate(state.get()) || state->Error) + if (!nds.DoSavestate(state.get()) || state->Error) { // If we couldn't load the savestate from the buffer... Platform::Log(Platform::LogLevel::Error, "Failed to load state file \"%s\" into emulator\n", filename.c_str()); return false; @@ -390,7 +391,7 @@ bool LoadState(const std::string& filename) return true; } -bool SaveState(const std::string& filename) +bool SaveState(NDS& nds, const std::string& filename) { FILE* file = fopen(filename.c_str(), "wb"); @@ -407,7 +408,7 @@ bool SaveState(const std::string& filename) } // Write the savestate to the in-memory buffer - NDS::DoSavestate(&state); + nds.DoSavestate(&state); if (state.Error) { @@ -439,7 +440,7 @@ bool SaveState(const std::string& filename) return true; } -void UndoStateLoad() +void UndoStateLoad(NDS& nds) { if (!SavestateLoaded || !BackupState) return; @@ -448,7 +449,7 @@ void UndoStateLoad() // pray that this works // what do we do if it doesn't??? // but it should work. - NDS::DoSavestate(BackupState.get()); + nds.DoSavestate(BackupState.get()); if (NDSSave && (!PreviousSaveFile.empty())) { @@ -457,36 +458,264 @@ void UndoStateLoad() } -void UnloadCheats() +void UnloadCheats(NDS& nds) { if (CheatFile) { delete CheatFile; CheatFile = nullptr; - NDS::AREngine->SetCodeFile(nullptr); + nds.AREngine.SetCodeFile(nullptr); } } -void LoadCheats() +void LoadCheats(NDS& nds) { - UnloadCheats(); + UnloadCheats(nds); std::string filename = GetAssetPath(false, Config::CheatFilePath, ".mch"); // TODO: check for error (malformed cheat file, ...) CheatFile = new ARCodeFile(filename); - NDS::AREngine->SetCodeFile(CheatsOn ? CheatFile : nullptr); + nds.AREngine.SetCodeFile(CheatsOn ? CheatFile : nullptr); } -void LoadBIOSFiles() +std::optional> LoadARM9BIOS() noexcept +{ + if (FileHandle* f = OpenLocalFile(Config::BIOS9Path, Read)) + { + std::array bios {}; + FileRewind(f); + FileRead(bios.data(), sizeof(bios), 1, f); + CloseFile(f); + Log(Info, "ARM9 BIOS loaded from %s\n", Config::BIOS9Path.c_str()); + return bios; + } + + Log(Warn, "ARM9 BIOS not found\n"); + return std::nullopt; +} + +std::optional> LoadARM7BIOS() noexcept +{ + if (FileHandle* f = OpenLocalFile(Config::BIOS7Path, Read)) + { + std::array bios {}; + FileRead(bios.data(), sizeof(bios), 1, f); + CloseFile(f); + Log(Info, "ARM7 BIOS loaded from %s\n", Config::BIOS7Path.c_str()); + return bios; + } + + Log(Warn, "ARM7 BIOS not found\n"); + return std::nullopt; +} + +std::optional> LoadDSiARM9BIOS() noexcept +{ + if (FileHandle* f = OpenLocalFile(Config::DSiBIOS9Path, Read)) + { + std::array bios {}; + FileRead(bios.data(), sizeof(bios), 1, f); + CloseFile(f); + Log(Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str()); + return bios; + } + + Log(Warn, "ARM9i BIOS not found\n"); + return std::nullopt; +} + +std::optional> LoadDSiARM7BIOS() noexcept +{ + if (FileHandle* f = OpenLocalFile(Config::DSiBIOS7Path, Read)) + { + std::array bios {}; + FileRead(bios.data(), sizeof(bios), 1, f); + CloseFile(f); + Log(Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str()); + return bios; + } + + Log(Warn, "ARM7i BIOS not found\n"); + return std::nullopt; +} + +Firmware GenerateFirmware(int type) noexcept +{ + // Construct the default firmware... + string settingspath; + Firmware firmware = Firmware(type); + assert(firmware.Buffer() != nullptr); + + // 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. + // We don't need to save the whole firmware, just the part that may actually change. + if (FileHandle* f = OpenLocalFile(Config::WifiSettingsPath, Read)) + {// If we have Wi-fi settings to load... + constexpr unsigned TOTAL_WFC_SETTINGS_SIZE = 3 * (sizeof(Firmware::WifiAccessPoint) + sizeof(Firmware::ExtendedWifiAccessPoint)); + + if (!FileRead(firmware.GetExtendedAccessPointPosition(), TOTAL_WFC_SETTINGS_SIZE, 1, f)) + { // If we couldn't read the Wi-fi settings from this file... + Log(Warn, "Failed to read Wi-fi settings from \"%s\"; using defaults instead\n", Config::WifiSettingsPath.c_str()); + + // 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.) + firmware.GetAccessPoints() = { + Firmware::WifiAccessPoint(type), + Firmware::WifiAccessPoint(), + Firmware::WifiAccessPoint(), + }; + + firmware.GetExtendedAccessPoints() = { + Firmware::ExtendedWifiAccessPoint(), + Firmware::ExtendedWifiAccessPoint(), + Firmware::ExtendedWifiAccessPoint(), + }; + firmware.UpdateChecksums(); + CloseFile(f); + } + } + + CustomizeFirmware(firmware); + + // If we don't have Wi-fi settings to load, + // then the defaults will have already been populated by the constructor. + return firmware; +} + +std::optional LoadFirmware(int type) noexcept +{ + const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath; + + Log(Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str()); + + FileHandle* file = OpenLocalFile(firmwarepath, Read); + + if (!file) + { + Log(Error, "SPI firmware: couldn't open firmware file!\n"); + return std::nullopt; + } + Firmware firmware(file); + CloseFile(file); + + if (!firmware.Buffer()) + { + Log(Error, "SPI firmware: couldn't read firmware file!\n"); + return std::nullopt; + } + + CustomizeFirmware(firmware); + + return firmware; +} + + +std::optional LoadNAND(const std::array& arm7ibios) noexcept +{ + FileHandle* nandfile = OpenLocalFile(Config::DSiNANDPath, ReadWriteExisting); + if (!nandfile) + return std::nullopt; + + DSi_NAND::NANDImage nandImage(nandfile, &arm7ibios[0x8308]); + if (!nandImage) + { + Log(Error, "Failed to parse DSi NAND\n"); + return std::nullopt; + // the NANDImage takes ownership of the FileHandle, no need to clean it up here + } + + // scoped so that mount isn't alive when we move the NAND image to DSi::NANDImage + { + auto mount = DSi_NAND::NANDMount(nandImage); + if (!mount) + { + Log(Error, "Failed to mount DSi NAND\n"); + return std::nullopt; + } + + DSi_NAND::DSiFirmwareSystemSettings settings {}; + if (!mount.ReadUserData(settings)) + { + Log(Error, "Failed to read DSi NAND user data\n"); + return std::nullopt; + } + + // override user settings, if needed + if (Config::FirmwareOverrideSettings) + { + // we store relevant strings as UTF-8, so we need to convert them to UTF-16 + auto converter = wstring_convert, char16_t>{}; + + // setting up username + std::u16string username = converter.from_bytes(Config::FirmwareUsername); + size_t usernameLength = std::min(username.length(), (size_t) 10); + memset(&settings.Nickname, 0, sizeof(settings.Nickname)); + memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t)); + + // setting language + settings.Language = static_cast(Config::FirmwareLanguage); + + // setting up color + settings.FavoriteColor = Config::FirmwareFavouriteColour; + + // setting up birthday + settings.BirthdayMonth = Config::FirmwareBirthdayMonth; + settings.BirthdayDay = Config::FirmwareBirthdayDay; + + // setup message + std::u16string message = converter.from_bytes(Config::FirmwareMessage); + size_t messageLength = std::min(message.length(), (size_t) 26); + memset(&settings.Message, 0, sizeof(settings.Message)); + memcpy(&settings.Message, message.data(), messageLength * sizeof(char16_t)); + + // TODO: make other items configurable? + } + + // fix touchscreen coords + settings.TouchCalibrationADC1 = {0, 0}; + settings.TouchCalibrationPixel1 = {0, 0}; + settings.TouchCalibrationADC2 = {255 << 4, 191 << 4}; + settings.TouchCalibrationPixel2 = {255, 191}; + + settings.UpdateHash(); + + if (!mount.ApplyUserData(settings)) + { + Log(LogLevel::Error, "Failed to write patched DSi NAND user data\n"); + return std::nullopt; + } + } + + return nandImage; +} + +constexpr int imgsizes[] = {0, 256, 512, 1024, 2048, 4096}; +std::optional LoadDSiSDCard() noexcept +{ + if (!Config::DSiSDEnable) + return std::nullopt; + + return FATStorage( + Config::DSiSDPath, + imgsizes[Config::DSiSDSize], + Config::DSiSDReadOnly, + Config::DSiSDFolderSync ? Config::DSiSDFolderPath : "" + ); +} + +void LoadBIOSFiles(NDS& nds) { if (Config::ExternalBIOSEnable) { if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS9Path, FileMode::Read)) { FileRewind(f); - FileRead(NDS::ARM9BIOS, sizeof(NDS::ARM9BIOS), 1, 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); @@ -496,12 +725,12 @@ void LoadBIOSFiles() Log(LogLevel::Warn, "ARM9 BIOS not found\n"); for (int i = 0; i < 16; i++) - ((u32*)NDS::ARM9BIOS)[i] = 0xE7FFDEFF; + ((u32*)nds.ARM9BIOS)[i] = 0xE7FFDEFF; } if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS7Path, FileMode::Read)) { - FileRead(NDS::ARM7BIOS, sizeof(NDS::ARM7BIOS), 1, f); + FileRead(nds.ARM7BIOS, sizeof(NDS::ARM7BIOS), 1, f); Log(LogLevel::Info, "ARM7 BIOS loaded from\n", Config::BIOS7Path.c_str()); Platform::CloseFile(f); @@ -511,21 +740,22 @@ void LoadBIOSFiles() Log(LogLevel::Warn, "ARM7 BIOS not found\n"); for (int i = 0; i < 16; i++) - ((u32*)NDS::ARM7BIOS)[i] = 0xE7FFDEFF; + ((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)); + memcpy(nds.ARM9BIOS, bios_arm9_bin, sizeof(bios_arm9_bin)); + memcpy(nds.ARM7BIOS, bios_arm7_bin, sizeof(bios_arm7_bin)); } if (Config::ConsoleType == 1) { + DSi& dsi = static_cast(nds); if (FileHandle* f = Platform::OpenLocalFile(Config::DSiBIOS9Path, FileMode::Read)) { - FileRead(DSi::ARM9iBIOS, sizeof(DSi::ARM9iBIOS), 1, f); + FileRead(dsi.ARM9iBIOS, sizeof(DSi::ARM9iBIOS), 1, f); Log(LogLevel::Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str()); Platform::CloseFile(f); @@ -535,13 +765,13 @@ void LoadBIOSFiles() Log(LogLevel::Warn, "ARM9i BIOS not found\n"); for (int i = 0; i < 16; i++) - ((u32*)DSi::ARM9iBIOS)[i] = 0xE7FFDEFF; + ((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); + FileRead(dsi.ARM7iBIOS, sizeof(DSi::ARM7iBIOS), 1, f); Log(LogLevel::Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str()); CloseFile(f); @@ -551,14 +781,14 @@ void LoadBIOSFiles() Log(LogLevel::Warn, "ARM7i BIOS not found\n"); for (int i = 0; i < 16; i++) - ((u32*)DSi::ARM7iBIOS)[i] = 0xE7FFDEFF; + ((u32*)dsi.ARM7iBIOS)[i] = 0xE7FFDEFF; } if (!Config::DSiFullBIOSBoot) { // herp - *(u32*)&DSi::ARM9iBIOS[0] = 0xEAFFFFFE; - *(u32*)&DSi::ARM7iBIOS[0] = 0xEAFFFFFE; + *(u32*)&dsi.ARM9iBIOS[0] = 0xEAFFFFFE; + *(u32*)&dsi.ARM7iBIOS[0] = 0xEAFFFFFE; // TODO!!!! // hax the upper 32K out of the goddamn DSi @@ -567,11 +797,11 @@ void LoadBIOSFiles() } } -void EnableCheats(bool enable) +void EnableCheats(NDS& nds, bool enable) { CheatsOn = enable; if (CheatFile) - NDS::AREngine->SetCodeFile(CheatsOn ? CheatFile : nullptr); + nds.AREngine.SetCodeFile(CheatsOn ? CheatFile : nullptr); } ARCodeFile* GetCheatFile() @@ -580,42 +810,44 @@ ARCodeFile* GetCheatFile() } -void SetBatteryLevels() +void SetBatteryLevels(NDS& nds) { - if (NDS::ConsoleType == 1) + if (nds.ConsoleType == 1) { - DSi::I2C->GetBPTWL()->SetBatteryLevel(Config::DSiBatteryLevel); - DSi::I2C->GetBPTWL()->SetBatteryCharging(Config::DSiBatteryCharging); + auto& dsi = static_cast(nds); + dsi.I2C.GetBPTWL()->SetBatteryLevel(Config::DSiBatteryLevel); + dsi.I2C.GetBPTWL()->SetBatteryCharging(Config::DSiBatteryCharging); } else { - NDS::SPI->GetPowerMan()->SetBatteryLevelOkay(Config::DSBatteryLevelOkay); + nds.SPI.GetPowerMan()->SetBatteryLevelOkay(Config::DSBatteryLevelOkay); } } -void SetDateTime() +void SetDateTime(NDS& nds) { QDateTime hosttime = QDateTime::currentDateTime(); QDateTime time = hosttime.addSecs(Config::RTCOffset); - NDS::RTC->SetDateTime(time.date().year(), time.date().month(), time.date().day(), + nds.RTC.SetDateTime(time.date().year(), time.date().month(), time.date().day(), time.time().hour(), time.time().minute(), time.time().second()); } -void Reset() +void Reset(EmuThread* thread) { - NDS::SetConsoleType(Config::ConsoleType); - if (Config::ConsoleType == 1) EjectGBACart(); - LoadBIOSFiles(); + thread->RecreateConsole(); - InstallFirmware(); + if (Config::ConsoleType == 1) EjectGBACart(*thread->NDS); + LoadBIOSFiles(*thread->NDS); + + InstallFirmware(*thread->NDS); if (Config::ConsoleType == 1) { - InstallNAND(&DSi::ARM7iBIOS[0x8308]); + InstallNAND(static_cast(*thread->NDS)); } - NDS::Reset(); - SetBatteryLevels(); - SetDateTime(); + thread->NDS->Reset(); + SetBatteryLevels(*thread->NDS); + SetDateTime(*thread->NDS); if ((CartType != -1) && NDSSave) { @@ -659,27 +891,27 @@ void Reset() if (!BaseROMName.empty()) { - if (Config::DirectBoot || NDS::NeedsDirectBoot()) + if (Config::DirectBoot || thread->NDS->NeedsDirectBoot()) { - NDS::SetupDirectBoot(BaseROMName); + thread->NDS->SetupDirectBoot(BaseROMName); } } } -bool LoadBIOS() +bool LoadBIOS(EmuThread* thread) { - NDS::SetConsoleType(Config::ConsoleType); + thread->RecreateConsole(); - LoadBIOSFiles(); + LoadBIOSFiles(*thread->NDS); - if (!InstallFirmware()) + if (!InstallFirmware(*thread->NDS)) return false; - if (Config::ConsoleType == 1 && !InstallNAND(&DSi::ARM7iBIOS[0x8308])) + if (Config::ConsoleType == 1 && !InstallNAND(static_cast(*thread->NDS))) return false; - if (NDS::NeedsDirectBoot()) + if (thread->NDS->NeedsDirectBoot()) return false; /*if (NDSSave) delete NDSSave; @@ -690,9 +922,9 @@ bool LoadBIOS() BaseROMName = ""; BaseAssetName = "";*/ - NDS::Reset(); - SetBatteryLevels(); - SetDateTime(); + thread->NDS->Reset(); + SetBatteryLevels(*thread->NDS); + SetDateTime(*thread->NDS); return true; } @@ -884,7 +1116,7 @@ pair, string> GenerateDefaultFirmware() return std::make_pair(std::move(firmware), std::move(wfcsettingspath)); } -void LoadUserSettingsFromConfig(Firmware& firmware) +void CustomizeFirmware(Firmware& firmware) noexcept { auto& currentData = firmware.GetEffectiveUserData(); @@ -992,13 +1224,13 @@ static Platform::FileHandle* OpenNANDFile() noexcept return nandfile; } -bool InstallNAND(const u8* es_keyY) +bool InstallNAND(DSi& dsi) { Platform::FileHandle* nandfile = OpenNANDFile(); if (!nandfile) return false; - DSi_NAND::NANDImage nandImage(nandfile, es_keyY); + DSi_NAND::NANDImage nandImage(nandfile, &dsi.ARM7iBIOS[0x8308]); if (!nandImage) { Log(LogLevel::Error, "Failed to parse DSi NAND\n"); @@ -1067,11 +1299,11 @@ bool InstallNAND(const u8* es_keyY) } } - DSi::NANDImage = std::make_unique(std::move(nandImage)); + dsi.NANDImage = std::make_unique(std::move(nandImage)); return true; } -bool InstallFirmware() +bool InstallFirmware(NDS& nds) { FirmwareSave.reset(); unique_ptr firmware; @@ -1098,15 +1330,15 @@ bool InstallFirmware() if (Config::FirmwareOverrideSettings) { - LoadUserSettingsFromConfig(*firmware); + CustomizeFirmware(*firmware); } FirmwareSave = std::make_unique(firmwarepath); - return NDS::SPI->GetFirmwareMem()->InstallFirmware(std::move(firmware)); + return nds.SPI.GetFirmwareMem()->InstallFirmware(std::move(firmware)); } -bool LoadROM(QStringList filepath, bool reset) +bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) { if (filepath.empty()) return false; @@ -1201,22 +1433,22 @@ bool LoadROM(QStringList filepath, bool reset) BaseROMName = romname; BaseAssetName = romname.substr(0, romname.rfind('.')); - if (!InstallFirmware()) + emuthread->RecreateConsole(); + if (!InstallFirmware(*emuthread->NDS)) { return false; } if (reset) { - NDS::SetConsoleType(Config::ConsoleType); - NDS::EjectCart(); - LoadBIOSFiles(); + emuthread->NDS->EjectCart(); + LoadBIOSFiles(*emuthread->NDS); if (Config::ConsoleType == 1) - InstallNAND(&DSi::ARM7iBIOS[0x8308]); + InstallNAND(static_cast(*emuthread->NDS)); - NDS::Reset(); - SetBatteryLevels(); - SetDateTime(); + emuthread->NDS->Reset(); + SetBatteryLevels(*emuthread->NDS); + SetDateTime(*emuthread->NDS); } u32 savelen = 0; @@ -1238,12 +1470,12 @@ bool LoadROM(QStringList filepath, bool reset) CloseFile(sav); } - bool res = NDS::LoadCart(filedata, filelen, savedata, savelen); + bool res = emuthread->NDS->LoadCart(filedata, filelen, savedata, savelen); if (res && reset) { - if (Config::DirectBoot || NDS::NeedsDirectBoot()) + if (Config::DirectBoot || emuthread->NDS->NeedsDirectBoot()) { - NDS::SetupDirectBoot(romname); + emuthread->NDS->SetupDirectBoot(romname); } } @@ -1252,7 +1484,7 @@ bool LoadROM(QStringList filepath, bool reset) CartType = 0; NDSSave = new SaveManager(savname); - LoadCheats(); + LoadCheats(*emuthread->NDS); } if (savedata) delete[] savedata; @@ -1260,14 +1492,14 @@ bool LoadROM(QStringList filepath, bool reset) return res; } -void EjectCart() +void EjectCart(NDS& nds) { if (NDSSave) delete NDSSave; NDSSave = nullptr; - UnloadCheats(); + UnloadCheats(nds); - NDS::EjectCart(); + nds.EjectCart(); CartType = -1; BaseROMDir = ""; @@ -1295,7 +1527,7 @@ QString CartLabel() } -bool LoadGBAROM(QStringList filepath) +bool LoadGBAROM(NDS& nds, QStringList filepath) { if (Config::ConsoleType == 1) return false; if (filepath.empty()) return false; @@ -1408,7 +1640,7 @@ bool LoadGBAROM(QStringList filepath) CloseFile(sav); } - bool res = NDS::LoadGBACart(filedata, filelen, savedata, savelen); + bool res = nds.LoadGBACart(filedata, filelen, savedata, savelen); if (res) { @@ -1421,14 +1653,14 @@ bool LoadGBAROM(QStringList filepath) return res; } -void LoadGBAAddon(int type) +void LoadGBAAddon(NDS& nds, int type) { if (Config::ConsoleType == 1) return; if (GBASave) delete GBASave; GBASave = nullptr; - NDS::LoadGBAAddon(type); + nds.LoadGBAAddon(type); GBACartType = type; BaseGBAROMDir = ""; @@ -1436,12 +1668,12 @@ void LoadGBAAddon(int type) BaseGBAAssetName = ""; } -void EjectGBACart() +void EjectGBACart(NDS& nds) { if (GBASave) delete GBASave; GBASave = nullptr; - NDS::EjectGBACart(); + nds.EjectGBACart(); GBACartType = -1; BaseGBAROMDir = ""; @@ -1471,7 +1703,7 @@ QString GBACartLabel() return ret; } - case NDS::GBAAddon_RAMExpansion: + case GBAAddon_RAMExpansion: return "Memory expansion"; } diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 92285608..2163a680 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -24,10 +24,19 @@ #include "AREngine.h" #include "DSi_NAND.h" +#include "MemConstants.h" +#include #include #include #include +namespace melonDS +{ +class NDS; +class DSi; +class FATStorage; +} +class EmuThread; namespace ROMManager { @@ -37,30 +46,41 @@ extern SaveManager* GBASave; extern std::unique_ptr FirmwareSave; QString VerifySetup(); -void Reset(); -bool LoadBIOS(); +void Reset(EmuThread* thread); +bool LoadBIOS(EmuThread* thread); void ClearBackupState(); -bool InstallFirmware(); -bool InstallNAND(const u8* es_keyY); -bool LoadROM(QStringList filepath, bool reset); -void EjectCart(); +std::optional> LoadARM9BIOS() noexcept; +std::optional> LoadARM7BIOS() noexcept; +std::optional> LoadDSiARM9BIOS() noexcept; +std::optional> LoadDSiARM7BIOS() noexcept; +std::optional LoadDSiSDCard() noexcept; +void CustomizeFirmware(Firmware& firmware) noexcept; +Firmware GenerateFirmware(int type) noexcept; +/// Loads and customizes a firmware image based on the values in Config +std::optional LoadFirmware(int type) noexcept; +/// Loads and customizes a NAND image based on the values in Config +std::optional LoadNAND(const std::array& arm7ibios) noexcept; +bool InstallFirmware(NDS& nds); +bool InstallNAND(DSi& dsi); +bool LoadROM(EmuThread*, QStringList filepath, bool reset); +void EjectCart(NDS& nds); bool CartInserted(); QString CartLabel(); -bool LoadGBAROM(QStringList filepath); -void LoadGBAAddon(int type); -void EjectGBACart(); +bool LoadGBAROM(NDS& nds, QStringList filepath); +void LoadGBAAddon(NDS& nds, int type); +void EjectGBACart(NDS& nds); bool GBACartInserted(); QString GBACartLabel(); std::string GetSavestateName(int slot); bool SavestateExists(int slot); -bool LoadState(const std::string& filename); -bool SaveState(const std::string& filename); -void UndoStateLoad(); +bool LoadState(NDS& nds, const std::string& filename); +bool SaveState(NDS& nds, const std::string& filename); +void UndoStateLoad(NDS& nds); -void EnableCheats(bool enable); +void EnableCheats(NDS& nds, bool enable); ARCodeFile* GetCheatFile(); void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32 (&iconRef)[32*32]); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 42864986..54a6f14f 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -203,6 +203,30 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) static_cast(mainWindow->panel)->transferLayout(this); } +std::unique_ptr EmuThread::CreateConsole() +{ + if (Config::ConsoleType == 1) + { + return std::make_unique(); + } + + return std::make_unique(); +} + +void EmuThread::RecreateConsole() +{ + if (!NDS || NDS->ConsoleType != Config::ConsoleType) + { + NDS = nullptr; // To ensure the destructor is called before a new one is created + NDS::Current = nullptr; + + NDS = CreateConsole(); + // TODO: Insert ROMs + NDS::Current = NDS.get(); + } +} + + void EmuThread::updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix) { screenSettingsLock.lock(); @@ -318,7 +342,7 @@ void EmuThread::run() u32 mainScreenPos[3]; Platform::FileHandle* file; - NDS::Init(); + RecreateConsole(); mainScreenPos[0] = 0; mainScreenPos[1] = 0; @@ -340,10 +364,10 @@ void EmuThread::run() videoRenderer = 0; } - NDS::GPU->InitRenderer(videoRenderer); - NDS::GPU->SetRenderSettings(videoRenderer, videoSettings); + NDS->GPU.InitRenderer(videoRenderer); + NDS->GPU.SetRenderSettings(videoRenderer, videoSettings); - NDS::SPU->SetInterpolation(Config::AudioInterp); + NDS->SPU.SetInterpolation(Config::AudioInterp); Input::Init(); @@ -362,7 +386,7 @@ void EmuThread::run() RTC::StateData state; Platform::FileRead(&state, sizeof(state), 1, file); Platform::CloseFile(file); - NDS::RTC->SetState(state); + NDS->RTC.SetState(state); } char melontitle[100]; @@ -384,8 +408,7 @@ void EmuThread::run() if (Input::HotkeyPressed(HK_SolarSensorDecrease)) { - assert(NDS::GBACartSlot != nullptr); - int level = NDS::GBACartSlot->SetInput(GBACart::Input_SolarSensorDown, true); + int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorDown, true); if (level != -1) { char msg[64]; @@ -395,8 +418,7 @@ void EmuThread::run() } if (Input::HotkeyPressed(HK_SolarSensorIncrease)) { - assert(NDS::GBACartSlot != nullptr); - int level = NDS::GBACartSlot->SetInput(GBACart::Input_SolarSensorUp, true); + int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorUp, true); if (level != -1) { char msg[64]; @@ -405,40 +427,41 @@ void EmuThread::run() } } - if (NDS::ConsoleType == 1) + if (NDS->ConsoleType == 1) { + DSi& dsi = static_cast(*NDS); double currentTime = SDL_GetPerformanceCounter() * perfCountsSec; // Handle power button if (Input::HotkeyDown(HK_PowerButton)) { - DSi::I2C->GetBPTWL()->SetPowerButtonHeld(currentTime); + dsi.I2C.GetBPTWL()->SetPowerButtonHeld(currentTime); } else if (Input::HotkeyReleased(HK_PowerButton)) { - DSi::I2C->GetBPTWL()->SetPowerButtonReleased(currentTime); + dsi.I2C.GetBPTWL()->SetPowerButtonReleased(currentTime); } // Handle volume buttons if (Input::HotkeyDown(HK_VolumeUp)) { - DSi::I2C->GetBPTWL()->SetVolumeSwitchHeld(DSi::I2C->GetBPTWL()->volumeKey_Up); + dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); } else if (Input::HotkeyReleased(HK_VolumeUp)) { - DSi::I2C->GetBPTWL()->SetVolumeSwitchReleased(DSi::I2C->GetBPTWL()->volumeKey_Up); + dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); } if (Input::HotkeyDown(HK_VolumeDown)) { - DSi::I2C->GetBPTWL()->SetVolumeSwitchHeld(DSi::I2C->GetBPTWL()->volumeKey_Down); + dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); } else if (Input::HotkeyReleased(HK_VolumeDown)) { - DSi::I2C->GetBPTWL()->SetVolumeSwitchReleased(DSi::I2C->GetBPTWL()->volumeKey_Down); + dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); } - DSi::I2C->GetBPTWL()->ProcessVolumeSwitchInput(currentTime); + dsi.I2C.GetBPTWL()->ProcessVolumeSwitchInput(currentTime); } if (EmuRunning == emuStatus_Running || EmuRunning == emuStatus_FrameStep) @@ -472,28 +495,28 @@ void EmuThread::run() videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; - NDS::GPU->SetRenderSettings(videoRenderer, videoSettings); + NDS->GPU.SetRenderSettings(videoRenderer, videoSettings); } // process input and hotkeys - NDS::SetKeyMask(Input::InputMask); + NDS->SetKeyMask(Input::InputMask); if (Input::HotkeyPressed(HK_Lid)) { - bool lid = !NDS::IsLidClosed(); - NDS::SetLidClosed(lid); + bool lid = !NDS->IsLidClosed(); + NDS->SetLidClosed(lid); OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); } // microphone input - AudioInOut::MicProcess(); + AudioInOut::MicProcess(*NDS); // auto screen layout if (Config::ScreenSizing == Frontend::screenSizing_Auto) { mainScreenPos[2] = mainScreenPos[1]; mainScreenPos[1] = mainScreenPos[0]; - mainScreenPos[0] = NDS::PowerControl9 >> 15; + mainScreenPos[0] = NDS->PowerControl9 >> 15; int guess; if (mainScreenPos[0] == mainScreenPos[2] && @@ -520,7 +543,7 @@ void EmuThread::run() // emulate - u32 nlines = NDS::RunFrame(); + u32 nlines = NDS->RunFrame(); if (ROMManager::NDSSave) ROMManager::NDSSave->CheckFlush(); @@ -534,12 +557,12 @@ void EmuThread::run() if (!oglContext) { FrontBufferLock.lock(); - FrontBuffer = NDS::GPU->FrontBuffer; + FrontBuffer = NDS->GPU.FrontBuffer; FrontBufferLock.unlock(); } else { - FrontBuffer = NDS::GPU->FrontBuffer; + FrontBuffer = NDS->GPU.FrontBuffer; drawScreenGL(); } @@ -563,9 +586,10 @@ void EmuThread::run() oglContext->SetSwapInterval(0); } - if (Config::DSiVolumeSync && NDS::ConsoleType == 1) + if (Config::DSiVolumeSync && NDS->ConsoleType == 1) { - u8 volumeLevel = DSi::I2C->GetBPTWL()->GetVolumeLevel(); + DSi& dsi = static_cast(*NDS); + u8 volumeLevel = dsi.I2C.GetBPTWL()->GetVolumeLevel(); if (volumeLevel != dsiVolumeLevel) { dsiVolumeLevel = volumeLevel; @@ -576,7 +600,7 @@ void EmuThread::run() } if (Config::AudioSync && !fastforward) - AudioInOut::AudioSync(); + AudioInOut::AudioSync(*emuThread->NDS); double frametimeStep = nlines / (60.0 * 263.0); @@ -669,16 +693,15 @@ void EmuThread::run() if (file) { RTC::StateData state; - NDS::RTC->GetState(state); + NDS->RTC.GetState(state); Platform::FileWrite(&state, sizeof(state), 1, file); Platform::CloseFile(file); } EmuStatus = emuStatus_Exit; - NDS::GPU->DeInitRenderer(); - NDS::DeInit(); - //Platform::LAN_DeInit(); + NDS::Current = nullptr; + // nds is out of scope, so unique_ptr cleans it up for us } void EmuThread::changeWindowTitle(char* title) @@ -759,6 +782,7 @@ bool EmuThread::emuIsActive() void EmuThread::drawScreenGL() { + if (!NDS) return; int w = windowInfo.surface_width; int h = windowInfo.surface_height; float factor = windowInfo.surface_scale; @@ -780,10 +804,10 @@ void EmuThread::drawScreenGL() glActiveTexture(GL_TEXTURE0); #ifdef OGLRENDERER_ENABLED - if (NDS::GPU->Renderer != 0) + if (NDS->GPU.Renderer != 0) { // hardware-accelerated render - NDS::GPU->CurGLCompositor->BindOutputTexture(frontbuf); + NDS->GPU.CurGLCompositor->BindOutputTexture(frontbuf); } else #endif @@ -791,12 +815,12 @@ void EmuThread::drawScreenGL() // regular render glBindTexture(GL_TEXTURE_2D, screenTexture); - if (NDS::GPU->Framebuffer[frontbuf][0] && NDS::GPU->Framebuffer[frontbuf][1]) + if (NDS->GPU.Framebuffer[frontbuf][0] && NDS->GPU.Framebuffer[frontbuf][1]) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, NDS::GPU->Framebuffer[frontbuf][0]); + GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][0]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, NDS::GPU->Framebuffer[frontbuf][1]); + GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][1]); } } @@ -927,7 +951,8 @@ void ScreenHandler::screenOnMousePress(QMouseEvent* event) if (Frontend::GetTouchCoords(x, y, false)) { touching = true; - NDS::TouchScreen(x, y); + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); } } @@ -939,7 +964,8 @@ void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) if (touching) { touching = false; - NDS::ReleaseScreen(); + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); } } @@ -956,7 +982,10 @@ void ScreenHandler::screenOnMouseMove(QMouseEvent* event) int y = event->pos().y(); if (Frontend::GetTouchCoords(x, y, true)) - NDS::TouchScreen(x, y); + { + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } } void ScreenHandler::screenHandleTablet(QTabletEvent* event) @@ -974,14 +1003,16 @@ void ScreenHandler::screenHandleTablet(QTabletEvent* event) if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) { touching = true; - NDS::TouchScreen(x, y); + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); } } break; case QEvent::TabletRelease: if (touching) { - NDS::ReleaseScreen(); + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); touching = false; } break; @@ -1007,14 +1038,16 @@ void ScreenHandler::screenHandleTouch(QTouchEvent* event) if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) { touching = true; - NDS::TouchScreen(x, y); + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); } } break; case QEvent::TouchEnd: if (touching) { - NDS::ReleaseScreen(); + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); touching = false; } break; @@ -1080,16 +1113,17 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) if (emuThread->emuIsActive()) { + assert(emuThread->NDS != nullptr); emuThread->FrontBufferLock.lock(); int frontbuf = emuThread->FrontBuffer; - if (!NDS::GPU->Framebuffer[frontbuf][0] || !NDS::GPU->Framebuffer[frontbuf][1]) + if (!emuThread->NDS->GPU.Framebuffer[frontbuf][0] || !emuThread->NDS->GPU.Framebuffer[frontbuf][1]) { emuThread->FrontBufferLock.unlock(); return; } - memcpy(screen[0].scanLine(0), NDS::GPU->Framebuffer[frontbuf][0], 256 * 192 * 4); - memcpy(screen[1].scanLine(0), NDS::GPU->Framebuffer[frontbuf][1], 256 * 192 * 4); + memcpy(screen[0].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][0], 256 * 192 * 4); + memcpy(screen[1].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][1], 256 * 192 * 4); emuThread->FrontBufferLock.unlock(); QRect screenrc(0, 0, 256, 192); @@ -1472,7 +1506,7 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) QMenu* submenu = menu->addMenu("Insert add-on cart"); actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); - actInsertGBAAddon[0]->setData(QVariant(NDS::GBAAddon_RAMExpansion)); + actInsertGBAAddon[0]->setData(QVariant(GBAAddon_RAMExpansion)); connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); } @@ -2025,7 +2059,7 @@ void MainWindow::dropEvent(QDropEvent* event) if (isNdsRom) { - if (!ROMManager::LoadROM(file, true)) + if (!ROMManager::LoadROM(emuThread, file, true)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "Failed to load the DS ROM."); @@ -2038,14 +2072,15 @@ void MainWindow::dropEvent(QDropEvent* event) recentFileList.prepend(barredFilename); updateRecentFilesMenu(); - NDS::Start(); + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); emuThread->emuRun(); updateCartInserted(false); } else if (isGbaRom) { - if (!ROMManager::LoadGBAROM(file)) + if (!ROMManager::LoadGBAROM(*emuThread->NDS, file)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); @@ -2111,7 +2146,7 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) bool gbaloaded = false; if (!gbafile.isEmpty()) { - if (!ROMManager::LoadGBAROM(gbafile)) + if (!ROMManager::LoadGBAROM(*emuThread->NDS, gbafile)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); @@ -2124,7 +2159,7 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) bool ndsloaded = false; if (!file.isEmpty()) { - if (!ROMManager::LoadROM(file, true)) + if (!ROMManager::LoadROM(emuThread, file, true)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); @@ -2140,7 +2175,7 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) { if (ndsloaded) { - NDS::Start(); + emuThread->NDS->Start(); emuThread->emuRun(); } else @@ -2333,7 +2368,7 @@ void MainWindow::onOpenFile() return; } - if (!ROMManager::LoadROM(file, true)) + if (!ROMManager::LoadROM(emuThread, file, true)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); @@ -2346,7 +2381,8 @@ void MainWindow::onOpenFile() recentFileList.prepend(filename); updateRecentFilesMenu(); - NDS::Start(); + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); emuThread->emuRun(); updateCartInserted(false); @@ -2431,7 +2467,7 @@ void MainWindow::onClickRecentFile() return; } - if (!ROMManager::LoadROM(file, true)) + if (!ROMManager::LoadROM(emuThread, file, true)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); @@ -2443,7 +2479,8 @@ void MainWindow::onClickRecentFile() recentFileList.prepend(filename); updateRecentFilesMenu(); - NDS::Start(); + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); emuThread->emuRun(); updateCartInserted(false); @@ -2459,7 +2496,7 @@ void MainWindow::onBootFirmware() return; } - if (!ROMManager::LoadBIOS()) + if (!ROMManager::LoadBIOS(emuThread)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); @@ -2467,7 +2504,8 @@ void MainWindow::onBootFirmware() return; } - NDS::Start(); + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); emuThread->emuRun(); } @@ -2482,7 +2520,7 @@ void MainWindow::onInsertCart() return; } - if (!ROMManager::LoadROM(file, false)) + if (!ROMManager::LoadROM(emuThread, file, false)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); @@ -2499,7 +2537,7 @@ void MainWindow::onEjectCart() { emuThread->emuPause(); - ROMManager::EjectCart(); + ROMManager::EjectCart(*emuThread->NDS); emuThread->emuUnpause(); @@ -2517,7 +2555,7 @@ void MainWindow::onInsertGBACart() return; } - if (!ROMManager::LoadGBAROM(file)) + if (!ROMManager::LoadGBAROM(*emuThread->NDS, file)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); @@ -2537,7 +2575,7 @@ void MainWindow::onInsertGBAAddon() emuThread->emuPause(); - ROMManager::LoadGBAAddon(type); + ROMManager::LoadGBAAddon(*emuThread->NDS, type); emuThread->emuUnpause(); @@ -2548,7 +2586,7 @@ void MainWindow::onEjectGBACart() { emuThread->emuPause(); - ROMManager::EjectGBACart(); + ROMManager::EjectGBACart(*emuThread->NDS); emuThread->emuUnpause(); @@ -2582,7 +2620,7 @@ void MainWindow::onSaveState() filename = qfilename.toStdString(); } - if (ROMManager::SaveState(filename)) + if (ROMManager::SaveState(*emuThread->NDS, filename)) { char msg[64]; if (slot > 0) sprintf(msg, "State saved to slot %d", slot); @@ -2637,7 +2675,7 @@ void MainWindow::onLoadState() return; } - if (ROMManager::LoadState(filename)) + if (ROMManager::LoadState(*emuThread->NDS, filename)) { char msg[64]; if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); @@ -2657,7 +2695,7 @@ void MainWindow::onLoadState() void MainWindow::onUndoStateLoad() { emuThread->emuPause(); - ROMManager::UndoStateLoad(); + ROMManager::UndoStateLoad(*emuThread->NDS); emuThread->emuUnpause(); OSD::AddMessage(0, "State load undone"); @@ -2696,7 +2734,7 @@ void MainWindow::onImportSavefile() return; } - ROMManager::Reset(); + ROMManager::Reset(emuThread); } u32 len = FileLength(f); @@ -2705,7 +2743,8 @@ void MainWindow::onImportSavefile() Platform::FileRewind(f); Platform::FileRead(data, len, 1, f); - NDS::LoadSave(data, len); + assert(emuThread->NDS != nullptr); + emuThread->NDS->LoadSave(data, len); delete[] data; CloseFile(f); @@ -2747,7 +2786,7 @@ void MainWindow::onReset() actUndoStateLoad->setEnabled(false); - ROMManager::Reset(); + ROMManager::Reset(emuThread); OSD::AddMessage(0, "Reset"); emuThread->emuRun(); @@ -2758,7 +2797,7 @@ void MainWindow::onStop() if (!RunningSomething) return; emuThread->emuPause(); - NDS::Stop(); + emuThread->NDS->Stop(); } void MainWindow::onFrameStep() @@ -2775,13 +2814,13 @@ void MainWindow::onOpenDateTime() void MainWindow::onOpenPowerManagement() { - PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this); + PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this, emuThread); } void MainWindow::onEnableCheats(bool checked) { Config::EnableCheats = checked?1:0; - ROMManager::EnableCheats(Config::EnableCheats != 0); + ROMManager::EnableCheats(*emuThread->NDS, Config::EnableCheats != 0); } void MainWindow::onSetupCheats() @@ -2799,12 +2838,14 @@ void MainWindow::onCheatsDialogFinished(int res) void MainWindow::onROMInfo() { - ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this); + auto cart = emuThread->NDS->NDSCartSlot.GetCart(); + if (cart) + ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this, *cart); } void MainWindow::onRAMInfo() { - RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this); + RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this, emuThread); } void MainWindow::onOpenTitleManager() @@ -2907,7 +2948,7 @@ void MainWindow::onCameraSettingsFinished(int res) void MainWindow::onOpenAudioSettings() { - AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this, emuThread->emuIsActive()); + AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this, emuThread->emuIsActive(), emuThread); connect(emuThread, &EmuThread::syncVolumeLevel, dlg, &AudioSettingsDialog::onSyncVolumeLevel); connect(emuThread, &EmuThread::windowEmuStart, dlg, &AudioSettingsDialog::onConsoleReset); connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings); @@ -2948,17 +2989,18 @@ void MainWindow::onPathSettingsFinished(int res) void MainWindow::onUpdateAudioSettings() { - NDS::SPU->SetInterpolation(Config::AudioInterp); + assert(emuThread->NDS != nullptr); + emuThread->NDS->SPU.SetInterpolation(Config::AudioInterp); if (Config::AudioBitDepth == 0) - NDS::SPU->SetDegrade10Bit(NDS::ConsoleType == 0); + emuThread->NDS->SPU.SetDegrade10Bit(emuThread->NDS->ConsoleType == 0); else - NDS::SPU->SetDegrade10Bit(Config::AudioBitDepth == 1); + emuThread->NDS->SPU.SetDegrade10Bit(Config::AudioBitDepth == 1); } void MainWindow::onAudioSettingsFinished(int res) { - AudioInOut::UpdateSettings(); + AudioInOut::UpdateSettings(*emuThread->NDS); } void MainWindow::onOpenMPSettings() @@ -3313,13 +3355,11 @@ int main(int argc, char** argv) #define SANITIZE(var, min, max) { var = std::clamp(var, min, max); } SANITIZE(Config::ConsoleType, 0, 1); - SANITIZE(Config::_3DRenderer, - 0, - 0 // Minimum, Software renderer - #ifdef OGLRENDERER_ENABLED - + 1 // OpenGL Renderer - #endif - ); +#ifdef OGLRENDERER_ENABLED + SANITIZE(Config::_3DRenderer, 0, 1); // 0 is the software renderer, 1 is the OpenGL renderer +#else + SANITIZE(Config::_3DRenderer, 0, 0); +#endif SANITIZE(Config::ScreenVSyncInterval, 1, 20); SANITIZE(Config::GL_ScaleFactor, 1, 16); SANITIZE(Config::AudioInterp, 0, 3); @@ -3333,7 +3373,6 @@ int main(int argc, char** argv) SANITIZE(Config::ScreenAspectBot, 0, AspectRatiosNum); #undef SANITIZE - AudioInOut::Init(); camStarted[0] = false; camStarted[1] = false; camManager[0] = new CameraManager(0, 640, 480, true); @@ -3341,7 +3380,6 @@ int main(int argc, char** argv) camManager[0]->setXFlip(Config::Camera[0].XFlip); camManager[1]->setXFlip(Config::Camera[1].XFlip); - ROMManager::EnableCheats(Config::EnableCheats != 0); Input::JoystickID = Config::JoystickID; Input::OpenJoystick(); @@ -3354,6 +3392,8 @@ int main(int argc, char** argv) emuThread->start(); emuThread->emuPause(); + AudioInOut::Init(emuThread); + ROMManager::EnableCheats(*emuThread->NDS, Config::EnableCheats != 0); AudioInOut::AudioMute(mainWindow); QObject::connect(&melon, &QApplication::applicationStateChanged, mainWindow, &MainWindow::onAppStateChanged); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 500aec1c..72ebfb19 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -40,6 +40,11 @@ #include "FrontendUtil.h" #include "duckstation/gl/context.h" +namespace melonDS +{ +class NDS; +} + class EmuThread : public QThread { Q_OBJECT @@ -67,7 +72,8 @@ public: QMutex FrontBufferLock; void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); - + void RecreateConsole(); + std::unique_ptr NDS; // TODO: Proper encapsulation and synchronization signals: void windowUpdate(); void windowTitleChange(QString title); @@ -90,6 +96,7 @@ signals: void syncVolumeLevel(); private: + std::unique_ptr CreateConsole(); void drawScreenGL(); void initOpenGL(); void deinitOpenGL(); From 7caddf9615875e54b725bd7df266361c46d47b6f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 29 Nov 2023 09:23:11 -0500 Subject: [PATCH 049/157] Clean up the 3D renderer for enhanced flexibility (#1895) * Give `GPU2D::Unit` a virtual destructor - Undefined behavior avoided! * Add an `array2d` alias * Move various parts of `GPU2D::SoftRenderer` to `constexpr` - `SoftRenderer::MosaicTable` is now initialized at compile-time - Most of the `SoftRenderer::Color*` functions are now `constexpr` - The aforementioned functions are used with a constant value in at least one place, so they'll be at least partially computed at compile-time * Generalize `GLRenderer::PrepareCaptureFrame` - Declare it in the base `Renderer3D` class, but make it empty * Remove unneeded `virtual` specifiers * Store `Framebuffer`'s memory in `unique_ptr`s - Reduce the risk of leaks this way * Clean up how `GLCompositor` is initialized - Return it as an `std::optional` instead of a `std::unique_ptr` - Make `GLCompositor` movable - Replace `GLCompositor`'s plain arrays with `std::array` to simplify moving * Pass `GPU` to `GLCompositor`'s important functions instead of passing it to the constructor * Move `GLCompositor` to be a field within `GLRenderer` - Some methods were moved up and made `virtual` * Fix some linker errors * Set the renderer in the frontend * Remove unneeded `virtual` specifiers * Remove `RenderSettings` in favor of just exposing the relevant member variables * Update the frontend to accommodate the core changes * Add `constexpr` and `const` to places in the interpolator * Qualify references to `size_t` * Construct the `optional` directly instead of using `make_optional` - It makes the Linux build choke - I think it's because `GLCompositor`'s constructor is `private` --- src/GPU.cpp | 150 +++++++---------------------------- src/GPU.h | 36 +++------ src/GPU2D.h | 2 +- src/GPU2D_Soft.cpp | 71 ++--------------- src/GPU2D_Soft.h | 72 +++++++++++++++-- src/GPU3D.cpp | 17 +++- src/GPU3D.h | 16 ++-- src/GPU3D_OpenGL.cpp | 43 ++++++++-- src/GPU3D_OpenGL.h | 28 ++++--- src/GPU3D_Soft.cpp | 14 ++-- src/GPU3D_Soft.h | 47 +++++------ src/GPU_OpenGL.cpp | 110 +++++++++++++++++++------ src/GPU_OpenGL.h | 46 +++++------ src/frontend/qt_sdl/main.cpp | 45 +++++++---- src/types.h | 3 + 15 files changed, 366 insertions(+), 334 deletions(-) diff --git a/src/GPU.cpp b/src/GPU.cpp index 7a81f7dd..c80e3119 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -24,7 +24,6 @@ #include "GPU2D_Soft.h" #include "GPU3D_Soft.h" -#include "GPU3D_OpenGL.h" namespace melonDS { @@ -64,33 +63,24 @@ enum VRAMDirty need to be reset for the respective VRAM bank. */ -GPU::GPU(melonDS::NDS& nds) noexcept : NDS(nds), GPU2D_A(0, *this), GPU2D_B(1, *this), GPU3D(nds) +GPU::GPU(melonDS::NDS& nds, std::unique_ptr&& renderer3d, std::unique_ptr&& renderer2d) noexcept : + NDS(nds), + GPU2D_A(0, *this), + GPU2D_B(1, *this), + GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique(*this)), + GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique(*this)) { NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); NDS.RegisterEventFunc(Event_LCD, LCD_StartScanline, MemberEventFunc(GPU, StartScanline)); NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); - GPU2D_Renderer = std::make_unique(*this); - FrontBuffer = 0; - Framebuffer[0][0] = NULL; Framebuffer[0][1] = NULL; - Framebuffer[1][0] = NULL; Framebuffer[1][1] = NULL; - Renderer = 0; } GPU::~GPU() noexcept { // All unique_ptr fields are automatically cleaned up - if (Framebuffer[0][0]) delete[] Framebuffer[0][0]; - 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; NDS.UnregisterEventFunc(Event_LCD, LCD_StartHBlank); NDS.UnregisterEventFunc(Event_LCD, LCD_StartScanline); @@ -198,9 +188,7 @@ void GPU::Reset() noexcept GPU3D.Reset(); int backbuf = FrontBuffer ? 0 : 1; - GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]); - - ResetRenderer(); + GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get()); ResetVRAMCache(); @@ -216,17 +204,12 @@ void GPU::Stop() noexcept else fbsize = 256 * 192; - memset(Framebuffer[0][0], 0, fbsize*4); - memset(Framebuffer[0][1], 0, fbsize*4); - memset(Framebuffer[1][0], 0, fbsize*4); - memset(Framebuffer[1][1], 0, fbsize*4); + memset(Framebuffer[0][0].get(), 0, fbsize*4); + memset(Framebuffer[0][1].get(), 0, fbsize*4); + memset(Framebuffer[1][0].get(), 0, fbsize*4); + memset(Framebuffer[1][1].get(), 0, fbsize*4); -#ifdef OGLRENDERER_ENABLED - // This needs a better way to know that we're - // using the OpenGL renderer specifically - if (GPU3D.IsRendererAccelerated()) - CurGLCompositor->Stop(); -#endif + GPU3D.Stop(); } void GPU::DoSavestate(Savestate* file) noexcept @@ -300,78 +283,20 @@ void GPU::AssignFramebuffers() noexcept int backbuf = FrontBuffer ? 0 : 1; if (NDS.PowerControl9 & (1<<15)) { - GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0], Framebuffer[backbuf][1]); + GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][0].get(), Framebuffer[backbuf][1].get()); } else { - GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1], Framebuffer[backbuf][0]); + GPU2D_Renderer->SetFramebuffer(Framebuffer[backbuf][1].get(), Framebuffer[backbuf][0].get()); } } -void GPU::InitRenderer(int renderer) noexcept +void GPU::SetRenderer3D(std::unique_ptr&& renderer) noexcept { -#ifdef OGLRENDERER_ENABLED - if (renderer == 1) - { - CurGLCompositor = GLCompositor::New(*this); - // Create opengl renderer - if (!CurGLCompositor) - { - // Fallback on software renderer - renderer = 0; - GPU3D.SetCurrentRenderer(std::make_unique(*this)); - } - GPU3D.SetCurrentRenderer(GLRenderer::New(*this)); - if (!GPU3D.GetCurrentRenderer()) - { - // Fallback on software renderer - CurGLCompositor.reset(); - renderer = 0; - GPU3D.SetCurrentRenderer(std::make_unique(*this)); - } - } - else -#endif - { + if (renderer == nullptr) GPU3D.SetCurrentRenderer(std::make_unique(*this)); - } - - Renderer = renderer; -} - -void GPU::DeInitRenderer() noexcept -{ - // Delete the 3D renderer, if it exists - GPU3D.SetCurrentRenderer(nullptr); - -#ifdef OGLRENDERER_ENABLED - // Delete the compositor, if one exists - CurGLCompositor.reset(); -#endif -} - -void GPU::ResetRenderer() noexcept -{ - if (Renderer == 0) - { - GPU3D.GetCurrentRenderer()->Reset(); - } -#ifdef OGLRENDERER_ENABLED else - { - CurGLCompositor->Reset(); - GPU3D.GetCurrentRenderer()->Reset(); - } -#endif -} - -void GPU::SetRenderSettings(int renderer, RenderSettings& settings) noexcept -{ - if (renderer != Renderer) - { - DeInitRenderer(); - InitRenderer(renderer); - } + GPU3D.SetCurrentRenderer(std::move(renderer)); int fbsize; if (GPU3D.IsRendererAccelerated()) @@ -379,34 +304,17 @@ void GPU::SetRenderSettings(int renderer, RenderSettings& settings) noexcept else fbsize = 256 * 192; - if (Framebuffer[0][0]) { delete[] Framebuffer[0][0]; Framebuffer[0][0] = nullptr; } - if (Framebuffer[1][0]) { delete[] Framebuffer[1][0]; Framebuffer[1][0] = nullptr; } - if (Framebuffer[0][1]) { delete[] Framebuffer[0][1]; Framebuffer[0][1] = nullptr; } - if (Framebuffer[1][1]) { delete[] Framebuffer[1][1]; Framebuffer[1][1] = nullptr; } + Framebuffer[0][0] = std::make_unique(fbsize); + Framebuffer[1][0] = std::make_unique(fbsize); + Framebuffer[0][1] = std::make_unique(fbsize); + Framebuffer[1][1] = std::make_unique(fbsize); - Framebuffer[0][0] = new u32[fbsize]; - Framebuffer[1][0] = new u32[fbsize]; - Framebuffer[0][1] = new u32[fbsize]; - Framebuffer[1][1] = new u32[fbsize]; - - memset(Framebuffer[0][0], 0, fbsize*4); - memset(Framebuffer[1][0], 0, fbsize*4); - memset(Framebuffer[0][1], 0, fbsize*4); - memset(Framebuffer[1][1], 0, fbsize*4); + memset(Framebuffer[0][0].get(), 0, fbsize*4); + memset(Framebuffer[1][0].get(), 0, fbsize*4); + memset(Framebuffer[0][1].get(), 0, fbsize*4); + memset(Framebuffer[1][1].get(), 0, fbsize*4); AssignFramebuffers(); - - if (Renderer == 0) - { - GPU3D.GetCurrentRenderer()->SetRenderSettings(settings); - } -#ifdef OGLRENDERER_ENABLED - else - { - CurGLCompositor->SetRenderSettings(settings); - GPU3D.GetCurrentRenderer()->SetRenderSettings(settings); - } -#endif } @@ -1026,8 +934,8 @@ void GPU::BlankFrame() noexcept else fbsize = 256 * 192; - memset(Framebuffer[backbuf][0], 0, fbsize*4); - memset(Framebuffer[backbuf][1], 0, fbsize*4); + memset(Framebuffer[backbuf][0].get(), 0, fbsize*4); + memset(Framebuffer[backbuf][1].get(), 0, fbsize*4); FrontBuffer = backbuf; AssignFramebuffers(); @@ -1123,11 +1031,9 @@ void GPU::StartScanline(u32 line) noexcept GPU2D_B.VBlank(); GPU3D.VBlank(); -#ifdef OGLRENDERER_ENABLED // Need a better way to identify the openGL renderer in particular if (GPU3D.IsRendererAccelerated()) - CurGLCompositor->RenderFrame(); -#endif + GPU3D.Blit(); } } diff --git a/src/GPU.h b/src/GPU.h index 84210c9d..21a05d58 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -25,10 +25,6 @@ #include "GPU3D.h" #include "NonStupidBitfield.h" -#ifdef OGLRENDERER_ENABLED -#include "GPU_OpenGL.h" -#endif - namespace melonDS { class GPU3D; @@ -56,33 +52,30 @@ struct VRAMTrackingSet NonStupidBitField DeriveState(u32* currentMappings, GPU& gpu); }; -struct RenderSettings -{ - bool Soft_Threaded; - - int GL_ScaleFactor; - bool GL_BetterPolygons; -}; - class GPU { public: - GPU(melonDS::NDS& nds) noexcept; + explicit GPU(melonDS::NDS& nds, std::unique_ptr&& renderer3d = nullptr, std::unique_ptr&& renderer2d = nullptr) noexcept; ~GPU() noexcept; void Reset() noexcept; void Stop() noexcept; void DoSavestate(Savestate* file) noexcept; - [[deprecated("Set the renderer directly instead of using an integer code")]] void InitRenderer(int renderer) noexcept; - void DeInitRenderer() noexcept; - void ResetRenderer() noexcept; - - void SetRenderSettings(int renderer, RenderSettings& settings) noexcept; + /// Sets the active renderer to the renderer given in the provided pointer. + /// The pointer is moved-from, so it will be \c nullptr after this method is called. + /// If the pointer is \c nullptr, the renderer is reset to the default renderer. + void SetRenderer3D(std::unique_ptr&& renderer) noexcept; + [[nodiscard]] const Renderer3D& GetRenderer3D() const noexcept { return GPU3D.GetCurrentRenderer(); } + [[nodiscard]] Renderer3D& GetRenderer3D() noexcept { return GPU3D.GetCurrentRenderer(); } u8* GetUniqueBankPtr(u32 mask, u32 offset) noexcept; const u8* GetUniqueBankPtr(u32 mask, u32 offset) const noexcept; + void SetRenderer2D(std::unique_ptr&& renderer) noexcept { GPU2D_Renderer = std::move(renderer); } + [[nodiscard]] const GPU2D::Renderer2D& GetRenderer2D() const noexcept { return *GPU2D_Renderer; } + [[nodiscard]] GPU2D::Renderer2D& GetRenderer2D() noexcept { return *GPU2D_Renderer; } + void MapVRAM_AB(u32 bank, u8 cnt) noexcept; void MapVRAM_CD(u32 bank, u8 cnt) noexcept; void MapVRAM_E(u32 bank, u8 cnt) noexcept; @@ -578,7 +571,7 @@ public: u8* VRAMPtr_BOBJ[0x8] {}; int FrontBuffer = 0; - u32* Framebuffer[2][2] {}; + std::unique_ptr Framebuffer[2][2] {}; GPU2D::Unit GPU2D_A; GPU2D::Unit GPU2D_B; @@ -611,11 +604,6 @@ public: u8 VRAMFlat_Texture[512*1024] {}; u8 VRAMFlat_TexPal[128*1024] {}; - - int Renderer = 0; -#ifdef OGLRENDERER_ENABLED - std::unique_ptr CurGLCompositor = nullptr; -#endif private: void ResetVRAMCache() noexcept; void AssignFramebuffers() noexcept; diff --git a/src/GPU2D.h b/src/GPU2D.h index dd3bbf6b..7367d07a 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -35,7 +35,7 @@ public: // take a reference to the GPU so we can access its state // and ensure that it's not null Unit(u32 num, melonDS::GPU& gpu); - + virtual ~Unit() = default; Unit(const Unit&) = delete; Unit& operator=(const Unit&) = delete; diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 9e7d8491..6d0252c3 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -27,68 +27,7 @@ namespace GPU2D SoftRenderer::SoftRenderer(melonDS::GPU& gpu) : Renderer2D(), GPU(gpu) { - // initialize mosaic table - for (int m = 0; m < 16; m++) - { - for (int x = 0; x < 256; x++) - { - int offset = x % (m+1); - MosaicTable[m][x] = offset; - } - } -} - -u32 SoftRenderer::ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb) -{ - u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000008) >> 4; - u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x000800) >> 4) & 0x007F00; - u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x080000) >> 4) & 0x7F0000; - - if (r > 0x00003F) r = 0x00003F; - if (g > 0x003F00) g = 0x003F00; - if (b > 0x3F0000) b = 0x3F0000; - - return r | g | b | 0xFF000000; -} - -u32 SoftRenderer::ColorBlend5(u32 val1, u32 val2) -{ - u32 eva = ((val1 >> 24) & 0x1F) + 1; - u32 evb = 32 - eva; - - if (eva == 32) return val1; - - u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000010) >> 5; - u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x001000) >> 5) & 0x007F00; - u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x100000) >> 5) & 0x7F0000; - - if (r > 0x00003F) r = 0x00003F; - if (g > 0x003F00) g = 0x003F00; - if (b > 0x3F0000) b = 0x3F0000; - - return r | g | b | 0xFF000000; -} - -u32 SoftRenderer::ColorBrightnessUp(u32 val, u32 factor, u32 bias) -{ - u32 rb = val & 0x3F003F; - u32 g = val & 0x003F00; - - rb += (((((0x3F003F - rb) * factor) + (bias*0x010001)) >> 4) & 0x3F003F); - g += (((((0x003F00 - g ) * factor) + (bias*0x000100)) >> 4) & 0x003F00); - - return rb | g | 0xFF000000; -} - -u32 SoftRenderer::ColorBrightnessDown(u32 val, u32 factor, u32 bias) -{ - u32 rb = val & 0x3F003F; - u32 g = val & 0x003F00; - - rb -= ((((rb * factor) + (bias*0x010001)) >> 4) & 0x3F003F); - g -= ((((g * factor) + (bias*0x000100)) >> 4) & 0x003F00); - - return rb | g | 0xFF000000; + // mosaic table is initialized at compile-time } u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2) @@ -365,11 +304,11 @@ void SoftRenderer::DrawScanline(u32 line, Unit* unit) void SoftRenderer::VBlankEnd(Unit* unitA, Unit* unitB) { #ifdef OGLRENDERER_ENABLED - if (GPU.GPU3D.IsRendererAccelerated()) + if (Renderer3D& renderer3d = GPU.GPU3D.GetCurrentRenderer(); renderer3d.Accelerated) { if ((unitA->CaptureCnt & (1<<31)) && (((unitA->CaptureCnt >> 29) & 0x3) != 1)) { - reinterpret_cast(GPU.GPU3D.GetCurrentRenderer())->PrepareCaptureFrame(); + renderer3d.PrepareCaptureFrame(); } } #endif @@ -779,7 +718,7 @@ void SoftRenderer::DrawScanline_BGOBJ(u32 line) memset(WindowMask, 0xFF, 256); ApplySpriteMosaicX(); - CurBGXMosaicTable = MosaicTable[CurUnit->BGMosaicSize[0]]; + CurBGXMosaicTable = MosaicTable[CurUnit->BGMosaicSize[0]].data(); switch (CurUnit->DispCnt & 0x7) { @@ -1564,7 +1503,7 @@ void SoftRenderer::ApplySpriteMosaicX() u32* objLine = OBJLine[CurUnit->Num]; - u8* curOBJXMosaicTable = MosaicTable[CurUnit->OBJMosaicSize[1]]; + u8* curOBJXMosaicTable = MosaicTable[CurUnit->OBJMosaicSize[1]].data(); u32 lastcolor = objLine[0]; diff --git a/src/GPU2D_Soft.h b/src/GPU2D_Soft.h index 86943d33..ca242a51 100644 --- a/src/GPU2D_Soft.h +++ b/src/GPU2D_Soft.h @@ -49,12 +49,74 @@ private: u32 NumSprites[2]; u8* CurBGXMosaicTable; - u8 MosaicTable[16][256]; + array2d MosaicTable = []() constexpr + { + array2d table {}; + // initialize mosaic table + for (int m = 0; m < 16; m++) + { + for (int x = 0; x < 256; x++) + { + int offset = x % (m+1); + table[m][x] = offset; + } + } - u32 ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb); - u32 ColorBlend5(u32 val1, u32 val2); - u32 ColorBrightnessUp(u32 val, u32 factor, u32 bias); - u32 ColorBrightnessDown(u32 val, u32 factor, u32 bias); + return table; + }(); + + static constexpr u32 ColorBlend4(u32 val1, u32 val2, u32 eva, u32 evb) noexcept + { + u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000008) >> 4; + u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x000800) >> 4) & 0x007F00; + u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x080000) >> 4) & 0x7F0000; + + if (r > 0x00003F) r = 0x00003F; + if (g > 0x003F00) g = 0x003F00; + if (b > 0x3F0000) b = 0x3F0000; + + return r | g | b | 0xFF000000; + } + + static constexpr u32 ColorBlend5(u32 val1, u32 val2) noexcept + { + u32 eva = ((val1 >> 24) & 0x1F) + 1; + u32 evb = 32 - eva; + + if (eva == 32) return val1; + + u32 r = (((val1 & 0x00003F) * eva) + ((val2 & 0x00003F) * evb) + 0x000010) >> 5; + u32 g = ((((val1 & 0x003F00) * eva) + ((val2 & 0x003F00) * evb) + 0x001000) >> 5) & 0x007F00; + u32 b = ((((val1 & 0x3F0000) * eva) + ((val2 & 0x3F0000) * evb) + 0x100000) >> 5) & 0x7F0000; + + if (r > 0x00003F) r = 0x00003F; + if (g > 0x003F00) g = 0x003F00; + if (b > 0x3F0000) b = 0x3F0000; + + return r | g | b | 0xFF000000; + } + + static constexpr u32 ColorBrightnessUp(u32 val, u32 factor, u32 bias) noexcept + { + u32 rb = val & 0x3F003F; + u32 g = val & 0x003F00; + + rb += (((((0x3F003F - rb) * factor) + (bias*0x010001)) >> 4) & 0x3F003F); + g += (((((0x003F00 - g ) * factor) + (bias*0x000100)) >> 4) & 0x003F00); + + return rb | g | 0xFF000000; + } + + static constexpr u32 ColorBrightnessDown(u32 val, u32 factor, u32 bias) noexcept + { + u32 rb = val & 0x3F003F; + u32 g = val & 0x003F00; + + rb -= ((((rb * factor) + (bias*0x010001)) >> 4) & 0x3F003F); + g -= ((((g * factor) + (bias*0x000100)) >> 4) & 0x003F00); + + return rb | g | 0xFF000000; + } u32 ColorComposite(int i, u32 val1, u32 val2); template void DrawScanlineBGMode(u32 line); diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 5603a136..049ae589 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -22,6 +22,7 @@ #include "NDS.h" #include "GPU.h" #include "FIFO.h" +#include "GPU3D_Soft.h" #include "Platform.h" namespace melonDS @@ -139,7 +140,9 @@ const u8 CmdNumParams[256] = void MatrixLoadIdentity(s32* m); -GPU3D::GPU3D(melonDS::NDS& nds) noexcept : NDS(nds) +GPU3D::GPU3D(melonDS::NDS& nds, std::unique_ptr&& renderer) noexcept : + NDS(nds), + CurrentRenderer(renderer ? std::move(renderer) : std::make_unique(nds.GPU)) { } @@ -2336,6 +2339,12 @@ void GPU3D::RestartFrame() noexcept CurrentRenderer->RestartFrame(); } +void GPU3D::Stop() noexcept +{ + if (CurrentRenderer) + CurrentRenderer->Stop(); +} + bool YSort(Polygon* a, Polygon* b) { @@ -2888,6 +2897,12 @@ void GPU3D::Write32(u32 addr, u32 val) noexcept Log(LogLevel::Debug, "unknown GPU3D write32 %08X %08X\n", addr, val); } +void GPU3D::Blit() noexcept +{ + if (CurrentRenderer) + CurrentRenderer->Blit(); +} + Renderer3D::Renderer3D(bool Accelerated) : Accelerated(Accelerated) { } diff --git a/src/GPU3D.h b/src/GPU3D.h index 1d3e1265..8e743fac 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -27,7 +27,7 @@ namespace melonDS { -struct RenderSettings; +class GPU; struct Vertex { @@ -86,7 +86,7 @@ class NDS; class GPU3D { public: - GPU3D(melonDS::NDS& nds) noexcept; + GPU3D(melonDS::NDS& nds, std::unique_ptr&& renderer = nullptr) noexcept; ~GPU3D() noexcept = default; void Reset() noexcept; @@ -106,6 +106,7 @@ public: void VCount215() noexcept; void RestartFrame() noexcept; + void Stop() noexcept; void SetRenderXPos(u16 xpos) noexcept; [[nodiscard]] u16 GetRenderXPos() const noexcept { return RenderXPos; } @@ -114,8 +115,8 @@ public: void WriteToGXFIFO(u32 val) noexcept; [[nodiscard]] bool IsRendererAccelerated() const noexcept; - [[nodiscard]] Renderer3D* GetCurrentRenderer() noexcept { return CurrentRenderer.get(); } - [[nodiscard]] const Renderer3D* GetCurrentRenderer() const noexcept { return CurrentRenderer.get(); } + [[nodiscard]] Renderer3D& GetCurrentRenderer() noexcept { return *CurrentRenderer; } + [[nodiscard]] const Renderer3D& GetCurrentRenderer() const noexcept { return *CurrentRenderer; } void SetCurrentRenderer(std::unique_ptr&& renderer) noexcept { CurrentRenderer = std::move(renderer); } u8 Read8(u32 addr) noexcept; @@ -124,6 +125,7 @@ public: void Write8(u32 addr, u8 val) noexcept; void Write16(u32 addr, u16 val) noexcept; void Write32(u32 addr, u32 val) noexcept; + void Blit() noexcept; private: melonDS::NDS& NDS; typedef union @@ -338,13 +340,13 @@ public: // are more detailed "traits" that we can ask of the Renderer3D type const bool Accelerated; - virtual void SetRenderSettings(const RenderSettings& settings) noexcept = 0; - virtual void VCount144() {}; - + virtual void Stop() {} virtual void RenderFrame() = 0; virtual void RestartFrame() {}; virtual u32* GetLine(int line) = 0; + virtual void Blit() {}; + virtual void PrepareCaptureFrame() {} protected: Renderer3D(bool Accelerated); }; diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index fb9f2461..bee04305 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -97,7 +97,10 @@ void SetupDefaultTexParams(GLuint tex) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } -GLRenderer::GLRenderer(melonDS::GPU& gpu) noexcept : Renderer3D(true), GPU(gpu) +GLRenderer::GLRenderer(GLCompositor&& compositor, melonDS::GPU& gpu) noexcept : + Renderer3D(true), + GPU(gpu), + CurGLCompositor(std::move(compositor)) { // GLRenderer::New() will be used to actually initialize the renderer; // The various glDelete* functions silently ignore invalid IDs, @@ -108,9 +111,14 @@ std::unique_ptr GLRenderer::New(melonDS::GPU& gpu) noexcept { assert(glEnable != nullptr); + std::optional compositor = GLCompositor::New(); + if (!compositor) + return nullptr; + // Will be returned if the initialization succeeds, // or cleaned up via RAII if it fails. - std::unique_ptr result = std::unique_ptr(new GLRenderer(gpu)); + std::unique_ptr result = std::unique_ptr(new GLRenderer(std::move(*compositor), gpu)); + compositor = std::nullopt; glEnable(GL_DEPTH_TEST); glEnable(GL_STENCIL_TEST); @@ -327,14 +335,29 @@ GLRenderer::~GLRenderer() void GLRenderer::Reset() { + // This is where the compositor's Reset() method would be called, + // except there's no such method right now. } -void GLRenderer::SetRenderSettings(const RenderSettings& settings) noexcept +void GLRenderer::SetBetterPolygons(bool betterpolygons) noexcept { - int scale = settings.GL_ScaleFactor; + SetRenderSettings(betterpolygons, ScaleFactor); +} +void GLRenderer::SetScaleFactor(int scale) noexcept +{ + SetRenderSettings(BetterPolygons, scale); +} + + +void GLRenderer::SetRenderSettings(bool betterpolygons, int scale) noexcept +{ + if (betterpolygons == BetterPolygons && scale == ScaleFactor) + return; + + CurGLCompositor.SetScaleFactor(scale); ScaleFactor = scale; - BetterPolygons = settings.GL_BetterPolygons; + BetterPolygons = betterpolygons; ScreenW = 256 * scale; ScreenH = 192 * scale; @@ -1302,6 +1325,11 @@ void GLRenderer::RenderFrame() FrontBuffer = FrontBuffer ? 0 : 1; } +void GLRenderer::Stop() +{ + CurGLCompositor.Stop(GPU); +} + void GLRenderer::PrepareCaptureFrame() { // TODO: make sure this picks the right buffer when doing antialiasing @@ -1317,6 +1345,11 @@ void GLRenderer::PrepareCaptureFrame() glReadPixels(0, 0, 256, 192, GL_BGRA, GL_UNSIGNED_BYTE, NULL); } +void GLRenderer::Blit() +{ + CurGLCompositor.RenderFrame(GPU, *this); +} + u32* GLRenderer::GetLine(int line) { int stride = 256; diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index 0bc20d81..63ee8de2 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -20,7 +20,7 @@ #ifdef OGLRENDERER_ENABLED #include "GPU3D.h" - +#include "GPU_OpenGL.h" #include "OpenGLSupport.h" namespace melonDS @@ -30,22 +30,31 @@ class GPU; class GLRenderer : public Renderer3D { public: - virtual ~GLRenderer() override; - virtual void Reset() override; + ~GLRenderer() override; + void Reset() override; - virtual void SetRenderSettings(const RenderSettings& settings) noexcept override; + void SetRenderSettings(bool betterpolygons, int scale) noexcept; + void SetBetterPolygons(bool betterpolygons) noexcept; + void SetScaleFactor(int scale) noexcept; + [[nodiscard]] bool GetBetterPolygons() const noexcept { return BetterPolygons; } + [[nodiscard]] int GetScaleFactor() const noexcept { return ScaleFactor; } - virtual void VCount144() override {}; - virtual void RenderFrame() override; - virtual u32* GetLine(int line) override; + void VCount144() override {}; + void RenderFrame() override; + void Stop() override; + u32* GetLine(int line) override; void SetupAccelFrame(); - void PrepareCaptureFrame(); + void PrepareCaptureFrame() override; + void Blit() override; + + [[nodiscard]] const GLCompositor& GetCompositor() const noexcept { return CurGLCompositor; } + GLCompositor& GetCompositor() noexcept { return CurGLCompositor; } static std::unique_ptr New(melonDS::GPU& gpu) noexcept; private: // Used by New() - GLRenderer(melonDS::GPU& gpu) noexcept; + GLRenderer(GLCompositor&& compositor, GPU& gpu) noexcept; // GL version requirements // * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS) @@ -66,6 +75,7 @@ private: }; melonDS::GPU& GPU; + GLCompositor CurGLCompositor; RendererPolygon PolygonList[2048] {}; bool BuildRenderShader(u32 flags, const char* vs, const char* fs); diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 809bd593..03c6265e 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -71,14 +71,13 @@ void SoftRenderer::SetupRenderThread() } -SoftRenderer::SoftRenderer(melonDS::GPU& gpu) noexcept - : Renderer3D(false), GPU(gpu) +SoftRenderer::SoftRenderer(melonDS::GPU& gpu, bool threaded) noexcept + : Renderer3D(false), GPU(gpu), Threaded(threaded) { Sema_RenderStart = Platform::Semaphore_Create(); Sema_RenderDone = Platform::Semaphore_Create(); Sema_ScanlineCount = Platform::Semaphore_Create(); - Threaded = false; RenderThreadRunning = false; RenderThreadRendering = false; RenderThread = nullptr; @@ -104,10 +103,13 @@ void SoftRenderer::Reset() SetupRenderThread(); } -void SoftRenderer::SetRenderSettings(const RenderSettings& settings) noexcept +void SoftRenderer::SetThreaded(bool threaded) noexcept { - Threaded = settings.Soft_Threaded; - SetupRenderThread(); + if (Threaded != threaded) + { + Threaded = threaded; + SetupRenderThread(); + } } void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 9fb9a718..2f5664e2 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -29,16 +29,17 @@ namespace melonDS class SoftRenderer : public Renderer3D { public: - SoftRenderer(melonDS::GPU& gpu) noexcept; - virtual ~SoftRenderer() override; - virtual void Reset() override; + SoftRenderer(melonDS::GPU& gpu, bool threaded = false) noexcept; + ~SoftRenderer() override; + void Reset() override; - virtual void SetRenderSettings(const RenderSettings& settings) noexcept override; + void SetThreaded(bool threaded) noexcept; + [[nodiscard]] bool IsThreaded() const noexcept { return Threaded; } - virtual void VCount144() override; - virtual void RenderFrame() override; - virtual void RestartFrame() override; - virtual u32* GetLine(int line) override; + void VCount144() override; + void RenderFrame() override; + void RestartFrame() override; + u32* GetLine(int line) override; void SetupRenderThread(); void StopRenderThread(); @@ -65,13 +66,13 @@ private: class Interpolator { public: - Interpolator() {} - Interpolator(s32 x0, s32 x1, s32 w0, s32 w1) + constexpr Interpolator() {} + constexpr Interpolator(s32 x0, s32 x1, s32 w0, s32 w1) { Setup(x0, x1, w0, w1); } - void Setup(s32 x0, s32 x1, s32 w0, s32 w1) + constexpr void Setup(s32 x0, s32 x1, s32 w0, s32 w1) { this->x0 = x0; this->x1 = x1; @@ -123,7 +124,7 @@ private: } } - void SetX(s32 x) + constexpr void SetX(s32 x) { x -= x0; this->x = x; @@ -139,7 +140,7 @@ private: } } - s32 Interpolate(s32 y0, s32 y1) + constexpr s32 Interpolate(s32 y0, s32 y1) const { if (xdiff == 0 || y0 == y1) return y0; @@ -161,7 +162,7 @@ private: } } - s32 InterpolateZ(s32 z0, s32 z1, bool wbuffer) + constexpr s32 InterpolateZ(s32 z0, s32 z1, bool wbuffer) const { if (xdiff == 0 || z0 == z1) return z0; @@ -228,9 +229,9 @@ private: class Slope { public: - Slope() {} + constexpr Slope() {} - s32 SetupDummy(s32 x0) + constexpr s32 SetupDummy(s32 x0) { dx = 0; @@ -249,7 +250,7 @@ private: return x0; } - s32 Setup(s32 x0, s32 x1, s32 y0, s32 y1, s32 w0, s32 w1, s32 y) + constexpr s32 Setup(s32 x0, s32 x1, s32 y0, s32 y1, s32 w0, s32 w1, s32 y) { this->x0 = x0; this->y = y; @@ -293,7 +294,7 @@ private: XMajor = (Increment > 0x40000); - if (side) + if constexpr (side) { // right @@ -324,7 +325,7 @@ private: return x; } - s32 Step() + constexpr s32 Step() { dx += Increment; y++; @@ -334,7 +335,7 @@ private: return x; } - s32 XVal() + constexpr s32 XVal() const { s32 ret; if (Negative) ret = x0 - (dx >> 18); @@ -346,7 +347,7 @@ private: } template - void EdgeParams_XMajor(s32* length, s32* coverage) + constexpr void EdgeParams_XMajor(s32* length, s32* coverage) const { // only do length calc for right side when swapped as it's // only needed for aa calcs, as actual line spans are broken @@ -372,7 +373,7 @@ private: } template - void EdgeParams_YMajor(s32* length, s32* coverage) + constexpr void EdgeParams_YMajor(s32* length, s32* coverage) const { *length = 1; @@ -404,7 +405,7 @@ private: } template - void EdgeParams(s32* length, s32* coverage) + constexpr void EdgeParams(s32* length, s32* coverage) const { if (XMajor) return EdgeParams_XMajor(length, coverage); diff --git a/src/GPU_OpenGL.cpp b/src/GPU_OpenGL.cpp index 2db38105..2e2857ce 100644 --- a/src/GPU_OpenGL.cpp +++ b/src/GPU_OpenGL.cpp @@ -33,13 +33,13 @@ namespace melonDS using namespace OpenGL; -std::unique_ptr GLCompositor::New(melonDS::GPU& gpu) noexcept +std::optional GLCompositor::New() noexcept { assert(glBindAttribLocation != nullptr); std::array CompShader {}; if (!OpenGL::BuildShaderProgram(kCompositorVS, kCompositorFS_Nearest, &CompShader[0], "CompositorShader")) - return nullptr; + return std::nullopt; glBindAttribLocation(CompShader[2], 0, "vPosition"); glBindAttribLocation(CompShader[2], 1, "vTexcoord"); @@ -48,12 +48,12 @@ std::unique_ptr GLCompositor::New(melonDS::GPU& gpu) noexcept if (!OpenGL::LinkShaderProgram(CompShader.data())) // OpenGL::LinkShaderProgram already deletes the shader program object // if linking the shaders together failed. - return nullptr; + return std::nullopt; - return std::unique_ptr(new GLCompositor(CompShader, gpu)); + return { GLCompositor(CompShader) }; } -GLCompositor::GLCompositor(std::array compShader, melonDS::GPU& gpu) noexcept : CompShader(compShader), GPU(gpu) +GLCompositor::GLCompositor(std::array compShader) noexcept : CompShader(compShader) { CompScaleLoc = glGetUniformLocation(CompShader[2], "u3DScale"); Comp3DXPosLoc = glGetUniformLocation(CompShader[2], "u3DXPos"); @@ -92,7 +92,7 @@ GLCompositor::GLCompositor(std::array compShader, melonDS::GPU& gpu) glGenBuffers(1, &CompVertexBufferID); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); - glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), CompVertices, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(CompVertices), &CompVertices[0], GL_STATIC_DRAW); glGenVertexArrays(1, &CompVertexArrayID); glBindVertexArray(CompVertexArrayID); @@ -101,7 +101,7 @@ GLCompositor::GLCompositor(std::array compShader, melonDS::GPU& gpu) glEnableVertexAttribArray(1); // texcoord glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(CompVertex), (void*)(offsetof(CompVertex, Texcoord))); - glGenFramebuffers(2, CompScreenOutputFB); + glGenFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]); glGenTextures(1, &CompScreenInputTex); glActiveTexture(GL_TEXTURE0); @@ -112,10 +112,10 @@ GLCompositor::GLCompositor(std::array compShader, melonDS::GPU& gpu) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, 256*3 + 1, 192*2, 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, NULL); - glGenTextures(2, CompScreenOutputTex); - for (int i = 0; i < 2; i++) + glGenTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]); + for (GLuint i : CompScreenOutputTex) { - glBindTexture(GL_TEXTURE_2D, CompScreenOutputTex[i]); + glBindTexture(GL_TEXTURE_2D, i); 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); @@ -129,9 +129,9 @@ GLCompositor::~GLCompositor() { assert(glDeleteFramebuffers != nullptr); - glDeleteFramebuffers(2, CompScreenOutputFB); + glDeleteFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]); glDeleteTextures(1, &CompScreenInputTex); - glDeleteTextures(2, CompScreenOutputTex); + glDeleteTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]); glDeleteVertexArrays(1, &CompVertexArrayID); glDeleteBuffers(1, &CompVertexBufferID); @@ -139,14 +139,75 @@ GLCompositor::~GLCompositor() OpenGL::DeleteShaderProgram(CompShader.data()); } -void GLCompositor::Reset() + +GLCompositor::GLCompositor(GLCompositor&& other) noexcept : + Scale(other.Scale), + ScreenH(other.ScreenH), + ScreenW(other.ScreenW), + CompScaleLoc(other.CompScaleLoc), + Comp3DXPosLoc(other.Comp3DXPosLoc), + CompVertices(other.CompVertices), + CompShader(other.CompShader), + CompVertexBufferID(other.CompVertexBufferID), + CompVertexArrayID(other.CompVertexArrayID), + CompScreenInputTex(other.CompScreenInputTex), + CompScreenOutputTex(other.CompScreenOutputTex), + CompScreenOutputFB(other.CompScreenOutputFB) { + other.CompScreenOutputFB = {}; + other.CompScreenInputTex = {}; + other.CompScreenOutputTex = {}; + other.CompVertexArrayID = {}; + other.CompVertexBufferID = {}; + other.CompShader = {}; +} + +GLCompositor& GLCompositor::operator=(GLCompositor&& other) noexcept +{ + if (this != &other) + { + Scale = other.Scale; + ScreenH = other.ScreenH; + ScreenW = other.ScreenW; + CompScaleLoc = other.CompScaleLoc; + Comp3DXPosLoc = other.Comp3DXPosLoc; + CompVertices = other.CompVertices; + + // Clean up these resources before overwriting them + OpenGL::DeleteShaderProgram(CompShader.data()); + CompShader = other.CompShader; + + glDeleteBuffers(1, &CompVertexBufferID); + CompVertexBufferID = other.CompVertexBufferID; + + glDeleteVertexArrays(1, &CompVertexArrayID); + CompVertexArrayID = other.CompVertexArrayID; + + glDeleteTextures(1, &CompScreenInputTex); + CompScreenInputTex = other.CompScreenInputTex; + + glDeleteTextures(CompScreenOutputTex.size(), &CompScreenOutputTex[0]); + CompScreenOutputTex = other.CompScreenOutputTex; + + glDeleteFramebuffers(CompScreenOutputFB.size(), &CompScreenOutputFB[0]); + CompScreenOutputFB = other.CompScreenOutputFB; + + other.CompScreenOutputFB = {}; + other.CompScreenInputTex = {}; + other.CompScreenOutputTex = {}; + other.CompVertexArrayID = {}; + other.CompVertexBufferID = {}; + other.CompShader = {}; + } + + return *this; } -void GLCompositor::SetRenderSettings(const RenderSettings& settings) noexcept +void GLCompositor::SetScaleFactor(int scale) noexcept { - int scale = settings.GL_ScaleFactor; + if (scale == Scale) + return; Scale = scale; ScreenW = 256 * scale; @@ -170,13 +231,12 @@ void GLCompositor::SetRenderSettings(const RenderSettings& settings) noexcept glBindFramebuffer(GL_FRAMEBUFFER, 0); } -void GLCompositor::Stop() +void GLCompositor::Stop(const GPU& gpu) noexcept { for (int i = 0; i < 2; i++) { - int frontbuf = GPU.FrontBuffer; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[gpu.FrontBuffer]); glClear(GL_COLOR_BUFFER_BIT); } @@ -184,9 +244,9 @@ void GLCompositor::Stop() glBindFramebuffer(GL_FRAMEBUFFER, 0); } -void GLCompositor::RenderFrame() +void GLCompositor::RenderFrame(const GPU& gpu, GLRenderer& renderer) noexcept { - int frontbuf = GPU.FrontBuffer; + int frontbuf = gpu.FrontBuffer; glBindFramebuffer(GL_READ_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, CompScreenOutputFB[frontbuf]); @@ -204,21 +264,21 @@ void GLCompositor::RenderFrame() glUniform1ui(CompScaleLoc, Scale); // TODO: support setting this midframe, if ever needed - glUniform1i(Comp3DXPosLoc, ((int)GPU.GPU3D.GetRenderXPos() << 23) >> 23); + glUniform1i(Comp3DXPosLoc, ((int)gpu.GPU3D.GetRenderXPos() << 23) >> 23); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, CompScreenInputTex); - if (GPU.Framebuffer[frontbuf][0] && GPU.Framebuffer[frontbuf][1]) + if (gpu.Framebuffer[frontbuf][0] && gpu.Framebuffer[frontbuf][1]) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU.Framebuffer[frontbuf][0]); + GL_UNSIGNED_BYTE, gpu.Framebuffer[frontbuf][0].get()); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256*3 + 1, 192, GL_RGBA_INTEGER, - GL_UNSIGNED_BYTE, GPU.Framebuffer[frontbuf][1]); + GL_UNSIGNED_BYTE, gpu.Framebuffer[frontbuf][1].get()); } glActiveTexture(GL_TEXTURE1); - reinterpret_cast(GPU.GPU3D.GetCurrentRenderer())->SetupAccelFrame(); + renderer.SetupAccelFrame(); glBindBuffer(GL_ARRAY_BUFFER, CompVertexBufferID); glBindVertexArray(CompVertexArrayID); diff --git a/src/GPU_OpenGL.h b/src/GPU_OpenGL.h index 68462a9a..9c040966 100644 --- a/src/GPU_OpenGL.h +++ b/src/GPU_OpenGL.h @@ -21,51 +21,51 @@ #include "OpenGLSupport.h" #include -#include +#include namespace melonDS { class GPU; struct RenderSettings; - +class GLRenderer; class GLCompositor { public: - static std::unique_ptr New(melonDS::GPU& gpu) noexcept; + static std::optional New() noexcept; GLCompositor(const GLCompositor&) = delete; GLCompositor& operator=(const GLCompositor&) = delete; + GLCompositor(GLCompositor&&) noexcept; + GLCompositor& operator=(GLCompositor&&) noexcept; ~GLCompositor(); - void Reset(); + void SetScaleFactor(int scale) noexcept; + [[nodiscard]] int GetScaleFactor() const noexcept { return Scale; } - void SetRenderSettings(const RenderSettings& settings) noexcept; - - void Stop(); - void RenderFrame(); + void Stop(const GPU& gpu) noexcept; + void RenderFrame(const GPU& gpu, GLRenderer& renderer) noexcept; void BindOutputTexture(int buf); private: - GLCompositor(std::array CompShader, melonDS::GPU& gpu) noexcept; - melonDS::GPU& GPU; - int Scale; - int ScreenH, ScreenW; + GLCompositor(std::array CompShader) noexcept; + int Scale = 0; + int ScreenH = 0, ScreenW = 0; - std::array CompShader; - GLuint CompScaleLoc; - GLuint Comp3DXPosLoc; + std::array CompShader {}; + GLuint CompScaleLoc = 0; + GLuint Comp3DXPosLoc = 0; - GLuint CompVertexBufferID; - GLuint CompVertexArrayID; + GLuint CompVertexBufferID = 0; + GLuint CompVertexArrayID = 0; struct CompVertex { - float Position[2]; - float Texcoord[2]; + std::array Position {}; + std::array Texcoord {}; }; - CompVertex CompVertices[2 * 3*2]; + std::array CompVertices {}; - GLuint CompScreenInputTex; - GLuint CompScreenOutputTex[2]; - GLuint CompScreenOutputFB[2]; + GLuint CompScreenInputTex = 0; + std::array CompScreenOutputTex {}; + std::array CompScreenOutputFB {}; }; } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 54a6f14f..a0ac0860 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -93,6 +93,8 @@ #include "RTC.h" #include "DSi.h" #include "DSi_I2C.h" +#include "GPU3D_Soft.h" +#include "GPU3D_OpenGL.h" #include "Savestate.h" @@ -163,7 +165,6 @@ EmuThread* emuThread; int autoScreenSizing = 0; int videoRenderer; -RenderSettings videoSettings; bool videoSettingsDirty; CameraManager* camManager[2]; @@ -350,9 +351,6 @@ void EmuThread::run() autoScreenSizing = 0; videoSettingsDirty = false; - videoSettings.Soft_Threaded = Config::Threaded3D != 0; - videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; - videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; if (mainWindow->hasOGL) { @@ -364,8 +362,16 @@ void EmuThread::run() videoRenderer = 0; } - NDS->GPU.InitRenderer(videoRenderer); - NDS->GPU.SetRenderSettings(videoRenderer, videoSettings); + if (videoRenderer == 0) + { // If we're using the software renderer... + NDS->GPU.SetRenderer3D(std::make_unique(NDS->GPU, Config::Threaded3D != 0)); + } + else + { + auto glrenderer = melonDS::GLRenderer::New(NDS->GPU); + glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); + NDS->GPU.SetRenderer3D(std::move(glrenderer)); + } NDS->SPU.SetInterpolation(Config::AudioInterp); @@ -491,11 +497,16 @@ void EmuThread::run() videoSettingsDirty = false; - videoSettings.Soft_Threaded = Config::Threaded3D != 0; - videoSettings.GL_ScaleFactor = Config::GL_ScaleFactor; - videoSettings.GL_BetterPolygons = Config::GL_BetterPolygons; - - NDS->GPU.SetRenderSettings(videoRenderer, videoSettings); + if (videoRenderer == 0) + { // If we're using the software renderer... + NDS->GPU.SetRenderer3D(std::make_unique(NDS->GPU, Config::Threaded3D != 0)); + } + else + { + auto glrenderer = melonDS::GLRenderer::New(NDS->GPU); + glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); + NDS->GPU.SetRenderer3D(std::move(glrenderer)); + } } // process input and hotkeys @@ -804,10 +815,10 @@ void EmuThread::drawScreenGL() glActiveTexture(GL_TEXTURE0); #ifdef OGLRENDERER_ENABLED - if (NDS->GPU.Renderer != 0) + if (NDS->GPU.GetRenderer3D().Accelerated) { // hardware-accelerated render - NDS->GPU.CurGLCompositor->BindOutputTexture(frontbuf); + static_cast(NDS->GPU.GetRenderer3D()).GetCompositor().BindOutputTexture(frontbuf); } else #endif @@ -818,9 +829,9 @@ void EmuThread::drawScreenGL() if (NDS->GPU.Framebuffer[frontbuf][0] && NDS->GPU.Framebuffer[frontbuf][1]) { glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][0]); + GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][0].get()); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][1]); + GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][1].get()); } } @@ -1122,8 +1133,8 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) return; } - memcpy(screen[0].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][0], 256 * 192 * 4); - memcpy(screen[1].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][1], 256 * 192 * 4); + memcpy(screen[0].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4); + memcpy(screen[1].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4); emuThread->FrontBufferLock.unlock(); QRect screenrc(0, 0, 256, 192); diff --git a/src/types.h b/src/types.h index d37b2251..86a10aa7 100644 --- a/src/types.h +++ b/src/types.h @@ -20,6 +20,7 @@ #define TYPES_H #include +#include namespace melonDS { @@ -32,5 +33,7 @@ typedef int16_t s16; typedef int32_t s32; typedef int64_t s64; +template +using array2d = std::array, A>; } #endif // TYPES_H From da8d413ad9e339c40178be18da20aee1435c8cd4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Mon, 4 Dec 2023 11:56:01 -0500 Subject: [PATCH 050/157] Slight cleanup to SPU (#1900) * Move `SPUChannel` and `SPUCaptureUnit` to be stored inside `array`s instead of allocated separately * Default-initialize most of `SPU`'s fields * Generate the interpolation tables at compile-time with `constexpr` - Now it's faster and thread-safe * Slight cleanup in SPU - Iniitialize most fields in the class declaration * Mark `SPU` as `explicit` --- src/SPU.cpp | 240 +++++++++++++++++++++++++--------------------------- src/SPU.h | 108 +++++++++++------------ 2 files changed, 168 insertions(+), 180 deletions(-) diff --git a/src/SPU.cpp b/src/SPU.cpp index 00db3691..cff38d3a 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -65,23 +65,69 @@ const s16 SPUChannel::PSGTable[8][8] = {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF} }; -s16 SPUChannel::InterpCos[0x100]; -s16 SPUChannel::InterpCubic[0x100][4]; -bool SPUChannel::InterpInited = false; +// generate interpolation tables +// values are 1:1:14 fixed-point +constexpr std::array InterpCos = []() constexpr { + std::array interp {}; + float m_pi = std::acos(-1.0f); + for (int i = 0; i < 0x100; i++) + { + float ratio = (i * m_pi) / 255.0f; + ratio = 1.0f - std::cos(ratio); -SPU::SPU(melonDS::NDS& nds) : NDS(nds) + interp[i] = (s16)(ratio * 0x2000); + } + + return interp; +}(); + +constexpr array2d InterpCubic = []() constexpr { + array2d interp {}; + + for (int i = 0; i < 0x100; i++) + { + s32 i1 = i << 6; + s32 i2 = (i * i) >> 2; + s32 i3 = (i * i * i) >> 10; + + interp[i][0] = -i3 + 2*i2 - i1; + interp[i][1] = i3 - 2*i2 + 0x4000; + interp[i][2] = -i3 + i2 + i1; + interp[i][3] = i3 - i2; + } + + return interp; +}(); + +SPU::SPU(melonDS::NDS& nds) : + NDS(nds), + Channels { + SPUChannel(0, nds), + SPUChannel(1, nds), + SPUChannel(2, nds), + SPUChannel(3, nds), + SPUChannel(4, nds), + SPUChannel(5, nds), + SPUChannel(6, nds), + SPUChannel(7, nds), + SPUChannel(8, nds), + SPUChannel(9, nds), + SPUChannel(10, nds), + SPUChannel(11, nds), + SPUChannel(12, nds), + SPUChannel(13, nds), + SPUChannel(14, nds), + SPUChannel(15, nds), + }, + Capture { + SPUCaptureUnit(0, nds), + SPUCaptureUnit(1, nds), + }, + AudioLock(Platform::Mutex_Create()) { NDS.RegisterEventFunc(Event_SPU, 0, MemberEventFunc(SPU, Mix)); - for (int i = 0; i < 16; i++) - Channels[i] = new SPUChannel(i, NDS); - - Capture[0] = new SPUCaptureUnit(0, NDS); - Capture[1] = new SPUCaptureUnit(1, NDS); - - AudioLock = Platform::Mutex_Create(); - ApplyBias = true; Degrade10Bit = false; @@ -90,50 +136,10 @@ SPU::SPU(melonDS::NDS& nds) : NDS(nds) OutputBackbufferWritePosition = 0; OutputFrontBufferReadPosition = 0; OutputFrontBufferWritePosition = 0; - - if (!SPUChannel::InterpInited) - { - // generate interpolation tables - // values are 1:1:14 fixed-point - - float m_pi = std::acos(-1.0f); - for (int i = 0; i < 0x100; i++) - { - float ratio = (i * m_pi) / 255.0f; - ratio = 1.0f - std::cos(ratio); - - SPUChannel::InterpCos[i] = (s16)(ratio * 0x2000); - } - - for (int i = 0; i < 0x100; i++) - { - s32 i1 = i << 6; - s32 i2 = (i * i) >> 2; - s32 i3 = (i * i * i) >> 10; - - SPUChannel::InterpCubic[i][0] = -i3 + 2*i2 - i1; - SPUChannel::InterpCubic[i][1] = i3 - 2*i2 + 0x4000; - SPUChannel::InterpCubic[i][2] = -i3 + i2 + i1; - SPUChannel::InterpCubic[i][3] = i3 - i2; - } - - SPUChannel::InterpInited = true; - } } SPU::~SPU() { - 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; @@ -149,10 +155,10 @@ void SPU::Reset() Bias = 0; for (int i = 0; i < 16; i++) - Channels[i]->Reset(); + Channels[i].Reset(); - Capture[0]->Reset(); - Capture[1]->Reset(); + Capture[0].Reset(); + Capture[1].Reset(); NDS.ScheduleEvent(Event_SPU, false, 1024, 0, 0); } @@ -176,11 +182,11 @@ void SPU::DoSavestate(Savestate* file) file->Var8(&MasterVolume); file->Var16(&Bias); - for (int i = 0; i < 16; i++) - Channels[i]->DoSavestate(file); + for (SPUChannel& channel : Channels) + channel.DoSavestate(file); - Capture[0]->DoSavestate(file); - Capture[1]->DoSavestate(file); + for (SPUCaptureUnit& capture : Capture) + capture.DoSavestate(file); } @@ -192,8 +198,8 @@ void SPU::SetPowerCnt(u32 val) void SPU::SetInterpolation(int type) { - for (int i = 0; i < 16; i++) - Channels[i]->InterpType = type; + for (SPUChannel& channel : Channels) + channel.InterpType = type; } void SPU::SetBias(u16 bias) @@ -212,14 +218,7 @@ void SPU::SetDegrade10Bit(bool enable) } -SPUChannel::SPUChannel(u32 num, melonDS::NDS& nds) : NDS(nds) -{ - Num = num; - - InterpType = 0; -} - -SPUChannel::~SPUChannel() +SPUChannel::SPUChannel(u32 num, melonDS::NDS& nds) : NDS(nds), Num(num) { } @@ -579,11 +578,6 @@ void SPUChannel::PanOutput(s32 in, s32& left, s32& right) SPUCaptureUnit::SPUCaptureUnit(u32 num, melonDS::NDS& nds) : NDS(nds), Num(num) -{ - Num = num; -} - -SPUCaptureUnit::~SPUCaptureUnit() { } @@ -713,21 +707,21 @@ void SPU::Mix(u32 dummy) if ((Cnt & (1<<15)) && (!dummy)) { - s32 ch0 = Channels[0]->DoRun(); - s32 ch1 = Channels[1]->DoRun(); - s32 ch2 = Channels[2]->DoRun(); - s32 ch3 = Channels[3]->DoRun(); + s32 ch0 = Channels[0].DoRun(); + s32 ch1 = Channels[1].DoRun(); + s32 ch2 = Channels[2].DoRun(); + s32 ch3 = Channels[3].DoRun(); // TODO: addition from capture registers - Channels[0]->PanOutput(ch0, left, right); - Channels[2]->PanOutput(ch2, left, right); + Channels[0].PanOutput(ch0, left, right); + Channels[2].PanOutput(ch2, left, right); - if (!(Cnt & (1<<12))) Channels[1]->PanOutput(ch1, left, right); - if (!(Cnt & (1<<13))) Channels[3]->PanOutput(ch3, left, right); + if (!(Cnt & (1<<12))) Channels[1].PanOutput(ch1, left, right); + if (!(Cnt & (1<<13))) Channels[3].PanOutput(ch3, left, right); for (int i = 4; i < 16; i++) { - SPUChannel* chan = Channels[i]; + SPUChannel* chan = &Channels[i]; s32 channel = chan->DoRun(); chan->PanOutput(channel, left, right); @@ -736,7 +730,7 @@ void SPU::Mix(u32 dummy) // sound capture // TODO: other sound capture sources, along with their bugs - if (Capture[0]->Cnt & (1<<7)) + if (Capture[0].Cnt & (1<<7)) { s32 val = left; @@ -744,10 +738,10 @@ void SPU::Mix(u32 dummy) if (val < -0x8000) val = -0x8000; else if (val > 0x7FFF) val = 0x7FFF; - Capture[0]->Run(val); + Capture[0].Run(val); } - if (Capture[1]->Cnt & (1<<7)) + if (Capture[1].Cnt & (1<<7)) { s32 val = right; @@ -755,7 +749,7 @@ void SPU::Mix(u32 dummy) if (val < -0x8000) val = -0x8000; else if (val > 0x7FFF) val = 0x7FFF; - Capture[1]->Run(val); + Capture[1].Run(val); } // final output @@ -767,20 +761,20 @@ void SPU::Mix(u32 dummy) break; case 0x0100: // channel 1 { - s32 pan = 128 - Channels[1]->Pan; + s32 pan = 128 - Channels[1].Pan; leftoutput = ((s64)ch1 * pan) >> 10; } break; case 0x0200: // channel 3 { - s32 pan = 128 - Channels[3]->Pan; + s32 pan = 128 - Channels[3].Pan; leftoutput = ((s64)ch3 * pan) >> 10; } break; case 0x0300: // channel 1+3 { - s32 pan1 = 128 - Channels[1]->Pan; - s32 pan3 = 128 - Channels[3]->Pan; + s32 pan1 = 128 - Channels[1].Pan; + s32 pan3 = 128 - Channels[3].Pan; leftoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10); } break; @@ -793,20 +787,20 @@ void SPU::Mix(u32 dummy) break; case 0x0400: // channel 1 { - s32 pan = Channels[1]->Pan; + s32 pan = Channels[1].Pan; rightoutput = ((s64)ch1 * pan) >> 10; } break; case 0x0800: // channel 3 { - s32 pan = Channels[3]->Pan; + s32 pan = Channels[3].Pan; rightoutput = ((s64)ch3 * pan) >> 10; } break; case 0x0C00: // channel 1+3 { - s32 pan1 = Channels[1]->Pan; - s32 pan3 = Channels[3]->Pan; + s32 pan1 = Channels[1].Pan; + s32 pan3 = Channels[3].Pan; rightoutput = (((s64)ch1 * pan1) >> 10) + (((s64)ch3 * pan3) >> 10); } break; @@ -982,7 +976,7 @@ u8 SPU::Read8(u32 addr) { if (addr < 0x04000500) { - SPUChannel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -999,8 +993,8 @@ u8 SPU::Read8(u32 addr) case 0x04000500: return Cnt & 0x7F; case 0x04000501: return Cnt >> 8; - case 0x04000508: return Capture[0]->Cnt; - case 0x04000509: return Capture[1]->Cnt; + case 0x04000508: return Capture[0].Cnt; + case 0x04000509: return Capture[1].Cnt; } } @@ -1012,7 +1006,7 @@ u16 SPU::Read16(u32 addr) { if (addr < 0x04000500) { - SPUChannel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1027,7 +1021,7 @@ u16 SPU::Read16(u32 addr) case 0x04000500: return Cnt; case 0x04000504: return Bias; - case 0x04000508: return Capture[0]->Cnt | (Capture[1]->Cnt << 8); + case 0x04000508: return Capture[0].Cnt | (Capture[1].Cnt << 8); } } @@ -1039,7 +1033,7 @@ u32 SPU::Read32(u32 addr) { if (addr < 0x04000500) { - SPUChannel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1053,10 +1047,10 @@ u32 SPU::Read32(u32 addr) case 0x04000500: return Cnt; case 0x04000504: return Bias; - case 0x04000508: return Capture[0]->Cnt | (Capture[1]->Cnt << 8); + case 0x04000508: return Capture[0].Cnt | (Capture[1].Cnt << 8); - case 0x04000510: return Capture[0]->DstAddr; - case 0x04000518: return Capture[1]->DstAddr; + case 0x04000510: return Capture[0].DstAddr; + case 0x04000518: return Capture[1].DstAddr; } } @@ -1068,7 +1062,7 @@ void SPU::Write8(u32 addr, u8 val) { if (addr < 0x04000500) { - SPUChannel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1092,11 +1086,11 @@ void SPU::Write8(u32 addr, u8 val) return; case 0x04000508: - Capture[0]->SetCnt(val); + Capture[0].SetCnt(val); if (val & 0x03) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %02X\n", val); return; case 0x04000509: - Capture[1]->SetCnt(val); + Capture[1].SetCnt(val); if (val & 0x03) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %02X\n", val); return; } @@ -1109,7 +1103,7 @@ void SPU::Write16(u32 addr, u16 val) { if (addr < 0x04000500) { - SPUChannel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1117,8 +1111,8 @@ void SPU::Write16(u32 addr, u16 val) case 0x2: chan->SetCnt((chan->Cnt & 0x0000FFFF) | (val << 16)); return; case 0x8: chan->SetTimerReload(val); - if ((addr & 0xF0) == 0x10) Capture[0]->SetTimerReload(val); - else if ((addr & 0xF0) == 0x30) Capture[1]->SetTimerReload(val); + if ((addr & 0xF0) == 0x10) Capture[0].SetTimerReload(val); + else if ((addr & 0xF0) == 0x30) Capture[1].SetTimerReload(val); return; case 0xA: chan->SetLoopPos(val); return; @@ -1141,13 +1135,13 @@ void SPU::Write16(u32 addr, u16 val) return; case 0x04000508: - Capture[0]->SetCnt(val & 0xFF); - Capture[1]->SetCnt(val >> 8); + Capture[0].SetCnt(val & 0xFF); + Capture[1].SetCnt(val >> 8); if (val & 0x0303) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %04X\n", val); return; - case 0x04000514: Capture[0]->SetLength(val); return; - case 0x0400051C: Capture[1]->SetLength(val); return; + case 0x04000514: Capture[0].SetLength(val); return; + case 0x0400051C: Capture[1].SetLength(val); return; } } @@ -1158,7 +1152,7 @@ void SPU::Write32(u32 addr, u32 val) { if (addr < 0x04000500) { - SPUChannel* chan = Channels[(addr >> 4) & 0xF]; + SPUChannel* chan = &Channels[(addr >> 4) & 0xF]; switch (addr & 0xF) { @@ -1168,8 +1162,8 @@ void SPU::Write32(u32 addr, u32 val) chan->SetLoopPos(val >> 16); val &= 0xFFFF; chan->SetTimerReload(val); - if ((addr & 0xF0) == 0x10) Capture[0]->SetTimerReload(val); - else if ((addr & 0xF0) == 0x30) Capture[1]->SetTimerReload(val); + if ((addr & 0xF0) == 0x10) Capture[0].SetTimerReload(val); + else if ((addr & 0xF0) == 0x30) Capture[1].SetTimerReload(val); return; case 0xC: chan->SetLength(val); return; } @@ -1189,15 +1183,15 @@ void SPU::Write32(u32 addr, u32 val) return; case 0x04000508: - Capture[0]->SetCnt(val & 0xFF); - Capture[1]->SetCnt(val >> 8); + Capture[0].SetCnt(val & 0xFF); + Capture[1].SetCnt(val >> 8); if (val & 0x0303) Log(LogLevel::Warn, "!! UNSUPPORTED SPU CAPTURE MODE %04X\n", val); return; - case 0x04000510: Capture[0]->SetDstAddr(val); return; - case 0x04000514: Capture[0]->SetLength(val & 0xFFFF); return; - case 0x04000518: Capture[1]->SetDstAddr(val); return; - case 0x0400051C: Capture[1]->SetLength(val & 0xFFFF); return; + case 0x04000510: Capture[0].SetDstAddr(val); return; + case 0x04000514: Capture[0].SetLength(val & 0xFFFF); return; + case 0x04000518: Capture[1].SetDstAddr(val); return; + case 0x0400051C: Capture[1].SetLength(val & 0xFFFF); return; } } } diff --git a/src/SPU.h b/src/SPU.h index bf0c658b..03d476e8 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -31,7 +31,6 @@ class SPUChannel { public: SPUChannel(u32 num, melonDS::NDS& nds); - ~SPUChannel(); void Reset(); void DoSavestate(Savestate* file); @@ -39,44 +38,40 @@ public: static const u16 ADPCMTable[89]; static const s16 PSGTable[8][8]; - static s16 InterpCos[0x100]; - static s16 InterpCubic[0x100][4]; - static bool InterpInited; - // audio interpolation is an improvement upon the original hardware // (which performs no interpolation) - int InterpType; + int InterpType = 0; - u32 Num; + const u32 Num; - u32 Cnt; - u32 SrcAddr; - u16 TimerReload; - u32 LoopPos; - u32 Length; + u32 Cnt = 0; + u32 SrcAddr = 0; + u16 TimerReload = 0; + u32 LoopPos = 0; + u32 Length = 0; - u8 Volume; - u8 VolumeShift; - u8 Pan; + u8 Volume = 0; + u8 VolumeShift = 0; + u8 Pan = 0; - bool KeyOn; - u32 Timer; - s32 Pos; - s16 PrevSample[3]; - s16 CurSample; - u16 NoiseVal; + bool KeyOn = false; + u32 Timer = 0; + s32 Pos = 0; + s16 PrevSample[3] {}; + s16 CurSample = 0; + u16 NoiseVal = 0; - s32 ADPCMVal; - s32 ADPCMIndex; - s32 ADPCMValLoop; - s32 ADPCMIndexLoop; - u8 ADPCMCurByte; + s32 ADPCMVal = 0; + s32 ADPCMIndex = 0; + s32 ADPCMValLoop = 0; + s32 ADPCMIndexLoop = 0; + u8 ADPCMCurByte = 0; - u32 FIFO[8]; - u32 FIFOReadPos; - u32 FIFOWritePos; - u32 FIFOReadOffset; - u32 FIFOLevel; + u32 FIFO[8] {}; + u32 FIFOReadPos = 0; + u32 FIFOWritePos = 0; + u32 FIFOReadOffset = 0; + u32 FIFOLevel = 0; void FIFO_BufferData(); template T FIFO_ReadData(); @@ -150,25 +145,24 @@ class SPUCaptureUnit { public: SPUCaptureUnit(u32 num, melonDS::NDS&); - ~SPUCaptureUnit(); void Reset(); void DoSavestate(Savestate* file); - u32 Num; + const u32 Num; - u8 Cnt; - u32 DstAddr; - u16 TimerReload; - u32 Length; + u8 Cnt = 0; + u32 DstAddr = 0; + u16 TimerReload = 0; + u32 Length = 0; - u32 Timer; - s32 Pos; + u32 Timer = 0; + s32 Pos = 0; - u32 FIFO[4]; - u32 FIFOReadPos; - u32 FIFOWritePos; - u32 FIFOWriteOffset; - u32 FIFOLevel; + u32 FIFO[4] {}; + u32 FIFOReadPos = 0; + u32 FIFOWritePos = 0; + u32 FIFOWriteOffset = 0; + u32 FIFOLevel = 0; void FIFO_FlushData(); template void FIFO_WriteData(T val); @@ -206,7 +200,7 @@ private: class SPU { public: - SPU(melonDS::NDS& nds); + explicit SPU(melonDS::NDS& nds); ~SPU(); void Reset(); void DoSavestate(Savestate* file); @@ -242,23 +236,23 @@ public: private: static const u32 OutputBufferSize = 2*2048; melonDS::NDS& NDS; - s16 OutputBackbuffer[2 * OutputBufferSize]; - u32 OutputBackbufferWritePosition; + s16 OutputBackbuffer[2 * OutputBufferSize] {}; + u32 OutputBackbufferWritePosition = 0; - s16 OutputFrontBuffer[2 * OutputBufferSize]; - u32 OutputFrontBufferWritePosition; - u32 OutputFrontBufferReadPosition; + s16 OutputFrontBuffer[2 * OutputBufferSize] {}; + u32 OutputFrontBufferWritePosition = 0; + u32 OutputFrontBufferReadPosition = 0; Platform::Mutex* AudioLock; - u16 Cnt; - u8 MasterVolume; - u16 Bias; - bool ApplyBias; - bool Degrade10Bit; + u16 Cnt = 0; + u8 MasterVolume = 0; + u16 Bias = 0; + bool ApplyBias = true; + bool Degrade10Bit = false; - SPUChannel* Channels[16]; - SPUCaptureUnit* Capture[2]; + std::array Channels; + std::array Capture; }; } From bb42c8b6392e4e99634dc52137d2974781192482 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Mon, 4 Dec 2023 11:57:22 -0500 Subject: [PATCH 051/157] Refactor how save data (including SD cards) is initialized (#1898) * Remove `FATStorage::Open` and `FATStorage::Close` - That's what the constructor and destructor are for, respectively * Add `FATStorage::IsReadOnly` * Slight cleanup of `FATStorage` - Make it move-constructible and move-assignable - Represent the absence of a sync directory with `std::optional`, not an empty string - Add `FATStorageArgs` for later use * Refactor `CartHomebrew` to accept an optional `FATStorageArgs` - `CartHomebrew` uses it to load an SD card image - Not passing a `FATStorage` directly because we won't know if we need to load the card until we parse the ROM - Store the `FATStorage` inside a `std::optional` instead of a pointer - `CartHomebrew::Reset` no longer reloads the SD card; the frontend needs to set it with the `SetSDCard` method * Close `NANDImage::CurFile` when move-assigning - Whoops * Add `Args.h` - To construct a `NDS` or `DSi` with arguments - Mostly intended for system files * Fix incorrect `final` placement * Refactor how `DSi`'s NAND and SD card are set - Provide them via a `DSiArgs` argument in the constructor - Give `DSi_MMCStorage` ownership of the `NANDImage` or `FATStorage` as needed, and expose getters/setters - Replace `DSi_SDHost::Ports` with a `array` to reduce the risk of leaks - Store `DSi_MMCStorage`'s disk images in a `std::variant` - The SD card and NAND image are no longer reset in `Reset()`; the frontend will need to do that itself * Add getters/setters on `DSi` itself for its storage media * Remove newly-unused `Platform::ConfigEntry`s * Use `DSi::SetNAND` in the frontend * Add `EmuThread::NeedToRecreateConsole` * Document `NDSArgs` and give its fields default values * Refactor how system files are loaded upon construction - Pass `NDSArgs&&` into `NDS`'s constructor - Use `std::array` for the emulator's BIOS images and the built-in FreeBIOS, to simplify copying and comparison - Initialize the BIOS, firmware, and SD cards from `NDSArgs` or `DSiArgs` - Add a new default constructor for `NDS` (not `DSi`) that initializes the DS with default system files - Embed `FirmwareMem::Firmware` directly instead of in a `unique_ptr` - `SPIHost` now takes a `Firmware&&` that it forwards to `FirmwareMem` - Add `Firmware` getters/setters plus `const` variants for `NDS`, `Firmware`, and `FirmwareMem` - Simplify installation of firmware * Initialize the DSi BIOS in the constructor - Change `DSi::ARM9iBIOS` and `ARM7iBIOS` to `std::array` * Update the frontend to reflect the core's changes * Remove `DSi_SDHost::CloseHandles` * Pass `nullopt` instead of the empty string when folder sync is off * Deduplicate ROM extraction logic - `LoadGBAROM` and `LoadROM` now delegate to `LoadROMData` - Also use `unique_ptr` instead of `new[]` * Oops, missed some `get()`'s * Move `NDS::IsLoadedARM9BIOSBuiltIn` to the header - So it's likelier to be inlined - Same for the ARM7 version * Remove `NDS::SetConsoleType` * Add `NDS::SetFirmware` * Move `GBACart::SetupSave` to be `protected` - It was only ever used inside the class * Rename `GBACart::LoadSave` to `SetSaveMemory` - Same for the cart slot * Declare `GBACartSlot` as a friend of `GBACart::CartCommon` * Revise `GBACartSlot`'s getters and setters - Rename `InsertROM` and `LoadROM` to `SetCart` - Add a `GetCart` method * Clean up getters and setters for NDS and GBA carts * Clean up how carts are inserted into the slots - Remove setters that operate directly on pointers, to simplify error-handling (use ParseROM instead) - Add overloads for all carts that accept a `const u8*` (to copy the ROM data) and a `unique_ptr` (to move the ROM data) - Store all ROM and RAM data in `unique_ptr` - Default-initialize all fields - Simplify constructors and destructors, inheriting where applicable * Refactor GBA save data insertion - Make `SetupSave` private and non-virtual and move its logic to be in `SetSaveMemory` - Add overloads for setting save data in the constructor - Update the SRAM completely in `SetSaveMemory` * Clean up `NDSCart::CartCommon::SetSaveMemory` - Move its declaration next to the other `SaveMemory` methods - Move its (empty) implementation to the header * Add some comments * Add Utils.cpp and Utils.h * Rename some functions in Utils for clarity * Add `GBACart::ParseROM` and `NDSCart::ParseROM` overloads that accept `unique_ptr` - The `u8*` overloads delegate to these new overloads - Also move `SetupSave` for both kinds of carts to be private non-virtual methods * Finalize the `NDSCart` refactor - Add `NDSCartArgs` to pass to `ParseROM` - Add SRAM arguments for all retail carts - Initialize SRAM inside the constructor - Delegate to other constructors where possible * Replace `ROMManager::NDSSave` and `GBASave` with `unique_ptr` * Make both cart slots return the previously-inserted cart in `EjectCart` - Primarily intended for reusing carts when resetting the console * Make `NDS::EjectCart` return the old cart * Initialize both cart slots with the provided ROM (if any) * Make `NDS::EjectGBACart` return the ejected cart * Clean up some comments in Args.h * Rename `ROMManager::LoadBIOS` to `BootToMenu` - Clarifies the intent * Add `ROMManager::LoadDLDISDCard` * Add a doc comment * Refactor how the `NDS` is created or updated - Rewrite `CreateConsole` to read from `Config` and load system files, but accept carts as arguments - Fail without creating an `NDS` if any required system file doesn't load - Add `UpdateConsole`, which delegates to `CreateConsole` if switching modes or starting the app - Use `std::variant` to indicate whether a cart should be removed, inserted, or reused - Load all system files (plus SD cards) in `UpdateConsole` - Eject the cart and reinsert it into the new console if applicable * Respect some more `Config` settings in the `Load*` functions * Remove `InstallNAND` in favor of `LoadNAND` * Fix some potential bugs in `LoadROMData` * Oops, forgot to delete the definition of `InstallNAND` * Add functions to get `FATStorageArgs` - Not the cards themselves, but to get the arguments you _would_ use to load the cards * Refactor `ROMManager::LoadROM` - Load the ROM and save data before trying to initialize the console * Clean up `ROMManager::Reset` and `BootToMenu` - Let `EmuThread::UpdateConsole` do the heavy lifting * Clean up `LoadGBAROM` * Remove some unused functions * Set the default DSi BIOS to be broken in `DSiArgs` * Respect `Config::DSiFullBIOSBoot` when loading DSi BIOS files * Remove some more unused functions * Remove redundant `virtual` specifiers * Refactor `NDSCart::CartCommon::Type()` to return a member instead of a constant - One less virtual dispatch - The cart type is read in `NDSCartSlot::DoSavestate`, which is a path downstream (due to rewinding) * Remove a hash that I computed for debugging purposes * Make `ByteSwap` `constexpr` * Remove an unused `#include` * Remove unnecessary functions from the NDS carts - Mostly overrides that added nothing * Default-initialize all NDSCart fields * Make `GBACart::Type()` not rely on virtual dispatch - `GBACartSlot::DoSavestate` calls it, and savestates can be a hot path downstream * Don't forget to reset the base class in `CartGameSolarSensor::Reset()` * Remove redundant `virtual` specifiers * Default-initialize some fields in `GBACart` * Fix ROMs not loading from archives in the frontend - Whoops * Change how the `Firmware` member is declared * Forgot an include in Utils.cpp * Rename `FirmwareMem::Firmware` to `FirmwareData` to fix a build error on Linux - One of these days I'll convince you people to let me use `camelCaseMemberNames` * Add `override` to places in `DSi_MMCStorage` that warrant it * Fix firmware saving on the frontend - Remove `GetConfigString` and `ConfigEntry::WifiSettingsPath` while I'm at it * Add a non-const `GetNAND()` --- src/Args.h | 100 ++ src/CMakeLists.txt | 2 + src/DSi.cpp | 84 +- src/DSi.h | 22 +- src/DSi_AES.cpp | 2 +- src/DSi_NAND.cpp | 3 + src/DSi_NWifi.cpp | 6 +- src/DSi_SD.cpp | 204 +-- src/DSi_SD.h | 75 +- src/FATStorage.cpp | 102 +- src/FATStorage.h | 30 +- src/FreeBIOS.cpp | 4 +- src/FreeBIOS.h | 7 +- src/GBACart.cpp | 1796 +++++++++++++-------------- src/GBACart.h | 546 ++++---- src/NDS.cpp | 84 +- src/NDS.h | 63 +- src/NDSCart.cpp | 379 +++--- src/NDSCart.h | 228 ++-- src/Platform.h | 17 - src/SPI.cpp | 94 +- src/SPI.h | 18 +- src/Utils.cpp | 66 + src/Utils.h | 43 + src/Wifi.cpp | 6 +- src/frontend/qt_sdl/ArchiveUtil.cpp | 7 +- src/frontend/qt_sdl/ArchiveUtil.h | 2 +- src/frontend/qt_sdl/Platform.cpp | 28 - src/frontend/qt_sdl/ROMManager.cpp | 647 +++------- src/frontend/qt_sdl/ROMManager.h | 19 +- src/frontend/qt_sdl/main.cpp | 168 ++- src/frontend/qt_sdl/main.h | 21 +- 32 files changed, 2511 insertions(+), 2362 deletions(-) create mode 100644 src/Args.h create mode 100644 src/Utils.cpp create mode 100644 src/Utils.h diff --git a/src/Args.h b/src/Args.h new file mode 100644 index 00000000..bfa1b13a --- /dev/null +++ b/src/Args.h @@ -0,0 +1,100 @@ +/* + 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_ARGS_H +#define MELONDS_ARGS_H + +#include +#include +#include + +#include "types.h" +#include "MemConstants.h" +#include "DSi_NAND.h" +#include "FATStorage.h" +#include "FreeBIOS.h" +#include "SPI_Firmware.h" + +namespace melonDS +{ +namespace NDSCart { class CartCommon; } +namespace GBACart { class CartCommon; } + +template +constexpr std::array BrokenBIOS = []() constexpr { + std::array broken {}; + + for (int i = 0; i < 16; i++) + { + broken[i*4+0] = 0xE7; + broken[i*4+1] = 0xFF; + broken[i*4+2] = 0xDE; + broken[i*4+3] = 0xFF; + } + + return broken; +}(); + +/// Arguments to pass into the NDS constructor. +/// New fields here should have default values if possible. +struct NDSArgs +{ + /// NDS ROM to install. + /// Defaults to nullptr, which means no cart. + /// Should be populated with the desired save data beforehand, + /// including an SD card if applicable. + std::unique_ptr NDSROM = nullptr; + + /// GBA ROM to install. + /// Defaults to nullptr, which means no cart. + /// Should be populated with the desired save data beforehand. + /// Ignored in DSi mode. + std::unique_ptr GBAROM = nullptr; + + /// NDS ARM9 BIOS to install. + /// Defaults to FreeBIOS, which is not compatible with DSi mode. + std::array ARM9BIOS = bios_arm9_bin; + + /// NDS ARM7 BIOS to install. + /// Defaults to FreeBIOS, which is not compatible with DSi mode. + std::array ARM7BIOS = bios_arm7_bin; + + /// Firmware image to install. + /// Defaults to generated NDS firmware. + /// Generated firmware is not compatible with DSi mode. + melonDS::Firmware Firmware {0}; +}; + +/// Arguments to pass into the DSi constructor. +/// New fields here should have default values if possible. +/// Contains no virtual methods, so there's no vtable. +struct DSiArgs final : public NDSArgs +{ + std::array ARM9iBIOS = BrokenBIOS; + std::array ARM7iBIOS = BrokenBIOS; + + /// NAND image to install. + /// Required, there is no default value. + DSi_NAND::NANDImage NANDImage; + + /// SD card to install. + /// Defaults to std::nullopt, which means no SD card. + std::optional DSiSDCard; +}; +} +#endif //MELONDS_ARGS_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9fe93ae5..ddb1b3c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,6 +49,8 @@ add_library(core STATIC SPI_Firmware.cpp SPU.cpp types.h + Utils.cpp + Utils.h version.h Wifi.cpp WifiAP.cpp diff --git a/src/DSi.cpp b/src/DSi.cpp index f2781e48..5dcd4193 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "Args.h" #include "NDS.h" #include "DSi.h" #include "ARM.h" @@ -68,8 +69,8 @@ const u32 NDMAModes[] = 0xFF, // wifi / GBA cart slot (TODO) }; -DSi::DSi() noexcept : - NDS(1), +DSi::DSi(DSiArgs&& args) noexcept : + NDS(std::move(args), 1), NDMAs { DSi_NDMA(0, 0, *this), DSi_NDMA(0, 1, *this), @@ -80,9 +81,11 @@ DSi::DSi() noexcept : DSi_NDMA(1, 2, *this), DSi_NDMA(1, 3, *this), }, + ARM7iBIOS(args.ARM7iBIOS), + ARM9iBIOS(args.ARM9iBIOS), DSP(*this), - SDMMC(*this, 0), - SDIO(*this, 1), + SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)), + SDIO(*this), I2C(*this), CamModule(*this), AES(*this) @@ -118,9 +121,6 @@ void DSi::Reset() CamModule.Reset(); DSP.Reset(); - SDMMC.CloseHandles(); - SDIO.CloseHandles(); - LoadNAND(); SDMMC.Reset(); @@ -162,24 +162,22 @@ void DSi::Stop(Platform::StopReason reason) CamModule.Stop(); } -bool DSi::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) +void DSi::SetNDSCart(std::unique_ptr&& cart) { - if (NDS::LoadCart(romdata, romlen, savedata, savelen)) - { - SetCartInserted(true); - return true; - } - - return false; + NDS::SetNDSCart(std::move(cart)); + SetCartInserted(NDSCartSlot.GetCart() != nullptr); } -void DSi::EjectCart() +std::unique_ptr DSi::EjectCart() { - NDS::EjectCart(); + auto oldcart = NDS::EjectCart(); SetCartInserted(false); + + return oldcart; } + void DSi::CamInputFrame(int cam, u32* data, int width, int height, bool rgb) { switch (cam) @@ -509,9 +507,9 @@ void DSi::SetupDirectBoot() ARM9Write32(0x02FFE000+i, tmp); } - if (NANDImage && *NANDImage) + if (DSi_NAND::NANDImage* image = SDMMC.GetNAND(); image && *image) { // If a NAND image is installed, and it's valid... - if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*NANDImage)) + if (DSi_NAND::NANDMount nand = DSi_NAND::NANDMount(*image)) { DSi_NAND::DSiFirmwareSystemSettings userdata {}; nand.ReadUserData(userdata); @@ -531,7 +529,7 @@ void DSi::SetupDirectBoot() } } - Firmware::WifiBoard nwifiver = SPI.GetFirmware()->GetHeader().WifiBoard; + Firmware::WifiBoard nwifiver = SPI.GetFirmware().GetHeader().WifiBoard; ARM9Write8(0x020005E0, static_cast(nwifiver)); // TODO: these should be taken from the wifi firmware in NAND @@ -674,9 +672,6 @@ void DSi::SoftReset() // the DSP most likely gets reset DSP.Reset(); - SDMMC.CloseHandles(); - SDIO.CloseHandles(); - LoadNAND(); SDMMC.Reset(); @@ -709,21 +704,22 @@ void DSi::SoftReset() bool DSi::LoadNAND() { - if (!NANDImage) + DSi_NAND::NANDImage* image = SDMMC.GetNAND(); + if (!(image && *image)) { Log(LogLevel::Error, "No NAND image loaded\n"); return false; } Log(LogLevel::Info, "Loading DSi NAND\n"); - DSi_NAND::NANDMount nandmount(*NANDImage); + DSi_NAND::NANDMount nandmount(*SDMMC.GetNAND()); if (!nandmount) { Log(LogLevel::Error, "Failed to load DSi NAND\n"); return false; } - FileHandle* nand = NANDImage->GetFile(); + FileHandle* nand = image->GetFile(); // Make sure NWRAM is accessible. // The Bits are set to the startup values in Reset() and we might @@ -879,9 +875,9 @@ bool DSi::LoadNAND() } } - const DSi_NAND::DSiKey& emmccid = NANDImage->GetEMMCID(); + const DSi_NAND::DSiKey& emmccid = image->GetEMMCID(); Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]); - Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", NANDImage->GetConsoleID()); + Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", image->GetConsoleID()); if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) { @@ -1728,12 +1724,12 @@ bool DSi::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) return false; } - region->Mem = ARM9BIOS; + region->Mem = &ARM9BIOS[0]; region->Mask = 0xFFF; } else { - region->Mem = ARM9iBIOS; + region->Mem = &ARM9iBIOS[0]; region->Mask = 0xFFFF; } return true; @@ -2678,14 +2674,14 @@ u8 DSi::ARM7IORead8(u32 addr) case 0x04004500: return I2C.ReadData(); case 0x04004501: return I2C.ReadCnt(); - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFF; - case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 8) & 0xFF; - case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFF; - case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 24) & 0xFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFF; - case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 40) & 0xFF; - case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 48) & 0xFF; - case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 56; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFF; + case 0x04004D01: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 8) & 0xFF; + case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFF; + case 0x04004D03: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 24) & 0xFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFF; + case 0x04004D05: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 40) & 0xFF; + case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 48) & 0xFF; + case 0x04004D07: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 56; case 0x04004D08: return 0; case 0x4004700: return DSP.ReadSNDExCnt() & 0xFF; @@ -2726,10 +2722,10 @@ u16 DSi::ARM7IORead16(u32 addr) CASE_READ16_32BIT(0x0400405C, MBK[1][7]) CASE_READ16_32BIT(0x04004060, MBK[1][8]) - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFF; - case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 16) & 0xFFFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (NANDImage->GetConsoleID() >> 32) & 0xFFFF; - case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 48; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFF; + case 0x04004D02: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 16) & 0xFFFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return (SDMMC.GetNAND()->GetConsoleID() >> 32) & 0xFFFF; + case 0x04004D06: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 48; case 0x04004D08: return 0; case 0x4004700: return DSP.ReadSNDExCnt(); @@ -2806,8 +2802,8 @@ u32 DSi::ARM7IORead32(u32 addr) case 0x04004400: return AES.ReadCnt(); case 0x0400440C: return AES.ReadOutputFIFO(); - case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() & 0xFFFFFFFF; - case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return NANDImage->GetConsoleID() >> 32; + case 0x04004D00: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() & 0xFFFFFFFF; + case 0x04004D04: if (SCFG_BIOS & (1<<10)) return 0; return SDMMC.GetNAND()->GetConsoleID() >> 32; case 0x04004D08: return 0; case 0x4004700: diff --git a/src/DSi.h b/src/DSi.h index a221b5d7..acd85c14 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -33,6 +33,7 @@ class DSi_I2CHost; class DSi_CamModule; class DSi_AES; class DSi_DSP; +class DSiArgs; namespace DSi_NAND { @@ -48,9 +49,8 @@ public: u16 SCFG_Clock9; u32 SCFG_EXT[2]; - u8 ARM9iBIOS[0x10000]; - u8 ARM7iBIOS[0x10000]; - std::unique_ptr NANDImage; + std::array ARM9iBIOS; + std::array ARM7iBIOS; DSi_SDHost SDMMC; DSi_SDHost SDIO; @@ -130,19 +130,29 @@ public: void ARM7IOWrite32(u32 addr, u32 val) override; public: - DSi() noexcept; + DSi(DSiArgs&& args) noexcept; ~DSi() noexcept override; DSi(const DSi&) = delete; DSi& operator=(const DSi&) = delete; DSi(DSi&&) = delete; DSi& operator=(DSi&&) = delete; - bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) override; - void EjectCart() override; + void SetNDSCart(std::unique_ptr&& cart) override; + std::unique_ptr EjectCart() override; bool NeedsDirectBoot() override { // for now, DSi mode requires original BIOS/NAND return false; } + + [[nodiscard]] const DSi_NAND::NANDImage& GetNAND() const noexcept { return *SDMMC.GetNAND(); } + [[nodiscard]] DSi_NAND::NANDImage& GetNAND() noexcept { return *SDMMC.GetNAND(); } + void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { SDMMC.SetNAND(std::move(nand)); } + u64 GetConsoleID() const noexcept { return SDMMC.GetNAND()->GetConsoleID(); } + + [[nodiscard]] const FATStorage* GetSDCard() const noexcept { return SDMMC.GetSDCard(); } + void SetSDCard(FATStorage&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); } + void SetSDCard(std::optional&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); } + void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) override; bool DMAsInMode(u32 cpu, u32 mode) override; bool DMAsRunning(u32 cpu) override; diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 8e04586a..47a613eb 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -78,7 +78,7 @@ void DSi_AES::Reset() OutputMACDue = false; // initialize keys - u64 consoleid = DSi.NANDImage->GetConsoleID(); + u64 consoleid = DSi.SDMMC.GetNAND()->GetConsoleID(); // slot 0: modcrypt *(u32*)&KeyX[0][0] = 0x746E694E; diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 59f582fd..b6b83ab6 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -131,6 +131,9 @@ NANDImage& NANDImage::operator=(NANDImage&& other) noexcept { if (this != &other) { + if (CurFile) + CloseFile(CurFile); + CurFile = other.CurFile; eMMC_CID = other.eMMC_CID; ConsoleID = other.ConsoleID; diff --git a/src/DSi_NWifi.cpp b/src/DSi_NWifi.cpp index 9e006e2c..a6177dec 100644 --- a/src/DSi_NWifi.cpp +++ b/src/DSi_NWifi.cpp @@ -165,13 +165,13 @@ void DSi_NWifi::Reset() for (int i = 0; i < 9; i++) Mailbox[i].Clear(); - const Firmware* fw = DSi.SPI.GetFirmware(); + const Firmware& fw = DSi.SPI.GetFirmware(); - MacAddress mac = fw->GetHeader().MacAddr; + MacAddress mac = fw.GetHeader().MacAddr; Log(LogLevel::Info, "NWifi MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - Firmware::WifiBoard type = fw->GetHeader().WifiBoard; + Firmware::WifiBoard type = fw.GetHeader().WifiBoard; switch (type) { case Firmware::WifiBoard::W015: // AR6002 diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index 4cbf595d..ff88defb 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -18,6 +18,7 @@ #include #include +#include "Args.h" #include "DSi.h" #include "DSi_SD.h" #include "DSi_NAND.h" @@ -26,6 +27,10 @@ namespace melonDS { +using std::holds_alternative; +using std::unique_ptr; +using std::get_if; +using std::get; using namespace Platform; // observed IRQ behavior during transfers @@ -57,36 +62,38 @@ enum }; -DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, u32 num) : DSi(dsi) +DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional&& sdcard) noexcept : DSi(dsi), Num(0) { - Num = num; - - DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, + DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer, Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); - DSi.RegisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, + DSi.RegisterEventFunc( Event_DSi_SDMMCTransfer, Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); - Ports[0] = nullptr; + Ports[0] = sdcard ? std::make_unique(DSi, this, std::move(*sdcard)) : nullptr; + sdcard = std::nullopt; // to ensure that sdcard isn't left with a moved-from object + Ports[1] = std::make_unique(DSi, this, std::move(nand)); +} + +// Creates an SDIO host +DSi_SDHost::DSi_SDHost(melonDS::DSi& dsi) noexcept : DSi(dsi), Num(1) +{ + DSi.RegisterEventFunc(Event_DSi_SDIOTransfer , + Transfer_TX, MemberEventFunc(DSi_SDHost, FinishTX)); + DSi.RegisterEventFunc(Event_DSi_SDIOTransfer, + Transfer_RX, MemberEventFunc(DSi_SDHost, FinishRX)); + + Ports[0] = std::make_unique(DSi, this); Ports[1] = nullptr; } DSi_SDHost::~DSi_SDHost() { - if (Ports[0]) delete Ports[0]; - if (Ports[1]) delete Ports[1]; - DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, Transfer_TX); DSi.UnregisterEventFunc(Num ? Event_DSi_SDIOTransfer : Event_DSi_SDMMCTransfer, Transfer_RX); -} -void DSi_SDHost::CloseHandles() -{ - if (Ports[0]) delete Ports[0]; - if (Ports[1]) delete Ports[1]; - Ports[0] = nullptr; - Ports[1] = nullptr; + // unique_ptr's destructor will clean up the ports } void DSi_SDHost::Reset() @@ -129,48 +136,70 @@ void DSi_SDHost::Reset() TXReq = false; - CloseHandles(); + if (Ports[0]) Ports[0]->Reset(); + if (Ports[1]) Ports[1]->Reset(); +} - if (Num == 0) +FATStorage* DSi_SDHost::GetSDCard() noexcept +{ + if (Num != 0) return nullptr; + return static_cast(Ports[0].get())->GetSDCard(); +} + +const FATStorage* DSi_SDHost::GetSDCard() const noexcept +{ + if (Num != 0) return nullptr; + return static_cast(Ports[0].get())->GetSDCard(); +} + +DSi_NAND::NANDImage* DSi_SDHost::GetNAND() noexcept +{ + if (Num != 0) return nullptr; + return static_cast(Ports[1].get())->GetNAND(); +} + +const DSi_NAND::NANDImage* DSi_SDHost::GetNAND() const noexcept +{ + if (Num != 0) return nullptr; + return static_cast(Ports[1].get())->GetNAND(); +} + +void DSi_SDHost::SetSDCard(FATStorage&& sdcard) noexcept +{ + if (Num != 0) return; + + static_cast(Ports[0].get())->SetSDCard(std::move(sdcard)); +} + +void DSi_SDHost::SetSDCard(std::optional&& sdcard) noexcept +{ + if (Num != 0) return; + + if (sdcard) { - DSi_MMCStorage* sd; - DSi_MMCStorage* mmc; - - if (Platform::GetConfigBool(Platform::DSiSD_Enable)) + if (!Ports[0]) { - std::string folderpath; - if (Platform::GetConfigBool(Platform::DSiSD_FolderSync)) - folderpath = Platform::GetConfigString(Platform::DSiSD_FolderPath); - else - folderpath = ""; - - sd = new DSi_MMCStorage(this, - false, - Platform::GetConfigString(Platform::DSiSD_ImagePath), - (u64)Platform::GetConfigInt(Platform::DSiSD_ImageSize) * 1024 * 1024, - Platform::GetConfigBool(Platform::DSiSD_ReadOnly), - folderpath); - u8 sd_cid[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00}; - sd->SetCID(sd_cid); + Ports[0] = std::make_unique(DSi, this, std::move(*sdcard)); } else - sd = nullptr; - - mmc = new DSi_MMCStorage(this, *DSi.NANDImage); - mmc->SetCID(DSi.NANDImage->GetEMMCID().data()); - - Ports[0] = sd; - Ports[1] = mmc; + { + static_cast(Ports[0].get())->SetSDCard(std::move(*sdcard)); + } } else { - DSi_NWifi* nwifi = new DSi_NWifi(DSi, this); - - Ports[0] = nwifi; + Ports[0] = nullptr; } - if (Ports[0]) Ports[0]->Reset(); - if (Ports[1]) Ports[1]->Reset(); + sdcard = std::nullopt; + // a moved-from optional isn't empty, it contains a moved-from object +} + +void DSi_SDHost::SetNAND(DSi_NAND::NANDImage&& nand) noexcept +{ + if (Num != 0) return; + + static_cast(Ports[1].get())->SetNAND(std::move(nand)); } void DSi_SDHost::DoSavestate(Savestate* file) @@ -261,7 +290,7 @@ void DSi_SDHost::SetCardIRQ() if (!(CardIRQCtl & (1<<0))) return; u16 oldflags = CardIRQStatus & ~CardIRQMask; - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); if (dev->IRQ) CardIRQStatus |= (1<<0); else CardIRQStatus &= ~(1<<0); @@ -332,7 +361,7 @@ u32 DSi_SDHost::DataRX(u8* data, u32 len) void DSi_SDHost::FinishTX(u32 param) { - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); if (BlockCountInternal == 0) { @@ -419,7 +448,7 @@ u32 DSi_SDHost::GetTransferrableLen(u32 len) void DSi_SDHost::CheckRX() { - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); CheckSwapFIFO(); @@ -459,7 +488,7 @@ void DSi_SDHost::CheckTX() return; } - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); if (dev) dev->ContinueTransfer(); } @@ -550,7 +579,6 @@ u16 DSi_SDHost::ReadFIFO16() return 0; } - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u16 ret = DataFIFO[f].Read(); if (DataFIFO[f].IsEmpty()) @@ -571,7 +599,6 @@ u32 DSi_SDHost::ReadFIFO32() return 0; } - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u32 ret = DataFIFO32.Read(); if (DataFIFO32.IsEmpty()) @@ -593,7 +620,7 @@ void DSi_SDHost::Write(u32 addr, u16 val) Command = val; u8 cmd = Command & 0x3F; - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; + DSi_SDDevice* dev = Ports[PortSelect & 0x1].get(); if (dev) { // CHECKME @@ -707,7 +734,6 @@ void DSi_SDHost::Write(u32 addr, u16 val) void DSi_SDHost::WriteFIFO16(u16 val) { - DSi_SDDevice* dev = Ports[PortSelect & 0x1]; u32 f = CurFIFO; if (DataFIFO[f].IsFull()) { @@ -780,34 +806,23 @@ void DSi_SDHost::CheckSwapFIFO() #define MMC_DESC (Internal?"NAND":"SDcard") -DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand) - : DSi_SDDevice(host), Internal(true), NAND(&nand), SD(nullptr) +DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept + : DSi_SDDevice(host), DSi(dsi), Storage(std::move(nand)) { ReadOnly = false; + SetCID(get(Storage).GetEMMCID().data()); } -DSi_MMCStorage::DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir) - : DSi_SDDevice(host) +DSi_MMCStorage::DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept + : DSi_SDDevice(host), DSi(dsi), Storage(std::move(sdcard)) { - Internal = internal; - NAND = nullptr; - - SD = new FATStorage(filename, size, readonly, sourcedir); - SD->Open(); - - ReadOnly = readonly; + ReadOnly = get(Storage).IsReadOnly(); + SetCID(DSiSDCardCID); } -DSi_MMCStorage::~DSi_MMCStorage() -{ - if (SD) - { - SD->Close(); - delete SD; - } - - // Do not close the NANDImage, it's not owned by this object -} +// The FATStorage or NANDImage is owned by this object; +// std::variant's destructor will clean it up. +DSi_MMCStorage::~DSi_MMCStorage() = default; void DSi_MMCStorage::Reset() { @@ -836,7 +851,7 @@ void DSi_MMCStorage::Reset() void DSi_MMCStorage::DoSavestate(Savestate* file) { - file->Section(Internal ? "NAND" : "SDCR"); + file->Section(holds_alternative(Storage) ? "NAND" : "SDCR"); file->VarArray(CID, 16); file->VarArray(CSD, 16); @@ -871,7 +886,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) case 1: // SEND_OP_COND // CHECKME!! // also TODO: it's different for the SD card - if (Internal) + if (std::holds_alternative(Storage)) { param &= ~(1<<30); OCR &= 0xBF000000; @@ -895,7 +910,7 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) return; case 3: // get/set RCA - if (Internal) + if (holds_alternative(Storage)) { RCA = param >> 16; Host->SendResponse(CSR|0x10000, true); // huh?? @@ -930,7 +945,8 @@ void DSi_MMCStorage::SendCMD(u8 cmd, u32 param) case 12: // stop operation SetState(0x04); - if (NAND) FileFlush(NAND->GetFile()); + if (auto* nand = get_if(&Storage)) + FileFlush(nand->GetFile()); RWCommand = 0; Host->SendResponse(CSR, true); return; @@ -1011,7 +1027,7 @@ void DSi_MMCStorage::SendACMD(u8 cmd, u32 param) // DSi boot2 sets this to 0x40100000 (hardcoded) // then has two codepaths depending on whether bit30 did get set // is it settable at all on the MMC? probably not. - if (Internal) param &= ~(1<<30); + if (holds_alternative(Storage)) param &= ~(1<<30); OCR &= 0xBF000000; OCR |= (param & 0x40FFFFFF); Host->SendResponse(OCR, true); @@ -1057,14 +1073,14 @@ u32 DSi_MMCStorage::ReadBlock(u64 addr) len = Host->GetTransferrableLen(len); u8 data[0x200]; - if (SD) + if (auto* sd = std::get_if(&Storage)) { - SD->ReadSectors((u32)(addr >> 9), 1, data); + sd->ReadSectors((u32)(addr >> 9), 1, data); } - else if (NAND) + else if (auto* nand = std::get_if(&Storage)) { - FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start); - FileRead(&data[addr & 0x1FF], 1, len, NAND->GetFile()); + FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start); + FileRead(&data[addr & 0x1FF], 1, len, nand->GetFile()); } return Host->DataRX(&data[addr & 0x1FF], len); @@ -1078,23 +1094,23 @@ u32 DSi_MMCStorage::WriteBlock(u64 addr) u8 data[0x200]; if (len < 0x200) { - if (SD) + if (auto* sd = get_if(&Storage)) { - SD->ReadSectors((u32)(addr >> 9), 1, data); + sd->ReadSectors((u32)(addr >> 9), 1, data); } } if ((len = Host->DataTX(&data[addr & 0x1FF], len))) { if (!ReadOnly) { - if (SD) + if (auto* sd = get_if(&Storage)) { - SD->WriteSectors((u32)(addr >> 9), 1, data); + sd->WriteSectors((u32)(addr >> 9), 1, data); } - else if (NAND) + else if (auto* nand = get_if(&Storage)) { - FileSeek(NAND->GetFile(), addr, FileSeekOrigin::Start); - FileWrite(&data[addr & 0x1FF], 1, len, NAND->GetFile()); + FileSeek(nand->GetFile(), addr, FileSeekOrigin::Start); + FileWrite(&data[addr & 0x1FF], 1, len, nand->GetFile()); } } } diff --git a/src/DSi_SD.h b/src/DSi_SD.h index 17ba8d30..05f8c9dd 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -20,28 +20,30 @@ #define DSI_SD_H #include +#include #include "FIFO.h" #include "FATStorage.h" +#include "DSi_NAND.h" #include "Savestate.h" namespace melonDS { -namespace DSi_NAND -{ - class NANDImage; -} - class DSi_SDDevice; class DSi; +using Nothing = std::monostate; +using DSiStorage = std::variant; class DSi_SDHost { public: - DSi_SDHost(melonDS::DSi& dsi, u32 num); + /// Creates an SDMMC host. + DSi_SDHost(melonDS::DSi& dsi, DSi_NAND::NANDImage&& nand, std::optional&& sdcard = std::nullopt) noexcept; + + /// Creates an SDIO host + explicit DSi_SDHost(melonDS::DSi& dsi) noexcept; ~DSi_SDHost(); - void CloseHandles(); void Reset(); void DoSavestate(Savestate* file); @@ -59,6 +61,15 @@ public: void SetCardIRQ(); + [[nodiscard]] FATStorage* GetSDCard() noexcept; + [[nodiscard]] const FATStorage* GetSDCard() const noexcept; + [[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept; + [[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept; + + void SetSDCard(FATStorage&& sdcard) noexcept; + void SetSDCard(std::optional&& sdcard) noexcept; + void SetNAND(DSi_NAND::NANDImage&& nand) noexcept; + u16 Read(u32 addr); void Write(u32 addr, u16 val); u16 ReadFIFO16(); @@ -96,7 +107,7 @@ private: u32 Param; u16 ResponseBuffer[8]; - DSi_SDDevice* Ports[2]; + std::array, 2> Ports {}; u32 CurFIFO; // FIFO accessible for read/write FIFO DataFIFO[2]; @@ -134,25 +145,53 @@ protected: class DSi_MMCStorage : public DSi_SDDevice { public: - DSi_MMCStorage(DSi_SDHost* host, DSi_NAND::NANDImage& nand); - DSi_MMCStorage(DSi_SDHost* host, bool internal, const std::string& filename, u64 size, bool readonly, const std::string& sourcedir); - ~DSi_MMCStorage(); + DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, DSi_NAND::NANDImage&& nand) noexcept; + DSi_MMCStorage(melonDS::DSi& dsi, DSi_SDHost* host, FATStorage&& sdcard) noexcept; + ~DSi_MMCStorage() override; - void Reset(); + [[nodiscard]] FATStorage* GetSDCard() noexcept { return std::get_if(&Storage); } + [[nodiscard]] const FATStorage* GetSDCard() const noexcept { return std::get_if(&Storage); } + [[nodiscard]] DSi_NAND::NANDImage* GetNAND() noexcept { return std::get_if(&Storage); } + [[nodiscard]] const DSi_NAND::NANDImage* GetNAND() const noexcept { return std::get_if(&Storage); } - void DoSavestate(Savestate* file); + void SetNAND(DSi_NAND::NANDImage&& nand) noexcept { Storage = std::move(nand); } + void SetSDCard(FATStorage&& sdcard) noexcept { Storage = std::move(sdcard); } + void SetSDCard(std::optional&& sdcard) noexcept + { + if (sdcard) + { // If we're setting a new SD card... + Storage = std::move(*sdcard); + sdcard = std::nullopt; + } + else + { + Storage = Nothing(); + } + } + + void SetStorage(DSiStorage&& storage) noexcept + { + Storage = std::move(storage); + storage = Nothing(); + // not sure if a moved-from variant is empty or contains a moved-from object; + // better to be safe than sorry + } + + void Reset() override; + + void DoSavestate(Savestate* file) override; void SetCID(const u8* cid) { memcpy(CID, cid, sizeof(CID)); } - void SendCMD(u8 cmd, u32 param); + void SendCMD(u8 cmd, u32 param) override; void SendACMD(u8 cmd, u32 param); - void ContinueTransfer(); + void ContinueTransfer() override; private: - bool Internal; - DSi_NAND::NANDImage* NAND; - FATStorage* SD; + static constexpr u8 DSiSDCardCID[16] = {0xBD, 0x12, 0x34, 0x56, 0x78, 0x03, 0x4D, 0x30, 0x30, 0x46, 0x50, 0x41, 0x00, 0x00, 0x15, 0x00}; + melonDS::DSi& DSi; + DSiStorage Storage; u8 CID[16]; u8 CSD[16]; diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index cd0a03c8..8799cb4b 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -29,39 +29,79 @@ namespace melonDS { namespace fs = std::filesystem; using namespace Platform; +using std::string; -FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir) +FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional& sourcedir) : + FilePath(filename), + FileSize(size), + ReadOnly(readonly), + SourceDir(sourcedir) { - ReadOnly = readonly; Load(filename, size, sourcedir); + File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); +} + +FATStorage::FATStorage(const FATStorageArgs& args) noexcept : + FATStorage(args.Filename, args.Size, args.ReadOnly, args.SourceDir) +{ +} + +FATStorage::FATStorage(FATStorageArgs&& args) noexcept : + FilePath(std::move(args.Filename)), + FileSize(args.Size), + ReadOnly(args.ReadOnly), + SourceDir(std::move(args.SourceDir)) +{ + Load(FilePath, FileSize, SourceDir); + File = nullptr; } +FATStorage::FATStorage(FATStorage&& other) noexcept +{ + FilePath = std::move(other.FilePath); + IndexPath = std::move(other.IndexPath); + SourceDir = std::move(other.SourceDir); + ReadOnly = other.ReadOnly; + File = other.File; + FileSize = other.FileSize; + DirIndex = std::move(other.DirIndex); + FileIndex = std::move(other.FileIndex); + + other.File = nullptr; +} + +FATStorage& FATStorage::operator=(FATStorage&& other) noexcept +{ + if (this != &other) + { + if (File) + CloseFile(File); + + FilePath = std::move(other.FilePath); + IndexPath = std::move(other.IndexPath); + SourceDir = std::move(other.SourceDir); + ReadOnly = other.ReadOnly; + File = other.File; + FileSize = other.FileSize; + DirIndex = std::move(other.DirIndex); + FileIndex = std::move(other.FileIndex); + + other.File = nullptr; + } + + return *this; +} + FATStorage::~FATStorage() { if (!ReadOnly) Save(); -} - -bool FATStorage::Open() -{ - File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); - if (!File) - { - return false; - } - - return true; -} - -void FATStorage::Close() -{ if (File) CloseFile(File); File = nullptr; } - bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) { if (!File) return false; @@ -930,19 +970,15 @@ u64 FATStorage::GetDirectorySize(fs::path sourcedir) return ret; } -bool FATStorage::Load(const std::string& filename, u64 size, const std::string& sourcedir) +bool FATStorage::Load(const std::string& filename, u64 size, const std::optional& sourcedir) { - FilePath = filename; - FileSize = size; - SourceDir = sourcedir; - - bool hasdir = !sourcedir.empty(); - if (hasdir) + bool hasdir = sourcedir && !sourcedir->empty(); + if (sourcedir) { - if (!fs::is_directory(fs::u8path(sourcedir))) + if (!fs::is_directory(fs::u8path(*sourcedir))) { hasdir = false; - SourceDir = ""; + SourceDir = std::nullopt; } } @@ -1005,7 +1041,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& { if (hasdir) { - FileSize = GetDirectorySize(fs::u8path(sourcedir)); + FileSize = GetDirectorySize(fs::u8path(*sourcedir)); FileSize += 0x8000000ULL; // 128MB leeway // make it a power of two @@ -1054,7 +1090,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& if (res == FR_OK) { if (hasdir) - ImportDirectory(sourcedir); + ImportDirectory(*sourcedir); } f_unmount("0:"); @@ -1068,9 +1104,9 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::string& bool FATStorage::Save() { - if (SourceDir.empty()) - { - return true; + if (!SourceDir) + { // If we're not syncing the SD card image to a host directory... + return true; // Not an error. } FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); @@ -1094,7 +1130,7 @@ bool FATStorage::Save() return false; } - ExportChanges(SourceDir); + ExportChanges(*SourceDir); SaveIndex(); diff --git a/src/FATStorage.h b/src/FATStorage.h index 2bdafad8..6e348ce6 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include "Platform.h" @@ -30,24 +31,41 @@ namespace melonDS { +/// Contains information necessary to load an SD card image. +/// The intended use case is for loading homebrew NDS ROMs; +/// you won't know that a ROM is homebrew until you parse it, +/// so if you load the SD card before the ROM +/// then you might end up discarding it. +struct FATStorageArgs +{ + std::string Filename; + u64 Size; + bool ReadOnly; + std::optional SourceDir; +}; + class FATStorage { public: - FATStorage(const std::string& filename, u64 size, bool readonly, const std::string& sourcedir); + FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional& sourcedir = std::nullopt); + FATStorage(const FATStorageArgs& args) noexcept; + FATStorage(FATStorageArgs&& args) noexcept; + FATStorage(FATStorage&& other) noexcept; + FATStorage(const FATStorage& other) = delete; + FATStorage& operator=(const FATStorage& other) = delete; + FATStorage& operator=(FATStorage&& other) noexcept; ~FATStorage(); - bool Open(); - void Close(); - bool InjectFile(const std::string& path, u8* data, u32 len); u32 ReadSectors(u32 start, u32 num, u8* data); u32 WriteSectors(u32 start, u32 num, u8* data); + [[nodiscard]] bool IsReadOnly() const noexcept { return ReadOnly; } private: std::string FilePath; std::string IndexPath; - std::string SourceDir; + std::optional SourceDir; bool ReadOnly; Platform::FileHandle* File; @@ -76,7 +94,7 @@ private: bool ImportDirectory(const std::string& sourcedir); u64 GetDirectorySize(std::filesystem::path sourcedir); - bool Load(const std::string& filename, u64 size, const std::string& sourcedir); + bool Load(const std::string& filename, u64 size, const std::optional& sourcedir); bool Save(); typedef struct diff --git a/src/FreeBIOS.cpp b/src/FreeBIOS.cpp index 7eef18b7..4f36e200 100644 --- a/src/FreeBIOS.cpp +++ b/src/FreeBIOS.cpp @@ -28,7 +28,7 @@ namespace melonDS { -unsigned char bios_arm7_bin[] = { +std::array bios_arm7_bin = { 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea, 0xe3, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, @@ -1397,7 +1397,7 @@ unsigned char bios_arm7_bin[] = { 0x00, 0x00, 0x00, 0x00 }; -unsigned char bios_arm9_bin[] = { +std::array bios_arm9_bin = { 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3c, 0x00, 0x00, 0xea, 0x3b, 0x00, 0x00, 0xea, 0x3a, 0x00, 0x00, 0xea, 0xad, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, diff --git a/src/FreeBIOS.h b/src/FreeBIOS.h index ce90725d..a60c1f88 100644 --- a/src/FreeBIOS.h +++ b/src/FreeBIOS.h @@ -28,10 +28,13 @@ #ifndef FREEBIOS_H #define FREEBIOS_H +#include +#include "MemConstants.h" + namespace melonDS { -extern unsigned char bios_arm7_bin[16384]; -extern unsigned char bios_arm9_bin[4096]; +extern std::array bios_arm7_bin; +extern std::array bios_arm9_bin; } #endif // FREEBIOS_H diff --git a/src/GBACart.cpp b/src/GBACart.cpp index f5e320f9..6cd6e39d 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -1,903 +1,895 @@ -/* - 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 -#include -#include -#include "NDS.h" -#include "GBACart.h" -#include "CRC32.h" -#include "Platform.h" - -namespace melonDS -{ -using Platform::Log; -using Platform::LogLevel; - -namespace GBACart -{ - -const char SOLAR_SENSOR_GAMECODES[10][5] = -{ - "U3IJ", // Bokura no Taiyou - Taiyou Action RPG (Japan) - "U3IE", // Boktai - The Sun Is in Your Hand (USA) - "U3IP", // Boktai - The Sun Is in Your Hand (Europe) - "U32J", // Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan) - "U32E", // Boktai 2 - Solar Boy Django (USA) - "U32P", // Boktai 2 - Solar Boy Django (Europe) - "U33J", // Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan) - "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) -}; - -CartCommon::CartCommon() -{ -} - -CartCommon::~CartCommon() -{ -} - -void CartCommon::Reset() -{ -} - -void CartCommon::DoSavestate(Savestate* file) -{ - file->Section("GBCS"); -} - -void CartCommon::SetupSave(u32 type) -{ -} - -void CartCommon::LoadSave(const u8* savedata, u32 savelen) -{ -} - -int CartCommon::SetInput(int num, bool pressed) -{ - return -1; -} - -u16 CartCommon::ROMRead(u32 addr) const -{ - return 0; -} - -void CartCommon::ROMWrite(u32 addr, u16 val) -{ -} - -u8 CartCommon::SRAMRead(u32 addr) -{ - return 0; -} - -void CartCommon::SRAMWrite(u32 addr, u8 val) -{ -} - -u8* CartCommon::GetSaveMemory() const -{ - return nullptr; -} - -u32 CartCommon::GetSaveMemoryLength() const -{ - return 0; -} - -CartGame::CartGame(u8* rom, u32 len) : CartCommon() -{ - ROM = rom; - ROMLength = len; - - SRAM = nullptr; - SRAMLength = 0; - SRAMType = S_NULL; - SRAMFlashState = {}; -} - -CartGame::~CartGame() -{ - if (SRAM) delete[] SRAM; - delete[] ROM; -} - -u32 CartGame::Checksum() const -{ - u32 crc = CRC32(ROM, 0xC0, 0); - - // TODO: hash more contents? - - return crc; -} - -void CartGame::Reset() -{ - memset(&GPIO, 0, sizeof(GPIO)); -} - -void CartGame::DoSavestate(Savestate* file) -{ - CartCommon::DoSavestate(file); - - file->Var16(&GPIO.control); - file->Var16(&GPIO.data); - file->Var16(&GPIO.direction); - - u32 oldlen = SRAMLength; - - file->Var32(&SRAMLength); - - if (SRAMLength != oldlen) - { - // reallocate save memory - if (oldlen) delete[] SRAM; - SRAM = nullptr; - if (SRAMLength) SRAM = new u8[SRAMLength]; - } - if (SRAMLength) - { - // fill save memory if data is present - file->VarArray(SRAM, SRAMLength); - } - else - { - // no save data, clear the current state - SRAMType = SaveType::S_NULL; - SRAM = nullptr; - return; - } - - // persist some extra state info - file->Var8(&SRAMFlashState.bank); - file->Var8(&SRAMFlashState.cmd); - file->Var8(&SRAMFlashState.device); - file->Var8(&SRAMFlashState.manufacturer); - file->Var8(&SRAMFlashState.state); - - file->Var8((u8*)&SRAMType); - - if ((!file->Saving) && SRAM) - Platform::WriteGBASave(SRAM, SRAMLength, 0, SRAMLength); -} - -void CartGame::SetupSave(u32 type) -{ - if (SRAM) delete[] SRAM; - SRAM = nullptr; - - // TODO: have type be determined from some list, like in NDSCart - // and not this gross hack!! - SRAMLength = type; - - if (SRAMLength) - { - SRAM = new u8[SRAMLength]; - memset(SRAM, 0xFF, SRAMLength); - } - - switch (SRAMLength) - { - case 512: - SRAMType = S_EEPROM4K; - break; - case 8192: - SRAMType = S_EEPROM64K; - break; - case 32768: - SRAMType = S_SRAM256K; - break; - case 65536: - SRAMType = S_FLASH512K; - break; - case 128*1024: - SRAMType = S_FLASH1M; - break; - case 0: - SRAMType = S_NULL; - break; - default: - Log(LogLevel::Warn, "!! BAD GBA SAVE LENGTH %d\n", SRAMLength); - } - - if (SRAMType == S_FLASH512K) - { - // Panasonic 64K chip - SRAMFlashState.device = 0x1B; - SRAMFlashState.manufacturer = 0x32; - } - else if (SRAMType == S_FLASH1M) - { - // Sanyo 128K chip - SRAMFlashState.device = 0x13; - SRAMFlashState.manufacturer = 0x62; - } -} - -void CartGame::LoadSave(const u8* savedata, u32 savelen) -{ - if (!SRAM) return; - - u32 len = std::min(savelen, SRAMLength); - memcpy(SRAM, savedata, len); - Platform::WriteGBASave(savedata, len, 0, len); -} - -u16 CartGame::ROMRead(u32 addr) const -{ - addr &= 0x01FFFFFF; - - if (addr >= 0xC4 && addr < 0xCA) - { - if (GPIO.control & 0x1) - { - switch (addr) - { - case 0xC4: return GPIO.data; - case 0xC6: return GPIO.direction; - case 0xC8: return GPIO.control; - } - } - else - return 0; - } - - // CHECKME: does ROM mirror? - if (addr < ROMLength) - return *(u16*)&ROM[addr]; - - return 0; -} - -void CartGame::ROMWrite(u32 addr, u16 val) -{ - addr &= 0x01FFFFFF; - - switch (addr) - { - case 0xC4: - GPIO.data &= ~GPIO.direction; - GPIO.data |= val & GPIO.direction; - ProcessGPIO(); - break; - - case 0xC6: - GPIO.direction = val; - break; - - case 0xC8: - GPIO.control = val; - break; - - default: - Log(LogLevel::Warn, "Unknown GBA GPIO write 0x%02X @ 0x%04X\n", val, addr); - break; - } -} - -u8 CartGame::SRAMRead(u32 addr) -{ - addr &= 0xFFFF; - - switch (SRAMType) - { - case S_EEPROM4K: - case S_EEPROM64K: - return SRAMRead_EEPROM(addr); - - case S_FLASH512K: - case S_FLASH1M: - return SRAMRead_FLASH(addr); - - case S_SRAM256K: - return SRAMRead_SRAM(addr); - default: - break; - } - - return 0xFF; -} - -void CartGame::SRAMWrite(u32 addr, u8 val) -{ - addr &= 0xFFFF; - - switch (SRAMType) - { - case S_EEPROM4K: - case S_EEPROM64K: - return SRAMWrite_EEPROM(addr, val); - - case S_FLASH512K: - case S_FLASH1M: - return SRAMWrite_FLASH(addr, val); - - case S_SRAM256K: - return SRAMWrite_SRAM(addr, val); - default: - break; - } -} - -u8* CartGame::GetSaveMemory() const -{ - return SRAM; -} - -u32 CartGame::GetSaveMemoryLength() const -{ - return SRAMLength; -} - -void CartGame::ProcessGPIO() -{ -} - -u8 CartGame::SRAMRead_EEPROM(u32 addr) -{ - return 0; -} - -void CartGame::SRAMWrite_EEPROM(u32 addr, u8 val) -{ - // TODO: could be used in homebrew? -} - -// mostly ported from DeSmuME -u8 CartGame::SRAMRead_FLASH(u32 addr) -{ - if (SRAMFlashState.cmd == 0) // no cmd - { - return *(u8*)&SRAM[addr + 0x10000 * SRAMFlashState.bank]; - } - - switch (SRAMFlashState.cmd) - { - case 0x90: // chip ID - if (addr == 0x0000) return SRAMFlashState.manufacturer; - if (addr == 0x0001) return SRAMFlashState.device; - break; - case 0xF0: // terminate command (TODO: break if non-Macronix chip and not at the end of an ID call?) - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - break; - case 0xA0: // write command - break; // ignore here, handled in Write_Flash() - case 0xB0: // bank switching (128K only) - break; // ignore here, handled in Write_Flash() - default: - Log(LogLevel::Warn, "GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlashState.cmd, addr); - break; - } - - return 0xFF; -} - -// mostly ported from DeSmuME -void CartGame::SRAMWrite_FLASH(u32 addr, u8 val) -{ - switch (SRAMFlashState.state) - { - case 0x00: - if (addr == 0x5555) - { - if (val == 0xF0) - { - // reset - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - return; - } - else if (val == 0xAA) - { - SRAMFlashState.state = 1; - return; - } - } - if (addr == 0x0000) - { - if (SRAMFlashState.cmd == 0xB0) - { - // bank switching - SRAMFlashState.bank = val; - SRAMFlashState.cmd = 0; - return; - } - } - break; - case 0x01: - if (addr == 0x2AAA && val == 0x55) - { - SRAMFlashState.state = 2; - return; - } - SRAMFlashState.state = 0; - break; - case 0x02: - if (addr == 0x5555) - { - // send command - switch (val) - { - case 0x80: // erase - SRAMFlashState.state = 0x80; - break; - case 0x90: // chip ID - SRAMFlashState.state = 0x90; - break; - case 0xA0: // write - SRAMFlashState.state = 0; - break; - default: - SRAMFlashState.state = 0; - break; - } - - SRAMFlashState.cmd = val; - return; - } - SRAMFlashState.state = 0; - break; - // erase - case 0x80: - if (addr == 0x5555 && val == 0xAA) - { - SRAMFlashState.state = 0x81; - return; - } - SRAMFlashState.state = 0; - break; - case 0x81: - if (addr == 0x2AAA && val == 0x55) - { - SRAMFlashState.state = 0x82; - return; - } - SRAMFlashState.state = 0; - break; - case 0x82: - if (val == 0x30) - { - u32 start_addr = addr + 0x10000 * SRAMFlashState.bank; - memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); - - Platform::WriteGBASave(SRAM, SRAMLength, start_addr, 0x1000); - } - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - return; - // chip ID - case 0x90: - if (addr == 0x5555 && val == 0xAA) - { - SRAMFlashState.state = 0x91; - return; - } - SRAMFlashState.state = 0; - break; - case 0x91: - if (addr == 0x2AAA && val == 0x55) - { - SRAMFlashState.state = 0x92; - return; - } - SRAMFlashState.state = 0; - break; - case 0x92: - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - return; - default: - break; - } - - if (SRAMFlashState.cmd == 0xA0) // write - { - SRAMWrite_SRAM(addr + 0x10000 * SRAMFlashState.bank, val); - SRAMFlashState.state = 0; - SRAMFlashState.cmd = 0; - return; - } - - Log(LogLevel::Debug, "GBACart_SRAM::Write_Flash: unknown write 0x%02X @ 0x%04X (state: 0x%02X)\n", - val, addr, SRAMFlashState.state); -} - -u8 CartGame::SRAMRead_SRAM(u32 addr) -{ - if (addr >= SRAMLength) return 0xFF; - - return SRAM[addr]; -} - -void CartGame::SRAMWrite_SRAM(u32 addr, u8 val) -{ - if (addr >= SRAMLength) return; - - u8 prev = *(u8*)&SRAM[addr]; - if (prev != val) - { - *(u8*)&SRAM[addr] = val; - - // TODO: optimize this!! - Platform::WriteGBASave(SRAM, SRAMLength, addr, 1); - } -} - - -const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183}; - -CartGameSolarSensor::CartGameSolarSensor(u8* rom, u32 len) : CartGame(rom, len) -{ -} - -CartGameSolarSensor::~CartGameSolarSensor() -{ -} - -void CartGameSolarSensor::Reset() -{ - LightEdge = false; - LightCounter = 0; - LightSample = 0xFF; - LightLevel = 0; -} - -void CartGameSolarSensor::DoSavestate(Savestate* file) -{ - CartGame::DoSavestate(file); - - file->Var8((u8*)&LightEdge); - file->Var8(&LightCounter); - file->Var8(&LightSample); - file->Var8(&LightLevel); -} - -int CartGameSolarSensor::SetInput(int num, bool pressed) -{ - if (!pressed) return -1; - - if (num == Input_SolarSensorDown) - { - if (LightLevel > 0) - LightLevel--; - - return LightLevel; - } - else if (num == Input_SolarSensorUp) - { - if (LightLevel < 10) - LightLevel++; - - return LightLevel; - } - - return -1; -} - -void CartGameSolarSensor::ProcessGPIO() -{ - if (GPIO.data & 4) return; // Boktai chip select - if (GPIO.data & 2) // Reset - { - u8 prev = LightSample; - LightCounter = 0; - LightSample = (0xFF - (0x16 + kLuxLevels[LightLevel])); - Log(LogLevel::Debug, "Solar sensor reset (sample: 0x%02X -> 0x%02X)\n", prev, LightSample); - } - if (GPIO.data & 1 && LightEdge) LightCounter++; - - LightEdge = !(GPIO.data & 1); - - bool sendBit = LightCounter >= LightSample; - if (GPIO.control & 1) - { - GPIO.data = (GPIO.data & GPIO.direction) | ((sendBit << 3) & ~GPIO.direction & 0xF); - } -} - - -CartRAMExpansion::CartRAMExpansion() : CartCommon() -{ -} - -CartRAMExpansion::~CartRAMExpansion() -{ -} - -void CartRAMExpansion::Reset() -{ - memset(RAM, 0xFF, sizeof(RAM)); - RAMEnable = 1; -} - -void CartRAMExpansion::DoSavestate(Savestate* file) -{ - CartCommon::DoSavestate(file); - - file->VarArray(RAM, sizeof(RAM)); - file->Var16(&RAMEnable); -} - -u16 CartRAMExpansion::ROMRead(u32 addr) const -{ - addr &= 0x01FFFFFF; - - if (addr < 0x01000000) - { - switch (addr) - { - case 0xB0: return 0xFFFF; - case 0xB2: return 0x0000; - case 0xB4: return 0x2400; - case 0xB6: return 0x2424; - case 0xB8: return 0xFFFF; - case 0xBA: return 0xFFFF; - case 0xBC: return 0xFFFF; - case 0xBE: return 0x7FFF; - - case 0x1FFFC: return 0xFFFF; - case 0x1FFFE: return 0x7FFF; - - case 0x240000: return RAMEnable; - case 0x240002: return 0x0000; - } - - return 0xFFFF; - } - else if (addr < 0x01800000) - { - if (!RAMEnable) return 0xFFFF; - - return *(u16*)&RAM[addr & 0x7FFFFF]; - } - - return 0xFFFF; -} - -void CartRAMExpansion::ROMWrite(u32 addr, u16 val) -{ - addr &= 0x01FFFFFF; - - if (addr < 0x01000000) - { - switch (addr) - { - case 0x240000: - RAMEnable = val & 0x0001; - return; - } - } - else if (addr < 0x01800000) - { - if (!RAMEnable) return; - - *(u16*)&RAM[addr & 0x7FFFFF] = val; - } -} - -void GBACartSlot::Reset() noexcept -{ - if (Cart) Cart->Reset(); -} - -void GBACartSlot::DoSavestate(Savestate* file) noexcept -{ - file->Section("GBAC"); // Game Boy Advance Cartridge - - // little state here - // no need to save OpenBusDecay, it will be set later - - u32 carttype = 0; - u32 cartchk = 0; - if (Cart) - { - carttype = Cart->Type(); - cartchk = Cart->Checksum(); - } - - if (file->Saving) - { - file->Var32(&carttype); - file->Var32(&cartchk); - } - else - { - u32 savetype; - file->Var32(&savetype); - if (savetype != carttype) return; - - u32 savechk; - file->Var32(&savechk); - if (savechk != cartchk) return; - } - - if (Cart) Cart->DoSavestate(file); -} - - -std::unique_ptr ParseROM(const u8* romdata, u32 romlen) -{ - if (romdata == nullptr) - { - Log(LogLevel::Error, "GBACart: romdata is null\n"); - return nullptr; - } - - if (romlen == 0) - { - Log(LogLevel::Error, "GBACart: romlen is zero\n"); - return nullptr; - } - - u32 cartromsize = 0x200; - while (cartromsize < romlen) - cartromsize <<= 1; - - u8* cartrom = nullptr; - try - { - cartrom = new u8[cartromsize]; - } - catch (const std::bad_alloc& e) - { - Log(LogLevel::Error, "GBACart: failed to allocate memory for ROM (%d bytes)\n", cartromsize); - - return nullptr; - } - - memset(cartrom, 0, cartromsize); - memcpy(cartrom, romdata, romlen); - - char gamecode[5] = { '\0' }; - memcpy(&gamecode, cartrom + 0xAC, 4); - - bool solarsensor = false; - for (const char* i : SOLAR_SENSOR_GAMECODES) - { - if (strcmp(gamecode, i) == 0) - solarsensor = true; - } - - if (solarsensor) - { - Log(LogLevel::Info, "GBA solar sensor support detected!\n"); - } - - std::unique_ptr cart; - if (solarsensor) - cart = std::make_unique(cartrom, cartromsize); - else - cart = std::make_unique(cartrom, cartromsize); - - cart->Reset(); - - // TODO: setup cart save here! from a list or something - - // save - //printf("GBA save file: %s\n", sram); - - // TODO: have a list of sorts like in NDSCart? to determine the savemem type - //if (Cart) Cart->LoadSave(sram, 0); - - return cart; -} - -bool GBACartSlot::InsertROM(std::unique_ptr&& cart) noexcept -{ - if (!cart) { - Log(LogLevel::Error, "Failed to insert invalid GBA cart; existing cart (if any) was not ejected.\n"); - return false; - } - - if (Cart != nullptr) - EjectCart(); - - Cart = std::move(cart); - - const u8* cartrom = Cart->GetROM(); - - if (cartrom) - { - char gamecode[5] = { '\0' }; - memcpy(&gamecode, Cart->GetROM() + 0xAC, 4); - Log(LogLevel::Info, "Inserted GBA cart with game code: %s\n", gamecode); - } - else - { - Log(LogLevel::Info, "Inserted GBA cart with no game code (it's probably an accessory)\n"); - } - - return true; -} - -bool GBACartSlot::LoadROM(const u8* romdata, u32 romlen) noexcept -{ - std::unique_ptr data = ParseROM(romdata, romlen); - - return InsertROM(std::move(data)); -} - -void GBACartSlot::LoadSave(const u8* savedata, u32 savelen) noexcept -{ - if (Cart) - { - // gross hack - Cart->SetupSave(savelen); - - Cart->LoadSave(savedata, savelen); - } -} - -void GBACartSlot::LoadAddon(int type) noexcept -{ - switch (type) - { - case GBAAddon_RAMExpansion: - Cart = std::make_unique(); - break; - - default: - Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); - return; - } -} - -void GBACartSlot::EjectCart() noexcept -{ - Cart = nullptr; -} - - -int GBACartSlot::SetInput(int num, bool pressed) noexcept -{ - if (Cart) return Cart->SetInput(num, pressed); - - return -1; -} - - -u16 GBACartSlot::ROMRead(u32 addr) const noexcept -{ - if (Cart) return Cart->ROMRead(addr); - - return ((addr >> 1) & 0xFFFF) | OpenBusDecay; -} - -void GBACartSlot::ROMWrite(u32 addr, u16 val) noexcept -{ - if (Cart) Cart->ROMWrite(addr, val); -} - -u8 GBACartSlot::SRAMRead(u32 addr) noexcept -{ - if (Cart) return Cart->SRAMRead(addr); - - return 0xFF; -} - -void GBACartSlot::SRAMWrite(u32 addr, u8 val) noexcept -{ - if (Cart) Cart->SRAMWrite(addr, val); -} - -} - +/* + 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 +#include +#include +#include "NDS.h" +#include "GBACart.h" +#include "CRC32.h" +#include "Platform.h" +#include "Utils.h" + +namespace melonDS +{ +using Platform::Log; +using Platform::LogLevel; + +namespace GBACart +{ + +const char SOLAR_SENSOR_GAMECODES[10][5] = +{ + "U3IJ", // Bokura no Taiyou - Taiyou Action RPG (Japan) + "U3IE", // Boktai - The Sun Is in Your Hand (USA) + "U3IP", // Boktai - The Sun Is in Your Hand (Europe) + "U32J", // Zoku Bokura no Taiyou - Taiyou Shounen Django (Japan) + "U32E", // Boktai 2 - Solar Boy Django (USA) + "U32P", // Boktai 2 - Solar Boy Django (Europe) + "U33J", // Shin Bokura no Taiyou - Gyakushuu no Sabata (Japan) + "A3IJ" // Boktai - The Sun Is in Your Hand (USA) (Sample) +}; + +CartCommon::CartCommon(GBACart::CartType type) : CartType(type) +{ +} + +void CartCommon::Reset() +{ +} + +void CartCommon::DoSavestate(Savestate* file) +{ + file->Section("GBCS"); +} + +void CartCommon::SetSaveMemory(const u8* savedata, u32 savelen) +{ +} + +int CartCommon::SetInput(int num, bool pressed) +{ + return -1; +} + +u16 CartCommon::ROMRead(u32 addr) const +{ + return 0; +} + +void CartCommon::ROMWrite(u32 addr, u16 val) +{ +} + +u8 CartCommon::SRAMRead(u32 addr) +{ + return 0; +} + +void CartCommon::SRAMWrite(u32 addr, u8 val) +{ +} + +u8* CartCommon::GetSaveMemory() const +{ + return nullptr; +} + +u32 CartCommon::GetSaveMemoryLength() const +{ + return 0; +} + +CartGame::CartGame(const u8* rom, u32 len, const u8* sram, u32 sramlen, GBACart::CartType type) : + CartGame(CopyToUnique(rom, len), len, CopyToUnique(sram, sramlen), sramlen, type) +{ +} + +CartGame::CartGame(std::unique_ptr&& rom, u32 len, std::unique_ptr&& sram, u32 sramlen, GBACart::CartType type) : + CartCommon(type), + ROM(std::move(rom)), + ROMLength(len), + SRAM(std::move(sram)), + SRAMLength(sramlen) +{ + if (SRAM && SRAMLength) + { + SetupSave(sramlen); + } +} + +CartGame::~CartGame() = default; +// unique_ptr cleans up the allocated memory + +u32 CartGame::Checksum() const +{ + u32 crc = CRC32(ROM.get(), 0xC0, 0); + + // TODO: hash more contents? + + return crc; +} + +void CartGame::Reset() +{ + memset(&GPIO, 0, sizeof(GPIO)); +} + +void CartGame::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + file->Var16(&GPIO.control); + file->Var16(&GPIO.data); + file->Var16(&GPIO.direction); + + u32 oldlen = SRAMLength; + + file->Var32(&SRAMLength); + + if (SRAMLength != oldlen) + { + // reallocate save memory + SRAM = SRAMLength ? std::make_unique(SRAMLength) : nullptr; + } + if (SRAMLength) + { + // fill save memory if data is present + file->VarArray(SRAM.get(), SRAMLength); + } + else + { + // no save data, clear the current state + SRAMType = SaveType::S_NULL; + SRAM = nullptr; + return; + } + + // persist some extra state info + file->Var8(&SRAMFlashState.bank); + file->Var8(&SRAMFlashState.cmd); + file->Var8(&SRAMFlashState.device); + file->Var8(&SRAMFlashState.manufacturer); + file->Var8(&SRAMFlashState.state); + + file->Var8((u8*)&SRAMType); + + if ((!file->Saving) && SRAM) + Platform::WriteGBASave(SRAM.get(), SRAMLength, 0, SRAMLength); +} + +void CartGame::SetupSave(u32 type) +{ + // TODO: have type be determined from some list, like in NDSCart + // and not this gross hack!! + SRAMLength = type; + switch (SRAMLength) + { + case 512: + SRAMType = S_EEPROM4K; + break; + case 8192: + SRAMType = S_EEPROM64K; + break; + case 32768: + SRAMType = S_SRAM256K; + break; + case 65536: + SRAMType = S_FLASH512K; + break; + case 128*1024: + SRAMType = S_FLASH1M; + break; + case 0: + SRAMType = S_NULL; + break; + default: + Log(LogLevel::Warn, "!! BAD GBA SAVE LENGTH %d\n", SRAMLength); + } + + if (SRAMType == S_FLASH512K) + { + // Panasonic 64K chip + SRAMFlashState.device = 0x1B; + SRAMFlashState.manufacturer = 0x32; + } + else if (SRAMType == S_FLASH1M) + { + // Sanyo 128K chip + SRAMFlashState.device = 0x13; + SRAMFlashState.manufacturer = 0x62; + } +} + +void CartGame::SetSaveMemory(const u8* savedata, u32 savelen) +{ + SetupSave(savelen); + + u32 len = std::min(savelen, SRAMLength); + memcpy(SRAM.get(), savedata, len); + Platform::WriteGBASave(savedata, len, 0, len); +} + +u16 CartGame::ROMRead(u32 addr) const +{ + addr &= 0x01FFFFFF; + + if (addr >= 0xC4 && addr < 0xCA) + { + if (GPIO.control & 0x1) + { + switch (addr) + { + case 0xC4: return GPIO.data; + case 0xC6: return GPIO.direction; + case 0xC8: return GPIO.control; + } + } + else + return 0; + } + + // CHECKME: does ROM mirror? + if (addr < ROMLength) + return *(u16*)&ROM[addr]; + + return 0; +} + +void CartGame::ROMWrite(u32 addr, u16 val) +{ + addr &= 0x01FFFFFF; + + switch (addr) + { + case 0xC4: + GPIO.data &= ~GPIO.direction; + GPIO.data |= val & GPIO.direction; + ProcessGPIO(); + break; + + case 0xC6: + GPIO.direction = val; + break; + + case 0xC8: + GPIO.control = val; + break; + + default: + Log(LogLevel::Warn, "Unknown GBA GPIO write 0x%02X @ 0x%04X\n", val, addr); + break; + } +} + +u8 CartGame::SRAMRead(u32 addr) +{ + addr &= 0xFFFF; + + switch (SRAMType) + { + case S_EEPROM4K: + case S_EEPROM64K: + return SRAMRead_EEPROM(addr); + + case S_FLASH512K: + case S_FLASH1M: + return SRAMRead_FLASH(addr); + + case S_SRAM256K: + return SRAMRead_SRAM(addr); + default: + break; + } + + return 0xFF; +} + +void CartGame::SRAMWrite(u32 addr, u8 val) +{ + addr &= 0xFFFF; + + switch (SRAMType) + { + case S_EEPROM4K: + case S_EEPROM64K: + return SRAMWrite_EEPROM(addr, val); + + case S_FLASH512K: + case S_FLASH1M: + return SRAMWrite_FLASH(addr, val); + + case S_SRAM256K: + return SRAMWrite_SRAM(addr, val); + default: + break; + } +} + +u8* CartGame::GetSaveMemory() const +{ + return SRAM.get(); +} + +u32 CartGame::GetSaveMemoryLength() const +{ + return SRAMLength; +} + +void CartGame::ProcessGPIO() +{ +} + +u8 CartGame::SRAMRead_EEPROM(u32 addr) +{ + return 0; +} + +void CartGame::SRAMWrite_EEPROM(u32 addr, u8 val) +{ + // TODO: could be used in homebrew? +} + +// mostly ported from DeSmuME +u8 CartGame::SRAMRead_FLASH(u32 addr) +{ + if (SRAMFlashState.cmd == 0) // no cmd + { + return *(u8*)&SRAM[addr + 0x10000 * SRAMFlashState.bank]; + } + + switch (SRAMFlashState.cmd) + { + case 0x90: // chip ID + if (addr == 0x0000) return SRAMFlashState.manufacturer; + if (addr == 0x0001) return SRAMFlashState.device; + break; + case 0xF0: // terminate command (TODO: break if non-Macronix chip and not at the end of an ID call?) + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + break; + case 0xA0: // write command + break; // ignore here, handled in Write_Flash() + case 0xB0: // bank switching (128K only) + break; // ignore here, handled in Write_Flash() + default: + Log(LogLevel::Warn, "GBACart_SRAM::Read_Flash: unknown command 0x%02X @ 0x%04X\n", SRAMFlashState.cmd, addr); + break; + } + + return 0xFF; +} + +// mostly ported from DeSmuME +void CartGame::SRAMWrite_FLASH(u32 addr, u8 val) +{ + switch (SRAMFlashState.state) + { + case 0x00: + if (addr == 0x5555) + { + if (val == 0xF0) + { + // reset + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + return; + } + else if (val == 0xAA) + { + SRAMFlashState.state = 1; + return; + } + } + if (addr == 0x0000) + { + if (SRAMFlashState.cmd == 0xB0) + { + // bank switching + SRAMFlashState.bank = val; + SRAMFlashState.cmd = 0; + return; + } + } + break; + case 0x01: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlashState.state = 2; + return; + } + SRAMFlashState.state = 0; + break; + case 0x02: + if (addr == 0x5555) + { + // send command + switch (val) + { + case 0x80: // erase + SRAMFlashState.state = 0x80; + break; + case 0x90: // chip ID + SRAMFlashState.state = 0x90; + break; + case 0xA0: // write + SRAMFlashState.state = 0; + break; + default: + SRAMFlashState.state = 0; + break; + } + + SRAMFlashState.cmd = val; + return; + } + SRAMFlashState.state = 0; + break; + // erase + case 0x80: + if (addr == 0x5555 && val == 0xAA) + { + SRAMFlashState.state = 0x81; + return; + } + SRAMFlashState.state = 0; + break; + case 0x81: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlashState.state = 0x82; + return; + } + SRAMFlashState.state = 0; + break; + case 0x82: + if (val == 0x30) + { + u32 start_addr = addr + 0x10000 * SRAMFlashState.bank; + memset((u8*)&SRAM[start_addr], 0xFF, 0x1000); + + Platform::WriteGBASave(SRAM.get(), SRAMLength, start_addr, 0x1000); + } + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + return; + // chip ID + case 0x90: + if (addr == 0x5555 && val == 0xAA) + { + SRAMFlashState.state = 0x91; + return; + } + SRAMFlashState.state = 0; + break; + case 0x91: + if (addr == 0x2AAA && val == 0x55) + { + SRAMFlashState.state = 0x92; + return; + } + SRAMFlashState.state = 0; + break; + case 0x92: + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + return; + default: + break; + } + + if (SRAMFlashState.cmd == 0xA0) // write + { + SRAMWrite_SRAM(addr + 0x10000 * SRAMFlashState.bank, val); + SRAMFlashState.state = 0; + SRAMFlashState.cmd = 0; + return; + } + + Log(LogLevel::Debug, "GBACart_SRAM::Write_Flash: unknown write 0x%02X @ 0x%04X (state: 0x%02X)\n", + val, addr, SRAMFlashState.state); +} + +u8 CartGame::SRAMRead_SRAM(u32 addr) +{ + if (addr >= SRAMLength) return 0xFF; + + return SRAM[addr]; +} + +void CartGame::SRAMWrite_SRAM(u32 addr, u8 val) +{ + if (addr >= SRAMLength) return; + + u8 prev = *(u8*)&SRAM[addr]; + if (prev != val) + { + *(u8*)&SRAM[addr] = val; + + // TODO: optimize this!! + Platform::WriteGBASave(SRAM.get(), SRAMLength, addr, 1); + } +} + + +CartGameSolarSensor::CartGameSolarSensor(const u8* rom, u32 len, const u8* sram, u32 sramlen) : + CartGameSolarSensor(CopyToUnique(rom, len), len, CopyToUnique(sram, sramlen), sramlen) +{ +} + +CartGameSolarSensor::CartGameSolarSensor(std::unique_ptr&& rom, u32 len, std::unique_ptr&& sram, u32 sramlen) : + CartGame(std::move(rom), len, std::move(sram), sramlen, CartType::GameSolarSensor) +{ +} + +const int CartGameSolarSensor::kLuxLevels[11] = {0, 5, 11, 18, 27, 42, 62, 84, 109, 139, 183}; + +void CartGameSolarSensor::Reset() +{ + CartGame::Reset(); + LightEdge = false; + LightCounter = 0; + LightSample = 0xFF; + LightLevel = 0; +} + +void CartGameSolarSensor::DoSavestate(Savestate* file) +{ + CartGame::DoSavestate(file); + + file->Var8((u8*)&LightEdge); + file->Var8(&LightCounter); + file->Var8(&LightSample); + file->Var8(&LightLevel); +} + +int CartGameSolarSensor::SetInput(int num, bool pressed) +{ + if (!pressed) return -1; + + if (num == Input_SolarSensorDown) + { + if (LightLevel > 0) + LightLevel--; + + return LightLevel; + } + else if (num == Input_SolarSensorUp) + { + if (LightLevel < 10) + LightLevel++; + + return LightLevel; + } + + return -1; +} + +void CartGameSolarSensor::ProcessGPIO() +{ + if (GPIO.data & 4) return; // Boktai chip select + if (GPIO.data & 2) // Reset + { + u8 prev = LightSample; + LightCounter = 0; + LightSample = (0xFF - (0x16 + kLuxLevels[LightLevel])); + Log(LogLevel::Debug, "Solar sensor reset (sample: 0x%02X -> 0x%02X)\n", prev, LightSample); + } + if (GPIO.data & 1 && LightEdge) LightCounter++; + + LightEdge = !(GPIO.data & 1); + + bool sendBit = LightCounter >= LightSample; + if (GPIO.control & 1) + { + GPIO.data = (GPIO.data & GPIO.direction) | ((sendBit << 3) & ~GPIO.direction & 0xF); + } +} + + +CartRAMExpansion::CartRAMExpansion() : CartCommon(RAMExpansion) +{ +} + +CartRAMExpansion::~CartRAMExpansion() = default; + +void CartRAMExpansion::Reset() +{ + memset(RAM, 0xFF, sizeof(RAM)); + RAMEnable = 1; +} + +void CartRAMExpansion::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + file->VarArray(RAM, sizeof(RAM)); + file->Var16(&RAMEnable); +} + +u16 CartRAMExpansion::ROMRead(u32 addr) const +{ + addr &= 0x01FFFFFF; + + if (addr < 0x01000000) + { + switch (addr) + { + case 0xB0: return 0xFFFF; + case 0xB2: return 0x0000; + case 0xB4: return 0x2400; + case 0xB6: return 0x2424; + case 0xB8: return 0xFFFF; + case 0xBA: return 0xFFFF; + case 0xBC: return 0xFFFF; + case 0xBE: return 0x7FFF; + + case 0x1FFFC: return 0xFFFF; + case 0x1FFFE: return 0x7FFF; + + case 0x240000: return RAMEnable; + case 0x240002: return 0x0000; + } + + return 0xFFFF; + } + else if (addr < 0x01800000) + { + if (!RAMEnable) return 0xFFFF; + + return *(u16*)&RAM[addr & 0x7FFFFF]; + } + + return 0xFFFF; +} + +void CartRAMExpansion::ROMWrite(u32 addr, u16 val) +{ + addr &= 0x01FFFFFF; + + if (addr < 0x01000000) + { + switch (addr) + { + case 0x240000: + RAMEnable = val & 0x0001; + return; + } + } + else if (addr < 0x01800000) + { + if (!RAMEnable) return; + + *(u16*)&RAM[addr & 0x7FFFFF] = val; + } +} + +GBACartSlot::GBACartSlot(std::unique_ptr&& cart) noexcept : Cart(std::move(cart)) +{ +} + +void GBACartSlot::Reset() noexcept +{ + if (Cart) Cart->Reset(); +} + +void GBACartSlot::DoSavestate(Savestate* file) noexcept +{ + file->Section("GBAC"); // Game Boy Advance Cartridge + + // little state here + // no need to save OpenBusDecay, it will be set later + + u32 carttype = 0; + u32 cartchk = 0; + if (Cart) + { + carttype = Cart->Type(); + cartchk = Cart->Checksum(); + } + + if (file->Saving) + { + file->Var32(&carttype); + file->Var32(&cartchk); + } + else + { + u32 savetype; + file->Var32(&savetype); + if (savetype != carttype) return; + + u32 savechk; + file->Var32(&savechk); + if (savechk != cartchk) return; + } + + if (Cart) Cart->DoSavestate(file); +} + +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen) +{ + return ParseROM(std::move(romdata), romlen, nullptr, 0); +} + +std::unique_ptr ParseROM(const u8* romdata, u32 romlen, const u8* sramdata, u32 sramlen) +{ + auto [romcopy, romcopylen] = PadToPowerOf2(romdata, romlen); + + return ParseROM(std::move(romcopy), romcopylen, CopyToUnique(sramdata, sramlen), sramlen); +} + +std::unique_ptr ParseROM(const u8* romdata, u32 romlen) +{ + return ParseROM(romdata, romlen, nullptr, 0); +} + +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::unique_ptr&& sramdata, u32 sramlen) +{ + if (romdata == nullptr) + { + Log(LogLevel::Error, "GBACart: romdata is null\n"); + return nullptr; + } + + if (romlen == 0) + { + Log(LogLevel::Error, "GBACart: romlen is zero\n"); + return nullptr; + } + + auto [cartrom, cartromsize] = PadToPowerOf2(std::move(romdata), romlen); + + std::unique_ptr cartsram; + try + { + cartsram = sramdata ? std::make_unique(sramlen) : nullptr; + } + catch (const std::bad_alloc& e) + { + Log(LogLevel::Error, "GBACart: failed to allocate memory for ROM (%d bytes)\n", cartromsize); + + return nullptr; + } + + if (cartsram) + { + memset(cartsram.get(), 0, sramlen); + memcpy(cartsram.get(), sramdata.get(), sramlen); + } + + char gamecode[5] = { '\0' }; + memcpy(&gamecode, cartrom.get() + 0xAC, 4); + + bool solarsensor = false; + for (const char* i : SOLAR_SENSOR_GAMECODES) + { + if (strcmp(gamecode, i) == 0) + solarsensor = true; + } + + if (solarsensor) + { + Log(LogLevel::Info, "GBA solar sensor support detected!\n"); + } + + std::unique_ptr cart; + if (solarsensor) + cart = std::make_unique(std::move(cartrom), cartromsize, std::move(cartsram), sramlen); + else + cart = std::make_unique(std::move(cartrom), cartromsize, std::move(cartsram), sramlen); + + cart->Reset(); + + // save + //printf("GBA save file: %s\n", sram); + + // TODO: have a list of sorts like in NDSCart? to determine the savemem type + //if (Cart) Cart->LoadSave(sram, 0); + + return cart; +} + +void GBACartSlot::SetCart(std::unique_ptr&& cart) noexcept +{ + Cart = std::move(cart); + + if (!Cart) + { + Log(LogLevel::Info, "Ejected GBA cart"); + return; + } + + const u8* cartrom = Cart->GetROM(); + + if (cartrom) + { + char gamecode[5] = { '\0' }; + memcpy(&gamecode, Cart->GetROM() + 0xAC, 4); + Log(LogLevel::Info, "Inserted GBA cart with game code: %s\n", gamecode); + } + else + { + Log(LogLevel::Info, "Inserted GBA cart with no game code (it's probably an accessory)\n"); + } +} + +void GBACartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept +{ + if (Cart) + { + Cart->SetSaveMemory(savedata, savelen); + } +} + +void GBACartSlot::LoadAddon(int type) noexcept +{ + switch (type) + { + case GBAAddon_RAMExpansion: + Cart = std::make_unique(); + break; + + default: + Log(LogLevel::Warn, "GBACart: !! invalid addon type %d\n", type); + return; + } +} + +std::unique_ptr GBACartSlot::EjectCart() noexcept +{ + return std::move(Cart); + // Cart will be nullptr after this function returns, due to the move +} + + +int GBACartSlot::SetInput(int num, bool pressed) noexcept +{ + if (Cart) return Cart->SetInput(num, pressed); + + return -1; +} + + +u16 GBACartSlot::ROMRead(u32 addr) const noexcept +{ + if (Cart) return Cart->ROMRead(addr); + + return ((addr >> 1) & 0xFFFF) | OpenBusDecay; +} + +void GBACartSlot::ROMWrite(u32 addr, u16 val) noexcept +{ + if (Cart) Cart->ROMWrite(addr, val); +} + +u8 GBACartSlot::SRAMRead(u32 addr) noexcept +{ + if (Cart) return Cart->SRAMRead(addr); + + return 0xFF; +} + +void GBACartSlot::SRAMWrite(u32 addr, u8 val) noexcept +{ + if (Cart) Cart->SRAMWrite(addr, val); +} + +} + } \ No newline at end of file diff --git a/src/GBACart.h b/src/GBACart.h index a5570897..493bf6b8 100644 --- a/src/GBACart.h +++ b/src/GBACart.h @@ -1,257 +1,289 @@ -/* - 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 GBACART_H -#define GBACART_H - -#include -#include "types.h" -#include "Savestate.h" - -namespace melonDS::GBACart -{ - -enum CartType -{ - Default = 0x001, - Game = 0x101, - GameSolarSensor = 0x102, - RAMExpansion = 0x201, -}; - -// CartCommon -- base code shared by all cart types -class CartCommon -{ -public: - CartCommon(); - virtual ~CartCommon(); - - virtual u32 Type() const = 0; - virtual u32 Checksum() const { return 0; } - - virtual void Reset(); - - virtual void DoSavestate(Savestate* file); - - virtual void SetupSave(u32 type); - virtual void LoadSave(const u8* savedata, u32 savelen); - - virtual int SetInput(int num, bool pressed); - - virtual u16 ROMRead(u32 addr) const; - virtual void ROMWrite(u32 addr, u16 val); - - virtual u8 SRAMRead(u32 addr); - virtual void SRAMWrite(u32 addr, u8 val); - - [[nodiscard]] virtual const u8* GetROM() const { return nullptr; } - [[nodiscard]] virtual u32 GetROMLength() const { return 0; } - - virtual u8* GetSaveMemory() const; - virtual u32 GetSaveMemoryLength() const; -}; - -// CartGame -- regular retail game cart (ROM, SRAM) -class CartGame : public CartCommon -{ -public: - CartGame(u8* rom, u32 len); - virtual ~CartGame() override; - - virtual u32 Type() const override { return CartType::Game; } - virtual u32 Checksum() const override; - - virtual void Reset() override; - - virtual void DoSavestate(Savestate* file) override; - - virtual void SetupSave(u32 type) override; - virtual void LoadSave(const u8* savedata, u32 savelen) override; - - virtual u16 ROMRead(u32 addr) const override; - virtual void ROMWrite(u32 addr, u16 val) override; - - virtual u8 SRAMRead(u32 addr) override; - virtual void SRAMWrite(u32 addr, u8 val) override; - - [[nodiscard]] const u8* GetROM() const override { return ROM; } - [[nodiscard]] u32 GetROMLength() const override { return ROMLength; } - - virtual u8* GetSaveMemory() const override; - virtual u32 GetSaveMemoryLength() const override; -protected: - virtual void ProcessGPIO(); - - u8 SRAMRead_EEPROM(u32 addr); - void SRAMWrite_EEPROM(u32 addr, u8 val); - u8 SRAMRead_FLASH(u32 addr); - void SRAMWrite_FLASH(u32 addr, u8 val); - u8 SRAMRead_SRAM(u32 addr); - void SRAMWrite_SRAM(u32 addr, u8 val); - - u8* ROM; - u32 ROMLength; - - struct - { - u16 data; - u16 direction; - u16 control; - - } GPIO; - - enum SaveType - { - S_NULL, - S_EEPROM4K, - S_EEPROM64K, - S_SRAM256K, - S_FLASH512K, - S_FLASH1M - }; - - // from DeSmuME - struct - { - u8 state; - u8 cmd; - u8 device; - u8 manufacturer; - u8 bank; - - } SRAMFlashState; - - u8* SRAM; - u32 SRAMLength; - SaveType SRAMType; -}; - -// CartGameSolarSensor -- Boktai game cart -class CartGameSolarSensor : public CartGame -{ -public: - CartGameSolarSensor(u8* rom, u32 len); - virtual ~CartGameSolarSensor() override; - - virtual u32 Type() const override { return CartType::GameSolarSensor; } - - virtual void Reset() override; - - virtual void DoSavestate(Savestate* file) override; - - virtual int SetInput(int num, bool pressed) override; - -private: - virtual void ProcessGPIO() override; - - static const int kLuxLevels[11]; - - bool LightEdge; - u8 LightCounter; - u8 LightSample; - u8 LightLevel; -}; - -// CartRAMExpansion -- RAM expansion cart (DS browser, ...) -class CartRAMExpansion : public CartCommon -{ -public: - CartRAMExpansion(); - ~CartRAMExpansion() override; - - virtual u32 Type() const override { return CartType::RAMExpansion; } - - void Reset() override; - - void DoSavestate(Savestate* file) override; - - u16 ROMRead(u32 addr) const override; - void ROMWrite(u32 addr, u16 val) override; - -private: - u8 RAM[0x800000]; - u16 RAMEnable; -}; - -// possible inputs for GBA carts that might accept user input -enum -{ - Input_SolarSensorDown = 0, - Input_SolarSensorUp, -}; - -class GBACartSlot -{ -public: - GBACartSlot() noexcept = default; - ~GBACartSlot() noexcept = default; - void Reset() noexcept; - void DoSavestate(Savestate* file) noexcept; - /// Applies the GBACartData to the emulator state and unloads an existing ROM if any. - /// Upon successful insertion, \c cart will be nullptr and the global GBACart state - /// (\c CartROM, CartInserted, etc.) will be updated. - bool InsertROM(std::unique_ptr&& cart) noexcept; - bool LoadROM(const u8* romdata, u32 romlen) noexcept; - void LoadSave(const u8* savedata, u32 savelen) noexcept; - - void LoadAddon(int type) noexcept; - - void EjectCart() noexcept; - - // TODO: make more flexible, support nonbinary inputs - int SetInput(int num, bool pressed) noexcept; - - void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; } - - u16 ROMRead(u32 addr) const noexcept; - void ROMWrite(u32 addr, u16 val) noexcept; - - u8 SRAMRead(u32 addr) noexcept; - void SRAMWrite(u32 addr, u8 val) noexcept; - - /// This function is intended to allow frontends to save and load SRAM - /// without using melonDS APIs. - /// Modifying the emulated SRAM for any other reason is strongly discouraged. - /// The returned pointer may be invalidated if the emulator is reset, - /// or when a new game is loaded. - /// Consequently, don't store the returned pointer for any longer than necessary. - /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. - [[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } - [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } - - /// @returns The length of the buffer returned by ::GetSaveMemory() - /// if a cart is loaded and supports SRAM, otherwise zero. - [[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } -private: - std::unique_ptr Cart = nullptr; - u16 OpenBusDecay = 0; -}; - -/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass -/// that can be inserted into the emulator or used to extract information about the cart beforehand. -/// @param romdata The ROM data to parse. -/// The returned cartridge will contain a copy of this data, -/// so the caller may deallocate \c romdata after this function returns. -/// @param romlen The length of the ROM data in bytes. -/// @returns A \c GBACart::CartCommon object representing the parsed ROM, -/// or \c nullptr if the ROM data couldn't be parsed. -std::unique_ptr ParseROM(const u8* romdata, u32 romlen); - -} - -#endif // GBACART_H +/* + 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 GBACART_H +#define GBACART_H + +#include +#include "types.h" +#include "Savestate.h" + +namespace melonDS::GBACart +{ + +enum CartType +{ + Default = 0x001, + Game = 0x101, + GameSolarSensor = 0x102, + RAMExpansion = 0x201, +}; + +// CartCommon -- base code shared by all cart types +class CartCommon +{ +public: + virtual ~CartCommon() = default; + + [[nodiscard]] u32 Type() const { return CartType; } + virtual u32 Checksum() const { return 0; } + + virtual void Reset(); + + virtual void DoSavestate(Savestate* file); + + virtual int SetInput(int num, bool pressed); + + virtual u16 ROMRead(u32 addr) const; + virtual void ROMWrite(u32 addr, u16 val); + + virtual u8 SRAMRead(u32 addr); + virtual void SRAMWrite(u32 addr, u8 val); + + [[nodiscard]] virtual const u8* GetROM() const { return nullptr; } + [[nodiscard]] virtual u32 GetROMLength() const { return 0; } + + virtual u8* GetSaveMemory() const; + virtual u32 GetSaveMemoryLength() const; + virtual void SetSaveMemory(const u8* savedata, u32 savelen); +protected: + CartCommon(GBACart::CartType type); + friend class GBACartSlot; +private: + GBACart::CartType CartType; +}; + +// CartGame -- regular retail game cart (ROM, SRAM) +class CartGame : public CartCommon +{ +public: + CartGame(const u8* rom, u32 len, const u8* sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game); + CartGame(std::unique_ptr&& rom, u32 len, std::unique_ptr&& sram, u32 sramlen, GBACart::CartType type = GBACart::CartType::Game); + ~CartGame() override; + + u32 Checksum() const override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) const override; + void ROMWrite(u32 addr, u16 val) override; + + u8 SRAMRead(u32 addr) override; + void SRAMWrite(u32 addr, u8 val) override; + + [[nodiscard]] const u8* GetROM() const override { return ROM.get(); } + [[nodiscard]] u32 GetROMLength() const override { return ROMLength; } + + u8* GetSaveMemory() const override; + u32 GetSaveMemoryLength() const override; + void SetSaveMemory(const u8* savedata, u32 savelen) override; +protected: + virtual void ProcessGPIO(); + + u8 SRAMRead_EEPROM(u32 addr); + void SRAMWrite_EEPROM(u32 addr, u8 val); + u8 SRAMRead_FLASH(u32 addr); + void SRAMWrite_FLASH(u32 addr, u8 val); + u8 SRAMRead_SRAM(u32 addr); + void SRAMWrite_SRAM(u32 addr, u8 val); + + std::unique_ptr ROM; + u32 ROMLength; + + struct + { + u16 data; + u16 direction; + u16 control; + + } GPIO {}; + + enum SaveType + { + S_NULL, + S_EEPROM4K, + S_EEPROM64K, + S_SRAM256K, + S_FLASH512K, + S_FLASH1M + }; + + // from DeSmuME + struct + { + u8 state; + u8 cmd; + u8 device; + u8 manufacturer; + u8 bank; + + } SRAMFlashState {}; + + std::unique_ptr SRAM = nullptr; + u32 SRAMLength = 0; + SaveType SRAMType = S_NULL; +private: + void SetupSave(u32 type); +}; + +// CartGameSolarSensor -- Boktai game cart +class CartGameSolarSensor : public CartGame +{ +public: + CartGameSolarSensor(const u8* rom, u32 len, const u8* sram, u32 sramlen); + CartGameSolarSensor(std::unique_ptr&& rom, u32 len, std::unique_ptr&& sram, u32 sramlen); + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + int SetInput(int num, bool pressed) override; + +protected: + void ProcessGPIO() override; + +private: + static const int kLuxLevels[11]; + + bool LightEdge = false; + u8 LightCounter = 0; + u8 LightSample = 0; + u8 LightLevel = 0; +}; + +// CartRAMExpansion -- RAM expansion cart (DS browser, ...) +class CartRAMExpansion : public CartCommon +{ +public: + CartRAMExpansion(); + ~CartRAMExpansion() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + u16 ROMRead(u32 addr) const override; + void ROMWrite(u32 addr, u16 val) override; + +private: + u8 RAM[0x800000] {}; + u16 RAMEnable = 0; +}; + +// possible inputs for GBA carts that might accept user input +enum +{ + Input_SolarSensorDown = 0, + Input_SolarSensorUp, +}; + +class GBACartSlot +{ +public: + GBACartSlot(std::unique_ptr&& cart = nullptr) noexcept; + ~GBACartSlot() noexcept = default; + void Reset() noexcept; + void DoSavestate(Savestate* file) noexcept; + + /// Ejects the cart in the GBA slot (if any) + /// and inserts the given one. + /// + /// To insert a cart that does not require ROM data + /// (such as the RAM expansion pack), + /// create it manually with std::make_unique and pass it here. + /// + /// @param cart Movable \c unique_ptr to the GBA cart object. + /// May be \c nullptr, in which case the cart slot remains empty. + /// @post \c cart is \c nullptr and the underlying object + /// is moved into the cart slot. + void SetCart(std::unique_ptr&& cart) noexcept; + [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); } + [[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); } + + void LoadAddon(int type) noexcept; + + /// @return The cart that was in the cart slot if any, + /// or \c nullptr if the cart slot was empty. + std::unique_ptr EjectCart() noexcept; + + // TODO: make more flexible, support nonbinary inputs + int SetInput(int num, bool pressed) noexcept; + + void SetOpenBusDecay(u16 val) noexcept { OpenBusDecay = val; } + + u16 ROMRead(u32 addr) const noexcept; + void ROMWrite(u32 addr, u16 val) noexcept; + + u8 SRAMRead(u32 addr) noexcept; + void SRAMWrite(u32 addr, u8 val) noexcept; + + /// This function is intended to allow frontends to save and load SRAM + /// without using melonDS APIs. + /// Modifying the emulated SRAM for any other reason is strongly discouraged. + /// The returned pointer may be invalidated if the emulator is reset, + /// or when a new game is loaded. + /// Consequently, don't store the returned pointer for any longer than necessary. + /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. + [[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + + /// Sets the loaded cart's SRAM. + /// Does nothing if no cart is inserted + /// or the inserted cart doesn't support SRAM. + /// + /// @param savedata Buffer containing the raw contents of the SRAM. + /// The contents of this buffer are copied into the cart slot, + /// so the caller may dispose of it after this method returns. + /// @param savelen The length of the buffer in \c savedata, in bytes. + void SetSaveMemory(const u8* savedata, u32 savelen) noexcept; + + /// @returns The length of the buffer returned by ::GetSaveMemory() + /// if a cart is loaded and supports SRAM, otherwise zero. + [[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } +private: + std::unique_ptr Cart = nullptr; + u16 OpenBusDecay = 0; +}; + +/// Parses the given ROM data and constructs a \c GBACart::CartCommon subclass +/// that can be inserted into the emulator or used to extract information about the cart beforehand. +/// @param romdata The ROM data to parse. +/// The returned cartridge will contain a copy of this data, +/// so the caller may deallocate \c romdata after this function returns. +/// @param romlen The length of the ROM data in bytes. +/// @returns A \c GBACart::CartCommon object representing the parsed ROM, +/// or \c nullptr if the ROM data couldn't be parsed. +std::unique_ptr ParseROM(const u8* romdata, u32 romlen); +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen); +std::unique_ptr ParseROM(const u8* romdata, u32 romlen, const u8* sramdata, u32 sramlen); + +/// @param romdata The ROM data to parse. Will be moved-from. +/// @param romlen Length of romdata in bytes. +/// @param sramdata The save data to add to the cart. +/// May be \c nullptr, in which case the cart will have no save data. +/// @param sramlen Length of sramdata in bytes. +/// May be zero, in which case the cart will have no save data. +/// @return Unique pointer to the parsed GBA cart, +/// or \c nullptr if there was an error. +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::unique_ptr&& sramdata, u32 sramlen); + +} + +#endif // GBACART_H diff --git a/src/NDS.cpp b/src/NDS.cpp index 3aa4a52a..f3e5a1af 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -34,6 +34,7 @@ #include "AREngine.h" #include "Platform.h" #include "FreeBIOS.h" +#include "Args.h" #include "DSi.h" #include "DSi_SPI_TSC.h" @@ -74,16 +75,31 @@ const s32 kIterationCycleMargin = 8; NDS* NDS::Current = nullptr; -NDS::NDS(int type) noexcept : +NDS::NDS() noexcept : + NDS( + NDSArgs { + nullptr, + nullptr, + bios_arm9_bin, + bios_arm7_bin, + Firmware(0), + } + ) +{ +} + +NDS::NDS(NDSArgs&& args, int type) noexcept : ConsoleType(type), + ARM7BIOS(args.ARM7BIOS), + ARM9BIOS(args.ARM9BIOS), JIT(*this), SPU(*this), GPU(*this), - SPI(*this), + SPI(*this, std::move(args.Firmware)), RTC(*this), Wifi(*this), - NDSCartSlot(*this), - GBACartSlot(), + NDSCartSlot(*this, std::move(args.NDSROM)), + GBACartSlot(type == 1 ? nullptr : std::move(args.GBAROM)), AREngine(*this), ARM9(*this), ARM7(*this), @@ -238,7 +254,7 @@ bool NDS::NeedsDirectBoot() return true; // DSi/3DS firmwares aren't bootable - if (!SPI.GetFirmware()->IsBootable()) + if (!SPI.GetFirmware().IsBootable()) return true; return false; @@ -710,42 +726,27 @@ bool NDS::DoSavestate(Savestate* file) return true; } -bool NDS::LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) +void NDS::SetNDSCart(std::unique_ptr&& cart) { - if (!NDSCartSlot.LoadROM(romdata, romlen)) - return false; - - if (savedata && savelen) - NDSCartSlot.LoadSave(savedata, savelen); - - return true; + NDSCartSlot.SetCart(std::move(cart)); + // The existing cart will always be ejected; + // if cart is null, then that's equivalent to ejecting a cart + // without inserting a new one. } -void NDS::LoadSave(const u8* savedata, u32 savelen) +void NDS::SetNDSSave(const u8* savedata, u32 savelen) { if (savedata && savelen) - NDSCartSlot.LoadSave(savedata, savelen); + NDSCartSlot.SetSaveMemory(savedata, savelen); } -void NDS::EjectCart() +void NDS::SetGBASave(const u8* savedata, u32 savelen) { - NDSCartSlot.EjectCart(); -} + if (ConsoleType == 0 && savedata && savelen) + { + GBACartSlot.SetSaveMemory(savedata, savelen); + } -bool NDS::CartInserted() -{ - return NDSCartSlot.GetCart() != nullptr; -} - -bool NDS::LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen) -{ - if (!GBACartSlot.LoadROM(romdata, romlen)) - return false; - - if (savedata && savelen) - GBACartSlot.LoadSave(savedata, savelen); - - return true; } void NDS::LoadGBAAddon(int type) @@ -753,26 +754,11 @@ void NDS::LoadGBAAddon(int type) GBACartSlot.LoadAddon(type); } -void NDS::EjectGBACart() -{ - GBACartSlot.EjectCart(); -} - void NDS::LoadBIOS() { Reset(); } -bool NDS::IsLoadedARM9BIOSBuiltIn() -{ - return memcmp(ARM9BIOS, bios_arm9_bin, sizeof(NDS::ARM9BIOS)) == 0; -} - -bool NDS::IsLoadedARM7BIOSBuiltIn() -{ - return memcmp(ARM7BIOS, bios_arm7_bin, sizeof(NDS::ARM7BIOS)) == 0; -} - u64 NDS::NextTarget() { u64 minEvent = UINT64_MAX; @@ -2252,7 +2238,7 @@ bool NDS::ARM9GetMemRegion(u32 addr, bool write, MemRegion* region) if ((addr & 0xFFFFF000) == 0xFFFF0000 && !write) { - region->Mem = ARM9BIOS; + region->Mem = &ARM9BIOS[0]; region->Mask = 0xFFF; return true; } @@ -2700,7 +2686,7 @@ bool NDS::ARM7GetMemRegion(u32 addr, bool write, MemRegion* region) { if (ARM7.R[15] < 0x4000 && (addr >= ARM7BIOSProt || ARM7.R[15] < ARM7BIOSProt)) { - region->Mem = ARM7BIOS; + region->Mem = &ARM7BIOS[0]; region->Mask = 0x3FFF; return true; } diff --git a/src/NDS.h b/src/NDS.h index 0979b2fa..b9f82916 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include "Platform.h" @@ -37,6 +37,7 @@ #include "GPU.h" #include "ARMJIT.h" #include "DMA.h" +#include "FreeBIOS.h" // when touching the main loop/timing code, pls test a lot of shit // with this enabled, to make sure it doesn't desync @@ -44,7 +45,8 @@ namespace melonDS { - +struct NDSArgs; +class Firmware; enum { Event_LCD = 0, @@ -255,8 +257,8 @@ public: u8 ROMSeed0[2*8]; u8 ROMSeed1[2*8]; - u8 ARM9BIOS[0x1000]; - u8 ARM7BIOS[0x4000]; + std::array ARM9BIOS; + std::array ARM7BIOS; u16 ARM7BIOSProt; u8* MainRAM; @@ -303,25 +305,51 @@ public: void SetARM9RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); - // 0=DS 1=DSi - void SetConsoleType(int type); - void LoadBIOS(); - bool IsLoadedARM9BIOSBuiltIn(); - bool IsLoadedARM7BIOSBuiltIn(); + [[nodiscard]] bool IsLoadedARM9BIOSBuiltIn() const noexcept { return ARM9BIOS == bios_arm9_bin; } + [[nodiscard]] bool IsLoadedARM7BIOSBuiltIn() const noexcept { return ARM7BIOS == bios_arm7_bin; } - virtual bool LoadCart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); - void LoadSave(const u8* savedata, u32 savelen); - virtual void EjectCart(); - bool CartInserted(); + [[nodiscard]] NDSCart::CartCommon* GetNDSCart() { return NDSCartSlot.GetCart(); } + [[nodiscard]] const NDSCart::CartCommon* GetNDSCart() const { return NDSCartSlot.GetCart(); } + virtual void SetNDSCart(std::unique_ptr&& cart); + [[nodiscard]] bool CartInserted() const noexcept { return NDSCartSlot.GetCart() != nullptr; } + virtual std::unique_ptr EjectCart() { return NDSCartSlot.EjectCart(); } + + [[nodiscard]] u8* GetNDSSave() { return NDSCartSlot.GetSaveMemory(); } + [[nodiscard]] const u8* GetNDSSave() const { return NDSCartSlot.GetSaveMemory(); } + [[nodiscard]] u32 GetNDSSaveLength() const { return NDSCartSlot.GetSaveMemoryLength(); } + void SetNDSSave(const u8* savedata, u32 savelen); + + const Firmware& GetFirmware() const { return SPI.GetFirmwareMem()->GetFirmware(); } + Firmware& GetFirmware() { return SPI.GetFirmwareMem()->GetFirmware(); } + void SetFirmware(Firmware&& firmware) { SPI.GetFirmwareMem()->SetFirmware(std::move(firmware)); } virtual bool NeedsDirectBoot(); void SetupDirectBoot(const std::string& romname); virtual void SetupDirectBoot(); - bool LoadGBACart(const u8* romdata, u32 romlen, const u8* savedata, u32 savelen); + [[nodiscard]] GBACart::CartCommon* GetGBACart() { return (ConsoleType == 1) ? nullptr : GBACartSlot.GetCart(); } + [[nodiscard]] const GBACart::CartCommon* GetGBACart() const { return (ConsoleType == 1) ? nullptr : GBACartSlot.GetCart(); } + + /// Inserts a GBA cart into the emulated console's Slot-2. + /// + /// @param cart The GBA cart, most likely (but not necessarily) returned from GBACart::ParseROM. + /// To insert an accessory that doesn't use a ROM image + /// (e.g. the Expansion Pak), create it manually and pass it here. + /// If \c nullptr, the existing cart is ejected. + /// If this is a DSi, this method does nothing. + /// + /// @post \c cart is \c nullptr and this NDS takes ownership + /// of the cart object it held, if any. + void SetGBACart(std::unique_ptr&& cart) { if (ConsoleType == 0) GBACartSlot.SetCart(std::move(cart)); } + + u8* GetGBASave() { return GBACartSlot.GetSaveMemory(); } + const u8* GetGBASave() const { return GBACartSlot.GetSaveMemory(); } + u32 GetGBASaveLength() const { return GBACartSlot.GetSaveMemoryLength(); } + void SetGBASave(const u8* savedata, u32 savelen); + void LoadGBAAddon(int type); - void EjectGBACart(); + std::unique_ptr EjectGBACart() { return GBACartSlot.EjectCart(); } u32 RunFrame(); @@ -456,7 +484,8 @@ private: template u32 RunFrame(); public: - NDS() noexcept : NDS(0) {} + NDS(NDSArgs&& args) noexcept : NDS(std::move(args), 0) {} + NDS() noexcept; virtual ~NDS() noexcept; NDS(const NDS&) = delete; NDS& operator=(const NDS&) = delete; @@ -465,7 +494,7 @@ public: // The frontend should set and unset this manually after creating and destroying the NDS object. [[deprecated("Temporary workaround until JIT code generation is revised to accommodate multiple NDS objects.")]] static NDS* Current; protected: - explicit NDS(int type) noexcept; + explicit NDS(NDSArgs&& args, int type) noexcept; virtual void DoSavestateExtra(Savestate* file) {} }; diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 95306fc5..848c6197 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -20,12 +20,12 @@ #include "NDS.h" #include "DSi.h" #include "NDSCart.h" -#include "ARM.h" #include "CRC32.h" #include "Platform.h" #include "ROMList.h" #include "melonDLDI.h" -#include "xxhash/xxhash.h" +#include "FATStorage.h" +#include "Utils.h" namespace melonDS { @@ -43,7 +43,7 @@ enum // SRAM TODO: emulate write delays??? -u32 ByteSwap(u32 val) +constexpr u32 ByteSwap(u32 val) { return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); } @@ -173,27 +173,29 @@ void NDSCartSlot::Key2_Encrypt(u8* data, u32 len) noexcept } -CartCommon::CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) +CartCommon::CartCommon(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, melonDS::NDSCart::CartType type) : + CartCommon(CopyToUnique(rom, len), len, chipid, badDSiDump, romparams, type) { - ROM = rom; - ROMLength = len; - ChipID = chipid; - ROMParams = romparams; +} - memcpy(&Header, rom, sizeof(Header)); +CartCommon::CartCommon(std::unique_ptr&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, melonDS::NDSCart::CartType type) : + ROM(std::move(rom)), + ROMLength(len), + ChipID(chipid), + ROMParams(romparams), + CartType(type) +{ + memcpy(&Header, ROM.get(), sizeof(Header)); IsDSi = Header.IsDSi() && !badDSiDump; DSiBase = Header.DSiRegionStart << 19; } -CartCommon::~CartCommon() -{ - delete[] ROM; -} +CartCommon::~CartCommon() = default; u32 CartCommon::Checksum() const { const NDSHeader& header = GetHeader(); - u32 crc = CRC32(ROM, 0x40); + u32 crc = CRC32(ROM.get(), 0x40); crc = CRC32(&ROM[header.ARM9ROMOffset], header.ARM9Size, crc); crc = CRC32(&ROM[header.ARM7ROMOffset], header.ARM7Size, crc); @@ -230,14 +232,6 @@ void CartCommon::DoSavestate(Savestate* file) file->Bool32(&DSiMode); } -void CartCommon::SetupSave(u32 type) -{ -} - -void CartCommon::LoadSave(const u8* savedata, u32 savelen) -{ -} - int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { if (CmdEncMode == 0) @@ -267,7 +261,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da case 0x3C: CmdEncMode = 1; - cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.ARM7BIOS, sizeof(NDS::ARM7BIOS)); + cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, &nds.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); DSiMode = false; return 0; @@ -276,7 +270,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da { auto& dsi = static_cast(nds); CmdEncMode = 1; - cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, dsi.ARM7iBIOS, sizeof(DSi::ARM7iBIOS)); + cartslot.Key1_InitKeycode(true, *(u32*)&ROM[0xC], 1, 2, &dsi.ARM7iBIOS[0], sizeof(DSi::ARM7iBIOS)); DSiMode = true; } return 0; @@ -360,23 +354,13 @@ u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last) return 0xFF; } -u8 *CartCommon::GetSaveMemory() const -{ - return nullptr; -} - -u32 CartCommon::GetSaveMemoryLength() const -{ - return 0; -} - void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) { if (addr >= ROMLength) return; if ((addr+len) > ROMLength) len = ROMLength - addr; - memcpy(data+offset, ROM+addr, len); + memcpy(data+offset, ROM.get()+addr, len); } const NDSBanner* CartCommon::Banner() const @@ -385,22 +369,64 @@ const NDSBanner* CartCommon::Banner() const size_t bannersize = header.IsDSi() ? 0x23C0 : 0xA40; if (header.BannerOffset >= 0x200 && header.BannerOffset < (ROMLength - bannersize)) { - return reinterpret_cast(ROM + header.BannerOffset); + return reinterpret_cast(ROM.get() + header.BannerOffset); } return nullptr; } -CartRetail::CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams) : CartCommon(rom, len, chipid, badDSiDump, romparams) +CartRetail::CartRetail(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen, melonDS::NDSCart::CartType type) : + CartRetail(CopyToUnique(rom, len), len, chipid, badDSiDump, romparams, std::move(sram), sramlen, type) { - SRAM = nullptr; } -CartRetail::~CartRetail() +CartRetail::CartRetail(std::unique_ptr&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen, melonDS::NDSCart::CartType type) : + CartCommon(std::move(rom), len, chipid, badDSiDump, romparams, type) { - if (SRAM) delete[] SRAM; + u32 savememtype = ROMParams.SaveMemType <= 10 ? ROMParams.SaveMemType : 0; + constexpr int sramlengths[] = + { + 0, + 512, + 8192, 65536, 128*1024, + 256*1024, 512*1024, 1024*1024, + 8192*1024, 16384*1024, 65536*1024 + }; + SRAMLength = sramlengths[savememtype]; + + if (SRAMLength) + { // If this cart should have any save data... + if (sram && sramlen == SRAMLength) + { // If we were given save data that already has the correct length... + SRAM = std::move(sram); + } + else + { // Copy in what we can, truncate the rest. + SRAM = std::make_unique(SRAMLength); + memset(SRAM.get(), 0xFF, SRAMLength); + memcpy(SRAM.get(), sram.get(), std::min(sramlen, SRAMLength)); + } + } + + switch (savememtype) + { + case 1: SRAMType = 1; break; // EEPROM, small + case 2: + case 3: + case 4: SRAMType = 2; break; // EEPROM, regular + case 5: + case 6: + case 7: SRAMType = 3; break; // FLASH + case 8: + case 9: + case 10: SRAMType = 4; break; // NAND + default: SRAMType = 0; break; // ...whatever else + } } +CartRetail::~CartRetail() = default; +// std::unique_ptr cleans up the SRAM and ROM + void CartRetail::Reset() { CartCommon::Reset(); @@ -425,13 +451,11 @@ void CartRetail::DoSavestate(Savestate* file) Log(LogLevel::Warn, "savestate: VERY BAD!!!! SRAM LENGTH DIFFERENT. %d -> %d\n", oldlen, SRAMLength); Log(LogLevel::Warn, "oh well. loading it anyway. adsfgdsf\n"); - if (oldlen) delete[] SRAM; - SRAM = nullptr; - if (SRAMLength) SRAM = new u8[SRAMLength]; + SRAM = SRAMLength ? std::make_unique(SRAMLength) : nullptr; } if (SRAMLength) { - file->VarArray(SRAM, SRAMLength); + file->VarArray(SRAM.get(), SRAMLength); } // SPI status shito @@ -441,53 +465,15 @@ void CartRetail::DoSavestate(Savestate* file) file->Var8(&SRAMStatus); if ((!file->Saving) && SRAM) - Platform::WriteNDSSave(SRAM, SRAMLength, 0, SRAMLength); + Platform::WriteNDSSave(SRAM.get(), SRAMLength, 0, SRAMLength); } -void CartRetail::SetupSave(u32 type) -{ - if (SRAM) delete[] SRAM; - SRAM = nullptr; - - if (type > 10) type = 0; - int sramlen[] = - { - 0, - 512, - 8192, 65536, 128*1024, - 256*1024, 512*1024, 1024*1024, - 8192*1024, 16384*1024, 65536*1024 - }; - SRAMLength = sramlen[type]; - - if (SRAMLength) - { - SRAM = new u8[SRAMLength]; - memset(SRAM, 0xFF, SRAMLength); - } - - switch (type) - { - case 1: SRAMType = 1; break; // EEPROM, small - case 2: - case 3: - case 4: SRAMType = 2; break; // EEPROM, regular - case 5: - case 6: - case 7: SRAMType = 3; break; // FLASH - case 8: - case 9: - case 10: SRAMType = 4; break; // NAND - default: SRAMType = 0; break; // ...whatever else - } -} - -void CartRetail::LoadSave(const u8* savedata, u32 savelen) +void CartRetail::SetSaveMemory(const u8* savedata, u32 savelen) { if (!SRAM) return; u32 len = std::min(savelen, SRAMLength); - memcpy(SRAM, savedata, len); + memcpy(SRAM.get(), savedata, len); Platform::WriteNDSSave(savedata, len, 0, len); } @@ -551,16 +537,6 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last) } } -u8 *CartRetail::GetSaveMemory() const -{ - return SRAM; -} - -u32 CartRetail::GetSaveMemoryLength() const -{ - return SRAMLength; -} - void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) { addr &= (ROMLength-1); @@ -578,7 +554,7 @@ void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) addr = 0x8000 + (addr & 0x1FF); } - memcpy(data+offset, ROM+addr, len); + memcpy(data+offset, ROM.get()+addr, len); } u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) @@ -613,7 +589,7 @@ u8 CartRetail::SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, (SRAMFirstAddr + ((SRAMCmd==0x0A)?0x100:0)) & 0x1FF, SRAMAddr-SRAMFirstAddr); } return 0; @@ -677,7 +653,7 @@ u8 CartRetail::SRAMWrite_EEPROM(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -734,7 +710,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -771,7 +747,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -817,7 +793,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -840,7 +816,7 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) if (last) { SRAMStatus &= ~(1<<1); - Platform::WriteNDSSave(SRAM, SRAMLength, + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMFirstAddr & (SRAMLength-1), SRAMAddr-SRAMFirstAddr); } return 0; @@ -852,15 +828,19 @@ u8 CartRetail::SRAMWrite_FLASH(u8 val, u32 pos, bool last) } } - -CartRetailNAND::CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams) +CartRetailNAND::CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetailNAND(CopyToUnique(rom, len), len, chipid, romparams, std::move(sram), sramlen) { } -CartRetailNAND::~CartRetailNAND() +CartRetailNAND::CartRetailNAND(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetail(std::move(rom), len, chipid, false, romparams, std::move(sram), sramlen, CartType::RetailNAND) { + BuildSRAMID(); } +CartRetailNAND::~CartRetailNAND() = default; + void CartRetailNAND::Reset() { CartRetail::Reset(); @@ -889,9 +869,9 @@ void CartRetailNAND::DoSavestate(Savestate* file) BuildSRAMID(); } -void CartRetailNAND::LoadSave(const u8* savedata, u32 savelen) +void CartRetailNAND::SetSaveMemory(const u8* savedata, u32 savelen) { - CartRetail::LoadSave(savedata, savelen); + CartRetail::SetSaveMemory(savedata, savelen); BuildSRAMID(); } @@ -924,7 +904,7 @@ int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8 if (SRAMLength && SRAMAddr < (SRAMBase+SRAMLength-0x20000)) { memcpy(&SRAM[SRAMAddr - SRAMBase], SRAMWriteBuffer, 0x800); - Platform::WriteNDSSave(SRAM, SRAMLength, SRAMAddr - SRAMBase, 0x800); + Platform::WriteNDSSave(SRAM.get(), SRAMLength, SRAMAddr - SRAMBase, 0x800); } SRAMAddr = 0; @@ -1080,15 +1060,28 @@ void CartRetailNAND::BuildSRAMID() } -CartRetailIR::CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams) : CartRetail(rom, len, chipid, badDSiDump, romparams) +CartRetailIR::CartRetailIR(const u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetailIR(CopyToUnique(rom, len), len, chipid, irversion, badDSiDump, romparams, std::move(sram), sramlen) { - IRVersion = irversion; } -CartRetailIR::~CartRetailIR() +CartRetailIR::CartRetailIR( + std::unique_ptr&& rom, + u32 len, + u32 chipid, + u32 irversion, + bool badDSiDump, + ROMListEntry romparams, + std::unique_ptr&& sram, + u32 sramlen +) : + CartRetail(std::move(rom), len, chipid, badDSiDump, romparams, std::move(sram), sramlen, CartType::RetailIR), + IRVersion(irversion) { } +CartRetailIR::~CartRetailIR() = default; + void CartRetailIR::Reset() { CartRetail::Reset(); @@ -1125,25 +1118,18 @@ u8 CartRetailIR::SPIWrite(u8 val, u32 pos, bool last) return 0; } +CartRetailBT::CartRetailBT(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetailBT(CopyToUnique(rom, len), len, chipid, romparams, std::move(sram), sramlen) +{ +} -CartRetailBT::CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartRetail(rom, len, chipid, false, romparams) +CartRetailBT::CartRetailBT(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen) : + CartRetail(std::move(rom), len, chipid, false, romparams, std::move(sram), sramlen, CartType::RetailBT) { Log(LogLevel::Info,"POKETYPE CART\n"); } -CartRetailBT::~CartRetailBT() -{ -} - -void CartRetailBT::Reset() -{ - CartRetail::Reset(); -} - -void CartRetailBT::DoSavestate(Savestate* file) -{ - CartRetail::DoSavestate(file); -} +CartRetailBT::~CartRetailBT() = default; u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) { @@ -1159,50 +1145,30 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) return 0; } - -CartHomebrew::CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams) : CartCommon(rom, len, chipid, false, romparams) +CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartHomebrew(CopyToUnique(rom, len), len, chipid, romparams, std::move(sdcard)) { - SD = nullptr; } -CartHomebrew::~CartHomebrew() +CartHomebrew::CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartCommon(std::move(rom), len, chipid, false, romparams, CartType::Homebrew), + SD(std::move(sdcard)) { - if (SD) - { - SD->Close(); - delete SD; - } + sdcard = std::nullopt; + // std::move on optionals usually results in an optional with a moved-from object } +CartHomebrew::~CartHomebrew() = default; +// The SD card is destroyed by the optional's destructor + void CartHomebrew::Reset() { CartCommon::Reset(); - ReadOnly = Platform::GetConfigBool(Platform::DLDI_ReadOnly); - if (SD) { - SD->Close(); - delete SD; + ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly()); } - - if (Platform::GetConfigBool(Platform::DLDI_Enable)) - { - std::string folderpath; - if (Platform::GetConfigBool(Platform::DLDI_FolderSync)) - folderpath = Platform::GetConfigString(Platform::DLDI_FolderPath); - else - folderpath = ""; - - ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), ReadOnly); - SD = new FATStorage(Platform::GetConfigString(Platform::DLDI_ImagePath), - (u64)Platform::GetConfigInt(Platform::DLDI_ImageSize) * 1024 * 1024, - ReadOnly, - folderpath); - SD->Open(); - } - else - SD = nullptr; } void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) @@ -1213,7 +1179,7 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) { // add the ROM to the SD volume - if (!SD->InjectFile(romname, ROM, ROMLength)) + if (!SD->InjectFile(romname, ROM.get(), ROMLength)) return; // setup argv command line @@ -1240,11 +1206,6 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) } } -void CartHomebrew::DoSavestate(Savestate* file) -{ - CartCommon::DoSavestate(file); -} - int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) { if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); @@ -1293,7 +1254,7 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len) case 0xC1: { u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - if (SD && (!ReadOnly)) SD->WriteSectors(sector, len>>9, data); + if (SD && !SD->IsReadOnly()) SD->WriteSectors(sector, len>>9, data); } break; @@ -1439,17 +1400,20 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) addr &= (ROMLength-1); - memcpy(data+offset, ROM+addr, len); + memcpy(data+offset, ROM.get()+addr, len); } -NDSCartSlot::NDSCartSlot(melonDS::NDS& nds) noexcept : NDS(nds) +NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom) noexcept : NDS(nds) { NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_PrepareData, MemberEventFunc(NDSCartSlot, ROMPrepareData)); NDS.RegisterEventFunc(Event_ROMTransfer, ROMTransfer_End, MemberEventFunc(NDSCartSlot, ROMEndTransfer)); NDS.RegisterEventFunc(Event_ROMSPITransfer, 0, MemberEventFunc(NDSCartSlot, SPITransferDone)); // All fields are default-constructed because they're listed as such in the class declaration + + if (rom) + SetCart(std::move(rom)); } NDSCartSlot::~NDSCartSlot() noexcept @@ -1569,16 +1533,13 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept memcpy(out, &cartrom[arm9base], 0x800); - Key1_InitKeycode(false, gamecode, 2, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, gamecode, 2, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); Key1_Decrypt((u32*)&out[0]); - Key1_InitKeycode(false, gamecode, 3, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, gamecode, 3, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); for (u32 i = 0; i < 0x800; i += 8) Key1_Decrypt((u32*)&out[i]); - XXH64_hash_t hash = XXH64(out, 0x800, 0); - Log(LogLevel::Debug, "Secure area post-decryption xxh64 hash: %zx\n", hash); - if (!strncmp((const char*)out, "encryObj", 8)) { Log(LogLevel::Info, "Secure area decryption OK\n"); @@ -1593,7 +1554,12 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept } } -std::unique_ptr ParseROM(const u8* romdata, u32 romlen) +std::unique_ptr ParseROM(const u8* romdata, u32 romlen, std::optional&& args) +{ + return ParseROM(CopyToUnique(romdata, romlen), romlen, std::move(args)); +} + +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::optional&& args) { if (romdata == nullptr) { @@ -1607,28 +1573,10 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen) return nullptr; } - u32 cartromsize = 0x200; - while (cartromsize < romlen) - cartromsize <<= 1; // ROM size must be a power of 2 - - u8* cartrom = nullptr; - try - { - cartrom = new u8[cartromsize]; - } - catch (const std::bad_alloc& e) - { - Log(LogLevel::Error, "NDSCart: failed to allocate memory for ROM (%d bytes)\n", cartromsize); - - return nullptr; - } - - // copy romdata into cartrom then zero out the remaining space - memcpy(cartrom, romdata, romlen); - memset(cartrom + romlen, 0, cartromsize - romlen); + auto [cartrom, cartromsize] = PadToPowerOf2(std::move(romdata), romlen); NDSHeader header {}; - memcpy(&header, cartrom, sizeof(header)); + memcpy(&header, cartrom.get(), sizeof(header)); bool dsi = header.IsDSi(); bool badDSiDump = false; @@ -1694,30 +1642,24 @@ std::unique_ptr ParseROM(const u8* romdata, u32 romlen) } std::unique_ptr cart; + auto [sram, sramlen] = args ? std::move(*args->SRAM) : std::make_pair(nullptr, 0); if (homebrew) - cart = std::make_unique(cartrom, cartromsize, cartid, romparams); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, args ? std::move(args->SDCard) : std::nullopt); else if (cartid & 0x08000000) - cart = std::make_unique(cartrom, cartromsize, cartid, romparams); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen); else if (irversion != 0) - cart = std::make_unique(cartrom, cartromsize, cartid, irversion, badDSiDump, romparams); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, irversion, badDSiDump, romparams, std::move(sram), sramlen); else if ((gamecode & 0xFFFFFF) == 0x505A55) // UZPx - cart = std::make_unique(cartrom, cartromsize, cartid, romparams); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen); else - cart = std::make_unique(cartrom, cartromsize, cartid, badDSiDump, romparams); - - if (romparams.SaveMemType > 0) - cart->SetupSave(romparams.SaveMemType); + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, badDSiDump, romparams, std::move(sram), sramlen); + args = std::nullopt; return cart; } -bool NDSCartSlot::InsertROM(std::unique_ptr&& cart) noexcept +void NDSCartSlot::SetCart(std::unique_ptr&& cart) noexcept { - if (!cart) { - Log(LogLevel::Error, "Failed to insert invalid cart; existing cart (if any) was not ejected.\n"); - return false; - } - if (Cart) EjectCart(); @@ -1725,6 +1667,10 @@ bool NDSCartSlot::InsertROM(std::unique_ptr&& cart) noexcept // and cloning polymorphic objects without knowing the underlying type is annoying. Cart = std::move(cart); + if (!Cart) + // If we're ejecting an existing cart without inserting a new one... + return; + Cart->Reset(); const NDSHeader& header = Cart->GetHeader(); @@ -1739,11 +1685,11 @@ bool NDSCartSlot::InsertROM(std::unique_ptr&& cart) noexcept strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8); - Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, romparams.GameCode, 3, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); for (u32 i = 0; i < 0x800; i += 8) Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]); - Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.ARM7BIOS, sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, romparams.GameCode, 2, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]); Log(LogLevel::Debug, "Re-encrypted cart secure area\n"); @@ -1757,21 +1703,12 @@ bool NDSCartSlot::InsertROM(std::unique_ptr&& cart) noexcept Log(LogLevel::Info, "Inserted cart with game code: %.4s\n", header.GameCode); Log(LogLevel::Info, "Inserted cart with ID: %08X\n", Cart->ID()); Log(LogLevel::Info, "ROM entry: %08X %08X\n", romparams.ROMSize, romparams.SaveMemType); - - return true; } -bool NDSCartSlot::LoadROM(const u8* romdata, u32 romlen) noexcept -{ - std::unique_ptr cart = ParseROM(romdata, romlen); - - return InsertROM(std::move(cart)); -} - -void NDSCartSlot::LoadSave(const u8* savedata, u32 savelen) noexcept +void NDSCartSlot::SetSaveMemory(const u8* savedata, u32 savelen) noexcept { if (Cart) - Cart->LoadSave(savedata, savelen); + Cart->SetSaveMemory(savedata, savelen); } void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept @@ -1780,15 +1717,15 @@ void NDSCartSlot::SetupDirectBoot(const std::string& romname) noexcept Cart->SetupDirectBoot(romname, NDS); } -void NDSCartSlot::EjectCart() noexcept +std::unique_ptr NDSCartSlot::EjectCart() noexcept { - if (!Cart) return; + if (!Cart) return nullptr; // ejecting the cart triggers the gamecard IRQ NDS.SetIRQ(0, IRQ_CartIREQMC); NDS.SetIRQ(1, IRQ_CartIREQMC); - Cart = nullptr; + return std::move(Cart); // CHECKME: does an eject imply anything for the ROM/SPI transfer registers? } diff --git a/src/NDSCart.h b/src/NDSCart.h index 24f9f9ee..43bf1fc9 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include "types.h" #include "Savestate.h" @@ -49,14 +49,32 @@ enum CartType class NDSCartSlot; +/// Arguments used to create and populate an NDS cart of unknown type. +/// Different carts take different subsets of these arguments, +/// but we won't know which ones to use +/// until we parse the header at runtime. +struct NDSCartArgs +{ + /// The arguments used to load a homebrew SD card image. + /// If \c nullopt, then the cart will not have an SD card. + /// Ignored for retail ROMs. + std::optional SDCard = std::nullopt; + + /// Save RAM to load into the cartridge. + /// If \c nullopt, then the cart's SRAM buffer will be empty. + /// Ignored for homebrew ROMs. + std::optional, u32>> SRAM = std::nullopt; +}; + // CartCommon -- base code shared by all cart types class CartCommon { public: - CartCommon(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams); + CartCommon(const u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, CartType type); + CartCommon(std::unique_ptr&& rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams, CartType type); virtual ~CartCommon(); - virtual u32 Type() const = 0; + [[nodiscard]] u32 Type() const { return CartType; }; [[nodiscard]] u32 Checksum() const; virtual void Reset(); @@ -64,16 +82,16 @@ public: virtual void DoSavestate(Savestate* file); - virtual void SetupSave(u32 type); - virtual void LoadSave(const u8* savedata, u32 savelen); virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len); virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len); virtual u8 SPIWrite(u8 val, u32 pos, bool last); - virtual u8* GetSaveMemory() const; - virtual u32 GetSaveMemoryLength() const; + virtual u8* GetSaveMemory() { return nullptr; } + virtual const u8* GetSaveMemory() const { return nullptr; } + virtual u32 GetSaveMemoryLength() const { return 0; } + virtual void SetSaveMemory(const u8* savedata, u32 savelen) {}; [[nodiscard]] const NDSHeader& GetHeader() const { return Header; } [[nodiscard]] NDSHeader& GetHeader() { return Header; } @@ -82,48 +100,65 @@ public: [[nodiscard]] const NDSBanner* Banner() const; [[nodiscard]] const ROMListEntry& GetROMParams() const { return ROMParams; }; [[nodiscard]] u32 ID() const { return ChipID; } - [[nodiscard]] const u8* GetROM() const { return ROM; } + [[nodiscard]] const u8* GetROM() const { return ROM.get(); } [[nodiscard]] u32 GetROMLength() const { return ROMLength; } protected: void ReadROM(u32 addr, u32 len, u8* data, u32 offset); - u8* ROM; - u32 ROMLength; - u32 ChipID; - bool IsDSi; - bool DSiMode; - u32 DSiBase; + std::unique_ptr ROM = nullptr; + u32 ROMLength = 0; + u32 ChipID = 0; + bool IsDSi = false; + bool DSiMode = false; + u32 DSiBase = 0; - u32 CmdEncMode; - u32 DataEncMode; + u32 CmdEncMode = 0; + u32 DataEncMode = 0; // Kept separate from the ROM data so we can decrypt the modcrypt area // without touching the overall ROM data - NDSHeader Header; - ROMListEntry ROMParams; + NDSHeader Header {}; + ROMListEntry ROMParams {}; + const melonDS::NDSCart::CartType CartType = Default; }; // CartRetail -- regular retail cart (ROM, SPI SRAM) class CartRetail : public CartCommon { public: - CartRetail(u8* rom, u32 len, u32 chipid, bool badDSiDump, ROMListEntry romparams); - virtual ~CartRetail() override; + CartRetail( + const u8* rom, + u32 len, + u32 chipid, + bool badDSiDump, + ROMListEntry romparams, + std::unique_ptr&& sram, + u32 sramlen, + melonDS::NDSCart::CartType type = CartType::Retail + ); + CartRetail( + std::unique_ptr&& rom, + u32 len, u32 chipid, + bool badDSiDump, + ROMListEntry romparams, + std::unique_ptr&& sram, + u32 sramlen, + melonDS::NDSCart::CartType type = CartType::Retail + ); + ~CartRetail() override; - virtual u32 Type() const override { return CartType::Retail; } + void Reset() override; - virtual void Reset() override; + void DoSavestate(Savestate* file) override; - virtual void DoSavestate(Savestate* file) override; + void SetSaveMemory(const u8* savedata, u32 savelen) override; - virtual void SetupSave(u32 type) override; - virtual void LoadSave(const u8* savedata, u32 savelen) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; - virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; + u8 SPIWrite(u8 val, u32 pos, bool last) override; - virtual u8 SPIWrite(u8 val, u32 pos, bool last) override; - - virtual u8* GetSaveMemory() const override; - virtual u32 GetSaveMemoryLength() const override; + u8* GetSaveMemory() override { return SRAM.get(); } + const u8* GetSaveMemory() const override { return SRAM.get(); } + u32 GetSaveMemoryLength() const override { return SRAMLength; } protected: void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); @@ -132,30 +167,29 @@ protected: u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last); u8 SRAMWrite_FLASH(u8 val, u32 pos, bool last); - u8* SRAM; - u32 SRAMLength; - u32 SRAMType; + std::unique_ptr SRAM = nullptr; + u32 SRAMLength = 0; + u32 SRAMType = 0; - u8 SRAMCmd; - u32 SRAMAddr; - u32 SRAMFirstAddr; - u8 SRAMStatus; + u8 SRAMCmd = 0; + u32 SRAMAddr = 0; + u32 SRAMFirstAddr = 0; + u8 SRAMStatus = 0; }; // CartRetailNAND -- retail cart with NAND SRAM (WarioWare DIY, Jam with the Band, ...) class CartRetailNAND : public CartRetail { public: - CartRetailNAND(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); + CartRetailNAND(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); + CartRetailNAND(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); ~CartRetailNAND() override; - virtual u32 Type() const override { return CartType::RetailNAND; } - void Reset() override; void DoSavestate(Savestate* file) override; - void LoadSave(const u8* savedata, u32 savelen) override; + void SetSaveMemory(const u8* savedata, u32 savelen) override; int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; @@ -165,22 +199,21 @@ public: private: void BuildSRAMID(); - u32 SRAMBase; - u32 SRAMWindow; + u32 SRAMBase = 0; + u32 SRAMWindow = 0; - u8 SRAMWriteBuffer[0x800]; - u32 SRAMWritePos; + u8 SRAMWriteBuffer[0x800] {}; + u32 SRAMWritePos = 0; }; // CartRetailIR -- SPI IR device and SRAM class CartRetailIR : public CartRetail { public: - CartRetailIR(u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams); + CartRetailIR(const u8* rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); + CartRetailIR(std::unique_ptr&& rom, u32 len, u32 chipid, u32 irversion, bool badDSiDump, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); ~CartRetailIR() override; - virtual u32 Type() const override { return CartType::RetailIR; } - void Reset() override; void DoSavestate(Savestate* file) override; @@ -188,23 +221,18 @@ public: u8 SPIWrite(u8 val, u32 pos, bool last) override; private: - u32 IRVersion; - u8 IRCmd; + u32 IRVersion = 0; + u8 IRCmd = 0; }; // CartRetailBT - Pok�mon Typing Adventure (SPI BT controller) class CartRetailBT : public CartRetail { public: - CartRetailBT(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); + CartRetailBT(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); + CartRetailBT(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::unique_ptr&& sram, u32 sramlen); ~CartRetailBT() override; - virtual u32 Type() const override { return CartType::RetailBT; } - - void Reset() override; - - void DoSavestate(Savestate* file) override; - u8 SPIWrite(u8 val, u32 pos, bool last) override; }; @@ -212,32 +240,38 @@ public: class CartHomebrew : public CartCommon { public: - CartHomebrew(u8* rom, u32 len, u32 chipid, ROMListEntry romparams); + CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); + CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); ~CartHomebrew() override; - virtual u32 Type() const override { return CartType::Homebrew; } - void Reset() override; void SetupDirectBoot(const std::string& romname, NDS& nds) override; - void DoSavestate(Savestate* file) override; - int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; + [[nodiscard]] const std::optional& GetSDCard() const noexcept { return SD; } + void SetSDCard(FATStorage&& sdcard) noexcept { SD = std::move(sdcard); } + void SetSDCard(std::optional&& sdcard) noexcept + { + SD = std::move(sdcard); + sdcard = std::nullopt; + // moving from an optional doesn't set it to nullopt, + // it just leaves behind an optional with a moved-from value + } + private: void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly); void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly); void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); - FATStorage* SD; - bool ReadOnly; + std::optional SD {}; }; class NDSCartSlot { public: - NDSCartSlot(melonDS::NDS& nds) noexcept; + explicit NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom = nullptr) noexcept; ~NDSCartSlot() noexcept; void Reset() noexcept; void ResetCart() noexcept; @@ -252,25 +286,16 @@ public: /// If the provided cart is not valid, /// then the currently-loaded ROM will not be ejected. /// - /// @param cart Movable reference to the cart. - /// @returns \c true if the cart was successfully loaded, - /// \c false otherwise. + /// @param cart Movable reference to the cart, + /// or \c nullptr to eject the cart. /// @post If the cart was successfully loaded, /// then \c cart will be \c nullptr /// and \c Cart will contain the object that \c cart previously pointed to. /// Otherwise, \c cart and \c Cart will be both be unchanged. - bool InsertROM(std::unique_ptr&& cart) noexcept; + void SetCart(std::unique_ptr&& cart) noexcept; + [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); } + [[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); } - /// Parses a ROM image and loads it into the emulator. - /// This function is equivalent to calling ::ParseROM() and ::InsertROM() in sequence. - /// @param romdata Pointer to the ROM image. - /// The cart emulator maintains its own copy of this data, - /// so the caller is free to discard this data after calling this function. - /// @param romlen The length of the ROM image, in bytes. - /// @returns \c true if the ROM image was successfully loaded, - /// \c false if not. - bool LoadROM(const u8* romdata, u32 romlen) noexcept; - void LoadSave(const u8* savedata, u32 savelen) noexcept; void SetupDirectBoot(const std::string& romname) noexcept; /// This function is intended to allow frontends to save and load SRAM @@ -282,11 +307,15 @@ public: /// @returns Pointer to this cart's SRAM if a cart is loaded and supports SRAM, otherwise \c nullptr. [[nodiscard]] const u8* GetSaveMemory() const noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } [[nodiscard]] u8* GetSaveMemory() noexcept { return Cart ? Cart->GetSaveMemory() : nullptr; } + void SetSaveMemory(const u8* savedata, u32 savelen) noexcept; /// @returns The length of the buffer returned by ::GetSaveMemory() /// if a cart is loaded and supports SRAM, otherwise zero. [[nodiscard]] u32 GetSaveMemoryLength() const noexcept { return Cart ? Cart->GetSaveMemoryLength() : 0; } - void EjectCart() noexcept; + + /// @return The cart that was in the slot before it was ejected, + /// or \c nullptr if the slot was already empty. + std::unique_ptr EjectCart() noexcept; u32 ReadROMData() noexcept; void WriteROMData(u32 val) noexcept; void WriteSPICnt(u16 val) noexcept; @@ -294,9 +323,6 @@ public: [[nodiscard]] u8 ReadSPIData() const noexcept; void WriteSPIData(u8 val) noexcept; - [[nodiscard]] CartCommon* GetCart() noexcept { return Cart.get(); } - [[nodiscard]] const CartCommon* GetCart() const noexcept { return Cart.get(); } - [[nodiscard]] u8 GetROMCommand(u8 index) const noexcept { return ROMCommand[index]; } void SetROMCommand(u8 index, u8 val) noexcept { ROMCommand[index] = val; } @@ -306,27 +332,27 @@ public: private: friend class CartCommon; melonDS::NDS& NDS; - u16 SPICnt {}; - u32 ROMCnt {}; + u16 SPICnt = 0; + u32 ROMCnt = 0; std::array ROMCommand {}; - u8 SPIData; - u32 SPIDataPos; - bool SPIHold; + u8 SPIData = 0; + u32 SPIDataPos = 0; + bool SPIHold = false; - u32 ROMData; + u32 ROMData = 0; - std::array TransferData; - u32 TransferPos; - u32 TransferLen; - u32 TransferDir; - std::array TransferCmd; + std::array TransferData {}; + u32 TransferPos = 0; + u32 TransferLen = 0; + u32 TransferDir = 0; + std::array TransferCmd {}; - std::unique_ptr Cart; + std::unique_ptr Cart = nullptr; - std::array Key1_KeyBuf; + std::array Key1_KeyBuf {}; - u64 Key2_X; - u64 Key2_Y; + u64 Key2_X = 0; + u64 Key2_Y = 0; void Key1_Encrypt(u32* data) noexcept; void Key1_Decrypt(u32* data) noexcept; @@ -346,9 +372,13 @@ private: /// The returned cartridge will contain a copy of this data, /// so the caller may deallocate \c romdata after this function returns. /// @param romlen The length of the ROM data in bytes. +/// @param sdcard The arguments to use for initializing the SD card. +/// Ignored if the parsed ROM is not homebrew. +/// If not given, the cart will not have an SD card. /// @returns A \c NDSCart::CartCommon object representing the parsed ROM, /// or \c nullptr if the ROM data couldn't be parsed. -std::unique_ptr ParseROM(const u8* romdata, u32 romlen); +std::unique_ptr ParseROM(const u8* romdata, u32 romlen, std::optional&& args = std::nullopt); +std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen, std::optional&& args = std::nullopt); } #endif diff --git a/src/Platform.h b/src/Platform.h index f8b0453c..2c9a6a4a 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -106,24 +106,8 @@ enum ConfigEntry ExternalBIOSEnable, - DLDI_Enable, - DLDI_ImagePath, - DLDI_ImageSize, - DLDI_ReadOnly, - DLDI_FolderSync, - DLDI_FolderPath, - - DSiSD_Enable, - DSiSD_ImagePath, - DSiSD_ImageSize, - DSiSD_ReadOnly, - DSiSD_FolderSync, - DSiSD_FolderPath, - Firm_MAC, - WifiSettingsPath, - AudioBitDepth, DSi_FullBIOSBoot, @@ -139,7 +123,6 @@ enum ConfigEntry int GetConfigInt(ConfigEntry entry); bool GetConfigBool(ConfigEntry entry); -std::string GetConfigString(ConfigEntry entry); bool GetConfigArray(ConfigEntry entry, void* data); /** diff --git a/src/SPI.cpp b/src/SPI.cpp index b3e5b4e1..3974e316 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -59,31 +59,22 @@ u16 CRC16(const u8* data, u32 len, u32 start) bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) { - u16 crc_stored = *(u16*)&Firmware->Buffer()[crcoffset]; - u16 crc_calced = CRC16(&Firmware->Buffer()[offset], len, start); + u16 crc_stored = *(u16*)&FirmwareData.Buffer()[crcoffset]; + u16 crc_calced = CRC16(&FirmwareData.Buffer()[offset], len, start); return (crc_stored == crc_calced); } -FirmwareMem::FirmwareMem(melonDS::NDS& nds) : SPIDevice(nds) +FirmwareMem::FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware) : SPIDevice(nds), FirmwareData(std::move(firmware)) { } -FirmwareMem::~FirmwareMem() -{ - RemoveFirmware(); -} +FirmwareMem::~FirmwareMem() = default; void FirmwareMem::Reset() { - if (!Firmware) - { - Log(LogLevel::Warn, "SPI firmware: no firmware loaded! Using default\n"); - Firmware = std::make_unique(NDS.ConsoleType); - } - // fix touchscreen coords - for (auto& u : Firmware->GetUserData()) + for (auto& u : FirmwareData.GetUserData()) { u.TouchCalibrationADC1[0] = 0; u.TouchCalibrationADC1[1] = 0; @@ -95,17 +86,17 @@ void FirmwareMem::Reset() u.TouchCalibrationPixel2[1] = 191; } - Firmware->UpdateChecksums(); + FirmwareData.UpdateChecksums(); // disable autoboot //Firmware[userdata+0x64] &= 0xBF; - MacAddress mac = Firmware->GetHeader().MacAddr; + MacAddress mac = FirmwareData.GetHeader().MacAddr; 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 - u32 mask = Firmware->Mask(); - Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&Firmware->Buffer()[0x2C], 0x2A)?"GOOD":"BAD"); + u32 mask = FirmwareData.Mask(); + Log(LogLevel::Debug, "FW: WIFI CRC16 = %s\n", VerifyCRC16(0x0000, 0x2C, *(u16*)&FirmwareData.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"); @@ -136,8 +127,8 @@ void FirmwareMem::DoSavestate(Savestate* file) void FirmwareMem::SetupDirectBoot() { - const auto& header = Firmware->GetHeader(); - const auto& userdata = Firmware->GetEffectiveUserData(); + const auto& header = FirmwareData.GetHeader(); + const auto& userdata = FirmwareData.GetEffectiveUserData(); if (NDS.ConsoleType == 1) { // The ARMWrite methods are virtual, they'll delegate to DSi if necessary @@ -163,58 +154,9 @@ void FirmwareMem::SetupDirectBoot() } } -const class Firmware* FirmwareMem::GetFirmware() -{ - return Firmware.get(); -} - bool FirmwareMem::IsLoadedFirmwareBuiltIn() { - return Firmware->GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER; -} - -bool FirmwareMem::InstallFirmware(class Firmware&& firmware) -{ - if (!firmware.Buffer()) - { - Log(LogLevel::Error, "SPI firmware: firmware buffer is null!\n"); - return false; - } - - Firmware = std::make_unique(std::move(firmware)); - - FirmwareIdentifier id = Firmware->GetHeader().Identifier; - Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]); - - return true; -} - -bool FirmwareMem::InstallFirmware(std::unique_ptr&& 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->GetHeader().Identifier; - Log(LogLevel::Debug, "Installed firmware (Identifier: %c%c%c%c)\n", id[0], id[1], id[2], id[3]); - - return true; -} - -void FirmwareMem::RemoveFirmware() -{ - Firmware.reset(); - Log(LogLevel::Debug, "Removed installed firmware (if any)\n"); + return FirmwareData.GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER; } void FirmwareMem::Write(u8 val) @@ -256,7 +198,7 @@ void FirmwareMem::Write(u8 val) } else { - Data = Firmware->Buffer()[Addr & Firmware->Mask()]; + Data = FirmwareData.Buffer()[Addr & FirmwareData.Mask()]; Addr++; } @@ -279,7 +221,7 @@ void FirmwareMem::Write(u8 val) } else { - Firmware->Buffer()[Addr & Firmware->Mask()] = val; + FirmwareData.Buffer()[Addr & FirmwareData.Mask()] = val; Data = val; Addr++; } @@ -314,11 +256,11 @@ void FirmwareMem::Release() { // 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->GetWifiAccessPointOffset(); + u32 wifioffset = FirmwareData.GetWifiAccessPointOffset(); // 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); + Platform::WriteFirmware(FirmwareData, wifioffset, FirmwareData.Length() - wifioffset); } SPIDevice::Release(); @@ -530,11 +472,11 @@ void TSC::Write(u8 val) -SPIHost::SPIHost(melonDS::NDS& nds) : NDS(nds) +SPIHost::SPIHost(melonDS::NDS& nds, Firmware&& firmware) : NDS(nds) { NDS.RegisterEventFunc(Event_SPITransfer, 0, MemberEventFunc(SPIHost, TransferDone)); - Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS); + Devices[SPIDevice_FirmwareMem] = new FirmwareMem(NDS, std::move(firmware)); Devices[SPIDevice_PowerMan] = new PowerMan(NDS); if (NDS.ConsoleType == 1) diff --git a/src/SPI.h b/src/SPI.h index f27f0c3e..aee41658 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -66,24 +66,23 @@ protected: class FirmwareMem : public SPIDevice { public: - FirmwareMem(melonDS::NDS& nds); + FirmwareMem(melonDS::NDS& nds, melonDS::Firmware&& firmware); ~FirmwareMem() override; void Reset() override; void DoSavestate(Savestate* file) override; void SetupDirectBoot(); - const class Firmware* GetFirmware(); + Firmware& GetFirmware() noexcept { return FirmwareData; } + [[nodiscard]] const Firmware& GetFirmware() const noexcept { return FirmwareData; } + void SetFirmware(Firmware&& firmware) { FirmwareData = std::move(firmware); } bool IsLoadedFirmwareBuiltIn(); - bool InstallFirmware(class Firmware&& firmware); - bool InstallFirmware(std::unique_ptr&& firmware); - void RemoveFirmware(); void Write(u8 val) override; void Release() override; private: - std::unique_ptr Firmware; + Firmware FirmwareData; u8 CurCmd; @@ -141,16 +140,19 @@ protected: class SPIHost { public: - SPIHost(melonDS::NDS& nds); + SPIHost(melonDS::NDS& nds, Firmware&& firmware); ~SPIHost(); void Reset(); void DoSavestate(Savestate* file); FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } + const FirmwareMem* GetFirmwareMem() const { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } PowerMan* GetPowerMan() { return (PowerMan*)Devices[SPIDevice_PowerMan]; } TSC* GetTSC() { return (TSC*)Devices[SPIDevice_TSC]; } - const Firmware* GetFirmware() { return GetFirmwareMem()->GetFirmware(); } + const Firmware& GetFirmware() const { return GetFirmwareMem()->GetFirmware(); } + Firmware& GetFirmware() { return GetFirmwareMem()->GetFirmware(); } + void SetFirmware(Firmware&& firmware) { GetFirmwareMem()->SetFirmware(std::move(firmware)); } u16 ReadCnt() { return Cnt; } void WriteCnt(u16 val); diff --git a/src/Utils.cpp b/src/Utils.cpp new file mode 100644 index 00000000..698cf9bd --- /dev/null +++ b/src/Utils.cpp @@ -0,0 +1,66 @@ +/* + 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 "Utils.h" + +#include + +namespace melonDS +{ +std::pair, u32> PadToPowerOf2(std::unique_ptr&& data, u32 len) noexcept +{ + if (data == nullptr || len == 0) + return {nullptr, 0}; + + if ((len & (len - 1)) == 0) + return {std::move(data), len}; + + u32 newlen = 1; + while (newlen < len) + newlen <<= 1; + + auto newdata = std::make_unique(newlen); + memcpy(newdata.get(), data.get(), len); + data = nullptr; + return {std::move(newdata), newlen}; +} + +std::pair, u32> PadToPowerOf2(const u8* data, u32 len) noexcept +{ + if (len == 0) + return {nullptr, 0}; + + u32 newlen = 1; + while (newlen < len) + newlen <<= 1; + + auto newdata = std::make_unique(newlen); + memcpy(newdata.get(), data, len); + return {std::move(newdata), newlen}; +} + +std::unique_ptr CopyToUnique(const u8* data, u32 len) noexcept +{ + if (data == nullptr || len == 0) + return nullptr; + + auto newdata = std::make_unique(len); + memcpy(newdata.get(), data, len); + return newdata; +} +} \ No newline at end of file diff --git a/src/Utils.h b/src/Utils.h new file mode 100644 index 00000000..63be217b --- /dev/null +++ b/src/Utils.h @@ -0,0 +1,43 @@ +/* + 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_UTILS_H +#define MELONDS_UTILS_H + +#include +#include "types.h" +#include + +namespace melonDS +{ +/// Ensures that the given array is a power of 2 in length. +/// +/// @return If \c len is a power of 2, returns \c data and \c len unchanged +/// without copying anything. +/// If \c data is \c nullptr, returns {nullptr, 0}. +/// Otherwise, return a copy of \c data with zero-padding to the next power of 2. +/// @post \c data is \c nullptr, even if it doesn't need to be copied. +std::pair, u32> PadToPowerOf2(std::unique_ptr&& data, u32 len) noexcept; + +std::pair, u32> PadToPowerOf2(const u8* data, u32 len) noexcept; + +std::unique_ptr CopyToUnique(const u8* data, u32 len) noexcept; + +} + +#endif // MELONDS_UTILS_H diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 50275236..9dc696b6 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -158,12 +158,12 @@ void Wifi::Reset() } #undef BBREG_FIXED - const Firmware* fw = NDS.SPI.GetFirmware(); + const Firmware& fw = NDS.SPI.GetFirmware(); - RFVersion = fw->GetHeader().RFChipType; + RFVersion = fw.GetHeader().RFChipType; memset(RFRegs, 0, 4*0x40); - Firmware::FirmwareConsoleType console = fw->GetHeader().ConsoleType; + Firmware::FirmwareConsoleType console = fw.GetHeader().ConsoleType; if (console == Firmware::FirmwareConsoleType::DS) IOPORT(0x000) = 0x1440; else if (console == Firmware::FirmwareConsoleType::DSLite) diff --git a/src/frontend/qt_sdl/ArchiveUtil.cpp b/src/frontend/qt_sdl/ArchiveUtil.cpp index 7d1eca9c..aa508e8d 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.cpp +++ b/src/frontend/qt_sdl/ArchiveUtil.cpp @@ -120,13 +120,12 @@ QVector ExtractFileFromArchive(QString path, QString wantedFile, QByteA } -s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize) +s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr& filedata, u32* filesize) { struct archive *a = archive_read_new(); struct archive_entry *entry; int r; - if (!filedata) return -1; archive_read_support_format_all(a); archive_read_support_filter_all(a); @@ -148,8 +147,8 @@ s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* size_t bytesToRead = archive_entry_size(entry); if (filesize) *filesize = bytesToRead; - *filedata = new u8[bytesToRead]; - ssize_t bytesRead = archive_read_data(a, *filedata, bytesToRead); + filedata = std::make_unique(bytesToRead); + ssize_t bytesRead = archive_read_data(a, filedata.get(), bytesToRead); archive_read_close(a); archive_read_free(a); diff --git a/src/frontend/qt_sdl/ArchiveUtil.h b/src/frontend/qt_sdl/ArchiveUtil.h index eaffb0dd..246670e7 100644 --- a/src/frontend/qt_sdl/ArchiveUtil.h +++ b/src/frontend/qt_sdl/ArchiveUtil.h @@ -37,7 +37,7 @@ namespace Archive using namespace melonDS; QVector ListArchive(QString path); -s32 ExtractFileFromArchive(QString path, QString wantedFile, u8** filedata, u32* filesize); +s32 ExtractFileFromArchive(QString path, QString wantedFile, std::unique_ptr& filedata, u32* filesize); //QVector ExtractFileFromArchive(QString path, QString wantedFile, QByteArray *romBuffer); //u32 ExtractFileFromArchive(const char* path, const char* wantedFile, u8 **romdata); diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index c2e2f47b..d410d4fb 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -204,10 +204,6 @@ int GetConfigInt(ConfigEntry entry) case JIT_MaxBlockSize: return Config::JIT_MaxBlockSize; #endif - case DLDI_ImageSize: return imgsizes[Config::DLDISize]; - - case DSiSD_ImageSize: return imgsizes[Config::DSiSDSize]; - case AudioBitDepth: return Config::AudioBitDepth; #ifdef GDBSTUB_ENABLED @@ -232,14 +228,6 @@ bool GetConfigBool(ConfigEntry entry) case ExternalBIOSEnable: return Config::ExternalBIOSEnable != 0; - case DLDI_Enable: return Config::DLDIEnable != 0; - case DLDI_ReadOnly: return Config::DLDIReadOnly != 0; - case DLDI_FolderSync: return Config::DLDIFolderSync != 0; - - case DSiSD_Enable: return Config::DSiSDEnable != 0; - case DSiSD_ReadOnly: return Config::DSiSDReadOnly != 0; - case DSiSD_FolderSync: return Config::DSiSDFolderSync != 0; - case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0; #ifdef GDBSTUB_ENABLED @@ -252,22 +240,6 @@ bool GetConfigBool(ConfigEntry entry) return false; } -std::string GetConfigString(ConfigEntry entry) -{ - switch (entry) - { - case DLDI_ImagePath: return Config::DLDISDPath; - case DLDI_FolderPath: return Config::DLDIFolderPath; - - case DSiSD_ImagePath: return Config::DSiSDPath; - case DSiSD_FolderPath: return Config::DSiSDFolderPath; - - case WifiSettingsPath: return Config::WifiSettingsPath; - } - - return ""; -} - bool GetConfigArray(ConfigEntry entry, void* data) { switch (entry) diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 2c0cee2c..fda043a4 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -68,8 +68,8 @@ std::string BaseGBAROMDir = ""; std::string BaseGBAROMName = ""; std::string BaseGBAAssetName = ""; -SaveManager* NDSSave = nullptr; -SaveManager* GBASave = nullptr; +std::unique_ptr NDSSave = nullptr; +std::unique_ptr GBASave = nullptr; std::unique_ptr FirmwareSave = nullptr; std::unique_ptr BackupState = nullptr; @@ -303,6 +303,28 @@ QString VerifySetup() return ""; } +std::string GetEffectiveFirmwareSavePath(EmuThread* thread) +{ + if (!Config::ExternalBIOSEnable) + { + return Config::WifiSettingsPath; + } + if (thread->NDS->ConsoleType == 1) + { + return Config::DSiFirmwarePath; + } + else + { + return Config::FirmwarePath; + } +} + +// Initializes the firmware save manager with the selected firmware image's path +// OR the path to the wi-fi settings. +void InitFirmwareSaveManager(EmuThread* thread) noexcept +{ + FirmwareSave = std::make_unique(GetEffectiveFirmwareSavePath(thread)); +} std::string GetSavestateName(int slot) { @@ -482,6 +504,11 @@ void LoadCheats(NDS& nds) std::optional> LoadARM9BIOS() noexcept { + if (!Config::ExternalBIOSEnable) + { + return Config::ConsoleType == 0 ? std::make_optional(bios_arm9_bin) : std::nullopt; + } + if (FileHandle* f = OpenLocalFile(Config::BIOS9Path, Read)) { std::array bios {}; @@ -498,6 +525,11 @@ std::optional> LoadARM9BIOS() noexcept std::optional> LoadARM7BIOS() noexcept { + if (!Config::ExternalBIOSEnable) + { + return Config::ConsoleType == 0 ? std::make_optional(bios_arm7_bin) : std::nullopt; + } + if (FileHandle* f = OpenLocalFile(Config::BIOS7Path, Read)) { std::array bios {}; @@ -518,6 +550,16 @@ std::optional> LoadDSiARM9BIOS() noexcept std::array bios {}; FileRead(bios.data(), sizeof(bios), 1, f); CloseFile(f); + + if (!Config::DSiFullBIOSBoot) + { + // herp + *(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector + + // TODO!!!! + // hax the upper 32K out of the goddamn DSi + // done that :) -pcy + } Log(Info, "ARM9i BIOS loaded from %s\n", Config::DSiBIOS9Path.c_str()); return bios; } @@ -533,6 +575,16 @@ std::optional> LoadDSiARM7BIOS() noexcept std::array bios {}; FileRead(bios.data(), sizeof(bios), 1, f); CloseFile(f); + + if (!Config::DSiFullBIOSBoot) + { + // herp + *(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector + + // TODO!!!! + // hax the upper 32K out of the goddamn DSi + // done that :) -pcy + } Log(Info, "ARM7i BIOS loaded from %s\n", Config::DSiBIOS7Path.c_str()); return bios; } @@ -589,6 +641,16 @@ Firmware GenerateFirmware(int type) noexcept std::optional LoadFirmware(int type) noexcept { + if (!Config::ExternalBIOSEnable) + { // If we're using built-in firmware... + if (type == 1) + { + Log(Error, "DSi firmware: cannot use built-in firmware in DSi mode!\n"); + return std::nullopt; + } + + return GenerateFirmware(type); + } const string& firmwarepath = type == 1 ? Config::DSiFirmwarePath : Config::FirmwarePath; Log(Debug, "SPI firmware: loading from file %s\n", firmwarepath.c_str()); @@ -609,7 +671,10 @@ std::optional LoadFirmware(int type) noexcept return std::nullopt; } - CustomizeFirmware(firmware); + if (Config::FirmwareOverrideSettings) + { + CustomizeFirmware(firmware); + } return firmware; } @@ -694,7 +759,20 @@ std::optional LoadNAND(const std::array& a return nandImage; } -constexpr int imgsizes[] = {0, 256, 512, 1024, 2048, 4096}; +constexpr u64 imgsizes[] = {0, 256, 512, 1024, 2048, 4096}; +std::optional GetDSiSDCardArgs() noexcept +{ + if (!Config::DSiSDEnable) + return std::nullopt; + + return FATStorageArgs { + Config::DSiSDPath, + imgsizes[Config::DSiSDSize], + Config::DSiSDReadOnly, + Config::DSiSDFolderSync ? std::make_optional(Config::DSiSDFolderPath) : std::nullopt + }; +} + std::optional LoadDSiSDCard() noexcept { if (!Config::DSiSDEnable) @@ -704,97 +782,34 @@ std::optional LoadDSiSDCard() noexcept Config::DSiSDPath, imgsizes[Config::DSiSDSize], Config::DSiSDReadOnly, - Config::DSiSDFolderSync ? Config::DSiSDFolderPath : "" + Config::DSiSDFolderSync ? std::make_optional(Config::DSiSDFolderPath) : std::nullopt ); } -void LoadBIOSFiles(NDS& nds) +std::optional GetDLDISDCardArgs() noexcept { - if (Config::ExternalBIOSEnable) - { - if (FileHandle* f = Platform::OpenLocalFile(Config::BIOS9Path, FileMode::Read)) - { - FileRewind(f); - FileRead(nds.ARM9BIOS, sizeof(NDS::ARM9BIOS), 1, f); + if (!Config::DLDIEnable) + return std::nullopt; - 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"); + return FATStorageArgs{ + Config::DLDISDPath, + imgsizes[Config::DLDISize], + Config::DLDIReadOnly, + Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt + }; +} - for (int i = 0; i < 16; i++) - ((u32*)nds.ARM9BIOS)[i] = 0xE7FFDEFF; - } +std::optional LoadDLDISDCard() noexcept +{ + if (!Config::DLDIEnable) + return std::nullopt; - 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) - { - DSi& dsi = static_cast(nds); - 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 - } - } + return FATStorage( + Config::DLDISDPath, + imgsizes[Config::DLDISize], + Config::DLDIReadOnly, + Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt + ); } void EnableCheats(NDS& nds, bool enable) @@ -835,16 +850,10 @@ void SetDateTime(NDS& nds) void Reset(EmuThread* thread) { - thread->RecreateConsole(); + thread->UpdateConsole(Keep {}, Keep {}); if (Config::ConsoleType == 1) EjectGBACart(*thread->NDS); - LoadBIOSFiles(*thread->NDS); - InstallFirmware(*thread->NDS); - if (Config::ConsoleType == 1) - { - InstallNAND(static_cast(*thread->NDS)); - } thread->NDS->Reset(); SetBatteryLevels(*thread->NDS); SetDateTime(*thread->NDS); @@ -867,6 +876,7 @@ void Reset(EmuThread* thread) GBASave->SetPath(newsave, false); } + InitFirmwareSaveManager(thread); if (FirmwareSave) { std::string oldsave = FirmwareSave->GetPath(); @@ -899,36 +909,25 @@ void Reset(EmuThread* thread) } -bool LoadBIOS(EmuThread* thread) +bool BootToMenu(EmuThread* thread) { - thread->RecreateConsole(); - - LoadBIOSFiles(*thread->NDS); - - if (!InstallFirmware(*thread->NDS)) - return false; - - if (Config::ConsoleType == 1 && !InstallNAND(static_cast(*thread->NDS))) + // Keep whatever cart is in the console, if any. + if (!thread->UpdateConsole(Keep {}, Keep {})) + // Try to update the console, but keep the existing cart. If that fails... return false; + // BIOS and firmware files are loaded, patched, and installed in UpdateConsole if (thread->NDS->NeedsDirectBoot()) return false; - /*if (NDSSave) delete NDSSave; - NDSSave = nullptr; - - CartType = -1; - BaseROMDir = ""; - BaseROMName = ""; - BaseAssetName = "";*/ - + InitFirmwareSaveManager(thread); thread->NDS->Reset(); SetBatteryLevels(*thread->NDS); SetDateTime(*thread->NDS); return true; } -u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent) +u32 DecompressROM(const u8* inContent, const u32 inSize, unique_ptr& outContent) { u64 realSize = ZSTD_getFrameContentSize(inContent, inSize); const u32 maxSize = 0x40000000; @@ -940,16 +939,15 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent) if (realSize != ZSTD_CONTENTSIZE_UNKNOWN) { - u8* realContent = new u8[realSize]; - u64 decompressed = ZSTD_decompress(realContent, realSize, inContent, inSize); + outContent = make_unique(realSize); + u64 decompressed = ZSTD_decompress(outContent.get(), realSize, inContent, inSize); if (ZSTD_isError(decompressed)) { - delete[] realContent; + outContent = nullptr; return 0; } - *outContent = realContent; return realSize; } else @@ -1005,8 +1003,8 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, u8** outContent) } while (inBuf.pos < inBuf.size); ZSTD_freeDStream(dStream); - *outContent = new u8[outBuf.pos]; - memcpy(*outContent, outBuf.dst, outBuf.pos); + outContent = make_unique(outBuf.pos); + memcpy(outContent.get(), outBuf.dst, outBuf.pos); ZSTD_freeDStream(dStream); free(outBuf.dst); @@ -1023,42 +1021,6 @@ 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, string> LoadFirmwareFromFile() -{ - string loadedpath; - unique_ptr 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(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, string> GenerateDefaultFirmware() { // Construct the default firmware... @@ -1068,7 +1030,7 @@ pair, string> GenerateDefaultFirmware() // 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); + std::string wfcsettingspath = Config::WifiSettingsPath; settingspath = wfcsettingspath + Platform::InstanceFileSuffix(); FileHandle* f = Platform::OpenLocalFile(settingspath, FileMode::Read); if (!f) @@ -1201,155 +1163,12 @@ void CustomizeFirmware(Firmware& firmware) noexcept firmware.UpdateChecksums(); } -static Platform::FileHandle* OpenNANDFile() noexcept -{ - std::string nandpath = Config::DSiNANDPath; - std::string instnand = nandpath + Platform::InstanceFileSuffix(); - - FileHandle* nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); - if ((!nandfile) && (Platform::InstanceID() > 0)) - { - FileHandle* orig = Platform::OpenLocalFile(nandpath, FileMode::Read); - if (!orig) - { - Log(LogLevel::Error, "Failed to open DSi NAND from %s\n", nandpath.c_str()); - return nullptr; - } - - QFile::copy(QString::fromStdString(nandpath), QString::fromStdString(instnand)); - - nandfile = Platform::OpenLocalFile(instnand, FileMode::ReadWriteExisting); - } - - return nandfile; -} - -bool InstallNAND(DSi& dsi) -{ - Platform::FileHandle* nandfile = OpenNANDFile(); - if (!nandfile) - return false; - - DSi_NAND::NANDImage nandImage(nandfile, &dsi.ARM7iBIOS[0x8308]); - if (!nandImage) - { - Log(LogLevel::Error, "Failed to parse DSi NAND\n"); - return false; - } - - // scoped so that mount isn't alive when we move the NAND image to DSi::NANDImage - { - auto mount = DSi_NAND::NANDMount(nandImage); - if (!mount) - { - Log(LogLevel::Error, "Failed to mount DSi NAND\n"); - return false; - } - - DSi_NAND::DSiFirmwareSystemSettings settings {}; - if (!mount.ReadUserData(settings)) - { - Log(LogLevel::Error, "Failed to read DSi NAND user data\n"); - return false; - } - - // override user settings, if needed - if (Config::FirmwareOverrideSettings) - { - // we store relevant strings as UTF-8, so we need to convert them to UTF-16 - auto converter = wstring_convert, char16_t>{}; - - // setting up username - std::u16string username = converter.from_bytes(Config::FirmwareUsername); - size_t usernameLength = std::min(username.length(), (size_t) 10); - memset(&settings.Nickname, 0, sizeof(settings.Nickname)); - memcpy(&settings.Nickname, username.data(), usernameLength * sizeof(char16_t)); - - // setting language - settings.Language = static_cast(Config::FirmwareLanguage); - - // setting up color - settings.FavoriteColor = Config::FirmwareFavouriteColour; - - // setting up birthday - settings.BirthdayMonth = Config::FirmwareBirthdayMonth; - settings.BirthdayDay = Config::FirmwareBirthdayDay; - - // setup message - std::u16string message = converter.from_bytes(Config::FirmwareMessage); - size_t messageLength = std::min(message.length(), (size_t) 26); - memset(&settings.Message, 0, sizeof(settings.Message)); - memcpy(&settings.Message, message.data(), messageLength * sizeof(char16_t)); - - // TODO: make other items configurable? - } - - // fix touchscreen coords - settings.TouchCalibrationADC1 = {0, 0}; - settings.TouchCalibrationPixel1 = {0, 0}; - settings.TouchCalibrationADC2 = {255 << 4, 191 << 4}; - settings.TouchCalibrationPixel2 = {255, 191}; - - settings.UpdateHash(); - - if (!mount.ApplyUserData(settings)) - { - Log(LogLevel::Error, "Failed to write patched DSi NAND user data\n"); - return false; - } - } - - dsi.NANDImage = std::make_unique(std::move(nandImage)); - return true; -} - -bool InstallFirmware(NDS& nds) -{ - FirmwareSave.reset(); - unique_ptr 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) - { - CustomizeFirmware(*firmware); - } - - FirmwareSave = std::make_unique(firmwarepath); - - return nds.SPI.GetFirmwareMem()->InstallFirmware(std::move(firmware)); -} - -bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) +// Loads ROM data without parsing it. Works for GBA and NDS ROMs. +bool LoadROMData(const QStringList& filepath, std::unique_ptr& filedata, u32& filelen, string& basepath, string& romname) noexcept { if (filepath.empty()) return false; - u8* filedata = nullptr; - u32 filelen; - - std::string basepath; - std::string romname; - - int num = filepath.count(); - if (num == 1) + if (int num = filepath.count(); num == 1) { // regular file @@ -1361,38 +1180,35 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) if (len > 0x40000000) { Platform::CloseFile(f); - delete[] filedata; return false; } Platform::FileRewind(f); - filedata = new u8[len]; - size_t nread = Platform::FileRead(filedata, (size_t)len, 1, f); + filedata = make_unique(len); + size_t nread = Platform::FileRead(filedata.get(), (size_t)len, 1, f); + Platform::CloseFile(f); if (nread != 1) { - Platform::CloseFile(f); - delete[] filedata; + filedata = nullptr; return false; } - Platform::CloseFile(f); filelen = (u32)len; if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst") { - u8* outContent = nullptr; - u32 decompressed = DecompressROM(filedata, len, &outContent); + filelen = DecompressROM(filedata.get(), len, filedata); - if (decompressed > 0) + if (filelen > 0) { - delete[] filedata; - filedata = outContent; - filelen = decompressed; filename = filename.substr(0, filename.length() - 4); } else { - delete[] filedata; + filedata = nullptr; + filelen = 0; + basepath = ""; + romname = ""; return false; } } @@ -1400,19 +1216,21 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) int pos = LastSep(filename); if(pos != -1) basepath = filename.substr(0, pos); + romname = filename.substr(pos+1); + return true; } #ifdef ARCHIVE_SUPPORT_ENABLED else if (num == 2) { // file inside archive - s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen); + s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), filedata, &filelen); if (lenread < 0) return false; if (!filedata) return false; if (lenread != filelen) { - delete[] filedata; + filedata = nullptr; return false; } @@ -1421,38 +1239,31 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) std::string std_romname = filepath.at(1).toStdString(); romname = std_romname.substr(LastSep(std_romname)+1); + return true; } #endif else return false; +} + +bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) +{ + unique_ptr filedata = nullptr; + u32 filelen; + std::string basepath; + std::string romname; + + if (!LoadROMData(filepath, filedata, filelen, basepath, romname)) + return false; - if (NDSSave) delete NDSSave; NDSSave = nullptr; BaseROMDir = basepath; BaseROMName = romname; BaseAssetName = romname.substr(0, romname.rfind('.')); - emuthread->RecreateConsole(); - if (!InstallFirmware(*emuthread->NDS)) - { - return false; - } - - if (reset) - { - emuthread->NDS->EjectCart(); - LoadBIOSFiles(*emuthread->NDS); - if (Config::ConsoleType == 1) - InstallNAND(static_cast(*emuthread->NDS)); - - emuthread->NDS->Reset(); - SetBatteryLevels(*emuthread->NDS); - SetDateTime(*emuthread->NDS); - } - u32 savelen = 0; - u8* savedata = nullptr; + std::unique_ptr savedata = nullptr; std::string savname = GetAssetPath(false, Config::SaveFilePath, ".sav"); std::string origsav = savname; @@ -1465,36 +1276,56 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) savelen = (u32)Platform::FileLength(sav); FileRewind(sav); - savedata = new u8[savelen]; - FileRead(savedata, savelen, 1, sav); + savedata = std::make_unique(savelen); + FileRead(savedata.get(), savelen, 1, sav); CloseFile(sav); } - bool res = emuthread->NDS->LoadCart(filedata, filelen, savedata, savelen); - if (res && reset) + NDSCart::NDSCartArgs cartargs { + // Don't load the SD card itself yet, because we don't know if + // the ROM is homebrew or not. + // So this is the card we *would* load if the ROM were homebrew. + .SDCard = GetDLDISDCardArgs(), + + .SRAM = std::make_pair(std::move(savedata), savelen), + }; + + auto cart = NDSCart::ParseROM(std::move(filedata), filelen, std::move(cartargs)); + if (!cart) + // If we couldn't parse the ROM... + return false; + + if (reset) { + if (!emuthread->UpdateConsole(std::move(cart), Keep {})) + return false; + + InitFirmwareSaveManager(emuthread); + emuthread->NDS->Reset(); + if (Config::DirectBoot || emuthread->NDS->NeedsDirectBoot()) - { + { // If direct boot is enabled or forced... emuthread->NDS->SetupDirectBoot(romname); } - } - if (res) + SetBatteryLevels(*emuthread->NDS); + SetDateTime(*emuthread->NDS); + } + else { - CartType = 0; - NDSSave = new SaveManager(savname); - - LoadCheats(*emuthread->NDS); + assert(emuthread->NDS != nullptr); + emuthread->NDS->SetNDSCart(std::move(cart)); } - if (savedata) delete[] savedata; - delete[] filedata; - return res; + CartType = 0; + NDSSave = std::make_unique(savname); + LoadCheats(*emuthread->NDS); + + return true; } void EjectCart(NDS& nds) { - if (NDSSave) delete NDSSave; NDSSave = nullptr; UnloadCheats(nds); @@ -1529,92 +1360,16 @@ QString CartLabel() bool LoadGBAROM(NDS& nds, QStringList filepath) { - if (Config::ConsoleType == 1) return false; - if (filepath.empty()) return false; + if (nds.ConsoleType == 1) return false; // DSi doesn't have a GBA slot - u8* filedata; + unique_ptr filedata = nullptr; u32 filelen; - std::string basepath; std::string romname; - int num = filepath.count(); - if (num == 1) - { - // regular file - - std::string filename = filepath.at(0).toStdString(); - FileHandle* f = Platform::OpenFile(filename, FileMode::Read); - if (!f) return false; - - long len = FileLength(f); - if (len > 0x40000000) - { - CloseFile(f); - return false; - } - - FileRewind(f); - filedata = new u8[len]; - size_t nread = FileRead(filedata, (size_t)len, 1, f); - if (nread != 1) - { - CloseFile(f); - delete[] filedata; - return false; - } - - CloseFile(f); - filelen = (u32)len; - - if (filename.length() > 4 && filename.substr(filename.length() - 4) == ".zst") - { - u8* outContent = nullptr; - u32 decompressed = DecompressROM(filedata, len, &outContent); - - if (decompressed > 0) - { - delete[] filedata; - filedata = outContent; - filelen = decompressed; - filename = filename.substr(0, filename.length() - 4); - } - else - { - delete[] filedata; - return false; - } - } - - int pos = LastSep(filename); - basepath = filename.substr(0, pos); - romname = filename.substr(pos+1); - } -#ifdef ARCHIVE_SUPPORT_ENABLED - else if (num == 2) - { - // file inside archive - - s32 lenread = Archive::ExtractFileFromArchive(filepath.at(0), filepath.at(1), &filedata, &filelen); - if (lenread < 0) return false; - if (!filedata) return false; - if (lenread != filelen) - { - delete[] filedata; - return false; - } - - std::string std_archivepath = filepath.at(0).toStdString(); - basepath = std_archivepath.substr(0, LastSep(std_archivepath)); - - std::string std_romname = filepath.at(1).toStdString(); - romname = std_romname.substr(LastSep(std_romname)+1); - } -#endif - else + if (!LoadROMData(filepath, filedata, filelen, basepath, romname)) return false; - if (GBASave) delete GBASave; GBASave = nullptr; BaseGBAROMDir = basepath; @@ -1622,7 +1377,7 @@ bool LoadGBAROM(NDS& nds, QStringList filepath) BaseGBAAssetName = romname.substr(0, romname.rfind('.')); u32 savelen = 0; - u8* savedata = nullptr; + std::unique_ptr savedata = nullptr; std::string savname = GetAssetPath(true, Config::SaveFilePath, ".sav"); std::string origsav = savname; @@ -1634,30 +1389,29 @@ bool LoadGBAROM(NDS& nds, QStringList filepath) { savelen = (u32)FileLength(sav); - FileRewind(sav); - savedata = new u8[savelen]; - FileRead(savedata, savelen, 1, sav); + if (savelen > 0) + { + FileRewind(sav); + savedata = std::make_unique(savelen); + FileRead(savedata.get(), savelen, 1, sav); + } CloseFile(sav); } - bool res = nds.LoadGBACart(filedata, filelen, savedata, savelen); + auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen); + if (!cart) + return false; - if (res) - { - GBACartType = 0; - GBASave = new SaveManager(savname); - } - - if (savedata) delete[] savedata; - delete[] filedata; - return res; + nds.SetGBACart(std::move(cart)); + GBACartType = 0; + GBASave = std::make_unique(savname); + return true; } void LoadGBAAddon(NDS& nds, int type) { if (Config::ConsoleType == 1) return; - if (GBASave) delete GBASave; GBASave = nullptr; nds.LoadGBAAddon(type); @@ -1670,7 +1424,6 @@ void LoadGBAAddon(NDS& nds, int type) void EjectGBACart(NDS& nds) { - if (GBASave) delete GBASave; GBASave = nullptr; nds.EjectGBACart(); diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 2163a680..0b640c84 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -35,34 +35,43 @@ namespace melonDS class NDS; class DSi; class FATStorage; +class FATStorageArgs; } class EmuThread; namespace ROMManager { using namespace melonDS; -extern SaveManager* NDSSave; -extern SaveManager* GBASave; +extern std::unique_ptr NDSSave; +extern std::unique_ptr GBASave; extern std::unique_ptr FirmwareSave; QString VerifySetup(); void Reset(EmuThread* thread); -bool LoadBIOS(EmuThread* thread); + +/// Boots the emulated console into its system menu without starting a game. +bool BootToMenu(EmuThread* thread); void ClearBackupState(); +/// Returns the configured ARM9 BIOS loaded from disk, +/// the FreeBIOS if external BIOS is disabled and we're in NDS mode, +/// or nullopt if loading failed. std::optional> LoadARM9BIOS() noexcept; std::optional> LoadARM7BIOS() noexcept; std::optional> LoadDSiARM9BIOS() noexcept; std::optional> LoadDSiARM7BIOS() noexcept; +std::optional GetDSiSDCardArgs() noexcept; std::optional LoadDSiSDCard() noexcept; +std::optional GetDLDISDCardArgs() noexcept; +std::optional LoadDLDISDCard() noexcept; void CustomizeFirmware(Firmware& firmware) noexcept; Firmware GenerateFirmware(int type) noexcept; /// Loads and customizes a firmware image based on the values in Config std::optional LoadFirmware(int type) noexcept; /// Loads and customizes a NAND image based on the values in Config std::optional LoadNAND(const std::array& arm7ibios) noexcept; -bool InstallFirmware(NDS& nds); -bool InstallNAND(DSi& dsi); + +/// Inserts a ROM into the emulated console. bool LoadROM(EmuThread*, QStringList filepath, bool reset); void EjectCart(NDS& nds); bool CartInserted(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index a0ac0860..5bd4d1b1 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -81,6 +81,7 @@ #include "FrontendUtil.h" #include "OSD.h" +#include "Args.h" #include "NDS.h" #include "NDSCart.h" #include "GBACart.h" @@ -204,29 +205,164 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) static_cast(mainWindow->panel)->transferLayout(this); } -std::unique_ptr EmuThread::CreateConsole() +std::unique_ptr EmuThread::CreateConsole( + std::unique_ptr&& ndscart, + std::unique_ptr&& gbacart +) noexcept { + auto arm7bios = ROMManager::LoadARM7BIOS(); + if (!arm7bios) + return nullptr; + + auto arm9bios = ROMManager::LoadARM9BIOS(); + if (!arm9bios) + return nullptr; + + auto firmware = ROMManager::LoadFirmware(Config::ConsoleType); + if (!firmware) + return nullptr; + + NDSArgs ndsargs { + std::move(ndscart), + std::move(gbacart), + *arm9bios, + *arm7bios, + std::move(*firmware), + }; + if (Config::ConsoleType == 1) { - return std::make_unique(); + auto arm7ibios = ROMManager::LoadDSiARM7BIOS(); + if (!arm7ibios) + return nullptr; + + auto arm9ibios = ROMManager::LoadDSiARM9BIOS(); + if (!arm9ibios) + return nullptr; + + auto nand = ROMManager::LoadNAND(*arm7ibios); + if (!nand) + return nullptr; + + auto sdcard = ROMManager::LoadDSiSDCard(); + DSiArgs args { + std::move(ndsargs), + *arm9ibios, + *arm7ibios, + std::move(*nand), + std::move(sdcard), + }; + + args.GBAROM = nullptr; + + return std::make_unique(std::move(args)); } - return std::make_unique(); + return std::make_unique(std::move(ndsargs)); } -void EmuThread::RecreateConsole() +bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept { - if (!NDS || NDS->ConsoleType != Config::ConsoleType) + // Let's get the cart we want to use; + // if we wnat to keep the cart, we'll eject it from the existing console first. + std::unique_ptr nextndscart; + if (std::holds_alternative(ndsargs)) + { // If we want to keep the existing cart (if any)... + nextndscart = NDS ? NDS->EjectCart() : nullptr; + ndsargs = {}; + } + else if (const auto ptr = std::get_if>(&ndsargs)) { - NDS = nullptr; // To ensure the destructor is called before a new one is created + nextndscart = std::move(*ptr); + ndsargs = {}; + } + + if (nextndscart && nextndscart->Type() == NDSCart::Homebrew) + { + // Load DLDISDCard will return nullopt if the SD card is disabled; + // SetSDCard will accept nullopt, which means no SD card + auto& homebrew = static_cast(*nextndscart); + homebrew.SetSDCard(ROMManager::LoadDLDISDCard()); + } + + std::unique_ptr nextgbacart; + if (std::holds_alternative(gbaargs)) + { + nextgbacart = NDS ? NDS->EjectGBACart() : nullptr; + } + else if (const auto ptr = std::get_if>(&gbaargs)) + { + nextgbacart = std::move(*ptr); + gbaargs = {}; + } + + if (!NDS || NDS->ConsoleType != Config::ConsoleType) + { // If we're switching between DS and DSi mode, or there's no console... + // To ensure the destructor is called before a new one is created, + // as the presence of global signal handlers still complicates things a bit + NDS = nullptr; NDS::Current = nullptr; - NDS = CreateConsole(); - // TODO: Insert ROMs + NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart)); NDS::Current = NDS.get(); - } -} + return NDS != nullptr; + } + + auto arm9bios = ROMManager::LoadARM9BIOS(); + if (!arm9bios) + return false; + + auto arm7bios = ROMManager::LoadARM7BIOS(); + if (!arm7bios) + return false; + + auto firmware = ROMManager::LoadFirmware(NDS->ConsoleType); + if (!firmware) + return false; + + if (NDS->ConsoleType == 1) + { // If the console we're updating is a DSi... + DSi& dsi = static_cast(*NDS); + + auto arm9ibios = ROMManager::LoadDSiARM9BIOS(); + if (!arm9ibios) + return false; + + auto arm7ibios = ROMManager::LoadDSiARM7BIOS(); + if (!arm7ibios) + return false; + + auto nandimage = ROMManager::LoadNAND(*arm7ibios); + if (!nandimage) + return false; + + auto dsisdcard = ROMManager::LoadDSiSDCard(); + + dsi.ARM7iBIOS = *arm7ibios; + dsi.ARM9iBIOS = *arm9ibios; + dsi.SetNAND(std::move(*nandimage)); + dsi.SetSDCard(std::move(dsisdcard)); + // We're moving the optional, not the card + // (inserting std::nullopt here is okay, it means no card) + + dsi.EjectGBACart(); + } + + if (NDS->ConsoleType == 0) + { + NDS->SetGBACart(std::move(nextgbacart)); + } + + NDS->ARM7BIOS = *arm7bios; + NDS->ARM9BIOS = *arm9bios; + NDS->SetFirmware(std::move(*firmware)); + NDS->SetNDSCart(std::move(nextndscart)); + + NDS::Current = NDS.get(); + + return true; +} void EmuThread::updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix) { @@ -343,7 +479,8 @@ void EmuThread::run() u32 mainScreenPos[3]; Platform::FileHandle* file; - RecreateConsole(); + UpdateConsole(nullptr, nullptr); + // No carts are inserted when melonDS first boots mainScreenPos[0] = 0; mainScreenPos[1] = 0; @@ -2507,7 +2644,7 @@ void MainWindow::onBootFirmware() return; } - if (!ROMManager::LoadBIOS(emuThread)) + if (!ROMManager::BootToMenu(emuThread)) { // TODO: better error reporting? QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); @@ -2750,13 +2887,12 @@ void MainWindow::onImportSavefile() u32 len = FileLength(f); - u8* data = new u8[len]; + std::unique_ptr data = std::make_unique(len); Platform::FileRewind(f); - Platform::FileRead(data, len, 1, f); + Platform::FileRead(data.get(), len, 1, f); assert(emuThread->NDS != nullptr); - emuThread->NDS->LoadSave(data, len); - delete[] data; + emuThread->NDS->SetNDSSave(data.get(), len); CloseFile(f); emuThread->emuUnpause(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 72ebfb19..ee2f7201 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -34,12 +34,18 @@ #include #include - +#include #include #include "FrontendUtil.h" #include "duckstation/gl/context.h" +#include "NDSCart.h" +#include "GBACart.h" + +using Keep = std::monostate; +using UpdateConsoleNDSArgs = std::variant>; +using UpdateConsoleGBAArgs = std::variant>; namespace melonDS { class NDS; @@ -72,7 +78,13 @@ public: QMutex FrontBufferLock; void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); - void RecreateConsole(); + + /// Applies the config in args. + /// Creates a new NDS console if needed, + /// modifies the existing one if possible. + /// @return \c true if the console was updated. + /// If this returns \c false, then the existing NDS console is not modified. + bool UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept; std::unique_ptr NDS; // TODO: Proper encapsulation and synchronization signals: void windowUpdate(); @@ -96,7 +108,10 @@ signals: void syncVolumeLevel(); private: - std::unique_ptr CreateConsole(); + std::unique_ptr CreateConsole( + std::unique_ptr&& ndscart, + std::unique_ptr&& gbacart + ) noexcept; void drawScreenGL(); void initOpenGL(); void deinitOpenGL(); From 2e8cca9ca18a7d4375bc1ae4be6dfedbba6c6d79 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Mon, 4 Dec 2023 11:57:51 -0500 Subject: [PATCH 052/157] Initialize the framebuffers within the constructor (#1901) --- src/GPU.cpp | 7 ++++++- src/GPU.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/GPU.cpp b/src/GPU.cpp index c80e3119..2c140a86 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -75,7 +75,7 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr&& renderer3d, std::uniqu NDS.RegisterEventFunc(Event_LCD, LCD_FinishFrame, MemberEventFunc(GPU, FinishFrame)); NDS.RegisterEventFunc(Event_DisplayFIFO, 0, MemberEventFunc(GPU, DisplayFIFO)); - FrontBuffer = 0; + InitFramebuffers(); } GPU::~GPU() noexcept @@ -298,6 +298,11 @@ void GPU::SetRenderer3D(std::unique_ptr&& renderer) noexcept else GPU3D.SetCurrentRenderer(std::move(renderer)); + InitFramebuffers(); +} + +void GPU::InitFramebuffers() noexcept +{ int fbsize; if (GPU3D.IsRendererAccelerated()) fbsize = (256*3 + 1) * 192; diff --git a/src/GPU.h b/src/GPU.h index 21a05d58..ee7311a6 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -607,6 +607,7 @@ public: private: void ResetVRAMCache() noexcept; void AssignFramebuffers() noexcept; + void InitFramebuffers() noexcept; template T ReadVRAM_ABGExtPal(u32 addr) const noexcept { From ae91d89f7c18f6b4153deeef7e3ebe14a1d849fe Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Tue, 5 Dec 2023 06:41:28 -0500 Subject: [PATCH 053/157] Use a `constexpr`-friendly cosine implementation (#1903) --- src/SPU.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/SPU.cpp b/src/SPU.cpp index cff38d3a..1ec3c90f 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -65,16 +65,56 @@ const s16 SPUChannel::PSGTable[8][8] = {-0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF} }; +template +constexpr T ipow(T num, unsigned int pow) +{ + T product = 1; + for (int i = 0; i < pow; ++i) + { + product *= num; + } + + return product; +} + +template +constexpr T factorial(T num) +{ + T product = 1; + for (T i = 1; i <= num; ++i) + { + product *= i; + } + + return product; +} + +// We can't use std::cos in constexpr functions until C++26, +// so we need to compute the cosine ourselves with the Taylor series. +// Code adapted from https://prosepoetrycode.potterpcs.net/2015/07/a-simple-constexpr-power-function-c/ +template +constexpr double cosine (double theta) +{ + return (ipow(-1, Iterations) * ipow(theta, 2 * Iterations)) / + static_cast(factorial(2ull * Iterations)) + + cosine(theta); +} + +template <> +constexpr double cosine<0> (double theta) +{ + return 1.0; +} + // generate interpolation tables // values are 1:1:14 fixed-point constexpr std::array InterpCos = []() constexpr { std::array interp {}; - float m_pi = std::acos(-1.0f); for (int i = 0; i < 0x100; i++) { - float ratio = (i * m_pi) / 255.0f; - ratio = 1.0f - std::cos(ratio); + float ratio = (i * M_PI) / 255.0f; + ratio = 1.0f - cosine(ratio); interp[i] = (s16)(ratio * 0x2000); } From 090627b3c19488e36677113e2f1ac16bdb4e2d05 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Tue, 5 Dec 2023 10:47:16 -0500 Subject: [PATCH 054/157] Remove the last `ConfigEntry` state (#1902) * Get rid of `ConfigEntry::ExternalBIOSEnable` - Now the BIOS files themselves are checked - The frontend's `Config::ExternalBIOSEnable` is not affected * Add `JITArgs` * Pass the JIT status to the `ARM` constructors * Encapsulate `NDS::EnableJIT` * Pass `JITArgs` to `ARMJIT`'s constructor * Remove the `JIT_*` `ConfigEntry`s in favor of members - Allow all the JIT args to be set with `NDS::SetJITArgs` - Encapsulate the JIT-related parameters in `ARMJIT` so they can reset the block cache if changed - Update the active (or newly-created) console in the frontend with adjusted JIT args * Make audio bit depth and interpolation configurable in `NDSArgs` - Define enums for both - Give those settings default values in `NDSArgs` - Remove `ConfigEntry::AudioBitDepth` - Initialize these settings in the relevant SPU constructors * Move the last DSi-specific logic in `Reset` to its own subclass * Remove `ConfigEntry::DSi_FullBIOSBoot` - Add members to `DSi` instead for getting and setting this - Update the frontend to accommodate these changes * Oops, missed a spot * Remove `ConfigEntry::Firm_MAC` and `Platform::GetConfigArray` - Also move the MAC parsing code to `ROMManager` * Remove the last `ConfigEntry` state - Make GDB support configurable via members * Add some `#ifdef`s that I'd almost forgotten --- src/ARM.cpp | 16 +++--- src/ARM.h | 10 ++-- src/ARMJIT.cpp | 60 +++++++++++++++++---- src/ARMJIT.h | 27 +++++++++- src/ARMJIT_A64/ARMJIT_LoadStore.cpp | 10 ++-- src/ARMJIT_x64/ARMJIT_LoadStore.cpp | 10 ++-- src/Args.h | 42 +++++++++++++++ src/DSi.cpp | 10 ++-- src/DSi.h | 3 ++ src/NDS.cpp | 58 +++++++++----------- src/NDS.h | 10 +++- src/NDSCart.cpp | 6 +-- src/NDSCart.h | 2 +- src/Platform.h | 33 ------------ src/SPU.cpp | 69 +++++++++++++++--------- src/SPU.h | 24 +++++++-- src/frontend/qt_sdl/AudioInOut.cpp | 2 +- src/frontend/qt_sdl/Platform.cpp | 84 ----------------------------- src/frontend/qt_sdl/ROMManager.cpp | 32 ++++++++++- src/frontend/qt_sdl/main.cpp | 45 ++++++++++++++-- 20 files changed, 323 insertions(+), 230 deletions(-) diff --git a/src/ARM.cpp b/src/ARM.cpp index 659d303b..2c19fa30 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -106,17 +106,17 @@ const u32 ARM::ConditionTable[16] = 0x0000 // NE }; -ARM::ARM(u32 num, melonDS::NDS& nds) : +ARM::ARM(u32 num, bool jit, std::optional gdb, melonDS::NDS& nds) : #ifdef GDBSTUB_ENABLED - GdbStub(this, Platform::GetConfigInt(num ? Platform::GdbPortARM7 : Platform::GdbPortARM9)), + GdbStub(this, gdb ? (num ? gdb->PortARM7 : gdb->PortARM9) : 0), #endif Num(num), // well uh NDS(nds) { #ifdef GDBSTUB_ENABLED - if (Platform::GetConfigBool(Platform::GdbEnabled) + if (gdb #ifdef JIT_ENABLED - && !Platform::GetConfigBool(Platform::JIT_Enable) + && !jit // TODO: Should we support toggling the GdbStub without destroying the ARM? #endif ) GdbStub.Init(); @@ -129,14 +129,14 @@ ARM::~ARM() // dorp } -ARMv5::ARMv5(melonDS::NDS& nds) : ARM(0, nds) +ARMv5::ARMv5(melonDS::NDS& nds, std::optional gdb, bool jit) : ARM(0, jit, gdb, nds) { DTCM = NDS.JIT.Memory.GetARM9DTCM(); PU_Map = PU_PrivMap; } -ARMv4::ARMv4(melonDS::NDS& nds) : ARM(1, nds) +ARMv4::ARMv4(melonDS::NDS& nds, std::optional gdb, bool jit) : ARM(1, jit, gdb, nds) { // } @@ -187,8 +187,6 @@ void ARM::Reset() #ifdef GDBSTUB_ENABLED IsSingleStep = false; BreakReq = false; - BreakOnStartup = Platform::GetConfigBool( - Num ? Platform::GdbARM7BreakOnStartup : Platform::GdbARM9BreakOnStartup); #endif // zorp @@ -224,7 +222,7 @@ void ARM::DoSavestate(Savestate* file) file->VarArray(R_UND, 3*sizeof(u32)); file->Var32(&CurInstr); #ifdef JIT_ENABLED - if (file->Saving && NDS.EnableJIT) + if (file->Saving && NDS.IsJITEnabled()) { // hack, the JIT doesn't really pipeline // but we still want JIT save states to be diff --git a/src/ARM.h b/src/ARM.h index 4becff02..65f78ab3 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -20,6 +20,7 @@ #define ARM_H #include +#include #include "types.h" #include "MemRegion.h" @@ -44,7 +45,7 @@ enum const u32 ITCMPhysicalSize = 0x8000; const u32 DTCMPhysicalSize = 0x4000; - +struct GDBArgs; class ARMJIT; class GPU; class ARMJIT_Memory; @@ -57,7 +58,7 @@ class ARM #endif { public: - ARM(u32 num, NDS& nds); + ARM(u32 num, bool jit, std::optional gdb, NDS& nds); virtual ~ARM(); // destroy shit virtual void Reset(); @@ -202,6 +203,7 @@ protected: bool IsSingleStep; bool BreakReq; bool BreakOnStartup; + u16 Port; public: int GetCPU() const override { return Num ? 7 : 9; } @@ -225,7 +227,7 @@ protected: class ARMv5 : public ARM { public: - ARMv5(melonDS::NDS& nds); + ARMv5(melonDS::NDS& nds, std::optional gdb, bool jit); ~ARMv5(); void Reset() override; @@ -377,7 +379,7 @@ protected: class ARMv4 : public ARM { public: - ARMv4(melonDS::NDS& nds); + ARMv4(melonDS::NDS& nds, std::optional gdb, bool jit); void FillPipeline() override; diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index b938dfb8..5e0e2079 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -237,16 +237,6 @@ ARMJIT::~ARMJIT() noexcept void ARMJIT::Reset() noexcept { - MaxBlockSize = Platform::GetConfigInt(Platform::JIT_MaxBlockSize); - LiteralOptimizations = Platform::GetConfigBool(Platform::JIT_LiteralOptimizations); - BranchOptimizations = Platform::GetConfigBool(Platform::JIT_BranchOptimizations); - FastMemory = Platform::GetConfigBool(Platform::JIT_FastMemory); - - if (MaxBlockSize < 1) - MaxBlockSize = 1; - if (MaxBlockSize > 32) - MaxBlockSize = 32; - JitEnableWrite(); ResetBlockCache(); @@ -491,6 +481,56 @@ void ARMJIT::RetireJitBlock(JitBlock* block) noexcept } } +void ARMJIT::SetJITArgs(JITArgs args) noexcept +{ + args.MaxBlockSize = std::clamp(args.MaxBlockSize, 1u, 32u); + + if (MaxBlockSize != args.MaxBlockSize + || LiteralOptimizations != args.LiteralOptimizations + || BranchOptimizations != args.BranchOptimizations + || FastMemory != args.FastMemory) + ResetBlockCache(); + + MaxBlockSize = args.MaxBlockSize; + LiteralOptimizations = args.LiteralOptimizations; + BranchOptimizations = args.BranchOptimizations; + FastMemory = args.FastMemory; +} + +void ARMJIT::SetMaxBlockSize(int size) noexcept +{ + size = std::clamp(size, 1, 32); + + if (size != MaxBlockSize) + ResetBlockCache(); + + MaxBlockSize = size; +} + +void ARMJIT::SetLiteralOptimizations(bool enabled) noexcept +{ + if (LiteralOptimizations != enabled) + ResetBlockCache(); + + LiteralOptimizations = enabled; +} + +void ARMJIT::SetBranchOptimizations(bool enabled) noexcept +{ + if (BranchOptimizations != enabled) + ResetBlockCache(); + + BranchOptimizations = enabled; +} + +void ARMJIT::SetFastMemory(bool enabled) noexcept +{ + if (FastMemory != enabled) + ResetBlockCache(); + + FastMemory = enabled; +} + void ARMJIT::CompileBlock(ARM* cpu) noexcept { bool thumb = cpu->CPSR & 0x20; diff --git a/src/ARMJIT.h b/src/ARMJIT.h index 9e1ca074..6390855d 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -19,6 +19,8 @@ #ifndef ARMJIT_H #define ARMJIT_H +#include +#include #include #include "types.h" @@ -30,6 +32,7 @@ #endif #include "ARMJIT_Compiler.h" +#include "Args.h" #include "MemConstants.h" namespace melonDS @@ -40,7 +43,15 @@ class JitBlock; class ARMJIT { public: - ARMJIT(melonDS::NDS& nds) noexcept : NDS(nds), Memory(nds), JITCompiler(nds) {}; + ARMJIT(melonDS::NDS& nds, std::optional jit) noexcept : + NDS(nds), + Memory(nds), + JITCompiler(nds), + MaxBlockSize(jit.has_value() ? std::clamp(jit->MaxBlockSize, 1u, 32u) : 32), + LiteralOptimizations(jit.has_value() ? jit->LiteralOptimizations : false), + BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false), + FastMemory(jit.has_value() ? jit->FastMemory : false) + {} ~ARMJIT() noexcept NOOP_IF_NO_JIT; void InvalidateByAddr(u32) noexcept NOOP_IF_NO_JIT; void CheckAndInvalidateWVRAM(int) noexcept NOOP_IF_NO_JIT; @@ -68,17 +79,29 @@ public: #endif ARMJIT_Memory Memory; +private: int MaxBlockSize {}; bool LiteralOptimizations = false; bool BranchOptimizations = false; bool FastMemory = false; - +public: melonDS::NDS& NDS; TinyVector InvalidLiterals {}; friend class ARMJIT_Memory; void blockSanityCheck(u32 num, u32 blockAddr, JitBlockEntry entry) noexcept; void RetireJitBlock(JitBlock* block) noexcept; + int GetMaxBlockSize() const noexcept { return MaxBlockSize; } + bool LiteralOptimizationsEnabled() const noexcept { return LiteralOptimizations; } + bool BranchOptimizationsEnabled() const noexcept { return BranchOptimizations; } + bool FastMemoryEnabled() const noexcept { return FastMemory; } + + void SetJITArgs(JITArgs args) noexcept; + void SetMaxBlockSize(int size) noexcept; + void SetLiteralOptimizations(bool enabled) noexcept; + void SetBranchOptimizations(bool enabled) noexcept; + void SetFastMemory(bool enabled) noexcept; + Compiler JITCompiler; std::unordered_map JitBlocks9 {}; std::unordered_map JitBlocks7 {}; diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp index 22a410ae..4007138f 100644 --- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp @@ -112,7 +112,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) if (size == 16) addressMask = ~1; - if (NDS.JIT.LiteralOptimizations && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) + if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && offset.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) { u32 addr = R15 + offset.Imm * ((flags & memop_SubtractOffset) ? -1 : 1); @@ -147,7 +147,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) MOV(W0, rnMapped); } - bool addrIsStatic = NDS.JIT.LiteralOptimizations + bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled() && RegCache.IsLiteral(rn) && offset.IsImm && !(flags & (memop_Writeback|memop_Post)); u32 staticAddress; if (addrIsStatic) @@ -189,7 +189,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, Op2 offset, int size, int flags) ? NDS.JIT.Memory.ClassifyAddress9(addrIsStatic ? staticAddress : CurInstr.DataRegion) : NDS.JIT.Memory.ClassifyAddress7(addrIsStatic ? staticAddress : CurInstr.DataRegion); - if (NDS.JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget))) + if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget))) { ptrdiff_t memopStart = GetCodeOffset(); LoadStorePatch patch; @@ -453,7 +453,7 @@ void Compiler::T_Comp_LoadPCRel() u32 offset = ((CurInstr.Instr & 0xFF) << 2); u32 addr = (R15 & ~0x2) + offset; - if (!NDS.JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) + if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0); } @@ -498,7 +498,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc ? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) : NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); - bool compileFastPath = NDS.JIT.FastMemory + bool compileFastPath = NDS.JIT.FastMemoryEnabled() && store && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)); { diff --git a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp index 72a073db..8520bebc 100644 --- a/src/ARMJIT_x64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_x64/ARMJIT_LoadStore.cpp @@ -119,7 +119,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag if (size == 16) addressMask = ~1; - if (NDS.JIT.LiteralOptimizations && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) + if (NDS.JIT.LiteralOptimizationsEnabled() && rn == 15 && rd != 15 && op2.IsImm && !(flags & (memop_Post|memop_Store|memop_Writeback))) { u32 addr = R15 + op2.Imm * ((flags & memop_SubtractOffset) ? -1 : 1); @@ -136,7 +136,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag Comp_AddCycles_CDI(); } - bool addrIsStatic = NDS.JIT.LiteralOptimizations + bool addrIsStatic = NDS.JIT.LiteralOptimizationsEnabled() && RegCache.IsLiteral(rn) && op2.IsImm && !(flags & (memop_Writeback|memop_Post)); u32 staticAddress; if (addrIsStatic) @@ -200,7 +200,7 @@ void Compiler::Comp_MemAccess(int rd, int rn, const Op2& op2, int size, int flag ? NDS.JIT.Memory.ClassifyAddress9(CurInstr.DataRegion) : NDS.JIT.Memory.ClassifyAddress7(CurInstr.DataRegion); - if (NDS.JIT.FastMemory && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget))) + if (NDS.JIT.FastMemoryEnabled() && ((!Thumb && CurInstr.Cond() != 0xE) || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget))) { if (rdMapped.IsImm()) { @@ -431,7 +431,7 @@ s32 Compiler::Comp_MemAccessBlock(int rn, BitSet16 regs, bool store, bool preinc else Comp_AddCycles_CD(); - bool compileFastPath = NDS.JIT.FastMemory + bool compileFastPath = NDS.JIT.FastMemoryEnabled() && !usermode && (CurInstr.Cond() < 0xE || NDS.JIT.Memory.IsFastmemCompatible(expectedTarget)); // we need to make sure that the stack stays aligned to 16 bytes @@ -809,7 +809,7 @@ void Compiler::T_Comp_LoadPCRel() { u32 offset = (CurInstr.Instr & 0xFF) << 2; u32 addr = (R15 & ~0x2) + offset; - if (!NDS.JIT.LiteralOptimizations || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) + if (!NDS.JIT.LiteralOptimizationsEnabled() || !Comp_MemLoadLiteral(32, false, CurInstr.T_Reg(8), addr)) Comp_MemAccess(CurInstr.T_Reg(8), 15, Op2(offset), 32, 0); } diff --git a/src/Args.h b/src/Args.h index bfa1b13a..c6d131c8 100644 --- a/src/Args.h +++ b/src/Args.h @@ -23,12 +23,15 @@ #include #include +#include "NDSCart.h" +#include "GBACart.h" #include "types.h" #include "MemConstants.h" #include "DSi_NAND.h" #include "FATStorage.h" #include "FreeBIOS.h" #include "SPI_Firmware.h" +#include "SPU.h" namespace melonDS { @@ -50,6 +53,29 @@ constexpr std::array BrokenBIOS = []() constexpr { return broken; }(); +/// Arguments that configure the JIT. +/// Ignored in builds that don't have the JIT included. +struct JITArgs +{ + unsigned MaxBlockSize = 32; + bool LiteralOptimizations = true; + bool BranchOptimizations = true; + + /// Ignored in builds that have fast memory excluded + /// (even if the JIT is otherwise available). + /// Enabled by default, but frontends should disable this when debugging + /// so the constants segfaults don't hinder debugging. + bool FastMemory = true; +}; + +struct GDBArgs +{ + u16 PortARM7 = 0; + u16 PortARM9 = 0; + bool ARM7BreakOnStartup = false; + bool ARM9BreakOnStartup = false; +}; + /// Arguments to pass into the NDS constructor. /// New fields here should have default values if possible. struct NDSArgs @@ -78,6 +104,20 @@ struct NDSArgs /// Defaults to generated NDS firmware. /// Generated firmware is not compatible with DSi mode. melonDS::Firmware Firmware {0}; + + /// How the JIT should be configured when initializing. + /// Defaults to enabled, with default settings. + /// To disable the JIT, set this to std::nullopt. + /// Ignored in builds that don't have the JIT included. + std::optional JIT = JITArgs(); + + AudioBitDepth BitDepth = AudioBitDepth::Auto; + AudioInterpolation Interpolation = AudioInterpolation::None; + + /// How the GDB stub should be handled. + /// Defaults to disabled. + /// Ignored in builds that don't have the GDB stub included. + std::optional GDB = std::nullopt; }; /// Arguments to pass into the DSi constructor. @@ -95,6 +135,8 @@ struct DSiArgs final : public NDSArgs /// SD card to install. /// Defaults to std::nullopt, which means no SD card. std::optional DSiSDCard; + + bool FullBIOSBoot = false; }; } #endif //MELONDS_ARGS_H diff --git a/src/DSi.cpp b/src/DSi.cpp index 5dcd4193..a5403d25 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -111,6 +111,8 @@ void DSi::Reset() //ARM9.CP15Write(0x100, ARM9.CP15Read(0x100) | 0x00050000); NDS::Reset(); + // The SOUNDBIAS register does nothing on DSi + SPU.SetApplyBias(false); KeyInput &= ~(1 << (16+6)); MapSharedWRAM(3); @@ -128,7 +130,7 @@ void DSi::Reset() AES.Reset(); - if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) + if (FullBIOSBoot) { SCFG_BIOS = 0x0000; } @@ -679,7 +681,7 @@ void DSi::SoftReset() AES.Reset(); - if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) + if (FullBIOSBoot) { SCFG_BIOS = 0x0000; } @@ -741,7 +743,7 @@ bool DSi::LoadNAND() memset(NWRAMMask, 0, sizeof(NWRAMMask)); u32 bootparams[8]; - if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) + if (FullBIOSBoot) { // TODO: figure out default MBK mapping // MBK1..5: disable mappings @@ -879,7 +881,7 @@ bool DSi::LoadNAND() Log(LogLevel::Debug, "eMMC CID: %08llX%08llX\n", *(const u64*)&emmccid[0], *(const u64*)&emmccid[8]); Log(LogLevel::Debug, "Console ID: %" PRIx64 "\n", image->GetConsoleID()); - if (Platform::GetConfigBool(Platform::DSi_FullBIOSBoot)) + if (FullBIOSBoot) { // point CPUs to boot ROM reset vectors ARM9.JumpTo(0xFFFF0000); diff --git a/src/DSi.h b/src/DSi.h index acd85c14..90bb0d42 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -172,7 +172,10 @@ public: u8 GPIO_IE; u8 GPIO_WiFi; + bool GetFullBIOSBoot() const noexcept { return FullBIOSBoot; } + void SetFullBIOSBoot(bool full) noexcept { FullBIOSBoot = full; } private: + bool FullBIOSBoot; void Set_SCFG_Clock9(u16 val); void Set_SCFG_MC(u32 val); void DecryptModcryptArea(u32 offset, u32 size, u8* iv); diff --git a/src/NDS.cpp b/src/NDS.cpp index f3e5a1af..54aa270d 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -92,8 +92,8 @@ NDS::NDS(NDSArgs&& args, int type) noexcept : ConsoleType(type), ARM7BIOS(args.ARM7BIOS), ARM9BIOS(args.ARM9BIOS), - JIT(*this), - SPU(*this), + JIT(*this, args.JIT), + SPU(*this, args.BitDepth, args.Interpolation), GPU(*this), SPI(*this, std::move(args.Firmware)), RTC(*this), @@ -101,8 +101,8 @@ NDS::NDS(NDSArgs&& args, int type) noexcept : NDSCartSlot(*this, std::move(args.NDSROM)), GBACartSlot(type == 1 ? nullptr : std::move(args.GBAROM)), AREngine(*this), - ARM9(*this), - ARM7(*this), + ARM9(*this, args.GDB, args.JIT.has_value()), + ARM7(*this, args.GDB, args.JIT.has_value()), DMAs { DMA(0, 0, *this), DMA(0, 1, *this), @@ -203,6 +203,22 @@ void NDS::SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswi } } +#ifdef JIT_ENABLED +void NDS::SetJITArgs(std::optional args) noexcept +{ + if (args) + { // If we want to turn the JIT on... + JIT.SetJITArgs(*args); + } + else if (args.has_value() != EnableJIT) + { // Else if we want to turn the JIT off, and it wasn't already off... + JIT.ResetBlockCache(); + } + + EnableJIT = args.has_value(); +} +#endif + void NDS::InitTimings() { // TODO, eventually: @@ -249,12 +265,12 @@ bool NDS::NeedsDirectBoot() } else { - // internal BIOS does not support direct boot - if (!Platform::GetConfigBool(Platform::ExternalBIOSEnable)) + // DSi/3DS firmwares aren't bootable, neither is the generated firmware + if (!SPI.GetFirmware().IsBootable()) return true; - // DSi/3DS firmwares aren't bootable - if (!SPI.GetFirmware().IsBootable()) + // FreeBIOS requires direct boot (it can't boot firmware) + if (IsLoadedARM7BIOSBuiltIn() || IsLoadedARM9BIOSBuiltIn()) return true; return false; @@ -394,10 +410,6 @@ void NDS::Reset() Platform::FileHandle* f; u32 i; -#ifdef JIT_ENABLED - EnableJIT = Platform::GetConfigBool(Platform::JIT_Enable); -#endif - RunningGame = false; LastSysClockCycles = 0; @@ -505,28 +517,6 @@ void NDS::Reset() SPI.Reset(); RTC.Reset(); Wifi.Reset(); - - // TODO: move the SOUNDBIAS/degrade logic to SPU? - - // The SOUNDBIAS register does nothing on DSi - SPU.SetApplyBias(ConsoleType == 0); - - bool degradeAudio = true; - - if (ConsoleType == 1) - { - //DSi::Reset(); - KeyInput &= ~(1 << (16+6)); - degradeAudio = false; - } - - int bitDepth = Platform::GetConfigInt(Platform::AudioBitDepth); - if (bitDepth == 1) // Always 10-bit - degradeAudio = true; - else if (bitDepth == 2) // Always 16-bit - degradeAudio = false; - - SPU.SetDegrade10Bit(degradeAudio); } void NDS::Start() diff --git a/src/NDS.h b/src/NDS.h index b9f82916..c0b429ee 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -219,11 +219,12 @@ class ARMJIT; class NDS { -public: - +private: #ifdef JIT_ENABLED bool EnableJIT; #endif + +public: int ConsoleType; int CurCPU; @@ -433,6 +434,11 @@ public: virtual void ARM7IOWrite16(u32 addr, u16 val); virtual void ARM7IOWrite32(u32 addr, u32 val); +#ifdef JIT_ENABLED + [[nodiscard]] bool IsJITEnabled() const noexcept { return EnableJIT; } + void SetJITArgs(std::optional args) noexcept; +#endif + private: void InitTimings(); u32 SchedListMask; diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 848c6197..65309e32 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -109,9 +109,9 @@ void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept } } -void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) noexcept +void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, u8 *bios, u32 biosLength) noexcept { - if (externalBios) + if (!NDS.IsLoadedARM7BIOSBuiltIn()) { u32 expected_bios_length = dsi ? 0x10000 : 0x4000; if (biosLength != expected_bios_length) @@ -138,7 +138,7 @@ void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 bio void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) noexcept { - Key1_LoadKeyBuf(dsi, Platform::GetConfigBool(Platform::ExternalBIOSEnable), bios, biosLength); + Key1_LoadKeyBuf(dsi, bios, biosLength); u32 keycode[3] = {idcode, idcode>>1, idcode<<1}; if (level >= 1) Key1_ApplyKeycode(keycode, mod); diff --git a/src/NDSCart.h b/src/NDSCart.h index 43bf1fc9..03e16e95 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -357,7 +357,7 @@ private: void Key1_Encrypt(u32* data) noexcept; void Key1_Decrypt(u32* data) noexcept; void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept; - void Key1_LoadKeyBuf(bool dsi, bool externalBios, u8 *bios, u32 biosLength) noexcept; + void Key1_LoadKeyBuf(bool dsi, u8 *bios, u32 biosLength) noexcept; void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) noexcept; void Key2_Encrypt(u8* data, u32 len) noexcept; void ROMEndTransfer(u32 param) noexcept; diff --git a/src/Platform.h b/src/Platform.h index 2c9a6a4a..21b3d465 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -92,39 +92,6 @@ int InstanceID(); */ std::string InstanceFileSuffix(); -// configuration values - -enum ConfigEntry -{ -#ifdef JIT_ENABLED - JIT_Enable, - JIT_MaxBlockSize, - JIT_LiteralOptimizations, - JIT_BranchOptimizations, - JIT_FastMemory, -#endif - - ExternalBIOSEnable, - - Firm_MAC, - - AudioBitDepth, - - DSi_FullBIOSBoot, - -#ifdef GDBSTUB_ENABLED - GdbEnabled, - GdbPortARM7, - GdbPortARM9, - GdbARM7BreakOnStartup, - GdbARM9BreakOnStartup, -#endif -}; - -int GetConfigInt(ConfigEntry entry); -bool GetConfigBool(ConfigEntry entry); -bool GetConfigArray(ConfigEntry entry, void* data); - /** * Denotes how a file will be opened and accessed. * Flags may or may not correspond to the operating system's file API. diff --git a/src/SPU.cpp b/src/SPU.cpp index 1ec3c90f..f1df9cf3 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -140,31 +140,32 @@ constexpr array2d InterpCubic = []() constexpr { return interp; }(); -SPU::SPU(melonDS::NDS& nds) : +SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpolation) : NDS(nds), Channels { - SPUChannel(0, nds), - SPUChannel(1, nds), - SPUChannel(2, nds), - SPUChannel(3, nds), - SPUChannel(4, nds), - SPUChannel(5, nds), - SPUChannel(6, nds), - SPUChannel(7, nds), - SPUChannel(8, nds), - SPUChannel(9, nds), - SPUChannel(10, nds), - SPUChannel(11, nds), - SPUChannel(12, nds), - SPUChannel(13, nds), - SPUChannel(14, nds), - SPUChannel(15, nds), + SPUChannel(0, nds, interpolation), + SPUChannel(1, nds, interpolation), + SPUChannel(2, nds, interpolation), + SPUChannel(3, nds, interpolation), + SPUChannel(4, nds, interpolation), + SPUChannel(5, nds, interpolation), + SPUChannel(6, nds, interpolation), + SPUChannel(7, nds, interpolation), + SPUChannel(8, nds, interpolation), + SPUChannel(9, nds, interpolation), + SPUChannel(10, nds, interpolation), + SPUChannel(11, nds, interpolation), + SPUChannel(12, nds, interpolation), + SPUChannel(13, nds, interpolation), + SPUChannel(14, nds, interpolation), + SPUChannel(15, nds, interpolation), }, Capture { SPUCaptureUnit(0, nds), SPUCaptureUnit(1, nds), }, - AudioLock(Platform::Mutex_Create()) + AudioLock(Platform::Mutex_Create()), + Degrade10Bit(bitdepth == AudioBitDepth::_10Bit || (nds.ConsoleType == 1 && bitdepth == AudioBitDepth::Auto)) { NDS.RegisterEventFunc(Event_SPU, 0, MemberEventFunc(SPU, Mix)); @@ -236,7 +237,7 @@ void SPU::SetPowerCnt(u32 val) } -void SPU::SetInterpolation(int type) +void SPU::SetInterpolation(AudioInterpolation type) { for (SPUChannel& channel : Channels) channel.InterpType = type; @@ -257,8 +258,26 @@ void SPU::SetDegrade10Bit(bool enable) Degrade10Bit = enable; } +void SPU::SetDegrade10Bit(AudioBitDepth depth) +{ + switch (depth) + { + case AudioBitDepth::Auto: + Degrade10Bit = (NDS.ConsoleType == 0); + break; + case AudioBitDepth::_10Bit: + Degrade10Bit = true; + break; + case AudioBitDepth::_16Bit: + Degrade10Bit = false; + break; + } +} -SPUChannel::SPUChannel(u32 num, melonDS::NDS& nds) : NDS(nds), Num(num) +SPUChannel::SPUChannel(u32 num, melonDS::NDS& nds, AudioInterpolation interpolation) : + NDS(nds), + Num(num), + InterpType(interpolation) { } @@ -559,7 +578,7 @@ s32 SPUChannel::Run() // for optional interpolation: save previous samples // the interpolated audio will be delayed by a couple samples, // but it's easier to deal with this way - if ((type < 3) && (InterpType != 0)) + if ((type < 3) && (InterpType != AudioInterpolation::None)) { PrevSample[2] = PrevSample[1]; PrevSample[1] = PrevSample[0]; @@ -579,24 +598,24 @@ s32 SPUChannel::Run() s32 val = (s32)CurSample; // interpolation (emulation improvement, not a hardware feature) - if ((type < 3) && (InterpType != 0)) + if ((type < 3) && (InterpType != AudioInterpolation::None)) { s32 samplepos = ((Timer - TimerReload) * 0x100) / (0x10000 - TimerReload); if (samplepos > 0xFF) samplepos = 0xFF; switch (InterpType) { - case 1: // linear + case AudioInterpolation::Linear: val = ((val * samplepos) + (PrevSample[0] * (0xFF-samplepos))) >> 8; break; - case 2: // cosine + case AudioInterpolation::Cosine: val = ((val * InterpCos[samplepos]) + (PrevSample[0] * InterpCos[0xFF-samplepos])) >> 14; break; - case 3: // cubic + case AudioInterpolation::Cubic: val = ((PrevSample[2] * InterpCubic[samplepos][0]) + (PrevSample[1] * InterpCubic[samplepos][1]) + (PrevSample[0] * InterpCubic[samplepos][2]) + diff --git a/src/SPU.h b/src/SPU.h index 03d476e8..1541c681 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -27,10 +27,25 @@ namespace melonDS class NDS; class SPU; +enum class AudioBitDepth +{ + Auto, + _10Bit, + _16Bit, +}; + +enum class AudioInterpolation +{ + None, + Linear, + Cosine, + Cubic, +}; + class SPUChannel { public: - SPUChannel(u32 num, melonDS::NDS& nds); + SPUChannel(u32 num, melonDS::NDS& nds, AudioInterpolation interpolation); void Reset(); void DoSavestate(Savestate* file); @@ -40,7 +55,7 @@ public: // audio interpolation is an improvement upon the original hardware // (which performs no interpolation) - int InterpType = 0; + AudioInterpolation InterpType = AudioInterpolation::None; const u32 Num; @@ -200,7 +215,7 @@ private: class SPU { public: - explicit SPU(melonDS::NDS& nds); + explicit SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpolation); ~SPU(); void Reset(); void DoSavestate(Savestate* file); @@ -210,10 +225,11 @@ public: void SetPowerCnt(u32 val); // 0=none 1=linear 2=cosine 3=cubic - void SetInterpolation(int type); + void SetInterpolation(AudioInterpolation type); void SetBias(u16 bias); void SetDegrade10Bit(bool enable); + void SetDegrade10Bit(AudioBitDepth depth); void SetApplyBias(bool enable); void Mix(u32 dummy); diff --git a/src/frontend/qt_sdl/AudioInOut.cpp b/src/frontend/qt_sdl/AudioInOut.cpp index ae5529d9..1f1ee1c5 100644 --- a/src/frontend/qt_sdl/AudioInOut.cpp +++ b/src/frontend/qt_sdl/AudioInOut.cpp @@ -369,7 +369,7 @@ void UpdateSettings(NDS& nds) { MicClose(); - nds.SPU.SetInterpolation(Config::AudioInterp); + nds.SPU.SetInterpolation(static_cast(Config::AudioInterp)); SetupMicInputData(); MicOpen(); diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index d410d4fb..46305821 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -193,90 +193,6 @@ std::string InstanceFileSuffix() return suffix; } - -int GetConfigInt(ConfigEntry entry) -{ - const int imgsizes[] = {0, 256, 512, 1024, 2048, 4096}; - - switch (entry) - { -#ifdef JIT_ENABLED - case JIT_MaxBlockSize: return Config::JIT_MaxBlockSize; -#endif - - case AudioBitDepth: return Config::AudioBitDepth; - -#ifdef GDBSTUB_ENABLED - case GdbPortARM7: return Config::GdbPortARM7; - case GdbPortARM9: return Config::GdbPortARM9; -#endif - } - - return 0; -} - -bool GetConfigBool(ConfigEntry entry) -{ - switch (entry) - { -#ifdef JIT_ENABLED - case JIT_Enable: return Config::JIT_Enable != 0; - case JIT_LiteralOptimizations: return Config::JIT_LiteralOptimisations != 0; - case JIT_BranchOptimizations: return Config::JIT_BranchOptimisations != 0; - case JIT_FastMemory: return Config::JIT_FastMemory != 0; -#endif - - case ExternalBIOSEnable: return Config::ExternalBIOSEnable != 0; - - case DSi_FullBIOSBoot: return Config::DSiFullBIOSBoot != 0; - -#ifdef GDBSTUB_ENABLED - case GdbEnabled: return Config::GdbEnabled; - case GdbARM7BreakOnStartup: return Config::GdbARM7BreakOnStartup; - case GdbARM9BreakOnStartup: return Config::GdbARM9BreakOnStartup; -#endif - } - - return false; -} - -bool GetConfigArray(ConfigEntry entry, void* data) -{ - switch (entry) - { - case Firm_MAC: - { - std::string& mac_in = Config::FirmwareMAC; - u8* mac_out = (u8*)data; - - int o = 0; - u8 tmp = 0; - for (int i = 0; i < 18; i++) - { - char c = mac_in[i]; - if (c == '\0') break; - - int n; - if (c >= '0' && c <= '9') n = c - '0'; - else if (c >= 'a' && c <= 'f') n = c - 'a' + 10; - else if (c >= 'A' && c <= 'F') n = c - 'A' + 10; - else continue; - - if (!(o & 1)) - tmp = n; - else - mac_out[o >> 1] = n | (tmp << 4); - - o++; - if (o >= 12) return true; - } - } - return false; - } - - return false; -} - constexpr char AccessMode(FileMode mode, bool file_exists) { if (!(mode & FileMode::Write)) diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index fda043a4..b065ad1a 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -1078,6 +1078,36 @@ pair, string> GenerateDefaultFirmware() return std::make_pair(std::move(firmware), std::move(wfcsettingspath)); } +bool ParseMacAddress(void* data) +{ + const std::string& mac_in = Config::FirmwareMAC; + u8* mac_out = (u8*)data; + + int o = 0; + u8 tmp = 0; + for (int i = 0; i < 18; i++) + { + char c = mac_in[i]; + if (c == '\0') break; + + int n; + if (c >= '0' && c <= '9') n = c - '0'; + else if (c >= 'a' && c <= 'f') n = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') n = c - 'A' + 10; + else continue; + + if (!(o & 1)) + tmp = n; + else + mac_out[o >> 1] = n | (tmp << 4); + + o++; + if (o >= 12) return true; + } + + return false; +} + void CustomizeFirmware(Firmware& firmware) noexcept { auto& currentData = firmware.GetEffectiveUserData(); @@ -1136,7 +1166,7 @@ void CustomizeFirmware(Firmware& firmware) noexcept MacAddress configuredMac; - rep = Platform::GetConfigArray(Platform::Firm_MAC, &configuredMac); + rep = ParseMacAddress(&configuredMac); rep &= (configuredMac != MacAddress()); if (rep) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 5bd4d1b1..30ea0ab6 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -222,12 +222,42 @@ std::unique_ptr EmuThread::CreateConsole( if (!firmware) return nullptr; +#ifdef JIT_ENABLED + JITArgs jitargs { + static_cast(Config::JIT_MaxBlockSize), + Config::JIT_LiteralOptimisations, + Config::JIT_BranchOptimisations, + Config::JIT_FastMemory, + }; +#endif + +#ifdef GDBSTUB_ENABLED + GDBArgs gdbargs { + static_cast(Config::GdbPortARM7), + static_cast(Config::GdbPortARM9), + Config::GdbARM7BreakOnStartup, + Config::GdbARM9BreakOnStartup, + }; +#endif + NDSArgs ndsargs { std::move(ndscart), std::move(gbacart), *arm9bios, *arm7bios, std::move(*firmware), +#ifdef JIT_ENABLED + Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt, +#else + std::nullopt, +#endif + static_cast(Config::AudioBitDepth), + static_cast(Config::AudioInterp), +#ifdef GDBSTUB_ENABLED + Config::GdbEnabled ? std::make_optional(gdbargs) : std::nullopt, +#else + std::nullopt, +#endif }; if (Config::ConsoleType == 1) @@ -251,6 +281,7 @@ std::unique_ptr EmuThread::CreateConsole( *arm7ibios, std::move(*nand), std::move(sdcard), + Config::DSiFullBIOSBoot, }; args.GBAROM = nullptr; @@ -339,6 +370,7 @@ bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAAr auto dsisdcard = ROMManager::LoadDSiSDCard(); + dsi.SetFullBIOSBoot(Config::DSiFullBIOSBoot); dsi.ARM7iBIOS = *arm7ibios; dsi.ARM9iBIOS = *arm9ibios; dsi.SetNAND(std::move(*nandimage)); @@ -354,10 +386,19 @@ bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAAr NDS->SetGBACart(std::move(nextgbacart)); } + JITArgs jitargs { + static_cast(Config::JIT_MaxBlockSize), + Config::JIT_LiteralOptimisations, + Config::JIT_BranchOptimisations, + Config::JIT_FastMemory, + }; NDS->ARM7BIOS = *arm7bios; NDS->ARM9BIOS = *arm9bios; NDS->SetFirmware(std::move(*firmware)); NDS->SetNDSCart(std::move(nextndscart)); + NDS->SetJITArgs(Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt); + NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); + NDS->SPU.SetDegrade10Bit(static_cast(Config::AudioBitDepth)); NDS::Current = NDS.get(); @@ -510,8 +551,6 @@ void EmuThread::run() NDS->GPU.SetRenderer3D(std::move(glrenderer)); } - NDS->SPU.SetInterpolation(Config::AudioInterp); - Input::Init(); u32 nframes = 0; @@ -3137,7 +3176,7 @@ void MainWindow::onPathSettingsFinished(int res) void MainWindow::onUpdateAudioSettings() { assert(emuThread->NDS != nullptr); - emuThread->NDS->SPU.SetInterpolation(Config::AudioInterp); + emuThread->NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); if (Config::AudioBitDepth == 0) emuThread->NDS->SPU.SetDegrade10Bit(emuThread->NDS->ConsoleType == 0); From 1b7b5106e24d1bad0776c97696f834209e30d900 Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Wed, 6 Dec 2023 00:03:53 -0800 Subject: [PATCH 055/157] FreeBIOS: Ensure upper 16 bits are cleared in the initial crc16 value. Fixes Castlevania: Dawn of Sorrow's checksumming which uses crc16 swi and has garbage in the upper 16 bits of r0. The official BIOS would seem to implicitly clear these upper 16 bits. --- freebios/bios_common.S | 3 + freebios/drastic_bios_arm7.bin | Bin 16384 -> 16384 bytes freebios/drastic_bios_arm9.bin | Bin 4096 -> 4096 bytes src/FreeBIOS.cpp | 688 ++++++++++++++++----------------- 4 files changed, 347 insertions(+), 344 deletions(-) diff --git a/freebios/bios_common.S b/freebios/bios_common.S index 56d349ef..308d67c4 100755 --- a/freebios/bios_common.S +++ b/freebios/bios_common.S @@ -517,6 +517,9 @@ swi_get_crc16: mov const_0x1E, #0x1E adr crc_table_ptr, crc_table + bic crc_value, crc_value, #0xFF000000 + bic crc_value, crc_value, #0x00FF0000 + movs crc_length, crc_length, lsr #1 beq 1f diff --git a/freebios/drastic_bios_arm7.bin b/freebios/drastic_bios_arm7.bin index e586eb9f0299b554bcd48294b36e985f0c1c1459..adffb53a43f521994acc7ab2fcea00ba4692f269 100755 GIT binary patch delta 268 zcmZo@U~Fh$oFKvRl%3&~7z@Lzjf&v{lgkAx8ShNqC}3^-L70Kz2N3s&Ffee4GB9ib z;vG<|Bgnvjj3*D$AQO_V}v_6{r>-d#lW!O zvCrhU!jpN`fjkz22~U(&Hat|B+$&ax7ku#V zKUcHChKG0l|Bt!})OBa_T2XDri<57P$}^sv{8Mx`z|inOKtTTK z28M|bW&Z*#oV-ycdh!7omd!k}6PS1t{``N%D7@e?BlE$hv6ENID>H^{J}a-S001qH BU~2#X delta 268 zcmZo@U~Fh$oFKvRn4RI37z@Lzjf&v{lgkAx8Lv#7WEnI@+QcW?&$|Njao@YrwiSK-OLnm`_l z!GtHuDjOcEO`a-Z&nPzes)({A@BjZ?%?2ADKKuVa>M@cU_Q`^x(TpD^=ZcyOKKchy za|fvACQ!}2$y-IW881(MC@Rl*dNQNfY{n&<=ZZ123C;kzhJm5sfq;Pg(+vz0AIfju lDD#<#H|fv+SB%079y2l@d>TJ_qr5U>*ygM9+6og5H~>65V;KMd diff --git a/freebios/drastic_bios_arm9.bin b/freebios/drastic_bios_arm9.bin index 51a82829b853ebdc52c28d404b375b328fac537c..95c330d0b3c981b9b127c65fef5917d828fb7681 100755 GIT binary patch delta 242 zcmZorXi%6S!Lgo^;gtmg!>h@G><=a}S}-xNPqt#TvJGMR|33zZKLBwK>;L}}Z2$ku zKye9>-op6*{{kotVk6@Vn>RA@GjaT9Iq>*D$AQO_4={IdhX4Qnih*Ilh@O><=a}S}^@!n{35sW$VN8|9=P&zX0MC*8l$n*#7?) zf#Mt>y@m1r{~1sk#74#^Hg9C)XPSJ1xq~zE|NmDE3=1BIPmW}n%xeVXu^3EvqO7vv zq5kBbEcT4@lPy`58O0{YvWhbbOfF@OX8bw%BCEOJ>wo{bnhiEQyz~Em)J>qS=aVJb wv>ESAc4d=iyfQhJ&7ASr bios_arm7_bin = { 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1c, 0x04, 0x00, 0xea, 0x1a, 0x04, 0x00, 0xea, 0x19, 0x04, 0x00, 0xea, 0x18, 0x04, 0x00, 0xea, - 0xe3, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, + 0xe5, 0x07, 0x00, 0xea, 0x16, 0x04, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -385,17 +385,17 @@ std::array bios_arm7_bin = { 0x80, 0x40, 0x04, 0xe2, 0x1f, 0x40, 0x84, 0xe3, 0x02, 0xc0, 0x5e, 0xe5, 0x04, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x20, 0x00, 0x5c, 0xe3, 0x01, 0xc0, 0xa0, 0xa3, 0x0c, 0xf1, 0x9f, 0xe7, 0x00, 0x00, 0xa0, 0xe1, - 0xd4, 0x1f, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, + 0xdc, 0x1f, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x5c, 0x11, 0x00, 0x00, 0x90, 0x11, 0x00, 0x00, 0x88, 0x11, 0x00, 0x00, 0x4c, 0x11, 0x00, 0x00, 0xbc, 0x11, 0x00, 0x00, 0xcc, 0x11, 0x00, 0x00, 0xe8, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x44, 0x12, 0x00, 0x00, 0xc4, 0x12, 0x00, 0x00, 0x04, 0x13, 0x00, 0x00, 0x50, 0x13, 0x00, 0x00, - 0xe8, 0x13, 0x00, 0x00, 0xf0, 0x13, 0x00, 0x00, 0x84, 0x14, 0x00, 0x00, - 0x00, 0x15, 0x00, 0x00, 0xac, 0x15, 0x00, 0x00, 0xb0, 0x15, 0x00, 0x00, - 0xb0, 0x15, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, - 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x88, 0x16, 0x00, 0x00, - 0x9c, 0x1c, 0x00, 0x00, 0xc4, 0x1f, 0x00, 0x00, 0x84, 0x1f, 0x00, 0x00, - 0xa0, 0x1f, 0x00, 0x00, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3, + 0xf0, 0x13, 0x00, 0x00, 0xf8, 0x13, 0x00, 0x00, 0x8c, 0x14, 0x00, 0x00, + 0x08, 0x15, 0x00, 0x00, 0xb4, 0x15, 0x00, 0x00, 0xb8, 0x15, 0x00, 0x00, + 0xb8, 0x15, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, + 0x2c, 0x11, 0x00, 0x00, 0x2c, 0x11, 0x00, 0x00, 0x90, 0x16, 0x00, 0x00, + 0xa4, 0x1c, 0x00, 0x00, 0xcc, 0x1f, 0x00, 0x00, 0x8c, 0x1f, 0x00, 0x00, + 0xa8, 0x1f, 0x00, 0x00, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3, 0x04, 0xf0, 0x29, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xf0, 0x69, 0xe1, 0x10, 0x50, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x20, 0xa0, 0xe3, 0x01, 0x23, 0xc0, 0xe5, @@ -442,205 +442,206 @@ std::array bios_arm7_bin = { 0x01, 0xf0, 0x00, 0x3c, 0x00, 0x28, 0x01, 0xe4, 0x01, 0xa0, 0x00, 0x6c, 0x00, 0x78, 0x01, 0xb4, 0x00, 0x50, 0x01, 0x9c, 0x01, 0x88, 0x00, 0x44, 0x20, 0x00, 0x2d, 0xe9, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2, - 0xa2, 0x20, 0xb0, 0xe1, 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, + 0xff, 0x04, 0xc0, 0xe3, 0xff, 0x08, 0xc0, 0xe3, 0xa2, 0x20, 0xb0, 0xe1, + 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, 0x80, 0x50, 0x04, 0xe0, + 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, + 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, + 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, - 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, + 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, - 0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, - 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, - 0x20, 0x00, 0xbd, 0xe8, 0x50, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, - 0x4e, 0xff, 0xff, 0xea, 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, - 0x02, 0xc0, 0xd2, 0xe5, 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, - 0xa4, 0x2f, 0xa0, 0xe1, 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, - 0x15, 0x5c, 0xa0, 0xe1, 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, - 0x00, 0x70, 0xa0, 0xe3, 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, - 0x83, 0x31, 0xa0, 0x01, 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, - 0x04, 0x00, 0x5c, 0xe3, 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, - 0x01, 0x60, 0xd0, 0x04, 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, - 0x36, 0x6c, 0xa0, 0xe1, 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, - 0x04, 0x90, 0x89, 0x10, 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, - 0x20, 0x00, 0x58, 0xe3, 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, - 0x00, 0x80, 0xa0, 0x03, 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, - 0xe0, 0x03, 0xbd, 0xe8, 0x29, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0x23, 0x24, 0xb0, 0xe1, 0x26, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, - 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, - 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, - 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, - 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x16, 0xff, 0xff, 0x0a, - 0x01, 0xe0, 0x5e, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, - 0xed, 0xff, 0xff, 0x5a, 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x0d, 0xff, 0xff, 0x0a, - 0x83, 0x30, 0xb0, 0xe1, 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, - 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, 0x07, 0xff, 0xff, 0x0a, - 0x20, 0x00, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, - 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, - 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, - 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, - 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, - 0x01, 0x50, 0xdc, 0x04, 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, - 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, - 0x20, 0x00, 0xbd, 0x08, 0xf0, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, - 0xf4, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, - 0xe5, 0xff, 0xff, 0xea, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, - 0x01, 0x40, 0xd0, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, + 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, + 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, 0x20, 0x00, 0xbd, 0xe8, + 0x4e, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, 0x4c, 0xff, 0xff, 0xea, + 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, 0x02, 0xc0, 0xd2, 0xe5, + 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, 0xa4, 0x2f, 0xa0, 0xe1, + 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, 0x15, 0x5c, 0xa0, 0xe1, + 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3, + 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, 0x83, 0x31, 0xa0, 0x01, + 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, 0x04, 0x00, 0x5c, 0xe3, + 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, 0x01, 0x60, 0xd0, 0x04, + 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, 0x36, 0x6c, 0xa0, 0xe1, + 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, 0x04, 0x90, 0x89, 0x10, + 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, 0x20, 0x00, 0x58, 0xe3, + 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, 0x00, 0x80, 0xa0, 0x03, + 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, 0xe0, 0x03, 0xbd, 0xe8, + 0x27, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, + 0x24, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3, + 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4, + 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1, + 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0, + 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0x40, 0xc1, 0xe4, 0x14, 0xff, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, + 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xed, 0xff, 0xff, 0x5a, + 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0x40, 0xc1, 0xe4, 0x0b, 0xff, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, + 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, + 0x23, 0x24, 0xb0, 0xe1, 0x05, 0xff, 0xff, 0x0a, 0x20, 0x00, 0x2d, 0xe9, + 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3, + 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4, + 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1, + 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0, + 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xdc, 0x04, + 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, - 0xe2, 0xfe, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, - 0xd9, 0xff, 0xff, 0xea, 0xde, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0x23, 0x24, 0xa0, 0xe1, 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, - 0x7f, 0x30, 0x03, 0xe2, 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, - 0x03, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, - 0xd3, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, - 0xf3, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0xcb, 0xfe, 0xff, 0x0a, - 0x01, 0x30, 0x53, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, - 0x00, 0x00, 0x24, 0x03, 0x48, 0x06, 0x6a, 0x09, 0x8c, 0x0c, 0xab, 0x0f, - 0xc8, 0x12, 0xe2, 0x15, 0xf9, 0x18, 0x0b, 0x1c, 0x1a, 0x1f, 0x23, 0x22, - 0x28, 0x25, 0x26, 0x28, 0x1f, 0x2b, 0x11, 0x2e, 0xfb, 0x30, 0xdf, 0x33, - 0xba, 0x36, 0x8c, 0x39, 0x56, 0x3c, 0x17, 0x3f, 0xce, 0x41, 0x7a, 0x44, - 0x1c, 0x47, 0xb4, 0x49, 0x3f, 0x4c, 0xbf, 0x4e, 0x33, 0x51, 0x9b, 0x53, - 0xf5, 0x55, 0x42, 0x58, 0x82, 0x5a, 0xb3, 0x5c, 0xd7, 0x5e, 0xeb, 0x60, - 0xf1, 0x62, 0xe8, 0x64, 0xcf, 0x66, 0xa6, 0x68, 0x6d, 0x6a, 0x23, 0x6c, - 0xc9, 0x6d, 0x5e, 0x6f, 0xe2, 0x70, 0x54, 0x72, 0xb5, 0x73, 0x04, 0x75, - 0x41, 0x76, 0x6b, 0x77, 0x84, 0x78, 0x89, 0x79, 0x7c, 0x7a, 0x5c, 0x7b, - 0x29, 0x7c, 0xe3, 0x7c, 0x89, 0x7d, 0x1d, 0x7e, 0x9c, 0x7e, 0x09, 0x7f, - 0x61, 0x7f, 0xa6, 0x7f, 0xd8, 0x7f, 0xf5, 0x7f, 0x00, 0x00, 0x80, 0xe0, - 0x8c, 0x10, 0x4f, 0xe2, 0xb0, 0x00, 0x91, 0xe1, 0xa4, 0xfe, 0xff, 0xea, - 0x00, 0x00, 0x3b, 0x00, 0x76, 0x00, 0xb2, 0x00, 0xed, 0x00, 0x28, 0x01, - 0x64, 0x01, 0x9f, 0x01, 0xdb, 0x01, 0x17, 0x02, 0x52, 0x02, 0x8e, 0x02, - 0xca, 0x02, 0x05, 0x03, 0x41, 0x03, 0x7d, 0x03, 0xb9, 0x03, 0xf5, 0x03, - 0x31, 0x04, 0x6e, 0x04, 0xaa, 0x04, 0xe6, 0x04, 0x22, 0x05, 0x5f, 0x05, - 0x9b, 0x05, 0xd8, 0x05, 0x14, 0x06, 0x51, 0x06, 0x8d, 0x06, 0xca, 0x06, - 0x07, 0x07, 0x43, 0x07, 0x80, 0x07, 0xbd, 0x07, 0xfa, 0x07, 0x37, 0x08, - 0x74, 0x08, 0xb1, 0x08, 0xef, 0x08, 0x2c, 0x09, 0x69, 0x09, 0xa7, 0x09, - 0xe4, 0x09, 0x21, 0x0a, 0x5f, 0x0a, 0x9c, 0x0a, 0xda, 0x0a, 0x18, 0x0b, - 0x56, 0x0b, 0x93, 0x0b, 0xd1, 0x0b, 0x0f, 0x0c, 0x4d, 0x0c, 0x8b, 0x0c, - 0xc9, 0x0c, 0x07, 0x0d, 0x45, 0x0d, 0x84, 0x0d, 0xc2, 0x0d, 0x00, 0x0e, - 0x3f, 0x0e, 0x7d, 0x0e, 0xbc, 0x0e, 0xfa, 0x0e, 0x39, 0x0f, 0x78, 0x0f, - 0xb6, 0x0f, 0xf5, 0x0f, 0x34, 0x10, 0x73, 0x10, 0xb2, 0x10, 0xf1, 0x10, - 0x30, 0x11, 0x6f, 0x11, 0xae, 0x11, 0xee, 0x11, 0x2d, 0x12, 0x6c, 0x12, - 0xac, 0x12, 0xeb, 0x12, 0x2b, 0x13, 0x6b, 0x13, 0xaa, 0x13, 0xea, 0x13, - 0x2a, 0x14, 0x6a, 0x14, 0xa9, 0x14, 0xe9, 0x14, 0x29, 0x15, 0x69, 0x15, - 0xaa, 0x15, 0xea, 0x15, 0x2a, 0x16, 0x6a, 0x16, 0xab, 0x16, 0xeb, 0x16, - 0x2c, 0x17, 0x6c, 0x17, 0xad, 0x17, 0xed, 0x17, 0x2e, 0x18, 0x6f, 0x18, - 0xb0, 0x18, 0xf0, 0x18, 0x31, 0x19, 0x72, 0x19, 0xb3, 0x19, 0xf5, 0x19, - 0x36, 0x1a, 0x77, 0x1a, 0xb8, 0x1a, 0xfa, 0x1a, 0x3b, 0x1b, 0x7d, 0x1b, - 0xbe, 0x1b, 0x00, 0x1c, 0x41, 0x1c, 0x83, 0x1c, 0xc5, 0x1c, 0x07, 0x1d, - 0x48, 0x1d, 0x8a, 0x1d, 0xcc, 0x1d, 0x0e, 0x1e, 0x51, 0x1e, 0x93, 0x1e, - 0xd5, 0x1e, 0x17, 0x1f, 0x5a, 0x1f, 0x9c, 0x1f, 0xdf, 0x1f, 0x21, 0x20, - 0x64, 0x20, 0xa6, 0x20, 0xe9, 0x20, 0x2c, 0x21, 0x6f, 0x21, 0xb2, 0x21, - 0xf5, 0x21, 0x38, 0x22, 0x7b, 0x22, 0xbe, 0x22, 0x01, 0x23, 0x44, 0x23, - 0x88, 0x23, 0xcb, 0x23, 0x0e, 0x24, 0x52, 0x24, 0x96, 0x24, 0xd9, 0x24, - 0x1d, 0x25, 0x61, 0x25, 0xa4, 0x25, 0xe8, 0x25, 0x2c, 0x26, 0x70, 0x26, - 0xb4, 0x26, 0xf8, 0x26, 0x3d, 0x27, 0x81, 0x27, 0xc5, 0x27, 0x0a, 0x28, - 0x4e, 0x28, 0x92, 0x28, 0xd7, 0x28, 0x1c, 0x29, 0x60, 0x29, 0xa5, 0x29, - 0xea, 0x29, 0x2f, 0x2a, 0x74, 0x2a, 0xb9, 0x2a, 0xfe, 0x2a, 0x43, 0x2b, - 0x88, 0x2b, 0xcd, 0x2b, 0x13, 0x2c, 0x58, 0x2c, 0x9d, 0x2c, 0xe3, 0x2c, - 0x28, 0x2d, 0x6e, 0x2d, 0xb4, 0x2d, 0xf9, 0x2d, 0x3f, 0x2e, 0x85, 0x2e, - 0xcb, 0x2e, 0x11, 0x2f, 0x57, 0x2f, 0x9d, 0x2f, 0xe3, 0x2f, 0x2a, 0x30, - 0x70, 0x30, 0xb6, 0x30, 0xfd, 0x30, 0x43, 0x31, 0x8a, 0x31, 0xd0, 0x31, - 0x17, 0x32, 0x5e, 0x32, 0xa5, 0x32, 0xec, 0x32, 0x32, 0x33, 0x79, 0x33, - 0xc1, 0x33, 0x08, 0x34, 0x4f, 0x34, 0x96, 0x34, 0xdd, 0x34, 0x25, 0x35, - 0x6c, 0x35, 0xb4, 0x35, 0xfb, 0x35, 0x43, 0x36, 0x8b, 0x36, 0xd3, 0x36, - 0x1a, 0x37, 0x62, 0x37, 0xaa, 0x37, 0xf2, 0x37, 0x3a, 0x38, 0x83, 0x38, - 0xcb, 0x38, 0x13, 0x39, 0x5c, 0x39, 0xa4, 0x39, 0xed, 0x39, 0x35, 0x3a, - 0x7e, 0x3a, 0xc6, 0x3a, 0x0f, 0x3b, 0x58, 0x3b, 0xa1, 0x3b, 0xea, 0x3b, - 0x33, 0x3c, 0x7c, 0x3c, 0xc5, 0x3c, 0x0e, 0x3d, 0x58, 0x3d, 0xa1, 0x3d, - 0xea, 0x3d, 0x34, 0x3e, 0x7d, 0x3e, 0xc7, 0x3e, 0x11, 0x3f, 0x5a, 0x3f, - 0xa4, 0x3f, 0xee, 0x3f, 0x38, 0x40, 0x82, 0x40, 0xcc, 0x40, 0x16, 0x41, - 0x61, 0x41, 0xab, 0x41, 0xf5, 0x41, 0x40, 0x42, 0x8a, 0x42, 0xd5, 0x42, - 0x1f, 0x43, 0x6a, 0x43, 0xb5, 0x43, 0x00, 0x44, 0x4b, 0x44, 0x95, 0x44, - 0xe1, 0x44, 0x2c, 0x45, 0x77, 0x45, 0xc2, 0x45, 0x0d, 0x46, 0x59, 0x46, - 0xa4, 0x46, 0xf0, 0x46, 0x3b, 0x47, 0x87, 0x47, 0xd3, 0x47, 0x1e, 0x48, - 0x6a, 0x48, 0xb6, 0x48, 0x02, 0x49, 0x4e, 0x49, 0x9a, 0x49, 0xe6, 0x49, - 0x33, 0x4a, 0x7f, 0x4a, 0xcb, 0x4a, 0x18, 0x4b, 0x64, 0x4b, 0xb1, 0x4b, - 0xfe, 0x4b, 0x4a, 0x4c, 0x97, 0x4c, 0xe4, 0x4c, 0x31, 0x4d, 0x7e, 0x4d, - 0xcb, 0x4d, 0x18, 0x4e, 0x66, 0x4e, 0xb3, 0x4e, 0x00, 0x4f, 0x4e, 0x4f, - 0x9b, 0x4f, 0xe9, 0x4f, 0x36, 0x50, 0x84, 0x50, 0xd2, 0x50, 0x20, 0x51, - 0x6e, 0x51, 0xbc, 0x51, 0x0a, 0x52, 0x58, 0x52, 0xa6, 0x52, 0xf4, 0x52, - 0x43, 0x53, 0x91, 0x53, 0xe0, 0x53, 0x2e, 0x54, 0x7d, 0x54, 0xcc, 0x54, - 0x1a, 0x55, 0x69, 0x55, 0xb8, 0x55, 0x07, 0x56, 0x56, 0x56, 0xa5, 0x56, - 0xf4, 0x56, 0x44, 0x57, 0x93, 0x57, 0xe2, 0x57, 0x32, 0x58, 0x82, 0x58, - 0xd1, 0x58, 0x21, 0x59, 0x71, 0x59, 0xc1, 0x59, 0x10, 0x5a, 0x60, 0x5a, - 0xb0, 0x5a, 0x01, 0x5b, 0x51, 0x5b, 0xa1, 0x5b, 0xf1, 0x5b, 0x42, 0x5c, - 0x92, 0x5c, 0xe3, 0x5c, 0x34, 0x5d, 0x84, 0x5d, 0xd5, 0x5d, 0x26, 0x5e, - 0x77, 0x5e, 0xc8, 0x5e, 0x19, 0x5f, 0x6a, 0x5f, 0xbb, 0x5f, 0x0d, 0x60, - 0x5e, 0x60, 0xb0, 0x60, 0x01, 0x61, 0x53, 0x61, 0xa4, 0x61, 0xf6, 0x61, - 0x48, 0x62, 0x9a, 0x62, 0xec, 0x62, 0x3e, 0x63, 0x90, 0x63, 0xe2, 0x63, - 0x34, 0x64, 0x87, 0x64, 0xd9, 0x64, 0x2c, 0x65, 0x7e, 0x65, 0xd1, 0x65, - 0x24, 0x66, 0x76, 0x66, 0xc9, 0x66, 0x1c, 0x67, 0x6f, 0x67, 0xc2, 0x67, - 0x15, 0x68, 0x69, 0x68, 0xbc, 0x68, 0x0f, 0x69, 0x63, 0x69, 0xb6, 0x69, - 0x0a, 0x6a, 0x5e, 0x6a, 0xb1, 0x6a, 0x05, 0x6b, 0x59, 0x6b, 0xad, 0x6b, - 0x01, 0x6c, 0x55, 0x6c, 0xaa, 0x6c, 0xfe, 0x6c, 0x52, 0x6d, 0xa7, 0x6d, - 0xfb, 0x6d, 0x50, 0x6e, 0xa4, 0x6e, 0xf9, 0x6e, 0x4e, 0x6f, 0xa3, 0x6f, - 0xf8, 0x6f, 0x4d, 0x70, 0xa2, 0x70, 0xf7, 0x70, 0x4d, 0x71, 0xa2, 0x71, - 0xf7, 0x71, 0x4d, 0x72, 0xa2, 0x72, 0xf8, 0x72, 0x4e, 0x73, 0xa4, 0x73, - 0xfa, 0x73, 0x50, 0x74, 0xa6, 0x74, 0xfc, 0x74, 0x52, 0x75, 0xa8, 0x75, - 0xff, 0x75, 0x55, 0x76, 0xac, 0x76, 0x02, 0x77, 0x59, 0x77, 0xb0, 0x77, - 0x07, 0x78, 0x5e, 0x78, 0xb4, 0x78, 0x0c, 0x79, 0x63, 0x79, 0xba, 0x79, - 0x11, 0x7a, 0x69, 0x7a, 0xc0, 0x7a, 0x18, 0x7b, 0x6f, 0x7b, 0xc7, 0x7b, - 0x1f, 0x7c, 0x77, 0x7c, 0xcf, 0x7c, 0x27, 0x7d, 0x7f, 0x7d, 0xd7, 0x7d, - 0x2f, 0x7e, 0x88, 0x7e, 0xe0, 0x7e, 0x38, 0x7f, 0x91, 0x7f, 0xea, 0x7f, - 0x42, 0x80, 0x9b, 0x80, 0xf4, 0x80, 0x4d, 0x81, 0xa6, 0x81, 0xff, 0x81, - 0x59, 0x82, 0xb2, 0x82, 0x0b, 0x83, 0x65, 0x83, 0xbe, 0x83, 0x18, 0x84, - 0x72, 0x84, 0xcb, 0x84, 0x25, 0x85, 0x7f, 0x85, 0xd9, 0x85, 0x33, 0x86, - 0x8e, 0x86, 0xe8, 0x86, 0x42, 0x87, 0x9d, 0x87, 0xf7, 0x87, 0x52, 0x88, - 0xac, 0x88, 0x07, 0x89, 0x62, 0x89, 0xbd, 0x89, 0x18, 0x8a, 0x73, 0x8a, - 0xce, 0x8a, 0x2a, 0x8b, 0x85, 0x8b, 0xe0, 0x8b, 0x3c, 0x8c, 0x97, 0x8c, - 0xf3, 0x8c, 0x4f, 0x8d, 0xab, 0x8d, 0x07, 0x8e, 0x63, 0x8e, 0xbf, 0x8e, - 0x1b, 0x8f, 0x77, 0x8f, 0xd4, 0x8f, 0x30, 0x90, 0x8c, 0x90, 0xe9, 0x90, - 0x46, 0x91, 0xa2, 0x91, 0xff, 0x91, 0x5c, 0x92, 0xb9, 0x92, 0x16, 0x93, - 0x73, 0x93, 0xd1, 0x93, 0x2e, 0x94, 0x8c, 0x94, 0xe9, 0x94, 0x47, 0x95, - 0xa4, 0x95, 0x02, 0x96, 0x60, 0x96, 0xbe, 0x96, 0x1c, 0x97, 0x7a, 0x97, - 0xd8, 0x97, 0x36, 0x98, 0x95, 0x98, 0xf3, 0x98, 0x52, 0x99, 0xb0, 0x99, - 0x0f, 0x9a, 0x6e, 0x9a, 0xcd, 0x9a, 0x2c, 0x9b, 0x8b, 0x9b, 0xea, 0x9b, - 0x49, 0x9c, 0xa8, 0x9c, 0x08, 0x9d, 0x67, 0x9d, 0xc7, 0x9d, 0x26, 0x9e, - 0x86, 0x9e, 0xe6, 0x9e, 0x46, 0x9f, 0xa6, 0x9f, 0x06, 0xa0, 0x66, 0xa0, - 0xc6, 0xa0, 0x27, 0xa1, 0x87, 0xa1, 0xe8, 0xa1, 0x48, 0xa2, 0xa9, 0xa2, - 0x0a, 0xa3, 0x6b, 0xa3, 0xcc, 0xa3, 0x2d, 0xa4, 0x8e, 0xa4, 0xef, 0xa4, - 0x50, 0xa5, 0xb2, 0xa5, 0x13, 0xa6, 0x75, 0xa6, 0xd6, 0xa6, 0x38, 0xa7, - 0x9a, 0xa7, 0xfc, 0xa7, 0x5e, 0xa8, 0xc0, 0xa8, 0x22, 0xa9, 0x84, 0xa9, - 0xe7, 0xa9, 0x49, 0xaa, 0xac, 0xaa, 0x0e, 0xab, 0x71, 0xab, 0xd4, 0xab, - 0x37, 0xac, 0x9a, 0xac, 0xfd, 0xac, 0x60, 0xad, 0xc3, 0xad, 0x27, 0xae, - 0x8a, 0xae, 0xed, 0xae, 0x51, 0xaf, 0xb5, 0xaf, 0x19, 0xb0, 0x7c, 0xb0, - 0xe0, 0xb0, 0x45, 0xb1, 0xa9, 0xb1, 0x0d, 0xb2, 0x71, 0xb2, 0xd6, 0xb2, - 0x3a, 0xb3, 0x9f, 0xb3, 0x03, 0xb4, 0x68, 0xb4, 0xcd, 0xb4, 0x32, 0xb5, - 0x97, 0xb5, 0xfc, 0xb5, 0x62, 0xb6, 0xc7, 0xb6, 0x2c, 0xb7, 0x92, 0xb7, - 0xf7, 0xb7, 0x5d, 0xb8, 0xc3, 0xb8, 0x29, 0xb9, 0x8f, 0xb9, 0xf5, 0xb9, - 0x5b, 0xba, 0xc1, 0xba, 0x28, 0xbb, 0x8e, 0xbb, 0xf5, 0xbb, 0x5b, 0xbc, - 0xc2, 0xbc, 0x29, 0xbd, 0x90, 0xbd, 0xf7, 0xbd, 0x5e, 0xbe, 0xc5, 0xbe, - 0x2c, 0xbf, 0x94, 0xbf, 0xfb, 0xbf, 0x63, 0xc0, 0xca, 0xc0, 0x32, 0xc1, - 0x9a, 0xc1, 0x02, 0xc2, 0x6a, 0xc2, 0xd2, 0xc2, 0x3a, 0xc3, 0xa2, 0xc3, - 0x0b, 0xc4, 0x73, 0xc4, 0xdc, 0xc4, 0x44, 0xc5, 0xad, 0xc5, 0x16, 0xc6, - 0x7f, 0xc6, 0xe8, 0xc6, 0x51, 0xc7, 0xbb, 0xc7, 0x24, 0xc8, 0x8d, 0xc8, - 0xf7, 0xc8, 0x60, 0xc9, 0xca, 0xc9, 0x34, 0xca, 0x9e, 0xca, 0x08, 0xcb, - 0x72, 0xcb, 0xdc, 0xcb, 0x47, 0xcc, 0xb1, 0xcc, 0x1b, 0xcd, 0x86, 0xcd, - 0xf1, 0xcd, 0x5b, 0xce, 0xc6, 0xce, 0x31, 0xcf, 0x9c, 0xcf, 0x08, 0xd0, - 0x73, 0xd0, 0xde, 0xd0, 0x4a, 0xd1, 0xb5, 0xd1, 0x21, 0xd2, 0x8d, 0xd2, - 0xf8, 0xd2, 0x64, 0xd3, 0xd0, 0xd3, 0x3d, 0xd4, 0xa9, 0xd4, 0x15, 0xd5, - 0x82, 0xd5, 0xee, 0xd5, 0x5b, 0xd6, 0xc7, 0xd6, 0x34, 0xd7, 0xa1, 0xd7, - 0x0e, 0xd8, 0x7b, 0xd8, 0xe9, 0xd8, 0x56, 0xd9, 0xc3, 0xd9, 0x31, 0xda, - 0x9e, 0xda, 0x0c, 0xdb, 0x7a, 0xdb, 0xe8, 0xdb, 0x56, 0xdc, 0xc4, 0xdc, - 0x32, 0xdd, 0xa0, 0xdd, 0x0f, 0xde, 0x7d, 0xde, 0xec, 0xde, 0x5b, 0xdf, - 0xc9, 0xdf, 0x38, 0xe0, 0xa7, 0xe0, 0x16, 0xe1, 0x86, 0xe1, 0xf5, 0xe1, - 0x64, 0xe2, 0xd4, 0xe2, 0x43, 0xe3, 0xb3, 0xe3, 0x23, 0xe4, 0x93, 0xe4, - 0x03, 0xe5, 0x73, 0xe5, 0xe3, 0xe5, 0x54, 0xe6, 0xc4, 0xe6, 0x35, 0xe7, - 0xa5, 0xe7, 0x16, 0xe8, 0x87, 0xe8, 0xf8, 0xe8, 0x69, 0xe9, 0xda, 0xe9, - 0x4b, 0xea, 0xbc, 0xea, 0x2e, 0xeb, 0x9f, 0xeb, 0x11, 0xec, 0x83, 0xec, - 0xf5, 0xec, 0x66, 0xed, 0xd9, 0xed, 0x4b, 0xee, 0xbd, 0xee, 0x2f, 0xef, - 0xa2, 0xef, 0x14, 0xf0, 0x87, 0xf0, 0xfa, 0xf0, 0x6d, 0xf1, 0xe0, 0xf1, - 0x53, 0xf2, 0xc6, 0xf2, 0x39, 0xf3, 0xad, 0xf3, 0x20, 0xf4, 0x94, 0xf4, - 0x07, 0xf5, 0x7b, 0xf5, 0xef, 0xf5, 0x63, 0xf6, 0xd7, 0xf6, 0x4c, 0xf7, - 0xc0, 0xf7, 0x34, 0xf8, 0xa9, 0xf8, 0x1e, 0xf9, 0x92, 0xf9, 0x07, 0xfa, - 0x7c, 0xfa, 0xf1, 0xfa, 0x66, 0xfb, 0xdc, 0xfb, 0x51, 0xfc, 0xc7, 0xfc, - 0x3c, 0xfd, 0xb2, 0xfd, 0x28, 0xfe, 0x9e, 0xfe, 0x14, 0xff, 0x8a, 0xff, - 0x98, 0x16, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0, 0x10, 0x10, 0x1f, 0xe5, - 0xb0, 0x00, 0x91, 0xe1, 0x1f, 0xfd, 0xff, 0xea, 0x00, 0x01, 0x01, 0x01, + 0xee, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, 0xf4, 0xff, 0xff, 0x1a, + 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, 0xe5, 0xff, 0xff, 0xea, + 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, 0x01, 0x40, 0xd0, 0x14, + 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, + 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, 0xe0, 0xfe, 0xff, 0x0a, + 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, 0xd9, 0xff, 0xff, 0xea, + 0xdc, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xa0, 0xe1, + 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, 0x7f, 0x30, 0x03, 0xe2, + 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2, + 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xd1, 0xfe, 0xff, 0x0a, + 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xf3, 0xff, 0xff, 0xea, + 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0xc0, 0xc1, 0xe4, 0xc9, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, + 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, 0x00, 0x00, 0x24, 0x03, + 0x48, 0x06, 0x6a, 0x09, 0x8c, 0x0c, 0xab, 0x0f, 0xc8, 0x12, 0xe2, 0x15, + 0xf9, 0x18, 0x0b, 0x1c, 0x1a, 0x1f, 0x23, 0x22, 0x28, 0x25, 0x26, 0x28, + 0x1f, 0x2b, 0x11, 0x2e, 0xfb, 0x30, 0xdf, 0x33, 0xba, 0x36, 0x8c, 0x39, + 0x56, 0x3c, 0x17, 0x3f, 0xce, 0x41, 0x7a, 0x44, 0x1c, 0x47, 0xb4, 0x49, + 0x3f, 0x4c, 0xbf, 0x4e, 0x33, 0x51, 0x9b, 0x53, 0xf5, 0x55, 0x42, 0x58, + 0x82, 0x5a, 0xb3, 0x5c, 0xd7, 0x5e, 0xeb, 0x60, 0xf1, 0x62, 0xe8, 0x64, + 0xcf, 0x66, 0xa6, 0x68, 0x6d, 0x6a, 0x23, 0x6c, 0xc9, 0x6d, 0x5e, 0x6f, + 0xe2, 0x70, 0x54, 0x72, 0xb5, 0x73, 0x04, 0x75, 0x41, 0x76, 0x6b, 0x77, + 0x84, 0x78, 0x89, 0x79, 0x7c, 0x7a, 0x5c, 0x7b, 0x29, 0x7c, 0xe3, 0x7c, + 0x89, 0x7d, 0x1d, 0x7e, 0x9c, 0x7e, 0x09, 0x7f, 0x61, 0x7f, 0xa6, 0x7f, + 0xd8, 0x7f, 0xf5, 0x7f, 0x00, 0x00, 0x80, 0xe0, 0x8c, 0x10, 0x4f, 0xe2, + 0xb0, 0x00, 0x91, 0xe1, 0xa2, 0xfe, 0xff, 0xea, 0x00, 0x00, 0x3b, 0x00, + 0x76, 0x00, 0xb2, 0x00, 0xed, 0x00, 0x28, 0x01, 0x64, 0x01, 0x9f, 0x01, + 0xdb, 0x01, 0x17, 0x02, 0x52, 0x02, 0x8e, 0x02, 0xca, 0x02, 0x05, 0x03, + 0x41, 0x03, 0x7d, 0x03, 0xb9, 0x03, 0xf5, 0x03, 0x31, 0x04, 0x6e, 0x04, + 0xaa, 0x04, 0xe6, 0x04, 0x22, 0x05, 0x5f, 0x05, 0x9b, 0x05, 0xd8, 0x05, + 0x14, 0x06, 0x51, 0x06, 0x8d, 0x06, 0xca, 0x06, 0x07, 0x07, 0x43, 0x07, + 0x80, 0x07, 0xbd, 0x07, 0xfa, 0x07, 0x37, 0x08, 0x74, 0x08, 0xb1, 0x08, + 0xef, 0x08, 0x2c, 0x09, 0x69, 0x09, 0xa7, 0x09, 0xe4, 0x09, 0x21, 0x0a, + 0x5f, 0x0a, 0x9c, 0x0a, 0xda, 0x0a, 0x18, 0x0b, 0x56, 0x0b, 0x93, 0x0b, + 0xd1, 0x0b, 0x0f, 0x0c, 0x4d, 0x0c, 0x8b, 0x0c, 0xc9, 0x0c, 0x07, 0x0d, + 0x45, 0x0d, 0x84, 0x0d, 0xc2, 0x0d, 0x00, 0x0e, 0x3f, 0x0e, 0x7d, 0x0e, + 0xbc, 0x0e, 0xfa, 0x0e, 0x39, 0x0f, 0x78, 0x0f, 0xb6, 0x0f, 0xf5, 0x0f, + 0x34, 0x10, 0x73, 0x10, 0xb2, 0x10, 0xf1, 0x10, 0x30, 0x11, 0x6f, 0x11, + 0xae, 0x11, 0xee, 0x11, 0x2d, 0x12, 0x6c, 0x12, 0xac, 0x12, 0xeb, 0x12, + 0x2b, 0x13, 0x6b, 0x13, 0xaa, 0x13, 0xea, 0x13, 0x2a, 0x14, 0x6a, 0x14, + 0xa9, 0x14, 0xe9, 0x14, 0x29, 0x15, 0x69, 0x15, 0xaa, 0x15, 0xea, 0x15, + 0x2a, 0x16, 0x6a, 0x16, 0xab, 0x16, 0xeb, 0x16, 0x2c, 0x17, 0x6c, 0x17, + 0xad, 0x17, 0xed, 0x17, 0x2e, 0x18, 0x6f, 0x18, 0xb0, 0x18, 0xf0, 0x18, + 0x31, 0x19, 0x72, 0x19, 0xb3, 0x19, 0xf5, 0x19, 0x36, 0x1a, 0x77, 0x1a, + 0xb8, 0x1a, 0xfa, 0x1a, 0x3b, 0x1b, 0x7d, 0x1b, 0xbe, 0x1b, 0x00, 0x1c, + 0x41, 0x1c, 0x83, 0x1c, 0xc5, 0x1c, 0x07, 0x1d, 0x48, 0x1d, 0x8a, 0x1d, + 0xcc, 0x1d, 0x0e, 0x1e, 0x51, 0x1e, 0x93, 0x1e, 0xd5, 0x1e, 0x17, 0x1f, + 0x5a, 0x1f, 0x9c, 0x1f, 0xdf, 0x1f, 0x21, 0x20, 0x64, 0x20, 0xa6, 0x20, + 0xe9, 0x20, 0x2c, 0x21, 0x6f, 0x21, 0xb2, 0x21, 0xf5, 0x21, 0x38, 0x22, + 0x7b, 0x22, 0xbe, 0x22, 0x01, 0x23, 0x44, 0x23, 0x88, 0x23, 0xcb, 0x23, + 0x0e, 0x24, 0x52, 0x24, 0x96, 0x24, 0xd9, 0x24, 0x1d, 0x25, 0x61, 0x25, + 0xa4, 0x25, 0xe8, 0x25, 0x2c, 0x26, 0x70, 0x26, 0xb4, 0x26, 0xf8, 0x26, + 0x3d, 0x27, 0x81, 0x27, 0xc5, 0x27, 0x0a, 0x28, 0x4e, 0x28, 0x92, 0x28, + 0xd7, 0x28, 0x1c, 0x29, 0x60, 0x29, 0xa5, 0x29, 0xea, 0x29, 0x2f, 0x2a, + 0x74, 0x2a, 0xb9, 0x2a, 0xfe, 0x2a, 0x43, 0x2b, 0x88, 0x2b, 0xcd, 0x2b, + 0x13, 0x2c, 0x58, 0x2c, 0x9d, 0x2c, 0xe3, 0x2c, 0x28, 0x2d, 0x6e, 0x2d, + 0xb4, 0x2d, 0xf9, 0x2d, 0x3f, 0x2e, 0x85, 0x2e, 0xcb, 0x2e, 0x11, 0x2f, + 0x57, 0x2f, 0x9d, 0x2f, 0xe3, 0x2f, 0x2a, 0x30, 0x70, 0x30, 0xb6, 0x30, + 0xfd, 0x30, 0x43, 0x31, 0x8a, 0x31, 0xd0, 0x31, 0x17, 0x32, 0x5e, 0x32, + 0xa5, 0x32, 0xec, 0x32, 0x32, 0x33, 0x79, 0x33, 0xc1, 0x33, 0x08, 0x34, + 0x4f, 0x34, 0x96, 0x34, 0xdd, 0x34, 0x25, 0x35, 0x6c, 0x35, 0xb4, 0x35, + 0xfb, 0x35, 0x43, 0x36, 0x8b, 0x36, 0xd3, 0x36, 0x1a, 0x37, 0x62, 0x37, + 0xaa, 0x37, 0xf2, 0x37, 0x3a, 0x38, 0x83, 0x38, 0xcb, 0x38, 0x13, 0x39, + 0x5c, 0x39, 0xa4, 0x39, 0xed, 0x39, 0x35, 0x3a, 0x7e, 0x3a, 0xc6, 0x3a, + 0x0f, 0x3b, 0x58, 0x3b, 0xa1, 0x3b, 0xea, 0x3b, 0x33, 0x3c, 0x7c, 0x3c, + 0xc5, 0x3c, 0x0e, 0x3d, 0x58, 0x3d, 0xa1, 0x3d, 0xea, 0x3d, 0x34, 0x3e, + 0x7d, 0x3e, 0xc7, 0x3e, 0x11, 0x3f, 0x5a, 0x3f, 0xa4, 0x3f, 0xee, 0x3f, + 0x38, 0x40, 0x82, 0x40, 0xcc, 0x40, 0x16, 0x41, 0x61, 0x41, 0xab, 0x41, + 0xf5, 0x41, 0x40, 0x42, 0x8a, 0x42, 0xd5, 0x42, 0x1f, 0x43, 0x6a, 0x43, + 0xb5, 0x43, 0x00, 0x44, 0x4b, 0x44, 0x95, 0x44, 0xe1, 0x44, 0x2c, 0x45, + 0x77, 0x45, 0xc2, 0x45, 0x0d, 0x46, 0x59, 0x46, 0xa4, 0x46, 0xf0, 0x46, + 0x3b, 0x47, 0x87, 0x47, 0xd3, 0x47, 0x1e, 0x48, 0x6a, 0x48, 0xb6, 0x48, + 0x02, 0x49, 0x4e, 0x49, 0x9a, 0x49, 0xe6, 0x49, 0x33, 0x4a, 0x7f, 0x4a, + 0xcb, 0x4a, 0x18, 0x4b, 0x64, 0x4b, 0xb1, 0x4b, 0xfe, 0x4b, 0x4a, 0x4c, + 0x97, 0x4c, 0xe4, 0x4c, 0x31, 0x4d, 0x7e, 0x4d, 0xcb, 0x4d, 0x18, 0x4e, + 0x66, 0x4e, 0xb3, 0x4e, 0x00, 0x4f, 0x4e, 0x4f, 0x9b, 0x4f, 0xe9, 0x4f, + 0x36, 0x50, 0x84, 0x50, 0xd2, 0x50, 0x20, 0x51, 0x6e, 0x51, 0xbc, 0x51, + 0x0a, 0x52, 0x58, 0x52, 0xa6, 0x52, 0xf4, 0x52, 0x43, 0x53, 0x91, 0x53, + 0xe0, 0x53, 0x2e, 0x54, 0x7d, 0x54, 0xcc, 0x54, 0x1a, 0x55, 0x69, 0x55, + 0xb8, 0x55, 0x07, 0x56, 0x56, 0x56, 0xa5, 0x56, 0xf4, 0x56, 0x44, 0x57, + 0x93, 0x57, 0xe2, 0x57, 0x32, 0x58, 0x82, 0x58, 0xd1, 0x58, 0x21, 0x59, + 0x71, 0x59, 0xc1, 0x59, 0x10, 0x5a, 0x60, 0x5a, 0xb0, 0x5a, 0x01, 0x5b, + 0x51, 0x5b, 0xa1, 0x5b, 0xf1, 0x5b, 0x42, 0x5c, 0x92, 0x5c, 0xe3, 0x5c, + 0x34, 0x5d, 0x84, 0x5d, 0xd5, 0x5d, 0x26, 0x5e, 0x77, 0x5e, 0xc8, 0x5e, + 0x19, 0x5f, 0x6a, 0x5f, 0xbb, 0x5f, 0x0d, 0x60, 0x5e, 0x60, 0xb0, 0x60, + 0x01, 0x61, 0x53, 0x61, 0xa4, 0x61, 0xf6, 0x61, 0x48, 0x62, 0x9a, 0x62, + 0xec, 0x62, 0x3e, 0x63, 0x90, 0x63, 0xe2, 0x63, 0x34, 0x64, 0x87, 0x64, + 0xd9, 0x64, 0x2c, 0x65, 0x7e, 0x65, 0xd1, 0x65, 0x24, 0x66, 0x76, 0x66, + 0xc9, 0x66, 0x1c, 0x67, 0x6f, 0x67, 0xc2, 0x67, 0x15, 0x68, 0x69, 0x68, + 0xbc, 0x68, 0x0f, 0x69, 0x63, 0x69, 0xb6, 0x69, 0x0a, 0x6a, 0x5e, 0x6a, + 0xb1, 0x6a, 0x05, 0x6b, 0x59, 0x6b, 0xad, 0x6b, 0x01, 0x6c, 0x55, 0x6c, + 0xaa, 0x6c, 0xfe, 0x6c, 0x52, 0x6d, 0xa7, 0x6d, 0xfb, 0x6d, 0x50, 0x6e, + 0xa4, 0x6e, 0xf9, 0x6e, 0x4e, 0x6f, 0xa3, 0x6f, 0xf8, 0x6f, 0x4d, 0x70, + 0xa2, 0x70, 0xf7, 0x70, 0x4d, 0x71, 0xa2, 0x71, 0xf7, 0x71, 0x4d, 0x72, + 0xa2, 0x72, 0xf8, 0x72, 0x4e, 0x73, 0xa4, 0x73, 0xfa, 0x73, 0x50, 0x74, + 0xa6, 0x74, 0xfc, 0x74, 0x52, 0x75, 0xa8, 0x75, 0xff, 0x75, 0x55, 0x76, + 0xac, 0x76, 0x02, 0x77, 0x59, 0x77, 0xb0, 0x77, 0x07, 0x78, 0x5e, 0x78, + 0xb4, 0x78, 0x0c, 0x79, 0x63, 0x79, 0xba, 0x79, 0x11, 0x7a, 0x69, 0x7a, + 0xc0, 0x7a, 0x18, 0x7b, 0x6f, 0x7b, 0xc7, 0x7b, 0x1f, 0x7c, 0x77, 0x7c, + 0xcf, 0x7c, 0x27, 0x7d, 0x7f, 0x7d, 0xd7, 0x7d, 0x2f, 0x7e, 0x88, 0x7e, + 0xe0, 0x7e, 0x38, 0x7f, 0x91, 0x7f, 0xea, 0x7f, 0x42, 0x80, 0x9b, 0x80, + 0xf4, 0x80, 0x4d, 0x81, 0xa6, 0x81, 0xff, 0x81, 0x59, 0x82, 0xb2, 0x82, + 0x0b, 0x83, 0x65, 0x83, 0xbe, 0x83, 0x18, 0x84, 0x72, 0x84, 0xcb, 0x84, + 0x25, 0x85, 0x7f, 0x85, 0xd9, 0x85, 0x33, 0x86, 0x8e, 0x86, 0xe8, 0x86, + 0x42, 0x87, 0x9d, 0x87, 0xf7, 0x87, 0x52, 0x88, 0xac, 0x88, 0x07, 0x89, + 0x62, 0x89, 0xbd, 0x89, 0x18, 0x8a, 0x73, 0x8a, 0xce, 0x8a, 0x2a, 0x8b, + 0x85, 0x8b, 0xe0, 0x8b, 0x3c, 0x8c, 0x97, 0x8c, 0xf3, 0x8c, 0x4f, 0x8d, + 0xab, 0x8d, 0x07, 0x8e, 0x63, 0x8e, 0xbf, 0x8e, 0x1b, 0x8f, 0x77, 0x8f, + 0xd4, 0x8f, 0x30, 0x90, 0x8c, 0x90, 0xe9, 0x90, 0x46, 0x91, 0xa2, 0x91, + 0xff, 0x91, 0x5c, 0x92, 0xb9, 0x92, 0x16, 0x93, 0x73, 0x93, 0xd1, 0x93, + 0x2e, 0x94, 0x8c, 0x94, 0xe9, 0x94, 0x47, 0x95, 0xa4, 0x95, 0x02, 0x96, + 0x60, 0x96, 0xbe, 0x96, 0x1c, 0x97, 0x7a, 0x97, 0xd8, 0x97, 0x36, 0x98, + 0x95, 0x98, 0xf3, 0x98, 0x52, 0x99, 0xb0, 0x99, 0x0f, 0x9a, 0x6e, 0x9a, + 0xcd, 0x9a, 0x2c, 0x9b, 0x8b, 0x9b, 0xea, 0x9b, 0x49, 0x9c, 0xa8, 0x9c, + 0x08, 0x9d, 0x67, 0x9d, 0xc7, 0x9d, 0x26, 0x9e, 0x86, 0x9e, 0xe6, 0x9e, + 0x46, 0x9f, 0xa6, 0x9f, 0x06, 0xa0, 0x66, 0xa0, 0xc6, 0xa0, 0x27, 0xa1, + 0x87, 0xa1, 0xe8, 0xa1, 0x48, 0xa2, 0xa9, 0xa2, 0x0a, 0xa3, 0x6b, 0xa3, + 0xcc, 0xa3, 0x2d, 0xa4, 0x8e, 0xa4, 0xef, 0xa4, 0x50, 0xa5, 0xb2, 0xa5, + 0x13, 0xa6, 0x75, 0xa6, 0xd6, 0xa6, 0x38, 0xa7, 0x9a, 0xa7, 0xfc, 0xa7, + 0x5e, 0xa8, 0xc0, 0xa8, 0x22, 0xa9, 0x84, 0xa9, 0xe7, 0xa9, 0x49, 0xaa, + 0xac, 0xaa, 0x0e, 0xab, 0x71, 0xab, 0xd4, 0xab, 0x37, 0xac, 0x9a, 0xac, + 0xfd, 0xac, 0x60, 0xad, 0xc3, 0xad, 0x27, 0xae, 0x8a, 0xae, 0xed, 0xae, + 0x51, 0xaf, 0xb5, 0xaf, 0x19, 0xb0, 0x7c, 0xb0, 0xe0, 0xb0, 0x45, 0xb1, + 0xa9, 0xb1, 0x0d, 0xb2, 0x71, 0xb2, 0xd6, 0xb2, 0x3a, 0xb3, 0x9f, 0xb3, + 0x03, 0xb4, 0x68, 0xb4, 0xcd, 0xb4, 0x32, 0xb5, 0x97, 0xb5, 0xfc, 0xb5, + 0x62, 0xb6, 0xc7, 0xb6, 0x2c, 0xb7, 0x92, 0xb7, 0xf7, 0xb7, 0x5d, 0xb8, + 0xc3, 0xb8, 0x29, 0xb9, 0x8f, 0xb9, 0xf5, 0xb9, 0x5b, 0xba, 0xc1, 0xba, + 0x28, 0xbb, 0x8e, 0xbb, 0xf5, 0xbb, 0x5b, 0xbc, 0xc2, 0xbc, 0x29, 0xbd, + 0x90, 0xbd, 0xf7, 0xbd, 0x5e, 0xbe, 0xc5, 0xbe, 0x2c, 0xbf, 0x94, 0xbf, + 0xfb, 0xbf, 0x63, 0xc0, 0xca, 0xc0, 0x32, 0xc1, 0x9a, 0xc1, 0x02, 0xc2, + 0x6a, 0xc2, 0xd2, 0xc2, 0x3a, 0xc3, 0xa2, 0xc3, 0x0b, 0xc4, 0x73, 0xc4, + 0xdc, 0xc4, 0x44, 0xc5, 0xad, 0xc5, 0x16, 0xc6, 0x7f, 0xc6, 0xe8, 0xc6, + 0x51, 0xc7, 0xbb, 0xc7, 0x24, 0xc8, 0x8d, 0xc8, 0xf7, 0xc8, 0x60, 0xc9, + 0xca, 0xc9, 0x34, 0xca, 0x9e, 0xca, 0x08, 0xcb, 0x72, 0xcb, 0xdc, 0xcb, + 0x47, 0xcc, 0xb1, 0xcc, 0x1b, 0xcd, 0x86, 0xcd, 0xf1, 0xcd, 0x5b, 0xce, + 0xc6, 0xce, 0x31, 0xcf, 0x9c, 0xcf, 0x08, 0xd0, 0x73, 0xd0, 0xde, 0xd0, + 0x4a, 0xd1, 0xb5, 0xd1, 0x21, 0xd2, 0x8d, 0xd2, 0xf8, 0xd2, 0x64, 0xd3, + 0xd0, 0xd3, 0x3d, 0xd4, 0xa9, 0xd4, 0x15, 0xd5, 0x82, 0xd5, 0xee, 0xd5, + 0x5b, 0xd6, 0xc7, 0xd6, 0x34, 0xd7, 0xa1, 0xd7, 0x0e, 0xd8, 0x7b, 0xd8, + 0xe9, 0xd8, 0x56, 0xd9, 0xc3, 0xd9, 0x31, 0xda, 0x9e, 0xda, 0x0c, 0xdb, + 0x7a, 0xdb, 0xe8, 0xdb, 0x56, 0xdc, 0xc4, 0xdc, 0x32, 0xdd, 0xa0, 0xdd, + 0x0f, 0xde, 0x7d, 0xde, 0xec, 0xde, 0x5b, 0xdf, 0xc9, 0xdf, 0x38, 0xe0, + 0xa7, 0xe0, 0x16, 0xe1, 0x86, 0xe1, 0xf5, 0xe1, 0x64, 0xe2, 0xd4, 0xe2, + 0x43, 0xe3, 0xb3, 0xe3, 0x23, 0xe4, 0x93, 0xe4, 0x03, 0xe5, 0x73, 0xe5, + 0xe3, 0xe5, 0x54, 0xe6, 0xc4, 0xe6, 0x35, 0xe7, 0xa5, 0xe7, 0x16, 0xe8, + 0x87, 0xe8, 0xf8, 0xe8, 0x69, 0xe9, 0xda, 0xe9, 0x4b, 0xea, 0xbc, 0xea, + 0x2e, 0xeb, 0x9f, 0xeb, 0x11, 0xec, 0x83, 0xec, 0xf5, 0xec, 0x66, 0xed, + 0xd9, 0xed, 0x4b, 0xee, 0xbd, 0xee, 0x2f, 0xef, 0xa2, 0xef, 0x14, 0xf0, + 0x87, 0xf0, 0xfa, 0xf0, 0x6d, 0xf1, 0xe0, 0xf1, 0x53, 0xf2, 0xc6, 0xf2, + 0x39, 0xf3, 0xad, 0xf3, 0x20, 0xf4, 0x94, 0xf4, 0x07, 0xf5, 0x7b, 0xf5, + 0xef, 0xf5, 0x63, 0xf6, 0xd7, 0xf6, 0x4c, 0xf7, 0xc0, 0xf7, 0x34, 0xf8, + 0xa9, 0xf8, 0x1e, 0xf9, 0x92, 0xf9, 0x07, 0xfa, 0x7c, 0xfa, 0xf1, 0xfa, + 0x66, 0xfb, 0xdc, 0xfb, 0x51, 0xfc, 0xc7, 0xfc, 0x3c, 0xfd, 0xb2, 0xfd, + 0x28, 0xfe, 0x9e, 0xfe, 0x14, 0xff, 0x8a, 0xff, 0xa0, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xe0, 0x10, 0x10, 0x1f, 0xe5, 0xb0, 0x00, 0x91, 0xe1, + 0x1d, 0xfd, 0xff, 0xea, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, @@ -648,74 +649,73 @@ std::array bios_arm7_bin = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, + 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, - 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, - 0x0e, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, - 0x10, 0x11, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, - 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, - 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, - 0x19, 0x19, 0x19, 0x1a, 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, - 0x1d, 0x1d, 0x1d, 0x1e, 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, - 0x21, 0x21, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, - 0x26, 0x26, 0x27, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, - 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31, - 0x32, 0x32, 0x33, 0x33, 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, - 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41, - 0x42, 0x42, 0x43, 0x44, 0x45, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4a, - 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, - 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x71, - 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x20, - 0x21, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, - 0x26, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, - 0x2b, 0x2c, 0x2c, 0x2d, 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x31, - 0x31, 0x32, 0x33, 0x33, 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, - 0x39, 0x39, 0x3a, 0x3b, 0x3b, 0x3c, 0x3d, 0x3e, 0x3e, 0x3f, 0x40, 0x40, - 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, - 0x4b, 0x4c, 0x4d, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x62, - 0x63, 0x64, 0x65, 0x66, 0x67, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6f, 0x70, - 0x71, 0x73, 0x74, 0x75, 0x77, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7e, 0x40, - 0x41, 0x42, 0x43, 0x43, 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, - 0x4b, 0x4c, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, - 0x62, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x70, - 0x71, 0x72, 0x74, 0x75, 0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x40, - 0x41, 0x42, 0x42, 0x43, 0x44, 0x45, 0x46, 0x46, 0x47, 0x48, 0x49, 0x4a, - 0x4b, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, - 0x62, 0x63, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x6f, - 0x71, 0x72, 0x73, 0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00, 0xa0, 0xe3, 0x3c, 0x10, 0xa0, 0xe3, - 0xff, 0x20, 0xa0, 0xe3, 0x0a, 0x0c, 0x80, 0xe3, 0x0b, 0x1b, 0x81, 0xe3, - 0x05, 0x2c, 0x82, 0xe3, 0x62, 0xfc, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, - 0x01, 0x03, 0xc1, 0xe5, 0x5f, 0xfc, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, - 0x01, 0x03, 0xa0, 0xe3, 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, - 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, 0x32, 0x1e, 0x4f, 0xe2, - 0x00, 0x00, 0xd1, 0xe7, 0x56, 0xfc, 0xff, 0xea, 0xdc, 0xff, 0x80, 0x03, - 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, - 0x04, 0x20, 0x20, 0xe5, 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, - 0x24, 0x10, 0x1f, 0xe5, 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, - 0x01, 0xd0, 0xa0, 0xe1, 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, - 0xd2, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, - 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, - 0x03, 0xf0, 0x2f, 0xe1, 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, - 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, + 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x0a, 0x0a, 0x0a, + 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, + 0x0d, 0x0d, 0x0d, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0e, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x11, 0x11, 0x11, + 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, + 0x14, 0x14, 0x14, 0x14, 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, + 0x17, 0x17, 0x17, 0x18, 0x18, 0x18, 0x18, 0x19, 0x19, 0x19, 0x19, 0x1a, + 0x1a, 0x1a, 0x1b, 0x1b, 0x1b, 0x1c, 0x1c, 0x1c, 0x1d, 0x1d, 0x1d, 0x1e, + 0x1e, 0x1e, 0x1f, 0x1f, 0x1f, 0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, + 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, + 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x31, 0x31, 0x32, 0x32, 0x33, 0x33, + 0x34, 0x35, 0x35, 0x36, 0x36, 0x37, 0x38, 0x38, 0x39, 0x3a, 0x3a, 0x3b, + 0x3c, 0x3c, 0x3d, 0x3e, 0x3f, 0x3f, 0x40, 0x41, 0x42, 0x42, 0x43, 0x44, + 0x45, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, + 0x4f, 0x50, 0x51, 0x52, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6e, 0x6f, 0x71, 0x72, 0x73, 0x75, 0x76, + 0x77, 0x79, 0x7a, 0x7b, 0x7d, 0x7e, 0x7f, 0x20, 0x21, 0x21, 0x21, 0x22, + 0x22, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x26, 0x27, + 0x27, 0x28, 0x28, 0x29, 0x29, 0x2a, 0x2a, 0x2b, 0x2b, 0x2c, 0x2c, 0x2d, + 0x2d, 0x2e, 0x2e, 0x2f, 0x2f, 0x30, 0x30, 0x31, 0x31, 0x32, 0x33, 0x33, + 0x34, 0x34, 0x35, 0x36, 0x36, 0x37, 0x37, 0x38, 0x39, 0x39, 0x3a, 0x3b, + 0x3b, 0x3c, 0x3d, 0x3e, 0x3e, 0x3f, 0x40, 0x40, 0x41, 0x42, 0x43, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x73, 0x74, 0x75, + 0x77, 0x78, 0x79, 0x7b, 0x7c, 0x7e, 0x7e, 0x40, 0x41, 0x42, 0x43, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4c, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x70, 0x71, 0x72, 0x74, 0x75, + 0x76, 0x78, 0x79, 0x7b, 0x7c, 0x7d, 0x7e, 0x40, 0x41, 0x42, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4b, 0x4c, 0x4d, + 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, 0x6f, 0x71, 0x72, 0x73, 0x75, + 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7e, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x2e, 0x00, 0xa0, 0xe3, 0x3c, 0x10, 0xa0, 0xe3, 0xff, 0x20, 0xa0, 0xe3, + 0x0a, 0x0c, 0x80, 0xe3, 0x0b, 0x1b, 0x81, 0xe3, 0x05, 0x2c, 0x82, 0xe3, + 0x60, 0xfc, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, 0x01, 0x03, 0xc1, 0xe5, + 0x5d, 0xfc, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, 0x01, 0x03, 0xa0, 0xe3, + 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, + 0x04, 0xf0, 0x5e, 0xe2, 0x32, 0x1e, 0x4f, 0xe2, 0x00, 0x00, 0xd1, 0xe7, + 0x54, 0xfc, 0xff, 0xea, 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3, + 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5, + 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5, + 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1, + 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3, + 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1, + 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, + 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1400,7 +1400,7 @@ std::array bios_arm7_bin = { std::array bios_arm9_bin = { 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3e, 0x00, 0x00, 0xea, 0x3c, 0x00, 0x00, 0xea, 0x3b, 0x00, 0x00, 0xea, 0x3a, 0x00, 0x00, 0xea, - 0xad, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, + 0xaf, 0x01, 0x00, 0xea, 0x38, 0x00, 0x00, 0xea, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1424,17 +1424,17 @@ std::array bios_arm9_bin = { 0x80, 0x40, 0x04, 0xe2, 0x1f, 0x40, 0x84, 0xe3, 0x02, 0xc0, 0x5e, 0xe5, 0x04, 0xf0, 0x29, 0xe1, 0x00, 0x40, 0x2d, 0xe9, 0x20, 0x00, 0x5c, 0xe3, 0x01, 0xc0, 0xa0, 0xa3, 0x0c, 0xf1, 0x9f, 0xe7, 0x00, 0x00, 0xa0, 0xe1, - 0xf8, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, + 0x00, 0x07, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xe0, 0x01, 0xff, 0xff, 0x20, 0x02, 0xff, 0xff, 0x18, 0x02, 0xff, 0xff, 0xd4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0x4c, 0x02, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xa8, 0x02, 0xff, 0xff, 0x28, 0x03, 0xff, 0xff, 0x68, 0x03, 0xff, 0xff, 0xb4, 0x03, 0xff, 0xff, - 0x4c, 0x04, 0xff, 0xff, 0x54, 0x04, 0xff, 0xff, 0xe8, 0x04, 0xff, 0xff, - 0x64, 0x05, 0xff, 0xff, 0x10, 0x06, 0xff, 0xff, 0x14, 0x06, 0xff, 0xff, - 0x14, 0x06, 0xff, 0xff, 0x6c, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, - 0x98, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, + 0x54, 0x04, 0xff, 0xff, 0x5c, 0x04, 0xff, 0xff, 0xf0, 0x04, 0xff, 0xff, + 0x6c, 0x05, 0xff, 0xff, 0x18, 0x06, 0xff, 0xff, 0x1c, 0x06, 0xff, 0xff, + 0x1c, 0x06, 0xff, 0xff, 0x74, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, + 0xa0, 0x06, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, 0xb4, 0x01, 0xff, 0xff, - 0xc8, 0x06, 0xff, 0xff, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3, + 0xd0, 0x06, 0xff, 0xff, 0x00, 0x40, 0xbd, 0xe8, 0xd3, 0x40, 0xa0, 0xe3, 0x04, 0xf0, 0x29, 0xe1, 0x10, 0x00, 0xbd, 0xe8, 0x04, 0xf0, 0x69, 0xe1, 0x10, 0x50, 0xbd, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa0, 0xe3, 0x90, 0x0f, 0x07, 0xee, 0xf4, 0xff, 0xff, 0xea, @@ -1478,83 +1478,83 @@ std::array bios_arm9_bin = { 0x01, 0xf0, 0x00, 0x3c, 0x00, 0x28, 0x01, 0xe4, 0x01, 0xa0, 0x00, 0x6c, 0x00, 0x78, 0x01, 0xb4, 0x00, 0x50, 0x01, 0x9c, 0x01, 0x88, 0x00, 0x44, 0x20, 0x00, 0x2d, 0xe9, 0x1e, 0x40, 0xa0, 0xe3, 0x30, 0xe0, 0x4f, 0xe2, - 0xa2, 0x20, 0xb0, 0xe1, 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, + 0xff, 0x04, 0xc0, 0xe3, 0xff, 0x08, 0xc0, 0xe3, 0xa2, 0x20, 0xb0, 0xe1, + 0x1e, 0x00, 0x00, 0x0a, 0xb2, 0x30, 0xd1, 0xe0, 0x80, 0x50, 0x04, 0xe0, + 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, + 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0x83, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, + 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, - 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x51, 0x04, 0xe0, + 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, 0x0c, 0x00, 0x20, 0xe0, - 0xa3, 0x53, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, - 0x80, 0x50, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x20, 0x02, 0xa0, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, - 0x0c, 0x00, 0x20, 0xe0, 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, - 0x20, 0x00, 0xbd, 0xe8, 0x59, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, - 0x57, 0xff, 0xff, 0xea, 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, - 0x02, 0xc0, 0xd2, 0xe5, 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, - 0xa4, 0x2f, 0xa0, 0xe1, 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, - 0x15, 0x5c, 0xa0, 0xe1, 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, - 0x00, 0x70, 0xa0, 0xe3, 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, - 0x83, 0x31, 0xa0, 0x01, 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, - 0x04, 0x00, 0x5c, 0xe3, 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, - 0x01, 0x60, 0xd0, 0x04, 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, - 0x36, 0x6c, 0xa0, 0xe1, 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, - 0x04, 0x90, 0x89, 0x10, 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, - 0x20, 0x00, 0x58, 0xe3, 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, - 0x00, 0x80, 0xa0, 0x03, 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, - 0xe0, 0x03, 0xbd, 0xe8, 0x32, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0x23, 0x24, 0xb0, 0xe1, 0x2f, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, - 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, - 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, - 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, - 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x1f, 0xff, 0xff, 0x0a, - 0x01, 0xe0, 0x5e, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, - 0xed, 0xff, 0xff, 0x5a, 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0x40, 0xc1, 0xe4, 0x16, 0xff, 0xff, 0x0a, - 0x83, 0x30, 0xb0, 0xe1, 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, - 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, 0x10, 0xff, 0xff, 0x0a, - 0x20, 0x00, 0x2d, 0xe9, 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, - 0x02, 0x35, 0x83, 0xe3, 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, - 0x01, 0x40, 0xd0, 0xe4, 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, - 0x24, 0xe6, 0xa0, 0xe1, 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, - 0x0c, 0xc0, 0x41, 0xe0, 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, - 0x01, 0x50, 0xdc, 0x04, 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, - 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, - 0x20, 0x00, 0xbd, 0x08, 0xf9, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, - 0xf4, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, - 0xe5, 0xff, 0xff, 0xea, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, - 0x01, 0x40, 0xd0, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, + 0xa3, 0x55, 0x04, 0xe0, 0xb5, 0xc0, 0x9e, 0xe1, 0x0c, 0x00, 0x20, 0xe0, + 0x01, 0x20, 0x52, 0xe2, 0xe0, 0xff, 0xff, 0x1a, 0x20, 0x00, 0xbd, 0xe8, + 0x57, 0xff, 0xff, 0xea, 0x00, 0x00, 0xa0, 0xe3, 0x55, 0xff, 0xff, 0xea, + 0xe0, 0x03, 0x2d, 0xe9, 0xb0, 0x30, 0xd2, 0xe1, 0x02, 0xc0, 0xd2, 0xe5, + 0x03, 0xe0, 0xd2, 0xe5, 0x04, 0x40, 0x92, 0xe5, 0xa4, 0x2f, 0xa0, 0xe1, + 0x02, 0x41, 0xc4, 0xe3, 0x01, 0x50, 0xa0, 0xe3, 0x15, 0x5c, 0xa0, 0xe1, + 0x01, 0x50, 0x45, 0xe2, 0x01, 0x60, 0xa0, 0xe3, 0x00, 0x70, 0xa0, 0xe3, + 0x00, 0x80, 0xa0, 0xe3, 0x01, 0x00, 0x5c, 0xe3, 0x83, 0x31, 0xa0, 0x01, + 0x02, 0x00, 0x5c, 0xe3, 0x03, 0x31, 0xa0, 0x01, 0x04, 0x00, 0x5c, 0xe3, + 0x83, 0x30, 0xa0, 0x01, 0x01, 0x00, 0x56, 0xe3, 0x01, 0x60, 0xd0, 0x04, + 0x01, 0x6c, 0x86, 0x03, 0x05, 0x90, 0x06, 0xe0, 0x36, 0x6c, 0xa0, 0xe1, + 0x00, 0x00, 0x59, 0xe3, 0x01, 0x00, 0x12, 0x03, 0x04, 0x90, 0x89, 0x10, + 0x19, 0x78, 0x87, 0xe1, 0x0e, 0x80, 0x88, 0xe0, 0x20, 0x00, 0x58, 0xe3, + 0x04, 0x70, 0x81, 0x04, 0x00, 0x70, 0xa0, 0x03, 0x00, 0x80, 0xa0, 0x03, + 0x01, 0x30, 0x53, 0xe2, 0xef, 0xff, 0xff, 0x1a, 0xe0, 0x03, 0xbd, 0xe8, + 0x30, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xb0, 0xe1, + 0x2d, 0xff, 0xff, 0x0a, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3, + 0x80, 0x00, 0x13, 0xe3, 0x10, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4, + 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1, + 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0, + 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x40, 0xdc, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0x40, 0xc1, 0xe4, 0x1d, 0xff, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, + 0xf9, 0xff, 0xff, 0x1a, 0x83, 0x30, 0xb0, 0xe1, 0xed, 0xff, 0xff, 0x5a, + 0xea, 0xff, 0xff, 0xea, 0x01, 0x40, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0x40, 0xc1, 0xe4, 0x14, 0xff, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, + 0xe6, 0xff, 0xff, 0x5a, 0xe3, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, + 0x23, 0x24, 0xb0, 0xe1, 0x0e, 0xff, 0xff, 0x0a, 0x20, 0x00, 0x2d, 0xe9, + 0x00, 0x50, 0xa0, 0xe3, 0x01, 0x30, 0xd0, 0xe4, 0x02, 0x35, 0x83, 0xe3, + 0x80, 0x00, 0x13, 0xe3, 0x15, 0x00, 0x00, 0x0a, 0x01, 0x40, 0xd0, 0xe4, + 0x01, 0xe0, 0xd0, 0xe4, 0x04, 0x44, 0x8e, 0xe1, 0x24, 0xe6, 0xa0, 0xe1, + 0x0f, 0xca, 0xc4, 0xe3, 0x03, 0xe0, 0x8e, 0xe2, 0x0c, 0xc0, 0x41, 0xe0, + 0x01, 0xc0, 0x4c, 0xe2, 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xdc, 0x04, + 0x01, 0x40, 0xdc, 0x14, 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, - 0xeb, 0xfe, 0xff, 0x0a, 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, - 0xd9, 0xff, 0xff, 0xea, 0xe7, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0x23, 0x24, 0xa0, 0xe1, 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, - 0x7f, 0x30, 0x03, 0xe2, 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, - 0x03, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, - 0xdc, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, - 0xf3, 0xff, 0xff, 0xea, 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x01, 0xc0, 0xc1, 0xe4, 0xd4, 0xfe, 0xff, 0x0a, - 0x01, 0x30, 0x53, 0xe2, 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, - 0x04, 0x30, 0x90, 0xe4, 0x01, 0xc0, 0xd0, 0xe4, 0x23, 0x24, 0xa0, 0xe1, - 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x42, 0xe2, 0x01, 0x30, 0xd0, 0xe4, - 0x01, 0x20, 0x52, 0xe2, 0x03, 0xc0, 0x8c, 0xe0, 0x01, 0xc0, 0xc1, 0xe4, - 0xfa, 0xff, 0xff, 0x1a, 0xc6, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, - 0xb2, 0xc0, 0xd0, 0xe0, 0x23, 0x24, 0xa0, 0xe1, 0xb2, 0xc0, 0xc1, 0xe0, - 0x01, 0x20, 0xc2, 0xe3, 0x02, 0x20, 0x42, 0xe2, 0xb2, 0x30, 0xd0, 0xe0, - 0x02, 0x20, 0x52, 0xe2, 0x03, 0xc0, 0x8c, 0xe0, 0xb2, 0xc0, 0xc1, 0xe0, - 0xfa, 0xff, 0xff, 0x1a, 0xba, 0xfe, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, - 0x00, 0x03, 0xc1, 0xe5, 0xb7, 0xfe, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, - 0x11, 0x0f, 0x19, 0xee, 0xff, 0x00, 0xc0, 0xe3, 0x01, 0x09, 0x80, 0xe2, - 0x0f, 0xe0, 0xa0, 0xe1, 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, - 0x04, 0xf0, 0x5e, 0xe2, 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3, - 0x80, 0x10, 0xa0, 0xe3, 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5, - 0x01, 0x10, 0x51, 0xe2, 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5, - 0xd3, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1, - 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3, - 0x03, 0xf0, 0x2f, 0xe1, 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1, - 0x02, 0xf0, 0x6f, 0xe1, 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, - 0xdc, 0xd0, 0x41, 0xe2, 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xf7, 0xfe, 0xff, 0x0a, 0x01, 0xe0, 0x5e, 0xe2, 0xf4, 0xff, 0xff, 0x1a, + 0x83, 0x30, 0xb0, 0xe1, 0xe8, 0xff, 0xff, 0x5a, 0xe5, 0xff, 0xff, 0xea, + 0x01, 0x00, 0x11, 0xe3, 0x01, 0x50, 0xd0, 0x04, 0x01, 0x40, 0xd0, 0x14, + 0x04, 0x44, 0x85, 0x11, 0xb1, 0x40, 0x41, 0x11, 0x01, 0x10, 0x81, 0xe2, + 0x01, 0x20, 0x52, 0xe2, 0x20, 0x00, 0xbd, 0x08, 0xe9, 0xfe, 0xff, 0x0a, + 0x83, 0x30, 0xb0, 0xe1, 0xdc, 0xff, 0xff, 0x5a, 0xd9, 0xff, 0xff, 0xea, + 0xe5, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0x23, 0x24, 0xa0, 0xe1, + 0x01, 0x30, 0xd0, 0xe4, 0x80, 0x00, 0x13, 0xe3, 0x7f, 0x30, 0x03, 0xe2, + 0x07, 0x00, 0x00, 0x0a, 0x01, 0xc0, 0xd0, 0xe4, 0x03, 0x30, 0x83, 0xe2, + 0x01, 0xc0, 0xc1, 0xe4, 0x01, 0x20, 0x52, 0xe2, 0xda, 0xfe, 0xff, 0x0a, + 0x01, 0x30, 0x53, 0xe2, 0xfa, 0xff, 0xff, 0x1a, 0xf3, 0xff, 0xff, 0xea, + 0x01, 0x30, 0x83, 0xe2, 0x01, 0xc0, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x01, 0xc0, 0xc1, 0xe4, 0xd2, 0xfe, 0xff, 0x0a, 0x01, 0x30, 0x53, 0xe2, + 0xf9, 0xff, 0xff, 0x1a, 0xeb, 0xff, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, + 0x01, 0xc0, 0xd0, 0xe4, 0x23, 0x24, 0xa0, 0xe1, 0x01, 0xc0, 0xc1, 0xe4, + 0x01, 0x20, 0x42, 0xe2, 0x01, 0x30, 0xd0, 0xe4, 0x01, 0x20, 0x52, 0xe2, + 0x03, 0xc0, 0x8c, 0xe0, 0x01, 0xc0, 0xc1, 0xe4, 0xfa, 0xff, 0xff, 0x1a, + 0xc4, 0xfe, 0xff, 0xea, 0x04, 0x30, 0x90, 0xe4, 0xb2, 0xc0, 0xd0, 0xe0, + 0x23, 0x24, 0xa0, 0xe1, 0xb2, 0xc0, 0xc1, 0xe0, 0x01, 0x20, 0xc2, 0xe3, + 0x02, 0x20, 0x42, 0xe2, 0xb2, 0x30, 0xd0, 0xe0, 0x02, 0x20, 0x52, 0xe2, + 0x03, 0xc0, 0x8c, 0xe0, 0xb2, 0xc0, 0xc1, 0xe0, 0xfa, 0xff, 0xff, 0x1a, + 0xb8, 0xfe, 0xff, 0xea, 0x01, 0x13, 0xa0, 0xe3, 0x00, 0x03, 0xc1, 0xe5, + 0xb5, 0xfe, 0xff, 0xea, 0x0f, 0x50, 0x2d, 0xe9, 0x11, 0x0f, 0x19, 0xee, + 0xff, 0x00, 0xc0, 0xe3, 0x01, 0x09, 0x80, 0xe2, 0x0f, 0xe0, 0xa0, 0xe1, + 0x04, 0xf0, 0x10, 0xe5, 0x0f, 0x50, 0xbd, 0xe8, 0x04, 0xf0, 0x5e, 0xe2, + 0xdc, 0xff, 0x80, 0x03, 0x01, 0x03, 0xa0, 0xe3, 0x80, 0x10, 0xa0, 0xe3, + 0x00, 0x20, 0xa0, 0xe3, 0x04, 0x20, 0x20, 0xe5, 0x01, 0x10, 0x51, 0xe2, + 0xfc, 0xff, 0xff, 0x1a, 0x24, 0x10, 0x1f, 0xe5, 0xd3, 0x30, 0xa0, 0xe3, + 0x03, 0xf0, 0x2f, 0xe1, 0x01, 0xd0, 0xa0, 0xe1, 0x02, 0xe0, 0xa0, 0xe1, + 0x02, 0xf0, 0x6f, 0xe1, 0xd2, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, + 0x2c, 0xd0, 0x41, 0xe2, 0x02, 0xe0, 0xa0, 0xe1, 0x02, 0xf0, 0x6f, 0xe1, + 0x5f, 0x30, 0xa0, 0xe3, 0x03, 0xf0, 0x2f, 0xe1, 0xdc, 0xd0, 0x41, 0xe2, + 0xff, 0x1f, 0x90, 0xe8, 0x0e, 0xf0, 0xb0, 0xe1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, From 2a35af5bb946d520000502c8f7405e27967b1d1f Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:10:30 -0500 Subject: [PATCH 056/157] Soften restrictions around `ARMJIT_Memory::RemapNWRAM` - If in NDS mode, return instead of `assert`ing - Use `static_cast` and `ConsoleType` instead of `dynamic_cast` --- src/ARMJIT_Memory.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index bf1fb063..101e3b16 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -473,8 +473,10 @@ void ARMJIT_Memory::RemapDTCM(u32 newBase, u32 newSize) noexcept void ARMJIT_Memory::RemapNWRAM(int num) noexcept { - auto* dsi = dynamic_cast(&NDS); - assert(dsi != nullptr); + if (NDS.ConsoleType == 0) + return; + + auto* dsi = static_cast(&NDS); for (int i = 0; i < Mappings[memregion_SharedWRAM].Length;) { Mapping& mapping = Mappings[memregion_SharedWRAM][i]; From f4377e4f0f512f02805994e51c2e95a0022ff1f7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:10:56 -0500 Subject: [PATCH 057/157] Remove a loose qualifier --- src/DSi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DSi.cpp b/src/DSi.cpp index a5403d25..468be4dc 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -93,7 +93,7 @@ DSi::DSi(DSiArgs&& args) noexcept : // Memory is owned by ARMJIT_Memory, don't free it NWRAM_A = JIT.Memory.GetNWRAM_A(); NWRAM_B = JIT.Memory.GetNWRAM_B(); - NWRAM_C = NDS::JIT.Memory.GetNWRAM_C(); + NWRAM_C = JIT.Memory.GetNWRAM_C(); } DSi::~DSi() noexcept From 644d190e985521fc337b0612391de2c3fd679340 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:12:56 -0500 Subject: [PATCH 058/157] Add stubs for `NDS::IsJITEnabled` and `SetJITArgs` for when the JIT is excluded --- src/NDS.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/NDS.h b/src/NDS.h index c0b429ee..1a6d475e 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -437,6 +437,9 @@ public: #ifdef JIT_ENABLED [[nodiscard]] bool IsJITEnabled() const noexcept { return EnableJIT; } void SetJITArgs(std::optional args) noexcept; +#else + [[nodiscard]] bool IsJITEnabled() const noexcept { return false; } + void SetJITArgs(std::optional args) noexcept {} #endif private: From 582a421447d7d6e5b414caa9189b583a0051617e Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:16:20 -0500 Subject: [PATCH 059/157] Don't set the JIT args if the JIT is off --- src/frontend/qt_sdl/main.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 30ea0ab6..45dc4e06 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -386,17 +386,19 @@ bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAAr NDS->SetGBACart(std::move(nextgbacart)); } +#ifdef JIT_ENABLED JITArgs jitargs { static_cast(Config::JIT_MaxBlockSize), Config::JIT_LiteralOptimisations, Config::JIT_BranchOptimisations, Config::JIT_FastMemory, }; + NDS->SetJITArgs(Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt); +#endif NDS->ARM7BIOS = *arm7bios; NDS->ARM9BIOS = *arm9bios; NDS->SetFirmware(std::move(*firmware)); NDS->SetNDSCart(std::move(nextndscart)); - NDS->SetJITArgs(Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt); NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); NDS->SPU.SetDegrade10Bit(static_cast(Config::AudioBitDepth)); From 733769303c8f6f422b0e837add453406b9da6720 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:17:09 -0500 Subject: [PATCH 060/157] Add `override` to the ARM64 JIT's destructor --- src/ARMJIT_A64/ARMJIT_Compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index 72dd7bcb..77656907 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -101,7 +101,7 @@ public: #else explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {} #endif - ~Compiler(); + ~Compiler() override; void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true); void PopRegs(bool saveHiRegs, bool saveRegsToBeChanged); From 53e5aa62983dd2db68ac78c2a1272aba456362e6 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:19:24 -0500 Subject: [PATCH 061/157] Exclude JIT-related declarations more aggressively --- src/ARMJIT.h | 58 ++++++++++++++++++++++---------- src/ARMJIT_A64/ARMJIT_Compiler.h | 8 ++--- src/ARMJIT_Compiler.h | 6 ++-- src/ARMJIT_x64/ARMJIT_Compiler.h | 7 ++-- src/NDS.h | 3 ++ 5 files changed, 53 insertions(+), 29 deletions(-) diff --git a/src/ARMJIT.h b/src/ARMJIT.h index 6390855d..7ea54724 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -23,7 +23,10 @@ #include #include #include "types.h" +#include "MemConstants.h" +#include "Args.h" +#ifdef JIT_ENABLED #include "ARMJIT_Memory.h" #include "JitBlock.h" @@ -32,8 +35,6 @@ #endif #include "ARMJIT_Compiler.h" -#include "Args.h" -#include "MemConstants.h" namespace melonDS { @@ -52,17 +53,16 @@ public: BranchOptimizations(jit.has_value() ? jit->BranchOptimizations : false), FastMemory(jit.has_value() ? jit->FastMemory : false) {} - ~ARMJIT() noexcept NOOP_IF_NO_JIT; - void InvalidateByAddr(u32) noexcept NOOP_IF_NO_JIT; - void CheckAndInvalidateWVRAM(int) noexcept NOOP_IF_NO_JIT; - void CheckAndInvalidateITCM() noexcept NOOP_IF_NO_JIT; - void Reset() noexcept NOOP_IF_NO_JIT; - void JitEnableWrite() noexcept NOOP_IF_NO_JIT; - void JitEnableExecute() noexcept NOOP_IF_NO_JIT; - void CompileBlock(ARM* cpu) noexcept NOOP_IF_NO_JIT; - void ResetBlockCache() noexcept NOOP_IF_NO_JIT; + ~ARMJIT() noexcept; + void InvalidateByAddr(u32) noexcept; + void CheckAndInvalidateWVRAM(int) noexcept; + void CheckAndInvalidateITCM() noexcept; + void Reset() noexcept; + void JitEnableWrite() noexcept; + void JitEnableExecute() noexcept; + void CompileBlock(ARM* cpu) noexcept; + void ResetBlockCache() noexcept; -#ifdef JIT_ENABLED template void CheckAndInvalidate(u32 addr) noexcept { @@ -73,10 +73,6 @@ public: JitBlockEntry LookUpBlock(u32 num, u64* entries, u32 offset, u32 addr) noexcept; bool SetupExecutableRegion(u32 num, u32 blockAddr, u64*& entry, u32& start, u32& size) noexcept; u32 LocaliseCodeAddress(u32 num, u32 addr) const noexcept; -#else - template - void CheckAndInvalidate(u32) noexcept {} -#endif ARMJIT_Memory Memory; private: @@ -185,5 +181,33 @@ public: // Defined in assembly extern "C" void ARM_Dispatch(melonDS::ARM* cpu, melonDS::JitBlockEntry entry); +#else +namespace melonDS +{ +class ARM; + +// This version is a stub; the methods all do nothing, +// but there's still a Memory member. +class ARMJIT +{ +public: + ARMJIT(melonDS::NDS& nds, std::optional) noexcept : Memory(nds) {} + ~ARMJIT() noexcept {} + void InvalidateByAddr(u32) noexcept {} + void CheckAndInvalidateWVRAM(int) noexcept {} + void CheckAndInvalidateITCM() noexcept {} + void Reset() noexcept {} + void JitEnableWrite() noexcept {} + void JitEnableExecute() noexcept {} + void CompileBlock(ARM*) noexcept {} + void ResetBlockCache() noexcept {} + template + void CheckAndInvalidate(u32 addr) noexcept {} + + ARMJIT_Memory Memory; +}; +} +#endif // JIT_ENABLED + +#endif // ARMJIT_H -#endif diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index 77656907..54e60542 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -19,6 +19,8 @@ #ifndef ARMJIT_A64_COMPILER_H #define ARMJIT_A64_COMPILER_H +#if defined(JIT_ENABLED) && defined(__aarch64__) + #include "../ARM.h" #include "../dolphin/Arm64Emitter.h" @@ -96,11 +98,7 @@ class Compiler : public Arm64Gen::ARM64XEmitter public: typedef void (Compiler::*CompileFunc)(); -#ifdef JIT_ENABLED explicit Compiler(melonDS::NDS& nds); -#else - explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {} -#endif ~Compiler() override; void PushRegs(bool saveHiRegs, bool saveRegsToBeChanged, bool allowUnload = true); @@ -291,3 +289,5 @@ public: } #endif + +#endif diff --git a/src/ARMJIT_Compiler.h b/src/ARMJIT_Compiler.h index 4ece834f..ff4f8ff7 100644 --- a/src/ARMJIT_Compiler.h +++ b/src/ARMJIT_Compiler.h @@ -20,10 +20,6 @@ #define ARMJIT_COMPILER_H #ifdef JIT_ENABLED -#define NOOP_IF_NO_JIT -#else -#define NOOP_IF_NO_JIT {} -#endif #if defined(__x86_64__) #include "ARMJIT_x64/ARMJIT_Compiler.h" @@ -34,3 +30,5 @@ #endif #endif + +#endif diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index fa6d78a4..aa80570f 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -19,6 +19,8 @@ #ifndef ARMJIT_X64_COMPILER_H #define ARMJIT_X64_COMPILER_H +#if defined(JIT_ENABLED) && defined(__x86_64__) + #include "../dolphin/x64Emitter.h" #include "../ARMJIT_Internal.h" @@ -81,11 +83,7 @@ struct Op2 class Compiler : public Gen::XEmitter { public: -#ifdef JIT_ENABLED explicit Compiler(melonDS::NDS& nds); -#else - explicit Compiler(melonDS::NDS& nds) : XEmitter(), NDS(nds) {} -#endif void Reset(); @@ -284,5 +282,6 @@ public: }; } +#endif #endif diff --git a/src/NDS.h b/src/NDS.h index 1a6d475e..d3a753a2 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -36,6 +36,9 @@ #include "AREngine.h" #include "GPU.h" #include "ARMJIT.h" +#include "MemRegion.h" +#include "ARMJIT_Memory.h" +#include "ARM.h" #include "DMA.h" #include "FreeBIOS.h" From 399a6af91c31d151444e2593ca43c8d17b168de7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:33:32 -0500 Subject: [PATCH 062/157] Move some constants to `MemConstants.h` --- src/ARM.h | 4 +--- src/MemConstants.h | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ARM.h b/src/ARM.h index 65f78ab3..565579a4 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -24,6 +24,7 @@ #include "types.h" #include "MemRegion.h" +#include "MemConstants.h" #ifdef GDBSTUB_ENABLED #include "debug/GdbStub.h" @@ -42,9 +43,6 @@ enum RWFlags_ForceUser = (1<<21), }; -const u32 ITCMPhysicalSize = 0x8000; -const u32 DTCMPhysicalSize = 0x4000; - struct GDBArgs; class ARMJIT; class GPU; diff --git a/src/MemConstants.h b/src/MemConstants.h index ab80faba..4cf2d36c 100644 --- a/src/MemConstants.h +++ b/src/MemConstants.h @@ -30,6 +30,8 @@ constexpr u32 NWRAMSize = 0x40000; constexpr u32 ARM9BIOSSize = 0x1000; constexpr u32 ARM7BIOSSize = 0x4000; constexpr u32 DSiBIOSSize = 0x10000; +constexpr u32 ITCMPhysicalSize = 0x8000; +constexpr u32 DTCMPhysicalSize = 0x4000; } #endif // MELONDS_MEMCONSTANTS_H \ No newline at end of file From 7cef13031fceeaff5b9269d05e1209b1e4661c2d Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:40:04 -0500 Subject: [PATCH 063/157] Add some headers that were transitively included by the JIT --- src/ARM.cpp | 1 + src/DSi.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/src/ARM.cpp b/src/ARM.cpp index 2c19fa30..c2f6a6c2 100644 --- a/src/ARM.cpp +++ b/src/ARM.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include "NDS.h" #include "DSi.h" diff --git a/src/DSi.cpp b/src/DSi.cpp index 468be4dc..ce716db6 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include #include "Args.h" From 72d4eba477d6c3189b05b4ac6c115716cc267f9b Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Wed, 6 Dec 2023 09:41:29 -0500 Subject: [PATCH 064/157] Rearrange some `#include`s --- src/ARMJIT.h | 2 +- src/ARMJIT_Memory.h | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/ARMJIT.h b/src/ARMJIT.h index 7ea54724..7619f234 100644 --- a/src/ARMJIT.h +++ b/src/ARMJIT.h @@ -25,9 +25,9 @@ #include "types.h" #include "MemConstants.h" #include "Args.h" +#include "ARMJIT_Memory.h" #ifdef JIT_ENABLED -#include "ARMJIT_Memory.h" #include "JitBlock.h" #if defined(__APPLE__) && defined(__aarch64__) diff --git a/src/ARMJIT_Memory.h b/src/ARMJIT_Memory.h index 487005b2..d36f6032 100644 --- a/src/ARMJIT_Memory.h +++ b/src/ARMJIT_Memory.h @@ -20,33 +20,33 @@ #define ARMJIT_MEMORY #include "types.h" -#include "TinyVector.h" - -#include "ARM.h" #include "MemConstants.h" -#if defined(__SWITCH__) -#include -#elif defined(_WIN32) +#ifdef JIT_ENABLED +# include "TinyVector.h" +# include "ARM.h" +# if defined(__SWITCH__) +# include +# elif defined(_WIN32) #include +# else +# include +# include +# include +# include +# include +# endif #else -#include -#include -#include -#include -#include -#endif - -#ifndef JIT_ENABLED -#include -#include "NDS.h" +# include #endif namespace melonDS { +#ifdef JIT_ENABLED namespace Platform { struct DynamicLibrary; } class Compiler; class ARMJIT; +#endif constexpr u32 RoundUp(u32 size) noexcept { From bbecab6cb01706dab18cabed24aaa7458d280b61 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 5 Dec 2023 17:35:07 -0500 Subject: [PATCH 065/157] Correctly use the refactored `JitEnableWrite` --- src/ARMJIT_A64/ARMJIT_Compiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.cpp b/src/ARMJIT_A64/ARMJIT_Compiler.cpp index c306dd84..7981ed67 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_A64/ARMJIT_Compiler.cpp @@ -276,7 +276,7 @@ Compiler::Compiler(melonDS::NDS& nds) : Arm64Gen::ARM64XEmitter(), NDS(nds) VirtualProtect(pageAligned, alignedSize, PAGE_EXECUTE_READWRITE, &dummy); #elif defined(__APPLE__) pageAligned = (u8*)mmap(NULL, 1024*1024*16, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT,-1, 0); - JitEnableWrite(); + nds.JIT.JitEnableWrite(); #else mprotect(pageAligned, alignedSize, PROT_EXEC | PROT_READ | PROT_WRITE); #endif From 5dac65f52e7e420e8d8a488368c7c8a7f6c6fe18 Mon Sep 17 00:00:00 2001 From: Jesse Talavera-Greenberg Date: Tue, 5 Dec 2023 09:20:57 -0500 Subject: [PATCH 066/157] Fix some minor instances of undefined behavior --- src/ARMJIT_Memory.cpp | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/ARMJIT_Memory.cpp b/src/ARMJIT_Memory.cpp index 101e3b16..c8969aee 100644 --- a/src/ARMJIT_Memory.cpp +++ b/src/ARMJIT_Memory.cpp @@ -1073,15 +1073,19 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept } else { - auto& dsi = static_cast(NDS); // ONLY use this if ConsoleType == 1! - if (NDS.ConsoleType == 1 && addr >= 0xFFFF0000 && !(dsi.SCFG_BIOS & (1<<1))) + if (NDS.ConsoleType == 1) { - if ((addr >= 0xFFFF8000) && (dsi.SCFG_BIOS & (1<<0))) - return memregion_Other; + auto& dsi = static_cast(NDS); + if (addr >= 0xFFFF0000 && !(dsi.SCFG_BIOS & (1<<1))) + { + if ((addr >= 0xFFFF8000) && (dsi.SCFG_BIOS & (1<<0))) + return memregion_Other; - return memregion_BIOS9DSi; + return memregion_BIOS9DSi; + } } - else if ((addr & 0xFFFFF000) == 0xFFFF0000) + + if ((addr & 0xFFFFF000) == 0xFFFF0000) { return memregion_BIOS9; } @@ -1093,6 +1097,7 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept case 0x03000000: if (NDS.ConsoleType == 1) { + auto& dsi = static_cast(NDS); if (addr >= dsi.NWRAMStart[0][0] && addr < dsi.NWRAMEnd[0][0]) return memregion_NewSharedWRAM_A; if (addr >= dsi.NWRAMStart[0][1] && addr < dsi.NWRAMEnd[0][1]) @@ -1118,15 +1123,19 @@ int ARMJIT_Memory::ClassifyAddress9(u32 addr) const noexcept int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept { - auto& dsi = static_cast(NDS); - if (NDS.ConsoleType == 1 && addr < 0x00010000 && !(dsi.SCFG_BIOS & (1<<9))) + if (NDS.ConsoleType == 1) { - if (addr >= 0x00008000 && dsi.SCFG_BIOS & (1<<8)) - return memregion_Other; + auto& dsi = static_cast(NDS); + if (addr < 0x00010000 && !(dsi.SCFG_BIOS & (1<<9))) + { + if (addr >= 0x00008000 && dsi.SCFG_BIOS & (1<<8)) + return memregion_Other; - return memregion_BIOS7DSi; + return memregion_BIOS7DSi; + } } - else if (addr < 0x00004000) + + if (addr < 0x00004000) { return memregion_BIOS7; } @@ -1140,6 +1149,7 @@ int ARMJIT_Memory::ClassifyAddress7(u32 addr) const noexcept case 0x03000000: if (NDS.ConsoleType == 1) { + auto& dsi = static_cast(NDS); if (addr >= dsi.NWRAMStart[1][0] && addr < dsi.NWRAMEnd[1][0]) return memregion_NewSharedWRAM_A; if (addr >= dsi.NWRAMStart[1][1] && addr < dsi.NWRAMEnd[1][1]) From 890a66c0eb245d77e168ef2b45b9adb90815d487 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 8 Dec 2023 17:27:06 +0100 Subject: [PATCH 067/157] I'm sick of this interfering with debugging --- CMakeLists.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 598b3bdb..337c4a16 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,8 +73,11 @@ if (ENABLE_LTO) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) endif() -set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og") -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") +if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") +endif() + string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") From 5ef35a4ccfc2d39dd62ca5901b435f4bcf149e7b Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 8 Dec 2023 17:39:56 +0100 Subject: [PATCH 068/157] Don't try to poke at the header on deinit if MPQueue is nullptr --- src/frontend/qt_sdl/LocalMP.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp index a0dfdf76..c69537a3 100644 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ b/src/frontend/qt_sdl/LocalMP.cpp @@ -303,10 +303,13 @@ void DeInit() if (MPQueue) { MPQueue->lock(); - MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); - header->ConnectedBitmask &= ~(1 << InstanceID); - header->InstanceBitmask &= ~(1 << InstanceID); - header->NumInstances--; + if (MPQueue->data() != nullptr) + { + MPQueueHeader *header = (MPQueueHeader *) MPQueue->data(); + header->ConnectedBitmask &= ~(1 << InstanceID); + header->InstanceBitmask &= ~(1 << InstanceID); + header->NumInstances--; + } MPQueue->unlock(); SemPoolDeinit(); From 69491004460fd623b81cfa3ad4bcede3e8d6250c Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 8 Dec 2023 18:29:30 +0100 Subject: [PATCH 069/157] Fix GBA<->DS comm not working when using FreeBIOS Thanks CasualPokePlayer for pointing this out --- src/NDS.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index 54aa270d..c00aa511 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -284,6 +284,13 @@ void NDS::SetupDirectBoot() const u8* cartrom = NDSCartSlot.GetCart()->GetROM(); MapSharedWRAM(3); + // Copy the Nintendo logo from the NDS ROM header to the ARM9 BIOS if using FreeBIOS + // Games need this for DS<->GBA comm to work + if (IsLoadedARM9BIOSBuiltIn()) + { + memcpy(ARM9BIOS.data() + 0x20, header.NintendoLogo, 0x9C); + } + // setup main RAM data for (u32 i = 0; i < 0x170; i+=4) @@ -4192,4 +4199,4 @@ void NDS::ARM7IOWrite32(u32 addr, u32 val) Log(LogLevel::Debug, "unknown ARM7 IO write32 %08X %08X %08X\n", addr, val, ARM7.R[15]); } -} \ No newline at end of file +} From 082310d5d5437bde589a99849f4cfacb58b1af53 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Fri, 8 Dec 2023 23:42:08 +0100 Subject: [PATCH 070/157] hopefully reset all GPU3D attributes properly --- src/GPU3D.cpp | 82 +++++++++++++++++++++++++++++++++++++-------------- src/GPU3D.h | 1 + 2 files changed, 61 insertions(+), 22 deletions(-) diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 049ae589..1a879abf 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -172,17 +172,6 @@ void GPU3D::Reset() noexcept CmdStallQueue.Clear(); - NumCommands = 0; - CurCommand = 0; - ParamCount = 0; - TotalParams = 0; - - NumPushPopCommands = 0; - NumTestCommands = 0; - - DispCnt = 0; - AlphaRef = 0; - ZeroDotWLimit = 0; // CHECKME GXStat = 0; @@ -190,7 +179,6 @@ void GPU3D::Reset() noexcept memset(ExecParams, 0, 32*4); ExecParamCount = 0; - Timestamp = 0; CycleCount = 0; VertexPipeline = 0; NormalPipeline = 0; @@ -198,6 +186,8 @@ void GPU3D::Reset() noexcept VertexSlotCounter = 0; VertexSlotsFree = 1; + NumPushPopCommands = 0; + NumTestCommands = 0; MatrixMode = 0; @@ -215,35 +205,83 @@ void GPU3D::Reset() noexcept memset(PosMatrixStack, 0, 31 * 16*4); memset(VecMatrixStack, 0, 31 * 16*4); memset(TexMatrixStack, 0, 16*4); + ProjMatrixStackPointer = 0; PosMatrixStackPointer = 0; TexMatrixStackPointer = 0; + NumCommands = 0; + CurCommand = 0; + ParamCount = 0; + TotalParams = 0; + + GeometryEnabled = false; + RenderingEnabled = false; + + DispCnt = 0; + AlphaRefVal = 0; + AlphaRef = 0; + + memset(ToonTable, 0, sizeof(ToonTable)); + memset(EdgeTable, 0, sizeof(EdgeTable)); + + // TODO: confirm initial polyid/color/fog values + FogOffset = 0; + FogColor = 0; + memset(FogDensityTable, 0, sizeof(FogDensityTable)); + + ClearAttr1 = 0x3F000000; + ClearAttr2 = 0x00007FFF; + + ResetRenderingState(); + + AbortFrame = false; + + Timestamp = 0; + + PolygonMode = 0; + memset(CurVertex, 0, sizeof(CurVertex)); + memset(VertexColor, 0, sizeof(VertexColor)); + memset(TexCoords, 0, sizeof(TexCoords)); + memset(RawTexCoords, 0, sizeof(RawTexCoords)); + memset(Normal, 0, sizeof(Normal)); + + memset(LightDirection, 0, sizeof(LightDirection)); + memset(LightColor, 0, sizeof(LightColor)); + memset(MatDiffuse, 0, sizeof(MatDiffuse)); + memset(MatAmbient, 0, sizeof(MatAmbient)); + memset(MatSpecular, 0, sizeof(MatSpecular)); + memset(MatEmission, 0, sizeof(MatSpecular)); + + UseShininessTable = false; + memset(ShininessTable, 0, sizeof(ShininessTable)); + + PolygonAttr = 0; + CurPolygonAttr = 0; + + TexParam = 0; + TexPalette = 0; + memset(PosTestResult, 0, 4*4); memset(VecTestResult, 0, 2*3); + memset(TempVertexBuffer, 0, sizeof(TempVertexBuffer)); VertexNum = 0; VertexNumInPoly = 0; + NumConsecutivePolygons = 0; + LastStripPolygon = nullptr; + NumOpaquePolygons = 0; - CurRAMBank = 0; CurVertexRAM = &VertexRAM[0]; CurPolygonRAM = &PolygonRAM[0]; NumVertices = 0; NumPolygons = 0; - NumOpaquePolygons = 0; - - // TODO: confirm initial polyid/color/fog values - ClearAttr1 = 0x3F000000; - ClearAttr2 = 0x00007FFF; + CurRAMBank = 0; FlushRequest = 0; FlushAttributes = 0; - ResetRenderingState(); - RenderXPos = 0; - - AbortFrame = false; } void GPU3D::DoSavestate(Savestate* file) noexcept diff --git a/src/GPU3D.h b/src/GPU3D.h index 8e743fac..dda78b78 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -254,6 +254,7 @@ public: u32 ClearAttr1 = 0; u32 ClearAttr2 = 0; + u32 RenderDispCnt = 0; u8 RenderAlphaRef = 0; From 81219a9f5d60d0e9c92a4c6cec279b087aae053d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Mon, 11 Dec 2023 10:56:09 +0100 Subject: [PATCH 071/157] Fix some conflicts with windows.h in some configurations Fixes build in the MSYS2 Clang/ClangARM64 environments. --- src/fatfs/ff.c | 186 ++++++++++++++++---------------- src/fatfs/ff.h | 60 +++++------ src/fatfs/ffsystem.c | 4 + src/frontend/qt_sdl/LocalMP.cpp | 2 +- 4 files changed, 128 insertions(+), 124 deletions(-) diff --git a/src/fatfs/ff.c b/src/fatfs/ff.c index 9d212949..385da84e 100644 --- a/src/fatfs/ff.c +++ b/src/fatfs/ff.c @@ -728,13 +728,13 @@ static int dbc_2nd (BYTE c) #if FF_USE_LFN -/* Get a Unicode code point from the TCHAR string in defined API encodeing */ +/* Get a Unicode code point from the FF_TCHAR string in defined API encodeing */ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */ - const TCHAR** str /* Pointer to pointer to TCHAR string in configured encoding */ + const FF_TCHAR** str /* Pointer to pointer to FF_TCHAR string in configured encoding */ ) { DWORD uc; - const TCHAR *p = *str; + const FF_TCHAR *p = *str; #if FF_LFN_UNICODE == 1 /* UTF-16 input */ WCHAR wc; @@ -771,7 +771,7 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on } #elif FF_LFN_UNICODE == 3 /* UTF-32 input */ - uc = (TCHAR)*p++; /* Get a unit */ + uc = (FF_TCHAR)*p++; /* Get a unit */ if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF; /* Wrong code? */ if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */ @@ -800,7 +800,7 @@ static DWORD tchar2uni ( /* Returns a character in UTF-16 encoding (>=0x10000 on /* Store a Unicode char in defined API encoding */ static UINT put_utf ( /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */ DWORD chr, /* UTF-16 encoded character (Surrogate pair if >=0x10000) */ - TCHAR* buf, /* Output buffer */ + FF_TCHAR* buf, /* Output buffer */ UINT szb /* Size of the buffer */ ) { @@ -824,20 +824,20 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over if (chr < 0x80) { /* Single byte code? */ if (szb < 1) return 0; /* Buffer overflow? */ - *buf = (TCHAR)chr; + *buf = (FF_TCHAR)chr; return 1; } if (chr < 0x800) { /* 2-byte sequence? */ if (szb < 2) return 0; /* Buffer overflow? */ - *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F)); - *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + *buf++ = (FF_TCHAR)(0xC0 | (chr >> 6 & 0x1F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F)); return 2; } if (chr < 0x10000) { /* 3-byte sequence? */ if (szb < 3 || IsSurrogate(chr)) return 0; /* Buffer overflow or wrong code? */ - *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F)); - *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); - *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + *buf++ = (FF_TCHAR)(0xE0 | (chr >> 12 & 0x0F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F)); return 3; } /* 4-byte sequence */ @@ -846,10 +846,10 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over chr = (chr & 0xFFFF) - 0xDC00; /* Get low 10 bits */ if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ chr = (hc | chr) + 0x10000; - *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07)); - *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F)); - *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F)); - *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F)); + *buf++ = (FF_TCHAR)(0xF0 | (chr >> 18 & 0x07)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 12 & 0x3F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 6 & 0x3F)); + *buf++ = (FF_TCHAR)(0x80 | (chr >> 0 & 0x3F)); return 4; #elif FF_LFN_UNICODE == 3 /* UTF-32 output */ @@ -862,7 +862,7 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over if (hc >= 0x100000 || chr >= 0x400) return 0; /* Wrong surrogate? */ chr = (hc | chr) + 0x10000; } - *buf++ = (TCHAR)chr; + *buf++ = (FF_TCHAR)chr; return 1; #else /* ANSI/OEM output */ @@ -872,11 +872,11 @@ static UINT put_utf ( /* Returns number of encoding units written (0:buffer over if (wc >= 0x100) { /* Is this a DBC? */ if (szb < 2) return 0; *buf++ = (char)(wc >> 8); /* Store DBC 1st byte */ - *buf++ = (TCHAR)wc; /* Store DBC 2nd byte */ + *buf++ = (FF_TCHAR)wc; /* Store DBC 2nd byte */ return 2; } if (wc == 0 || szb < 1) return 0; /* Invalid char or buffer overflow? */ - *buf++ = (TCHAR)wc; /* Store the character */ + *buf++ = (FF_TCHAR)wc; /* Store the character */ return 1; #endif } @@ -2595,7 +2595,7 @@ static void get_fileinfo ( FATFS *fs = dp->obj.fs; UINT nw; #else - TCHAR c; + FF_TCHAR c; #endif @@ -2668,7 +2668,7 @@ static void get_fileinfo ( if (nw == 0) { di = 0; break; } /* Buffer overflow? */ di += nw; #else /* ANSI/OEM output */ - fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */ + fno->altname[di++] = (FF_TCHAR)wc; /* Store it without any conversion */ #endif } fno->altname[di] = 0; /* Terminate the SFN (null string means SFN is invalid) */ @@ -2681,7 +2681,7 @@ static void get_fileinfo ( wc = (WCHAR)fno->altname[si]; if (wc == '.') lcf = NS_EXT; if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20; - fno->fname[di] = (TCHAR)wc; + fno->fname[di] = (FF_TCHAR)wc; } } fno->fname[di] = 0; /* Terminate the LFN */ @@ -2691,7 +2691,7 @@ static void get_fileinfo ( #else /* Non-LFN configuration */ si = di = 0; while (si < 11) { /* Copy name body and extension */ - c = (TCHAR)dp->dir[si++]; + c = (FF_TCHAR)dp->dir[si++]; if (c == ' ') continue; /* Skip padding spaces */ if (c == RDDEM) c = DDEM; /* Restore replaced DDEM character */ if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */ @@ -2719,7 +2719,7 @@ static void get_fileinfo ( static DWORD get_achar ( /* Get a character and advance ptr */ - const TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ + const FF_TCHAR** ptr /* Pointer to pointer to the ANSI/OEM or Unicode string */ ) { DWORD chr; @@ -2750,13 +2750,13 @@ static DWORD get_achar ( /* Get a character and advance ptr */ static int pattern_match ( /* 0:mismatched, 1:matched */ - const TCHAR* pat, /* Matching pattern */ - const TCHAR* nam, /* String to be tested */ + const FF_TCHAR* pat, /* Matching pattern */ + const FF_TCHAR* nam, /* String to be tested */ UINT skip, /* Number of pre-skip chars (number of ?s, b8:infinite (* specified)) */ UINT recur /* Recursion count */ ) { - const TCHAR *pptr, *nptr; + const FF_TCHAR *pptr, *nptr; DWORD pchr, nchr; UINT sk; @@ -2800,7 +2800,7 @@ static int pattern_match ( /* 0:mismatched, 1:matched */ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ FF_DIR* dp, /* Pointer to the directory object */ - const TCHAR** path /* Pointer to pointer to the segment in the path string */ + const FF_TCHAR** path /* Pointer to pointer to the segment in the path string */ ) { #if FF_USE_LFN /* LFN configuration */ @@ -2808,7 +2808,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr WCHAR wc, *lfn; DWORD uc; UINT i, ni, si, di; - const TCHAR *p; + const FF_TCHAR *p; /* Create LFN into LFN working buffer */ @@ -3002,7 +3002,7 @@ static FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not cr static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ FF_DIR* dp, /* Directory object to return last directory and found object */ - const TCHAR* path /* Full-path string to find a file or directory */ + const FF_TCHAR* path /* Full-path string to find a file or directory */ ) { FRESULT res; @@ -3088,11 +3088,11 @@ static FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ /*-----------------------------------------------------------------------*/ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive number or null pointer) */ - const TCHAR** path /* Pointer to pointer to the path name */ + const FF_TCHAR** path /* Pointer to pointer to the path name */ ) { - const TCHAR *tp, *tt; - TCHAR tc; + const FF_TCHAR *tp, *tt; + FF_TCHAR tc; int i; int vol = -1; #if FF_STR_VOLUME_ID /* Find string volume ID */ @@ -3118,7 +3118,7 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb c = *sp++; tc = *tp++; if (IsLower(c)) c -= 0x20; if (IsLower(tc)) tc -= 0x20; - } while (c && (TCHAR)c == tc); + } while (c && (FF_TCHAR)c == tc); } while ((c || tp != tt) && ++i < FF_VOLUMES); /* Repeat for each id until pattern match */ } #endif @@ -3138,7 +3138,7 @@ static int get_ldnumber ( /* Returns logical drive number (-1:invalid drive numb c = *sp++; tc = *(++tt); if (IsLower(c)) c -= 0x20; if (IsLower(tc)) tc -= 0x20; - } while (c && (TCHAR)c == tc); + } while (c && (FF_TCHAR)c == tc); } while ((c || (tc != '/' && !IsTerminator(tc))) && ++i < FF_VOLUMES); /* Repeat for each ID until pattern match */ if (i < FF_VOLUMES) { /* If a volume ID is found, get the drive number and strip it */ vol = i; /* Drive number */ @@ -3330,7 +3330,7 @@ static UINT find_volume ( /* Returns BS status found in the hosting drive */ /*-----------------------------------------------------------------------*/ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */ - const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + const FF_TCHAR** path, /* Pointer to pointer to the path name (drive number) */ FATFS** rfs, /* Pointer to pointer to the found filesystem object */ BYTE mode /* !=0: Check write protection for write access */ ) @@ -3604,14 +3604,14 @@ static FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ FRESULT f_mount ( FATFS* fs, /* Pointer to the filesystem object to be registered (NULL:unmount)*/ - const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + const FF_TCHAR* path, /* Logical drive number to be mounted/unmounted */ BYTE opt /* Mount option: 0=Do not mount (delayed mount), 1=Mount immediately */ ) { FATFS *cfs; int vol; FRESULT res; - const TCHAR *rp = path; + const FF_TCHAR *rp = path; /* Get logical drive number */ @@ -3652,7 +3652,7 @@ FRESULT f_mount ( FRESULT f_open ( FF_FIL* fp, /* Pointer to the blank file object */ - const TCHAR* path, /* Pointer to the file name */ + const FF_TCHAR* path, /* Pointer to the file name */ BYTE mode /* Access mode and open mode flags */ ) { @@ -4186,7 +4186,7 @@ FRESULT f_close ( /*-----------------------------------------------------------------------*/ FRESULT f_chdrive ( - const TCHAR* path /* Drive number to set */ + const FF_TCHAR* path /* Drive number to set */ ) { int vol; @@ -4203,7 +4203,7 @@ FRESULT f_chdrive ( FRESULT f_chdir ( - const TCHAR* path /* Pointer to the directory path */ + const FF_TCHAR* path /* Pointer to the directory path */ ) { #if FF_STR_VOLUME_ID == 2 @@ -4265,8 +4265,8 @@ FRESULT f_chdir ( #if FF_FS_RPATH >= 2 FRESULT f_getcwd ( - TCHAR* buff, /* Pointer to the directory path */ - UINT len /* Size of buff in unit of TCHAR */ + FF_TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of buff in unit of FF_TCHAR */ ) { FRESULT res; @@ -4274,7 +4274,7 @@ FRESULT f_getcwd ( FATFS *fs; UINT i, n; DWORD ccl; - TCHAR *tp = buff; + FF_TCHAR *tp = buff; #if FF_VOLUMES >= 2 UINT vl; #if FF_STR_VOLUME_ID @@ -4287,7 +4287,7 @@ FRESULT f_getcwd ( /* Get logical drive */ buff[0] = 0; /* Set null string to get current volume */ - res = mount_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ + res = mount_volume((const FF_TCHAR**)&buff, &fs, 0); /* Get current volume */ if (res == FR_OK) { dj.obj.fs = fs; INIT_NAMBUF(fs); @@ -4328,15 +4328,15 @@ FRESULT f_getcwd ( #if FF_STR_VOLUME_ID >= 1 /* String volume ID */ for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ; if (i >= n + 2) { - if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/'; - for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ; - if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':'; + if (FF_STR_VOLUME_ID == 2) *tp++ = (FF_TCHAR)'/'; + for (vl = 0; vl < n; *tp++ = (FF_TCHAR)vp[vl], vl++) ; + if (FF_STR_VOLUME_ID == 1) *tp++ = (FF_TCHAR)':'; vl++; } #else /* Numeric volume ID */ if (i >= 3) { - *tp++ = (TCHAR)'0' + CurrVol; - *tp++ = (TCHAR)':'; + *tp++ = (FF_TCHAR)'0' + CurrVol; + *tp++ = (FF_TCHAR)':'; vl = 2; } #endif @@ -4530,7 +4530,7 @@ FRESULT f_lseek ( FRESULT f_opendir ( FF_DIR* dp, /* Pointer to directory object to create */ - const TCHAR* path /* Pointer to the directory path */ + const FF_TCHAR* path /* Pointer to the directory path */ ) { FRESULT res; @@ -4688,8 +4688,8 @@ FRESULT f_findnext ( FRESULT f_findfirst ( FF_DIR* dp, /* Pointer to the blank directory object */ FF_FILINFO* fno, /* Pointer to the file information structure */ - const TCHAR* path, /* Pointer to the directory to open */ - const TCHAR* pattern /* Pointer to the matching pattern */ + const FF_TCHAR* path, /* Pointer to the directory to open */ + const FF_TCHAR* pattern /* Pointer to the matching pattern */ ) { FRESULT res; @@ -4713,7 +4713,7 @@ FRESULT f_findfirst ( /*-----------------------------------------------------------------------*/ FRESULT f_stat ( - const TCHAR* path, /* Pointer to the file path */ + const FF_TCHAR* path, /* Pointer to the file path */ FF_FILINFO* fno /* Pointer to file information to return */ ) { @@ -4748,7 +4748,7 @@ FRESULT f_stat ( /*-----------------------------------------------------------------------*/ FRESULT f_getfree ( - const TCHAR* path, /* Logical drive number */ + const FF_TCHAR* path, /* Logical drive number */ DWORD* nclst, /* Pointer to a variable to return number of free clusters */ FATFS** fatfs /* Pointer to return pointer to corresponding filesystem object */ ) @@ -4890,7 +4890,7 @@ FRESULT f_truncate ( /*-----------------------------------------------------------------------*/ FRESULT f_unlink ( - const TCHAR* path /* Pointer to the file or directory path */ + const FF_TCHAR* path /* Pointer to the file or directory path */ ) { FRESULT res; @@ -4984,7 +4984,7 @@ FRESULT f_unlink ( /*-----------------------------------------------------------------------*/ FRESULT f_mkdir ( - const TCHAR* path /* Pointer to the directory path */ + const FF_TCHAR* path /* Pointer to the directory path */ ) { FRESULT res; @@ -5068,8 +5068,8 @@ FRESULT f_mkdir ( /*-----------------------------------------------------------------------*/ FRESULT f_rename ( - const TCHAR* path_old, /* Pointer to the object name to be renamed */ - const TCHAR* path_new /* Pointer to the new name */ + const FF_TCHAR* path_old, /* Pointer to the object name to be renamed */ + const FF_TCHAR* path_new /* Pointer to the new name */ ) { FRESULT res; @@ -5178,7 +5178,7 @@ FRESULT f_rename ( /*-----------------------------------------------------------------------*/ FRESULT f_chmod ( - const TCHAR* path, /* Pointer to the file path */ + const FF_TCHAR* path, /* Pointer to the file path */ BYTE attr, /* Attribute bits */ BYTE mask /* Attribute mask to change */ ) @@ -5225,7 +5225,7 @@ FRESULT f_chmod ( /*-----------------------------------------------------------------------*/ FRESULT f_utime ( - const TCHAR* path, /* Pointer to the file/directory name */ + const FF_TCHAR* path, /* Pointer to the file/directory name */ const FF_FILINFO* fno /* Pointer to the timestamp to be set */ ) { @@ -5272,8 +5272,8 @@ FRESULT f_utime ( /*-----------------------------------------------------------------------*/ FRESULT f_getlabel ( - const TCHAR* path, /* Logical drive number */ - TCHAR* label, /* Buffer to store the volume label */ + const FF_TCHAR* path, /* Logical drive number */ + FF_TCHAR* label, /* Buffer to store the volume label */ DWORD* vsn /* Variable to store the volume serial number */ ) { @@ -5322,7 +5322,7 @@ FRESULT f_getlabel ( if (wc == 0) { di = 0; break; } /* Invalid char in current code page? */ di += put_utf(wc, &label[di], 4); /* Store it in Unicode */ #else /* ANSI/OEM output */ - label[di++] = (TCHAR)wc; + label[di++] = (FF_TCHAR)wc; #endif } do { /* Truncate trailing spaces */ @@ -5369,7 +5369,7 @@ FRESULT f_getlabel ( /*-----------------------------------------------------------------------*/ FRESULT f_setlabel ( - const TCHAR* label /* Volume label to set with heading logical drive number */ + const FF_TCHAR* label /* Volume label to set with heading logical drive number */ ) { FRESULT res; @@ -5800,7 +5800,7 @@ static FRESULT create_partition ( FRESULT f_mkfs ( - const TCHAR* path, /* Logical drive number */ + const FF_TCHAR* path, /* Logical drive number */ const FF_MKFS_PARM* opt, /* Format options */ void* work, /* Pointer to working buffer (null: use heap memory) */ UINT len /* Size of working buffer [byte] */ @@ -6335,14 +6335,14 @@ FRESULT f_fdisk ( /* Get a String from the File */ /*-----------------------------------------------------------------------*/ -TCHAR* f_gets ( - TCHAR* buff, /* Pointer to the buffer to store read string */ +FF_TCHAR* f_gets ( + FF_TCHAR* buff, /* Pointer to the buffer to store read string */ int len, /* Size of string buffer (items) */ FF_FIL* fp /* Pointer to the file object */ ) { int nc = 0; - TCHAR *p = buff; + FF_TCHAR *p = buff; BYTE s[4]; UINT rc; DWORD dc; @@ -6407,32 +6407,32 @@ TCHAR* f_gets ( if (FF_USE_STRFUNC == 2 && dc == '\r') continue; /* Strip \r off if needed */ #if FF_LFN_UNICODE == 1 || FF_LFN_UNICODE == 3 /* Output it in UTF-16/32 encoding */ if (FF_LFN_UNICODE == 1 && dc >= 0x10000) { /* Out of BMP at UTF-16? */ - *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */ + *p++ = (FF_TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++; /* Make and output high surrogate */ dc = 0xDC00 | (dc & 0x3FF); /* Make low surrogate */ } - *p++ = (TCHAR)dc; nc++; + *p++ = (FF_TCHAR)dc; nc++; if (dc == '\n') break; /* End of line? */ #elif FF_LFN_UNICODE == 2 /* Output it in UTF-8 encoding */ if (dc < 0x80) { /* Single byte? */ - *p++ = (TCHAR)dc; + *p++ = (FF_TCHAR)dc; nc++; if (dc == '\n') break; /* End of line? */ } else { if (dc < 0x800) { /* 2-byte sequence? */ - *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F)); - *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + *p++ = (FF_TCHAR)(0xC0 | (dc >> 6 & 0x1F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F)); nc += 2; } else { if (dc < 0x10000) { /* 3-byte sequence? */ - *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F)); - *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); - *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + *p++ = (FF_TCHAR)(0xE0 | (dc >> 12 & 0x0F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F)); nc += 3; } else { /* 4-byte sequence? */ - *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07)); - *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F)); - *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F)); - *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F)); + *p++ = (FF_TCHAR)(0xF0 | (dc >> 18 & 0x07)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 12 & 0x3F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 6 & 0x3F)); + *p++ = (FF_TCHAR)(0x80 | (dc >> 0 & 0x3F)); nc += 4; } } @@ -6447,7 +6447,7 @@ TCHAR* f_gets ( if (rc != 1) break; /* EOF? */ dc = s[0]; if (FF_USE_STRFUNC == 2 && dc == '\r') continue; - *p++ = (TCHAR)dc; nc++; + *p++ = (FF_TCHAR)dc; nc++; if (dc == '\n') break; } #endif @@ -6485,7 +6485,7 @@ typedef struct { /* Buffered file write with code conversion */ -static void putc_bfd (putbuff* pb, TCHAR c) +static void putc_bfd (putbuff* pb, FF_TCHAR c) { UINT n; int i, nc; @@ -6493,7 +6493,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) WCHAR hs, wc; #if FF_LFN_UNICODE == 2 DWORD dc; - const TCHAR *tp; + const FF_TCHAR *tp; #endif #endif @@ -6535,7 +6535,7 @@ static void putc_bfd (putbuff* pb, TCHAR c) return; } } - tp = (const TCHAR*)pb->bs; + tp = (const FF_TCHAR*)pb->bs; dc = tchar2uni(&tp); /* UTF-8 ==> UTF-16 */ if (dc == 0xFFFFFFFF) return; /* Wrong code? */ wc = (WCHAR)dc; @@ -6638,7 +6638,7 @@ static void putc_init (putbuff* pb, FF_FIL* fp) int f_putc ( - TCHAR c, /* A character to be output */ + FF_TCHAR c, /* A character to be output */ FF_FIL* fp /* Pointer to the file object */ ) { @@ -6658,7 +6658,7 @@ int f_putc ( /*-----------------------------------------------------------------------*/ int f_puts ( - const TCHAR* str, /* Pointer to the string to be output */ + const FF_TCHAR* str, /* Pointer to the string to be output */ FF_FIL* fp /* Pointer to the file object */ ) { @@ -6727,7 +6727,7 @@ static void ftoa ( char* buf, /* Buffer to output the floating point string */ double val, /* Value to output */ int prec, /* Number of fractional digits */ - TCHAR fmt /* Notation */ + FF_TCHAR fmt /* Notation */ ) { int d; @@ -6800,7 +6800,7 @@ static void ftoa ( int f_printf ( FF_FIL* fp, /* Pointer to the file object */ - const TCHAR* fmt, /* Pointer to the format string */ + const FF_TCHAR* fmt, /* Pointer to the format string */ ... /* Optional arguments... */ ) { @@ -6813,8 +6813,8 @@ int f_printf ( #else DWORD v; #endif - TCHAR tc, pad, *tp; - TCHAR nul = 0; + FF_TCHAR tc, pad, *tp; + FF_TCHAR nul = 0; char d, str[SZ_NUM_BUF]; @@ -6879,10 +6879,10 @@ int f_printf ( case 'X': /* Unsigned hexdecimal (upper case) */ r = 16; break; case 'c': /* Character */ - putc_bfd(&pb, (TCHAR)va_arg(arp, int)); + putc_bfd(&pb, (FF_TCHAR)va_arg(arp, int)); continue; case 's': /* String */ - tp = va_arg(arp, TCHAR*); /* Get a pointer argument */ + tp = va_arg(arp, FF_TCHAR*); /* Get a pointer argument */ if (!tp) tp = &nul; /* Null ptr generates a null string */ for (j = 0; tp[j]; j++) ; /* j = tcslen(tp) */ if (prec >= 0 && j > (UINT)prec) j = prec; /* Limited length of string body */ @@ -6937,7 +6937,7 @@ int f_printf ( if (f & 1) str[i++] = '-'; /* Sign */ /* Write it */ for (j = i; !(f & 2) && j < w; j++) putc_bfd(&pb, pad); /* Left pads */ - do putc_bfd(&pb, (TCHAR)str[--i]); while (i); /* Body */ + do putc_bfd(&pb, (FF_TCHAR)str[--i]); while (i); /* Body */ while (j++ < w) putc_bfd(&pb, ' '); /* Right pads */ } diff --git a/src/fatfs/ff.h b/src/fatfs/ff.h index c2832be1..1662d836 100644 --- a/src/fatfs/ff.h +++ b/src/fatfs/ff.h @@ -85,24 +85,24 @@ typedef DWORD LBA_t; -/* Type of path name strings on FatFs API (TCHAR) */ +/* Type of path name strings on FatFs API (FF_TCHAR) */ #if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ -typedef WCHAR TCHAR; +typedef WCHAR FF_TCHAR; #define _T(x) L ## x #define _TEXT(x) L ## x #elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ -typedef char TCHAR; +typedef char FF_TCHAR; #define _T(x) u8 ## x #define _TEXT(x) u8 ## x #elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ -typedef DWORD TCHAR; +typedef DWORD FF_TCHAR; #define _T(x) U ## x #define _TEXT(x) U ## x #elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) #error Wrong FF_LFN_UNICODE setting #else /* ANSI/OEM code in SBCS/DBCS */ -typedef char TCHAR; +typedef char FF_TCHAR; #define _T(x) x #define _TEXT(x) x #endif @@ -236,7 +236,7 @@ typedef struct { DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ #endif #if FF_USE_FIND - const TCHAR* pat; /* Pointer to the name matching pattern */ + const FF_TCHAR* pat; /* Pointer to the name matching pattern */ #endif } FF_DIR; @@ -250,10 +250,10 @@ typedef struct { WORD ftime; /* Modified time */ BYTE fattrib; /* File attribute */ #if FF_USE_LFN - TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ - TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ + FF_TCHAR altname[FF_SFN_BUF + 1];/* Altenative file name */ + FF_TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ #else - TCHAR fname[12 + 1]; /* File name */ + FF_TCHAR fname[12 + 1]; /* File name */ #endif } FF_FILINFO; @@ -301,40 +301,40 @@ typedef enum { /*--------------------------------------------------------------*/ /* FatFs module application interface */ -FRESULT f_open (FF_FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_open (FF_FIL* fp, const FF_TCHAR* path, BYTE mode); /* Open or create a file */ FRESULT f_close (FF_FIL* fp); /* Close an open file object */ FRESULT f_read (FF_FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ FRESULT f_write (FF_FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ FRESULT f_lseek (FF_FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ FRESULT f_truncate (FF_FIL* fp); /* Truncate the file */ FRESULT f_sync (FF_FIL* fp); /* Flush cached data of the writing file */ -FRESULT f_opendir (FF_DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_opendir (FF_DIR* dp, const FF_TCHAR* path); /* Open a directory */ FRESULT f_closedir (FF_DIR* dp); /* Close an open directory */ FRESULT f_readdir (FF_DIR* dp, FF_FILINFO* fno); /* Read a directory item */ -FRESULT f_findfirst (FF_DIR* dp, FF_FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findfirst (FF_DIR* dp, FF_FILINFO* fno, const FF_TCHAR* path, const FF_TCHAR* pattern); /* Find first file */ FRESULT f_findnext (FF_DIR* dp, FF_FILINFO* fno); /* Find next file */ -FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ -FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ -FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ -FRESULT f_stat (const TCHAR* path, FF_FILINFO* fno); /* Get file status */ -FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ -FRESULT f_utime (const TCHAR* path, const FF_FILINFO* fno); /* Change timestamp of a file/dir */ -FRESULT f_chdir (const TCHAR* path); /* Change current directory */ -FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ -FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ -FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ -FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ -FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_mkdir (const FF_TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const FF_TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const FF_TCHAR* path_old, const FF_TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const FF_TCHAR* path, FF_FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const FF_TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const FF_TCHAR* path, const FF_FILINFO* fno); /* Change timestamp of a file/dir */ +FRESULT f_chdir (const FF_TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const FF_TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (FF_TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const FF_TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const FF_TCHAR* path, FF_TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const FF_TCHAR* label); /* Set volume label */ FRESULT f_forward (FF_FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ FRESULT f_expand (FF_FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */ -FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ -FRESULT f_mkfs (const TCHAR* path, const FF_MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_mount (FATFS* fs, const FF_TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const FF_TCHAR* path, const FF_MKFS_PARM* opt, void* work, UINT len); /* Create a FAT volume */ FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work); /* Divide a physical drive into some partitions */ FRESULT f_setcp (WORD cp); /* Set current code page */ -int f_putc (TCHAR c, FF_FIL* fp); /* Put a character to the file */ -int f_puts (const TCHAR* str, FF_FIL* cp); /* Put a string to the file */ -int f_printf (FF_FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ -TCHAR* f_gets (TCHAR* buff, int len, FF_FIL* fp); /* Get a string from the file */ +int f_putc (FF_TCHAR c, FF_FIL* fp); /* Put a character to the file */ +int f_puts (const FF_TCHAR* str, FF_FIL* cp); /* Put a string to the file */ +int f_printf (FF_FIL* fp, const FF_TCHAR* str, ...); /* Put a formatted string to the file */ +FF_TCHAR* f_gets (FF_TCHAR* buff, int len, FF_FIL* fp); /* Get a string from the file */ #define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) #define f_error(fp) ((fp)->err) diff --git a/src/fatfs/ffsystem.c b/src/fatfs/ffsystem.c index 63fedf65..ebde84a5 100644 --- a/src/fatfs/ffsystem.c +++ b/src/fatfs/ffsystem.c @@ -110,7 +110,11 @@ DWORD get_fattime(void) time_t timestamp = time(NULL); struct tm timedata; +#if defined(_MSC_VER) + localtime_s(&timedata, ×tamp); +#else localtime_r(×tamp, &timedata); +#endif DWORD ret; ret = (timedata.tm_sec >> 1); diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp index c69537a3..466f90cf 100644 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ b/src/frontend/qt_sdl/LocalMP.cpp @@ -130,7 +130,7 @@ bool SemInit(int num) char semname[64]; sprintf(semname, "Local\\melonNIFI_Sem%02d", num); - HANDLE sem = CreateSemaphore(nullptr, 0, 64, semname); + HANDLE sem = CreateSemaphoreA(nullptr, 0, 64, semname); SemPool[num] = sem; SemInited[num] = true; return sem != INVALID_HANDLE_VALUE; From 2cba2e783a2ef3a83b7d8bf0cb6e42a6298edbf6 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Mon, 11 Dec 2023 18:04:04 -0500 Subject: [PATCH 072/157] fix default emu settings tab (#1912) --- src/frontend/qt_sdl/EmuSettingsDialog.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.ui b/src/frontend/qt_sdl/EmuSettingsDialog.ui index 74bc0865..2746e1da 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.ui +++ b/src/frontend/qt_sdl/EmuSettingsDialog.ui @@ -26,7 +26,7 @@ - 5 + 0 From 9bfc9c08ffe88de4b54734d6fd03182c0a51e181 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Tue, 12 Dec 2023 05:07:22 -0500 Subject: [PATCH 073/157] Sprinkle `const` around where appropriate (#1909) * Sprinkle `const` around where appropriate - This will make it easier to use `NDS` objects in `const` contexts (e.g. `const` parameters or methods) * Remove the `const` qualifier on `DSi_DSP::DSPRead16` - MMIO reads can be non-pure, so this may not be `const` in the future --- src/AREngine.cpp | 6 +++--- src/AREngine.h | 2 +- src/ARM.h | 6 +++--- src/ARMJIT_A64/ARMJIT_Compiler.h | 4 ++-- src/ARMJIT_A64/ARMJIT_LoadStore.cpp | 2 +- src/ARMJIT_Internal.h | 2 +- src/ARMJIT_RegisterCache.h | 2 +- src/ARMJIT_x64/ARMJIT_Compiler.cpp | 4 ++-- src/ARMJIT_x64/ARMJIT_Compiler.h | 4 ++-- src/CP15.cpp | 2 +- src/DSi.cpp | 12 +++++------ src/DSi.h | 14 ++++++------- src/DSi_AES.cpp | 2 +- src/DSi_AES.h | 2 +- src/DSi_Camera.cpp | 10 ++++----- src/DSi_Camera.h | 12 ++++++----- src/DSi_DSP.cpp | 8 ++++---- src/DSi_DSP.h | 10 ++++----- src/DSi_I2C.cpp | 14 ++++++------- src/DSi_I2C.h | 14 ++++++------- src/DSi_NAND.cpp | 2 +- src/DSi_NAND.h | 2 +- src/DSi_NDMA.h | 4 ++-- src/DSi_SD.cpp | 4 ++-- src/DSi_SD.h | 4 ++-- src/DSi_SPI_TSC.cpp | 2 +- src/DSi_SPI_TSC.h | 2 +- src/FATStorage.cpp | 6 +++--- src/FATStorage.h | 6 +++--- src/FIFO.h | 24 +++++++++++----------- src/GPU2D.cpp | 6 +++--- src/GPU2D.h | 8 ++++---- src/GPU2D_Soft.cpp | 2 +- src/GPU2D_Soft.h | 2 +- src/GPU3D.cpp | 4 ++-- src/GPU3D.h | 4 ++-- src/GPU3D_OpenGL.cpp | 20 +++++++++--------- src/GPU3D_OpenGL.h | 10 ++++----- src/GPU3D_Soft.cpp | 14 ++++++------- src/GPU3D_Soft.h | 18 ++++++++-------- src/JitBlock.h | 12 +++++------ src/NDS.cpp | 12 +++++------ src/NDS.h | 14 ++++++------- src/NDSCart.cpp | 32 ++++++++++++++--------------- src/NDSCart.h | 32 ++++++++++++++--------------- src/NonStupidBitfield.h | 8 ++++---- src/RTC.cpp | 16 +++++++-------- src/RTC.h | 16 +++++++-------- src/SPI.cpp | 10 ++++----- src/SPI.h | 16 ++++++++------- src/SPU.cpp | 2 +- src/SPU.h | 2 +- src/TinyVector.h | 8 +++++++- src/Wifi.cpp | 16 +++++++-------- src/Wifi.h | 12 +++++------ src/WifiAP.cpp | 8 ++++---- src/WifiAP.h | 4 ++-- 57 files changed, 253 insertions(+), 243 deletions(-) diff --git a/src/AREngine.cpp b/src/AREngine.cpp index 4e13a39a..c7d49fe6 100644 --- a/src/AREngine.cpp +++ b/src/AREngine.cpp @@ -40,16 +40,16 @@ AREngine::AREngine(melonDS::NDS& nds) : NDS(nds) case ((x)+0x08): case ((x)+0x09): case ((x)+0x0A): case ((x)+0x0B): \ case ((x)+0x0C): case ((x)+0x0D): case ((x)+0x0E): case ((x)+0x0F) -void AREngine::RunCheat(ARCode& arcode) +void AREngine::RunCheat(const ARCode& arcode) { - u32* code = &arcode.Code[0]; + const u32* code = &arcode.Code[0]; u32 offset = 0; u32 datareg = 0; u32 cond = 1; u32 condstack = 0; - u32* loopstart = code; + const u32* loopstart = code; u32 loopcount = 0; u32 loopcond = 1; u32 loopcondstack = 0; diff --git a/src/AREngine.h b/src/AREngine.h index 1f2ee186..21044676 100644 --- a/src/AREngine.h +++ b/src/AREngine.h @@ -33,7 +33,7 @@ public: void SetCodeFile(ARCodeFile* file) { CodeFile = file; } void RunCheats(); - void RunCheat(ARCode& arcode); + void RunCheat(const ARCode& arcode); private: melonDS::NDS& NDS; ARCodeFile* CodeFile; // AR code file - frontend is responsible for managing this diff --git a/src/ARM.h b/src/ARM.h index 565579a4..1e0b71b8 100644 --- a/src/ARM.h +++ b/src/ARM.h @@ -80,7 +80,7 @@ public: virtual void ExecuteJIT() = 0; #endif - bool CheckCondition(u32 code) + bool CheckCondition(u32 code) const { if (code == 0xE) return true; if (ConditionTable[code] & (1 << (CPSR>>28))) return true; @@ -109,7 +109,7 @@ public: if (v) CPSR |= 0x10000000; } - inline bool ModeIs(u32 mode) + inline bool ModeIs(u32 mode) const { u32 cm = CPSR & 0x1f; mode &= 0x1f; @@ -315,7 +315,7 @@ public: void ICacheInvalidateAll(); void CP15Write(u32 id, u32 val); - u32 CP15Read(u32 id); + u32 CP15Read(u32 id) const; u32 CP15Control; diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index 54e60542..04f12e85 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -114,7 +114,7 @@ public: bool CanCompile(bool thumb, u16 kind); - bool FlagsNZNeeded() + bool FlagsNZNeeded() const { return CurInstr.SetFlags & 0xC; } @@ -234,7 +234,7 @@ public: return (u8*)entry - GetRXBase(); } - bool IsJITFault(u8* pc); + bool IsJITFault(const u8* pc); u8* RewriteMemAccess(u8* pc); void SwapCodeRegion() diff --git a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp index 4007138f..e108b7b4 100644 --- a/src/ARMJIT_A64/ARMJIT_LoadStore.cpp +++ b/src/ARMJIT_A64/ARMJIT_LoadStore.cpp @@ -28,7 +28,7 @@ using namespace Arm64Gen; namespace melonDS { -bool Compiler::IsJITFault(u8* pc) +bool Compiler::IsJITFault(const u8* pc) { return (u64)pc >= (u64)GetRXBase() && (u64)pc - (u64)GetRXBase() < (JitMemMainSize + JitMemSecondarySize); } diff --git a/src/ARMJIT_Internal.h b/src/ARMJIT_Internal.h index 72d40a5f..8429bade 100644 --- a/src/ARMJIT_Internal.h +++ b/src/ARMJIT_Internal.h @@ -85,7 +85,7 @@ typedef void (*InterpreterFunc)(ARM* cpu); extern InterpreterFunc InterpretARM[]; extern InterpreterFunc InterpretTHUMB[]; -inline bool PageContainsCode(AddressRange* range) +inline bool PageContainsCode(const AddressRange* range) { for (int i = 0; i < 8; i++) { diff --git a/src/ARMJIT_RegisterCache.h b/src/ARMJIT_RegisterCache.h index 3cb0f79f..e5f28dd6 100644 --- a/src/ARMJIT_RegisterCache.h +++ b/src/ARMJIT_RegisterCache.h @@ -99,7 +99,7 @@ public: LiteralsLoaded &= ~(1 << reg); } - bool IsLiteral(int reg) + bool IsLiteral(int reg) const { return LiteralsLoaded & (1 << reg); } diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.cpp b/src/ARMJIT_x64/ARMJIT_Compiler.cpp index eec4d7d1..b18837f3 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.cpp +++ b/src/ARMJIT_x64/ARMJIT_Compiler.cpp @@ -651,7 +651,7 @@ const Compiler::CompileFunc T_Comp[ARMInstrInfo::tk_Count] = { }; #undef F -bool Compiler::CanCompile(bool thumb, u16 kind) +bool Compiler::CanCompile(bool thumb, u16 kind) const { return (thumb ? T_Comp[kind] : A_Comp[kind]) != NULL; } @@ -667,7 +667,7 @@ void Compiler::Reset() LoadStorePatches.clear(); } -bool Compiler::IsJITFault(u8* addr) +bool Compiler::IsJITFault(const u8* addr) { return (u64)addr >= (u64)ResetStart && (u64)addr < (u64)ResetStart + CodeMemSize; } diff --git a/src/ARMJIT_x64/ARMJIT_Compiler.h b/src/ARMJIT_x64/ARMJIT_Compiler.h index aa80570f..941d8924 100644 --- a/src/ARMJIT_x64/ARMJIT_Compiler.h +++ b/src/ARMJIT_x64/ARMJIT_Compiler.h @@ -92,7 +92,7 @@ public: void LoadReg(int reg, Gen::X64Reg nativeReg); void SaveReg(int reg, Gen::X64Reg nativeReg); - bool CanCompile(bool thumb, u16 kind); + bool CanCompile(bool thumb, u16 kind) const; typedef void (Compiler::*CompileFunc)(); @@ -234,7 +234,7 @@ public: SetCodePtr(FarCode); } - bool IsJITFault(u8* addr); + bool IsJITFault(const u8* addr); u8* RewriteMemAccess(u8* pc); diff --git a/src/CP15.cpp b/src/CP15.cpp index 7cea845d..58137fdd 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -668,7 +668,7 @@ void ARMv5::CP15Write(u32 id, u32 val) Log(LogLevel::Debug, "unknown CP15 write op %03X %08X\n", id, val); } -u32 ARMv5::CP15Read(u32 id) +u32 ARMv5::CP15Read(u32 id) const { //printf("CP15 read op %03X %08X\n", id, NDS::ARM9->R[15]); diff --git a/src/DSi.cpp b/src/DSi.cpp index ce716db6..c929c6d2 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -181,7 +181,7 @@ std::unique_ptr DSi::EjectCart() return oldcart; } -void DSi::CamInputFrame(int cam, u32* data, int width, int height, bool rgb) +void DSi::CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) { switch (cam) { @@ -277,7 +277,7 @@ void DSi::SetCartInserted(bool inserted) SCFG_MC |= 1; } -void DSi::DecryptModcryptArea(u32 offset, u32 size, u8* iv) +void DSi::DecryptModcryptArea(u32 offset, u32 size, const u8* iv) { AES_ctx ctx; u8 key[16]; @@ -957,21 +957,21 @@ void DSi::StallNDMAs() } -bool DSi::DMAsInMode(u32 cpu, u32 mode) +bool DSi::DMAsInMode(u32 cpu, u32 mode) const { if (NDS::DMAsInMode(cpu, mode)) return true; return NDMAsInMode(cpu, NDMAModes[mode]); } -bool DSi::DMAsRunning(u32 cpu) +bool DSi::DMAsRunning(u32 cpu) const { if (NDS::DMAsRunning(cpu)) return true; return NDMAsRunning(cpu); } -bool DSi::NDMAsInMode(u32 cpu, u32 mode) +bool DSi::NDMAsInMode(u32 cpu, u32 mode) const { cpu <<= 2; if (NDMAs[cpu+0].IsInMode(mode)) return true; @@ -981,7 +981,7 @@ bool DSi::NDMAsInMode(u32 cpu, u32 mode) return false; } -bool DSi::NDMAsRunning(u32 cpu) +bool DSi::NDMAsRunning(u32 cpu) const { cpu <<= 2; if (NDMAs[cpu+0].IsRunning()) return true; diff --git a/src/DSi.h b/src/DSi.h index 90bb0d42..1d010e0f 100644 --- a/src/DSi.h +++ b/src/DSi.h @@ -87,8 +87,8 @@ public: void RunNDMAs(u32 cpu); void StallNDMAs(); - bool NDMAsInMode(u32 cpu, u32 mode); - bool NDMAsRunning(u32 cpu); + bool NDMAsInMode(u32 cpu, u32 mode) const; + bool NDMAsRunning(u32 cpu) const; void CheckNDMAs(u32 cpu, u32 mode); void StopNDMAs(u32 cpu, u32 mode); @@ -138,7 +138,7 @@ public: DSi& operator=(DSi&&) = delete; void SetNDSCart(std::unique_ptr&& cart) override; std::unique_ptr EjectCart() override; - bool NeedsDirectBoot() override + bool NeedsDirectBoot() const override { // for now, DSi mode requires original BIOS/NAND return false; @@ -153,9 +153,9 @@ public: void SetSDCard(FATStorage&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); } void SetSDCard(std::optional&& sdcard) noexcept { SDMMC.SetSDCard(std::move(sdcard)); } - void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) override; - bool DMAsInMode(u32 cpu, u32 mode) override; - bool DMAsRunning(u32 cpu) override; + void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) override; + bool DMAsInMode(u32 cpu, u32 mode) const override; + bool DMAsRunning(u32 cpu) const override; void StopDMAs(u32 cpu, u32 mode) override; void CheckDMAs(u32 cpu, u32 mode) override; u16 SCFG_Clock7; @@ -178,7 +178,7 @@ private: bool FullBIOSBoot; void Set_SCFG_Clock9(u16 val); void Set_SCFG_MC(u32 val); - void DecryptModcryptArea(u32 offset, u32 size, u8* iv); + void DecryptModcryptArea(u32 offset, u32 size, const u8* iv); void ApplyNewRAMSize(u32 size); }; diff --git a/src/DSi_AES.cpp b/src/DSi_AES.cpp index 47a613eb..379dea13 100644 --- a/src/DSi_AES.cpp +++ b/src/DSi_AES.cpp @@ -235,7 +235,7 @@ void DSi_AES::ProcessBlock_CTR() } -u32 DSi_AES::ReadCnt() +u32 DSi_AES::ReadCnt() const { u32 ret = Cnt; diff --git a/src/DSi_AES.h b/src/DSi_AES.h index 4df82695..d83c870e 100644 --- a/src/DSi_AES.h +++ b/src/DSi_AES.h @@ -54,7 +54,7 @@ public: void Reset(); void DoSavestate(Savestate* file); - u32 ReadCnt(); + u32 ReadCnt() const; void WriteCnt(u32 val); void WriteBlkCnt(u32 val); diff --git a/src/DSi_Camera.cpp b/src/DSi_Camera.cpp index 225bea8b..a1cdbe0a 100644 --- a/src/DSi_Camera.cpp +++ b/src/DSi_Camera.cpp @@ -438,7 +438,7 @@ void DSi_Camera::Stop() Platform::Camera_Stop(Num); } -bool DSi_Camera::IsActivated() +bool DSi_Camera::IsActivated() const { if (StandbyCnt & (1<<14)) return false; // standby if (!(MiscCnt & (1<<9))) return false; // data transfer not enabled @@ -477,7 +477,7 @@ void DSi_Camera::StartTransfer() Platform::Camera_CaptureFrame(Num, FrameBuffer, 640, 480, true); } -bool DSi_Camera::TransferDone() +bool DSi_Camera::TransferDone() const { return TransferY >= FrameHeight; } @@ -590,7 +590,7 @@ void DSi_Camera::Write(u8 val, bool last) else DataPos++; } -u16 DSi_Camera::I2C_ReadReg(u16 addr) +u16 DSi_Camera::I2C_ReadReg(u16 addr) const { switch (addr) { @@ -695,7 +695,7 @@ void DSi_Camera::I2C_WriteReg(u16 addr, u16 val) // TODO: not sure at all what is the accessible range // or if there is any overlap in the address range -u8 DSi_Camera::MCU_Read(u16 addr) +u8 DSi_Camera::MCU_Read(u16 addr) const { addr &= 0x7FFF; @@ -724,7 +724,7 @@ void DSi_Camera::MCU_Write(u16 addr, u8 val) } -void DSi_Camera::InputFrame(u32* data, int width, int height, bool rgb) +void DSi_Camera::InputFrame(const u32* data, int width, int height, bool rgb) { // TODO: double-buffering? diff --git a/src/DSi_Camera.h b/src/DSi_Camera.h index ec409223..363cea43 100644 --- a/src/DSi_Camera.h +++ b/src/DSi_Camera.h @@ -38,10 +38,10 @@ public: void Reset() override; void Stop(); - bool IsActivated(); + bool IsActivated() const; void StartTransfer(); - bool TransferDone(); + bool TransferDone() const; // lengths in words int TransferScanline(u32* buffer, int maxlen); @@ -50,7 +50,7 @@ public: u8 Read(bool last) override; void Write(u8 val, bool last) override; - void InputFrame(u32* data, int width, int height, bool rgb); + void InputFrame(const u32* data, int width, int height, bool rgb); u32 Num; @@ -59,7 +59,7 @@ private: u32 RegAddr; u16 RegData; - u16 I2C_ReadReg(u16 addr); + u16 I2C_ReadReg(u16 addr) const; void I2C_WriteReg(u16 addr, u16 val); u16 PLLDiv; @@ -72,7 +72,7 @@ private: u16 MCUAddr; u8 MCURegs[0x8000]; - u8 MCU_Read(u16 addr); + u8 MCU_Read(u16 addr) const; void MCU_Write(u16 addr, u8 val); u16 FrameWidth, FrameHeight; @@ -91,7 +91,9 @@ public: void Stop(); void DoSavestate(Savestate* file); + const DSi_Camera* GetOuterCamera() const { return Camera0; } DSi_Camera* GetOuterCamera() { return Camera0; } + const DSi_Camera* GetInnerCamera() const { return Camera1; } DSi_Camera* GetInnerCamera() { return Camera1; } void IRQ(u32 param); diff --git a/src/DSi_DSP.cpp b/src/DSi_DSP.cpp index 088943a9..25abd474 100644 --- a/src/DSi_DSP.cpp +++ b/src/DSi_DSP.cpp @@ -34,7 +34,7 @@ const u32 DSi_DSP::DataMemoryOffset = 0x20000; // from Teakra memory_interface.h // NOTE: ^ IS IN DSP WORDS, NOT IN BYTES! -u16 DSi_DSP::GetPSTS() +u16 DSi_DSP::GetPSTS() const { u16 r = DSP_PSTS & (1<<9); // this is the only sticky bit //r &= ~((1<<2)|(1<<7)); // we support instant resets and wrfifo xfers @@ -182,7 +182,7 @@ void DSi_DSP::Reset() SNDExCnt = 0; } -bool DSi_DSP::IsRstReleased() +bool DSi_DSP::IsRstReleased() const { return SCFG_RST; } @@ -193,12 +193,12 @@ void DSi_DSP::SetRstLine(bool release) DSPTimestamp = DSi.ARM9Timestamp; // only start now! } -inline bool DSi_DSP::IsDSPCoreEnabled() +inline bool DSi_DSP::IsDSPCoreEnabled() const { return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST && (!(DSP_PCFG & (1<<0))); } -inline bool DSi_DSP::IsDSPIOEnabled() +inline bool DSi_DSP::IsDSPIOEnabled() const { return (DSi.SCFG_Clock9 & (1<<1)) && SCFG_RST; } diff --git a/src/DSi_DSP.h b/src/DSi_DSP.h index a18dabf1..f76b4202 100644 --- a/src/DSi_DSP.h +++ b/src/DSi_DSP.h @@ -41,7 +41,7 @@ public: void DSPCatchUpU32(u32 _); // SCFG_RST bit0 - bool IsRstReleased(); + bool IsRstReleased() const; void SetRstLine(bool release); // DSP_* regs (0x040043xx) (NOTE: checks SCFG_EXT) @@ -54,7 +54,7 @@ public: u32 Read32(u32 addr); void Write32(u32 addr, u32 val); - u16 ReadSNDExCnt() { return SNDExCnt; } + u16 ReadSNDExCnt() const { return SNDExCnt; } void WriteSNDExCnt(u16 val, u16 mask); // NOTE: checks SCFG_CLK9 @@ -93,10 +93,10 @@ private: static const u32 DataMemoryOffset; - u16 GetPSTS(); + u16 GetPSTS() const; - inline bool IsDSPCoreEnabled(); - inline bool IsDSPIOEnabled(); + inline bool IsDSPCoreEnabled() const; + inline bool IsDSPIOEnabled() const; bool DSPCatchUp(); diff --git a/src/DSi_I2C.cpp b/src/DSi_I2C.cpp index d5ea60c6..28f98dc8 100644 --- a/src/DSi_I2C.cpp +++ b/src/DSi_I2C.cpp @@ -117,20 +117,20 @@ void DSi_BPTWL::DoSavestate(Savestate* file) } // TODO: Needs more investigation on the other bits -inline bool DSi_BPTWL::GetIRQMode() +inline bool DSi_BPTWL::GetIRQMode() const { return Registers[0x12] & 0x01; } -u8 DSi_BPTWL::GetBootFlag() { return Registers[0x70]; } +u8 DSi_BPTWL::GetBootFlag() const { return Registers[0x70]; } -bool DSi_BPTWL::GetBatteryCharging() { return Registers[0x20] >> 7; } +bool DSi_BPTWL::GetBatteryCharging() const { return Registers[0x20] >> 7; } void DSi_BPTWL::SetBatteryCharging(bool charging) { Registers[0x20] = (((charging ? 0x8 : 0x0) << 4) | (Registers[0x20] & 0x0F)); } -u8 DSi_BPTWL::GetBatteryLevel() { return Registers[0x20] & 0xF; } +u8 DSi_BPTWL::GetBatteryLevel() const { return Registers[0x20] & 0xF; } void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel) { Registers[0x20] = ((Registers[0x20] & 0xF0) | (batteryLevel & 0x0F)); @@ -143,13 +143,13 @@ void DSi_BPTWL::SetBatteryLevel(u8 batteryLevel) } -u8 DSi_BPTWL::GetVolumeLevel() { return Registers[0x40]; } +u8 DSi_BPTWL::GetVolumeLevel() const { return Registers[0x40]; } void DSi_BPTWL::SetVolumeLevel(u8 volume) { Registers[0x40] = volume & 0x1F; } -u8 DSi_BPTWL::GetBacklightLevel() { return Registers[0x41]; } +u8 DSi_BPTWL::GetBacklightLevel() const { return Registers[0x41]; } void DSi_BPTWL::SetBacklightLevel(u8 backlight) { Registers[0x41] = backlight > 4 ? 4 : backlight; @@ -246,7 +246,7 @@ void DSi_BPTWL::SetVolumeSwitchReleased(u32 key) VolumeSwitchRepeatTime = 0.0; } -inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid() +inline bool DSi_BPTWL::CheckVolumeSwitchKeysValid() const { bool up = VolumeSwitchKeysDown & (1 << volumeKey_Up); bool down = VolumeSwitchKeysDown & (1 << volumeKey_Down); diff --git a/src/DSi_I2C.h b/src/DSi_I2C.h index 51fe78e6..5dfeebd0 100644 --- a/src/DSi_I2C.h +++ b/src/DSi_I2C.h @@ -86,20 +86,20 @@ public: void Reset() override; void DoSavestate(Savestate* file) override; - u8 GetBootFlag(); + u8 GetBootFlag() const; - bool GetBatteryCharging(); + bool GetBatteryCharging() const; void SetBatteryCharging(bool charging); - u8 GetBatteryLevel(); + u8 GetBatteryLevel() const; void SetBatteryLevel(u8 batteryLevel); // 0-31 - u8 GetVolumeLevel(); + u8 GetVolumeLevel() const; void SetVolumeLevel(u8 volume); // 0-4 - u8 GetBacklightLevel(); + u8 GetBacklightLevel() const; void SetBacklightLevel(u8 backlight); void DoHardwareReset(bool direct); @@ -144,10 +144,10 @@ private: u8 Registers[0x100]; u32 CurPos; - bool GetIRQMode(); + bool GetIRQMode() const; void ResetButtonState(); - bool CheckVolumeSwitchKeysValid(); + bool CheckVolumeSwitchKeysValid() const; }; diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index b6b83ab6..5f767142 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -365,7 +365,7 @@ bool NANDImage::ESEncrypt(u8* data, u32 len) const return true; } -bool NANDImage::ESDecrypt(u8* data, u32 len) +bool NANDImage::ESDecrypt(u8* data, u32 len) const { AES_ctx ctx; u8 iv[16]; diff --git a/src/DSi_NAND.h b/src/DSi_NAND.h index 699397bd..104845d5 100644 --- a/src/DSi_NAND.h +++ b/src/DSi_NAND.h @@ -71,7 +71,7 @@ private: u32 ReadFATBlock(u64 addr, u32 len, u8* buf); u32 WriteFATBlock(u64 addr, u32 len, const u8* buf); bool ESEncrypt(u8* data, u32 len) const; - bool ESDecrypt(u8* data, u32 len); + bool ESDecrypt(u8* data, u32 len) const; Platform::FileHandle* CurFile = nullptr; DSiKey eMMC_CID; u64 ConsoleID; diff --git a/src/DSi_NDMA.h b/src/DSi_NDMA.h index 7e87da7b..fb34dbdf 100644 --- a/src/DSi_NDMA.h +++ b/src/DSi_NDMA.h @@ -44,12 +44,12 @@ public: void Run9(); void Run7(); - bool IsInMode(u32 mode) + bool IsInMode(u32 mode) const { return ((mode == StartMode) && (Cnt & 0x80000000)); } - bool IsRunning() { return Running!=0; } + bool IsRunning() const { return Running!=0; } void StartIfNeeded(u32 mode) { diff --git a/src/DSi_SD.cpp b/src/DSi_SD.cpp index ff88defb..72fe3756 100644 --- a/src/DSi_SD.cpp +++ b/src/DSi_SD.cpp @@ -336,7 +336,7 @@ void DSi_SDHost::FinishRX(u32 param) SetIRQ(24); } -u32 DSi_SDHost::DataRX(u8* data, u32 len) +u32 DSi_SDHost::DataRX(const u8* data, u32 len) { if (len != BlockLen16) { Log(LogLevel::Warn, "!! BAD BLOCKLEN\n"); len = BlockLen16; } @@ -440,7 +440,7 @@ u32 DSi_SDHost::DataTX(u8* data, u32 len) return len; } -u32 DSi_SDHost::GetTransferrableLen(u32 len) +u32 DSi_SDHost::GetTransferrableLen(u32 len) const { if (len > BlockLen16) len = BlockLen16; // checkme return len; diff --git a/src/DSi_SD.h b/src/DSi_SD.h index 05f8c9dd..29620dc5 100644 --- a/src/DSi_SD.h +++ b/src/DSi_SD.h @@ -51,9 +51,9 @@ public: void FinishRX(u32 param); void FinishTX(u32 param); void SendResponse(u32 val, bool last); - u32 DataRX(u8* data, u32 len); + u32 DataRX(const u8* data, u32 len); u32 DataTX(u8* data, u32 len); - u32 GetTransferrableLen(u32 len); + u32 GetTransferrableLen(u32 len) const; void CheckRX(); void CheckTX(); diff --git a/src/DSi_SPI_TSC.cpp b/src/DSi_SPI_TSC.cpp index 6c7a15c7..d515db9f 100644 --- a/src/DSi_SPI_TSC.cpp +++ b/src/DSi_SPI_TSC.cpp @@ -121,7 +121,7 @@ void DSi_TSC::SetTouchCoords(u16 x, u16 y) } } -void DSi_TSC::MicInputFrame(s16* data, int samples) +void DSi_TSC::MicInputFrame(const s16* data, int samples) { if (TSCMode == 0x00) return TSC::MicInputFrame(data, samples); diff --git a/src/DSi_SPI_TSC.h b/src/DSi_SPI_TSC.h index 47777da5..d1a71063 100644 --- a/src/DSi_SPI_TSC.h +++ b/src/DSi_SPI_TSC.h @@ -40,7 +40,7 @@ public: void SetMode(u8 mode); void SetTouchCoords(u16 x, u16 y) override; - void MicInputFrame(s16* data, int samples) override; + void MicInputFrame(const s16* data, int samples) override; void Write(u8 val) override; void Release() override; diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index 8799cb4b..2de42e8f 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -145,12 +145,12 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) } -u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data) +u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data) const { return ReadSectorsInternal(File, FileSize, start, num, data); } -u32 FATStorage::WriteSectors(u32 start, u32 num, u8* data) +u32 FATStorage::WriteSectors(u32 start, u32 num, const u8* data) { if (ReadOnly) return 0; return WriteSectorsInternal(File, FileSize, start, num, data); @@ -947,7 +947,7 @@ bool FATStorage::ImportDirectory(const std::string& sourcedir) return true; } -u64 FATStorage::GetDirectorySize(fs::path sourcedir) +u64 FATStorage::GetDirectorySize(fs::path sourcedir) const { u64 ret = 0; u32 csize = 0x1000; // this is an estimate diff --git a/src/FATStorage.h b/src/FATStorage.h index 6e348ce6..0774df32 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -58,8 +58,8 @@ public: bool InjectFile(const std::string& path, u8* data, u32 len); - u32 ReadSectors(u32 start, u32 num, u8* data); - u32 WriteSectors(u32 start, u32 num, u8* data); + u32 ReadSectors(u32 start, u32 num, u8* data) const; + u32 WriteSectors(u32 start, u32 num, const u8* data); [[nodiscard]] bool IsReadOnly() const noexcept { return ReadOnly; } private: @@ -92,7 +92,7 @@ private: void CleanupDirectory(const std::string& sourcedir, const std::string& path, int level); bool ImportFile(const std::string& path, std::filesystem::path in); bool ImportDirectory(const std::string& sourcedir); - u64 GetDirectorySize(std::filesystem::path sourcedir); + u64 GetDirectorySize(std::filesystem::path sourcedir) const; bool Load(const std::string& filename, u64 size, const std::optional& sourcedir); bool Save(); diff --git a/src/FIFO.h b/src/FIFO.h index cbff4ab9..026c2c7f 100644 --- a/src/FIFO.h +++ b/src/FIFO.h @@ -74,12 +74,12 @@ public: return ret; } - T Peek() + T Peek() const { return Entries[ReadPos]; } - T Peek(u32 offset) + T Peek(u32 offset) const { u32 pos = ReadPos + offset; if (pos >= NumEntries) @@ -88,11 +88,11 @@ public: return Entries[pos]; } - u32 Level() { return NumOccupied; } - bool IsEmpty() { return NumOccupied == 0; } - bool IsFull() { return NumOccupied >= NumEntries; } + u32 Level() const { return NumOccupied; } + bool IsEmpty() const { return NumOccupied == 0; } + bool IsFull() const { return NumOccupied >= NumEntries; } - bool CanFit(u32 num) { return ((NumOccupied + num) <= NumEntries); } + bool CanFit(u32 num) const { return ((NumOccupied + num) <= NumEntries); } private: T Entries[NumEntries] = {0}; @@ -164,12 +164,12 @@ public: return ret; } - T Peek() + T Peek() const { return Entries[ReadPos]; } - T Peek(u32 offset) + T Peek(u32 offset) const { u32 pos = ReadPos + offset; if (pos >= NumEntries) @@ -178,11 +178,11 @@ public: return Entries[pos]; } - u32 Level() { return NumOccupied; } - bool IsEmpty() { return NumOccupied == 0; } - bool IsFull() { return NumOccupied >= NumEntries; } + u32 Level() const { return NumOccupied; } + bool IsEmpty() const { return NumOccupied == 0; } + bool IsFull() const { return NumOccupied >= NumEntries; } - bool CanFit(u32 num) { return ((NumOccupied + num) <= NumEntries); } + bool CanFit(u32 num) const { return ((NumOccupied + num) <= NumEntries); } private: u32 NumEntries; diff --git a/src/GPU2D.cpp b/src/GPU2D.cpp index e7fb9a29..e0aa630d 100644 --- a/src/GPU2D.cpp +++ b/src/GPU2D.cpp @@ -648,7 +648,7 @@ void Unit::CheckWindows(u32 line) else if (line == Win1Coords[2]) Win1Active |= 0x1; } -void Unit::CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow) +void Unit::CalculateWindowMask(u32 line, u8* windowMask, const u8* objWindow) { for (u32 i = 0; i < 256; i++) windowMask[i] = WinCnt[2]; // window outside @@ -694,7 +694,7 @@ void Unit::CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow) } } -void Unit::GetBGVRAM(u8*& data, u32& mask) +void Unit::GetBGVRAM(u8*& data, u32& mask) const { if (Num == 0) { @@ -708,7 +708,7 @@ void Unit::GetBGVRAM(u8*& data, u32& mask) } } -void Unit::GetOBJVRAM(u8*& data, u32& mask) +void Unit::GetOBJVRAM(u8*& data, u32& mask) const { if (Num == 0) { diff --git a/src/GPU2D.h b/src/GPU2D.h index 7367d07a..e87167cb 100644 --- a/src/GPU2D.h +++ b/src/GPU2D.h @@ -52,7 +52,7 @@ public: void Write16(u32 addr, u16 val); void Write32(u32 addr, u32 val); - bool UsesFIFO() + bool UsesFIFO() const { if (((DispCnt >> 16) & 0x3) == 3) return true; @@ -72,11 +72,11 @@ public: u16* GetBGExtPal(u32 slot, u32 pal); u16* GetOBJExtPal(); - void GetBGVRAM(u8*& data, u32& mask); - void GetOBJVRAM(u8*& data, u32& mask); + void GetBGVRAM(u8*& data, u32& mask) const; + void GetOBJVRAM(u8*& data, u32& mask) const; void UpdateMosaicCounters(u32 line); - void CalculateWindowMask(u32 line, u8* windowMask, u8* objWindow); + void CalculateWindowMask(u32 line, u8* windowMask, const u8* objWindow); u32 Num; bool Enabled; diff --git a/src/GPU2D_Soft.cpp b/src/GPU2D_Soft.cpp index 6d0252c3..e01d3665 100644 --- a/src/GPU2D_Soft.cpp +++ b/src/GPU2D_Soft.cpp @@ -30,7 +30,7 @@ SoftRenderer::SoftRenderer(melonDS::GPU& gpu) // mosaic table is initialized at compile-time } -u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2) +u32 SoftRenderer::ColorComposite(int i, u32 val1, u32 val2) const { u32 coloreffect = 0; u32 eva, evb; diff --git a/src/GPU2D_Soft.h b/src/GPU2D_Soft.h index ca242a51..befb67f6 100644 --- a/src/GPU2D_Soft.h +++ b/src/GPU2D_Soft.h @@ -117,7 +117,7 @@ private: return rb | g | 0xFF000000; } - u32 ColorComposite(int i, u32 val1, u32 val2); + u32 ColorComposite(int i, u32 val1, u32 val2) const; template void DrawScanlineBGMode(u32 line); void DrawScanlineBGMode6(u32 line); diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 1a879abf..7e7df244 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -1493,7 +1493,7 @@ void GPU3D::CalculateLighting() noexcept } -void GPU3D::BoxTest(u32* params) noexcept +void GPU3D::BoxTest(const u32* params) noexcept { Vertex cube[8]; Vertex face[10]; @@ -1626,7 +1626,7 @@ void GPU3D::VecTest(u32 param) noexcept -void GPU3D::CmdFIFOWrite(CmdFIFOEntry& entry) noexcept +void GPU3D::CmdFIFOWrite(const CmdFIFOEntry& entry) noexcept { if (CmdFIFO.IsEmpty() && !CmdPIPE.IsFull()) { diff --git a/src/GPU3D.h b/src/GPU3D.h index dda78b78..eb975c68 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -147,10 +147,10 @@ private: void SubmitPolygon() noexcept; void SubmitVertex() noexcept; void CalculateLighting() noexcept; - void BoxTest(u32* params) noexcept; + void BoxTest(const u32* params) noexcept; void PosTest() noexcept; void VecTest(u32 param) noexcept; - void CmdFIFOWrite(CmdFIFOEntry& entry) noexcept; + void CmdFIFOWrite(const CmdFIFOEntry& entry) noexcept; CmdFIFOEntry CmdFIFORead() noexcept; void FinishWork(s32 cycles) noexcept; void VertexPipelineSubmitCmd() noexcept diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index bee04305..55a034cd 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -406,7 +406,7 @@ void GLRenderer::SetRenderSettings(bool betterpolygons, int scale) noexcept } -void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon) +void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon) const { rp->PolyData = polygon; @@ -452,7 +452,7 @@ void GLRenderer::SetupPolygon(GLRenderer::RendererPolygon* rp, Polygon* polygon) } } -u32* GLRenderer::SetupVertex(Polygon* poly, int vid, Vertex* vtx, u32 vtxattr, u32* vptr) +u32* GLRenderer::SetupVertex(const Polygon* poly, int vid, const Vertex* vtx, u32 vtxattr, u32* vptr) const { u32 z = poly->FinalZ[vid]; u32 w = poly->FinalW[vid]; @@ -735,18 +735,18 @@ void GLRenderer::BuildPolygons(GLRenderer::RendererPolygon* polygons, int npolys NumEdgeIndices = eidx - EdgeIndicesOffset; } -int GLRenderer::RenderSinglePolygon(int i) +int GLRenderer::RenderSinglePolygon(int i) const { - RendererPolygon* rp = &PolygonList[i]; + const RendererPolygon* rp = &PolygonList[i]; glDrawElements(rp->PrimType, rp->NumIndices, GL_UNSIGNED_SHORT, (void*)(uintptr_t)(rp->IndicesOffset * 2)); return 1; } -int GLRenderer::RenderPolygonBatch(int i) +int GLRenderer::RenderPolygonBatch(int i) const { - RendererPolygon* rp = &PolygonList[i]; + const RendererPolygon* rp = &PolygonList[i]; GLuint primtype = rp->PrimType; u32 key = rp->RenderKey; int numpolys = 0; @@ -754,7 +754,7 @@ int GLRenderer::RenderPolygonBatch(int i) for (int iend = i; iend < NumFinalPolys; iend++) { - RendererPolygon* cur_rp = &PolygonList[iend]; + const RendererPolygon* cur_rp = &PolygonList[iend]; if (cur_rp->PrimType != primtype) break; if (cur_rp->RenderKey != key) break; @@ -766,16 +766,16 @@ int GLRenderer::RenderPolygonBatch(int i) return numpolys; } -int GLRenderer::RenderPolygonEdgeBatch(int i) +int GLRenderer::RenderPolygonEdgeBatch(int i) const { - RendererPolygon* rp = &PolygonList[i]; + const RendererPolygon* rp = &PolygonList[i]; u32 key = rp->RenderKey; int numpolys = 0; u32 numindices = 0; for (int iend = i; iend < NumFinalPolys; iend++) { - RendererPolygon* cur_rp = &PolygonList[iend]; + const RendererPolygon* cur_rp = &PolygonList[iend]; if (cur_rp->RenderKey != key) break; numpolys++; diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index 63ee8de2..286d9f58 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -80,12 +80,12 @@ private: bool BuildRenderShader(u32 flags, const char* vs, const char* fs); void UseRenderShader(u32 flags); - void SetupPolygon(RendererPolygon* rp, Polygon* polygon); - u32* SetupVertex(Polygon* poly, int vid, Vertex* vtx, u32 vtxattr, u32* vptr); + void SetupPolygon(RendererPolygon* rp, Polygon* polygon) const; + u32* SetupVertex(const Polygon* poly, int vid, const Vertex* vtx, u32 vtxattr, u32* vptr) const; void BuildPolygons(RendererPolygon* polygons, int npolys); - int RenderSinglePolygon(int i); - int RenderPolygonBatch(int i); - int RenderPolygonEdgeBatch(int i); + int RenderSinglePolygon(int i) const; + int RenderPolygonBatch(int i) const; + int RenderPolygonEdgeBatch(int i) const; void RenderSceneChunk(int y, int h); enum diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 03c6265e..894ac94a 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -112,7 +112,7 @@ void SoftRenderer::SetThreaded(bool threaded) noexcept } } -void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) +void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const { u32 vramaddr = (texparam & 0xFFFF) << 3; @@ -388,7 +388,7 @@ bool DepthTest_LessThan_FrontFacing(s32 dstz, s32 z, u32 dstattr) return false; } -u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept +u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) const noexcept { u32 dstalpha = dstcolor >> 24; @@ -418,7 +418,7 @@ u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept return srcR | (srcG << 8) | (srcB << 16) | (dstalpha << 24); } -u32 SoftRenderer::RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) +u32 SoftRenderer::RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const { u8 r, g, b, a; @@ -565,7 +565,7 @@ void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 pol AttrBuffer[pixeladdr] = attr; } -void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y) +void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y) const { Polygon* polygon = rp->PolyData; @@ -592,7 +592,7 @@ void SoftRenderer::SetupPolygonLeftEdge(SoftRenderer::RendererPolygon* rp, s32 y polygon->FinalW[rp->CurVL], polygon->FinalW[rp->NextVL], y); } -void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 y) +void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 y) const { Polygon* polygon = rp->PolyData; @@ -619,7 +619,7 @@ void SoftRenderer::SetupPolygonRightEdge(SoftRenderer::RendererPolygon* rp, s32 polygon->FinalW[rp->CurVR], polygon->FinalW[rp->NextVR], y); } -void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* polygon) +void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* polygon) const { u32 nverts = polygon->NumVertices; @@ -1375,7 +1375,7 @@ void SoftRenderer::RenderScanline(s32 y, int npolys) } } -u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) +u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) const { u32 z = DepthBuffer[pixeladdr]; u32 densityid, densityfrac; diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 2f5664e2..f405b2d8 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -429,16 +429,16 @@ private: }; template - inline T ReadVRAM_Texture(u32 addr) + inline T ReadVRAM_Texture(u32 addr) const { return *(T*)&GPU.VRAMFlat_Texture[addr & 0x7FFFF]; } template - inline T ReadVRAM_TexPal(u32 addr) + inline T ReadVRAM_TexPal(u32 addr) const { return *(T*)&GPU.VRAMFlat_TexPal[addr & 0x1FFFF]; } - u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) noexcept; + u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) const noexcept; struct RendererPolygon { @@ -454,16 +454,16 @@ private: melonDS::GPU& GPU; RendererPolygon PolygonList[2048]; - void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha); - u32 RenderPixel(Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t); + void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const; + u32 RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const; void PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow); - void SetupPolygonLeftEdge(RendererPolygon* rp, s32 y); - void SetupPolygonRightEdge(RendererPolygon* rp, s32 y); - void SetupPolygon(RendererPolygon* rp, Polygon* polygon); + void SetupPolygonLeftEdge(RendererPolygon* rp, s32 y) const; + void SetupPolygonRightEdge(RendererPolygon* rp, s32 y) const; + void SetupPolygon(RendererPolygon* rp, Polygon* polygon) const; void RenderShadowMaskScanline(RendererPolygon* rp, s32 y); void RenderPolygonScanline(RendererPolygon* rp, s32 y); void RenderScanline(s32 y, int npolys); - u32 CalculateFogDensity(u32 pixeladdr); + u32 CalculateFogDensity(u32 pixeladdr) const; void ScanlineFinalPass(s32 y); void ClearBuffers(); void RenderPolygons(bool threaded, Polygon** polygons, int npolys); diff --git a/src/JitBlock.h b/src/JitBlock.h index 6a187b27..9b31d6d7 100644 --- a/src/JitBlock.h +++ b/src/JitBlock.h @@ -46,12 +46,12 @@ public: JitBlockEntry EntryPoint; - u32* AddressRanges() - { return &Data[0]; } - u32* AddressMasks() - { return &Data[NumAddresses]; } - u32* Literals() - { return &Data[NumAddresses * 2]; } + const u32* AddressRanges() const { return &Data[0]; } + u32* AddressRanges() { return &Data[0]; } + const u32* AddressMasks() const { return &Data[NumAddresses]; } + u32* AddressMasks() { return &Data[NumAddresses]; } + const u32* Literals() const { return &Data[NumAddresses * 2]; } + u32* Literals() { return &Data[NumAddresses * 2]; } private: TinyVector Data; diff --git a/src/NDS.cpp b/src/NDS.cpp index c00aa511..5d2a1ce2 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -256,7 +256,7 @@ void NDS::InitTimings() // handled later: GBA slot, wifi } -bool NDS::NeedsDirectBoot() +bool NDS::NeedsDirectBoot() const { if (ConsoleType == 1) { @@ -1152,7 +1152,7 @@ void NDS::SetKeyMask(u32 mask) CheckKeyIRQ(1, oldkey, KeyInput); } -bool NDS::IsLidClosed() +bool NDS::IsLidClosed() const { if (KeyInput & (1<<23)) return true; return false; @@ -1345,7 +1345,7 @@ void NDS::ClearIRQ2(u32 irq) UpdateIRQ(1); } -bool NDS::HaltInterrupted(u32 cpu) +bool NDS::HaltInterrupted(u32 cpu) const { if (cpu == 0) { @@ -1416,7 +1416,7 @@ void NDS::EnterSleepMode() ARM7.Halt(2); } -u32 NDS::GetPC(u32 cpu) +u32 NDS::GetPC(u32 cpu) const { return cpu ? ARM7.R[15] : ARM9.R[15]; } @@ -1644,7 +1644,7 @@ void NDS::TimerStart(u32 id, u16 cnt) -bool NDS::DMAsInMode(u32 cpu, u32 mode) +bool NDS::DMAsInMode(u32 cpu, u32 mode) const { cpu <<= 2; if (DMAs[cpu+0].IsInMode(mode)) return true; @@ -1655,7 +1655,7 @@ bool NDS::DMAsInMode(u32 cpu, u32 mode) return false; } -bool NDS::DMAsRunning(u32 cpu) +bool NDS::DMAsRunning(u32 cpu) const { cpu <<= 2; if (DMAs[cpu+0].IsRunning()) return true; diff --git a/src/NDS.h b/src/NDS.h index d3a753a2..e178c4a2 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -328,7 +328,7 @@ public: Firmware& GetFirmware() { return SPI.GetFirmwareMem()->GetFirmware(); } void SetFirmware(Firmware&& firmware) { SPI.GetFirmwareMem()->SetFirmware(std::move(firmware)); } - virtual bool NeedsDirectBoot(); + virtual bool NeedsDirectBoot() const; void SetupDirectBoot(const std::string& romname); virtual void SetupDirectBoot(); @@ -364,10 +364,10 @@ public: void SetKeyMask(u32 mask); - bool IsLidClosed(); + bool IsLidClosed() const; void SetLidClosed(bool closed); - virtual void CamInputFrame(int cam, u32* data, int width, int height, bool rgb) {} + virtual void CamInputFrame(int cam, const u32* data, int width, int height, bool rgb) {} void MicInputFrame(s16* data, int samples); void RegisterEventFunc(u32 id, u32 funcid, EventFunc func); @@ -386,20 +386,20 @@ public: void ClearIRQ(u32 cpu, u32 irq); void SetIRQ2(u32 irq); void ClearIRQ2(u32 irq); - bool HaltInterrupted(u32 cpu); + bool HaltInterrupted(u32 cpu) const; void StopCPU(u32 cpu, u32 mask); void ResumeCPU(u32 cpu, u32 mask); void GXFIFOStall(); void GXFIFOUnstall(); - u32 GetPC(u32 cpu); + u32 GetPC(u32 cpu) const; u64 GetSysClockCycles(int num); void NocashPrint(u32 cpu, u32 addr); void MonitorARM9Jump(u32 addr); - virtual bool DMAsInMode(u32 cpu, u32 mode); - virtual bool DMAsRunning(u32 cpu); + virtual bool DMAsInMode(u32 cpu, u32 mode) const; + virtual bool DMAsRunning(u32 cpu) const; virtual void CheckDMAs(u32 cpu, u32 mode); virtual void StopDMAs(u32 cpu, u32 mode); diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 65309e32..c0e1c5ff 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -48,7 +48,7 @@ constexpr u32 ByteSwap(u32 val) return (val >> 24) | ((val >> 8) & 0xFF00) | ((val << 8) & 0xFF0000) | (val << 24); } -void NDSCartSlot::Key1_Encrypt(u32* data) noexcept +void NDSCartSlot::Key1_Encrypt(u32* data) const noexcept { u32 y = data[0]; u32 x = data[1]; @@ -69,7 +69,7 @@ void NDSCartSlot::Key1_Encrypt(u32* data) noexcept data[1] = y ^ Key1_KeyBuf[0x11]; } -void NDSCartSlot::Key1_Decrypt(u32* data) noexcept +void NDSCartSlot::Key1_Decrypt(u32* data) const noexcept { u32 y = data[0]; u32 x = data[1]; @@ -109,7 +109,7 @@ void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept } } -void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, u8 *bios, u32 biosLength) noexcept +void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept { if (!NDS.IsLoadedARM7BIOSBuiltIn()) { @@ -136,7 +136,7 @@ void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, u8 *bios, u32 biosLength) noexcept } } -void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) noexcept +void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept { Key1_LoadKeyBuf(dsi, bios, biosLength); @@ -152,7 +152,7 @@ void NDSCartSlot::Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 } -void NDSCartSlot::Key2_Encrypt(u8* data, u32 len) noexcept +void NDSCartSlot::Key2_Encrypt(const u8* data, u32 len) noexcept { for (u32 i = 0; i < len; i++) { @@ -232,7 +232,7 @@ void CartCommon::DoSavestate(Savestate* file) file->Bool32(&DSiMode); } -int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) +int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) { if (CmdEncMode == 0) { @@ -345,7 +345,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, u8* cmd, u8* da return 0; } -void CartCommon::ROMCommandFinish(u8* cmd, u8* data, u32 len) +void CartCommon::ROMCommandFinish(const u8* cmd, u8* data, u32 len) { } @@ -354,7 +354,7 @@ u8 CartCommon::SPIWrite(u8 val, u32 pos, bool last) return 0xFF; } -void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) +void CartCommon::ReadROM(u32 addr, u32 len, u8* data, u32 offset) const { if (addr >= ROMLength) return; if ((addr+len) > ROMLength) @@ -477,7 +477,7 @@ void CartRetail::SetSaveMemory(const u8* savedata, u32 savelen) Platform::WriteNDSSave(savedata, len, 0, len); } -int CartRetail::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) +int CartRetail::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) { if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); @@ -537,7 +537,7 @@ u8 CartRetail::SPIWrite(u8 val, u32 pos, bool last) } } -void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) +void CartRetail::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const { addr &= (ROMLength-1); @@ -875,7 +875,7 @@ void CartRetailNAND::SetSaveMemory(const u8* savedata, u32 savelen) BuildSRAMID(); } -int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) +int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) { if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); @@ -1011,7 +1011,7 @@ int CartRetailNAND::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8 } } -void CartRetailNAND::ROMCommandFinish(u8* cmd, u8* data, u32 len) +void CartRetailNAND::ROMCommandFinish(const u8* cmd, u8* data, u32 len) { if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); @@ -1206,7 +1206,7 @@ void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) } } -int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) +int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) { if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); @@ -1243,7 +1243,7 @@ int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* } } -void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len) +void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len) { if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); @@ -1263,7 +1263,7 @@ void CartHomebrew::ROMCommandFinish(u8* cmd, u8* data, u32 len) } } -void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) +void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const { if (patch[0x0D] > binary[dldioffset+0x0F]) { @@ -1394,7 +1394,7 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) } } -void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) +void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const { // TODO: how strict should this be for homebrew? diff --git a/src/NDSCart.h b/src/NDSCart.h index 03e16e95..dcbc1eb2 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -83,8 +83,8 @@ public: virtual void DoSavestate(Savestate* file); - virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len); - virtual void ROMCommandFinish(u8* cmd, u8* data, u32 len); + virtual int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len); + virtual void ROMCommandFinish(const u8* cmd, u8* data, u32 len); virtual u8 SPIWrite(u8 val, u32 pos, bool last); @@ -103,7 +103,7 @@ public: [[nodiscard]] const u8* GetROM() const { return ROM.get(); } [[nodiscard]] u32 GetROMLength() const { return ROMLength; } protected: - void ReadROM(u32 addr, u32 len, u8* data, u32 offset); + void ReadROM(u32 addr, u32 len, u8* data, u32 offset) const; std::unique_ptr ROM = nullptr; u32 ROMLength = 0; @@ -152,7 +152,7 @@ public: void SetSaveMemory(const u8* savedata, u32 savelen) override; - int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; u8 SPIWrite(u8 val, u32 pos, bool last) override; @@ -161,7 +161,7 @@ public: u32 GetSaveMemoryLength() const override { return SRAMLength; } protected: - void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); + void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const; u8 SRAMWrite_EEPROMTiny(u8 val, u32 pos, bool last); u8 SRAMWrite_EEPROM(u8 val, u32 pos, bool last); @@ -191,8 +191,8 @@ public: void SetSaveMemory(const u8* savedata, u32 savelen) override; - int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; - void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override; u8 SPIWrite(u8 val, u32 pos, bool last) override; @@ -247,8 +247,8 @@ public: void Reset() override; void SetupDirectBoot(const std::string& romname, NDS& nds) override; - int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, u8* cmd, u8* data, u32 len) override; - void ROMCommandFinish(u8* cmd, u8* data, u32 len) override; + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override; [[nodiscard]] const std::optional& GetSDCard() const noexcept { return SD; } void SetSDCard(FATStorage&& sdcard) noexcept { SD = std::move(sdcard); } @@ -261,9 +261,9 @@ public: } private: - void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly); + void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const; void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly); - void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset); + void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const; std::optional SD {}; }; @@ -354,12 +354,12 @@ private: u64 Key2_X = 0; u64 Key2_Y = 0; - void Key1_Encrypt(u32* data) noexcept; - void Key1_Decrypt(u32* data) noexcept; + void Key1_Encrypt(u32* data) const noexcept; + void Key1_Decrypt(u32* data) const noexcept; void Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept; - void Key1_LoadKeyBuf(bool dsi, u8 *bios, u32 biosLength) noexcept; - void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, u8 *bios, u32 biosLength) noexcept; - void Key2_Encrypt(u8* data, u32 len) noexcept; + void Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept; + void Key1_InitKeycode(bool dsi, u32 idcode, u32 level, u32 mod, const u8 *bios, u32 biosLength) noexcept; + void Key2_Encrypt(const u8* data, u32 len) noexcept; void ROMEndTransfer(u32 param) noexcept; void ROMPrepareData(u32 param) noexcept; void AdvanceROMTransfer() noexcept; diff --git a/src/NonStupidBitfield.h b/src/NonStupidBitfield.h index eb5e1f2b..4a5550f1 100644 --- a/src/NonStupidBitfield.h +++ b/src/NonStupidBitfield.h @@ -42,7 +42,7 @@ struct NonStupidBitField NonStupidBitField& BitField; u32 Idx; - operator bool() + operator bool() const { return BitField.Data[Idx >> 6] & (1ULL << (Idx & 0x3F)); } @@ -62,13 +62,13 @@ struct NonStupidBitField u32 BitIdx; u64 RemainingBits; - u32 operator*() { return DataIdx * 64 + BitIdx; } + u32 operator*() const { return DataIdx * 64 + BitIdx; } - bool operator==(const Iterator& other) + bool operator==(const Iterator& other) const { return other.DataIdx == DataIdx; } - bool operator!=(const Iterator& other) + bool operator!=(const Iterator& other) const { return other.DataIdx != DataIdx; } diff --git a/src/RTC.cpp b/src/RTC.cpp index b5e497a2..d8219df1 100644 --- a/src/RTC.cpp +++ b/src/RTC.cpp @@ -86,17 +86,17 @@ void RTC::DoSavestate(Savestate* file) } -u8 RTC::BCD(u8 val) +u8 RTC::BCD(u8 val) const { return (val % 10) | ((val / 10) << 4); } -u8 RTC::FromBCD(u8 val) +u8 RTC::FromBCD(u8 val) const { return (val & 0xF) + ((val >> 4) * 10); } -u8 RTC::BCDIncrement(u8 val) +u8 RTC::BCDIncrement(u8 val) const { val++; if ((val & 0x0F) >= 0x0A) @@ -106,7 +106,7 @@ u8 RTC::BCDIncrement(u8 val) return val; } -u8 RTC::BCDSanitize(u8 val, u8 vmin, u8 vmax) +u8 RTC::BCDSanitize(u8 val, u8 vmin, u8 vmax) const { if (val < vmin || val > vmax) val = vmin; @@ -119,12 +119,12 @@ u8 RTC::BCDSanitize(u8 val, u8 vmin, u8 vmax) } -void RTC::GetState(StateData& state) +void RTC::GetState(StateData& state) const { memcpy(&state, &State, sizeof(State)); } -void RTC::SetState(StateData& state) +void RTC::SetState(const StateData& state) { memcpy(&State, &state, sizeof(State)); @@ -134,7 +134,7 @@ void RTC::SetState(StateData& state) WriteDateTime(i+1, State.DateTime[i]); } -void RTC::GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) +void RTC::GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) const { year = FromBCD(State.DateTime[0]); year += 2000; @@ -374,7 +374,7 @@ void RTC::ProcessIRQ(int type) // 0=minute carry 1=periodic 2=status reg write } -u8 RTC::DaysInMonth() +u8 RTC::DaysInMonth() const { u8 numdays; diff --git a/src/RTC.h b/src/RTC.h index 0caf5ee5..1477e0eb 100644 --- a/src/RTC.h +++ b/src/RTC.h @@ -55,9 +55,9 @@ public: void DoSavestate(Savestate* file); - void GetState(StateData& state); - void SetState(StateData& state); - void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second); + void GetState(StateData& state) const; + void SetState(const StateData& state); + void GetDateTime(int& year, int& month, int& day, int& hour, int& minute, int& second) const; void SetDateTime(int year, int month, int day, int hour, int minute, int second); void ClockTimer(u32 param); @@ -87,16 +87,16 @@ private: void ResetState(); void ScheduleTimer(bool first); - u8 BCD(u8 val); - u8 FromBCD(u8 val); - u8 BCDIncrement(u8 val); - u8 BCDSanitize(u8 val, u8 vmin, u8 vmax); + u8 BCD(u8 val) const; + u8 FromBCD(u8 val) const; + u8 BCDIncrement(u8 val) const; + u8 BCDSanitize(u8 val, u8 vmin, u8 vmax) const; void SetIRQ(u8 irq); void ClearIRQ(u8 irq); void ProcessIRQ(int type); - u8 DaysInMonth(); + u8 DaysInMonth() const; void CountYear(); void CountMonth(); void CheckEndOfMonth(); diff --git a/src/SPI.cpp b/src/SPI.cpp index 3974e316..2aa915c6 100644 --- a/src/SPI.cpp +++ b/src/SPI.cpp @@ -57,7 +57,7 @@ u16 CRC16(const u8* data, u32 len, u32 start) -bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) +bool FirmwareMem::VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) const { u16 crc_stored = *(u16*)&FirmwareData.Buffer()[crcoffset]; u16 crc_calced = CRC16(&FirmwareData.Buffer()[offset], len, start); @@ -154,7 +154,7 @@ void FirmwareMem::SetupDirectBoot() } } -bool FirmwareMem::IsLoadedFirmwareBuiltIn() +bool FirmwareMem::IsLoadedFirmwareBuiltIn() const { return FirmwareData.GetHeader().Identifier == GENERATED_FIRMWARE_IDENTIFIER; } @@ -308,7 +308,7 @@ void PowerMan::DoSavestate(Savestate* file) file->VarArray(RegMasks, 8); // is that needed?? } -bool PowerMan::GetBatteryLevelOkay() { return !Registers[1]; } +bool PowerMan::GetBatteryLevelOkay() const { return !Registers[1]; } void PowerMan::SetBatteryLevelOkay(bool okay) { Registers[1] = okay ? 0x00 : 0x01; } void PowerMan::Write(u8 val) @@ -404,7 +404,7 @@ void TSC::SetTouchCoords(u16 x, u16 y) NDS.KeyInput &= ~(1 << (16+6)); } -void TSC::MicInputFrame(s16* data, int samples) +void TSC::MicInputFrame(const s16* data, int samples) { if (!data) { @@ -549,7 +549,7 @@ void SPIHost::TransferDone(u32 param) NDS.SetIRQ(1, IRQ_SPI); } -u8 SPIHost::ReadData() +u8 SPIHost::ReadData() const { if (!(Cnt & (1<<15))) return 0; if (Cnt & (1<<7)) return 0; // checkme diff --git a/src/SPI.h b/src/SPI.h index aee41658..7ed889a4 100644 --- a/src/SPI.h +++ b/src/SPI.h @@ -51,7 +51,7 @@ public: virtual void Reset() = 0; virtual void DoSavestate(Savestate* file) = 0; - virtual u8 Read() { return Data; } + virtual u8 Read() const { return Data; } virtual void Write(u8 val) = 0; virtual void Release() { Hold = false; DataPos = 0; } @@ -76,7 +76,7 @@ public: Firmware& GetFirmware() noexcept { return FirmwareData; } [[nodiscard]] const Firmware& GetFirmware() const noexcept { return FirmwareData; } void SetFirmware(Firmware&& firmware) { FirmwareData = std::move(firmware); } - bool IsLoadedFirmwareBuiltIn(); + bool IsLoadedFirmwareBuiltIn() const; void Write(u8 val) override; void Release() override; @@ -89,7 +89,7 @@ private: u8 StatusReg; u32 Addr; - bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset); + bool VerifyCRC16(u32 start, u32 offset, u32 len, u32 crcoffset) const; }; class PowerMan : public SPIDevice @@ -100,7 +100,7 @@ public: void Reset() override; void DoSavestate(Savestate* file) override; - bool GetBatteryLevelOkay(); + bool GetBatteryLevelOkay() const; void SetBatteryLevelOkay(bool okay); void Write(u8 val) override; @@ -121,7 +121,7 @@ public: virtual void DoSavestate(Savestate* file) override; virtual void SetTouchCoords(u16 x, u16 y); - virtual void MicInputFrame(s16* data, int samples); + virtual void MicInputFrame(const s16* data, int samples); virtual void Write(u8 val) override; @@ -148,16 +148,18 @@ public: FirmwareMem* GetFirmwareMem() { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } const FirmwareMem* GetFirmwareMem() const { return (FirmwareMem*)Devices[SPIDevice_FirmwareMem]; } PowerMan* GetPowerMan() { return (PowerMan*)Devices[SPIDevice_PowerMan]; } + const PowerMan* GetPowerMan() const { return (PowerMan*)Devices[SPIDevice_PowerMan]; } TSC* GetTSC() { return (TSC*)Devices[SPIDevice_TSC]; } + const TSC* GetTSC() const { return (TSC*)Devices[SPIDevice_TSC]; } const Firmware& GetFirmware() const { return GetFirmwareMem()->GetFirmware(); } Firmware& GetFirmware() { return GetFirmwareMem()->GetFirmware(); } void SetFirmware(Firmware&& firmware) { GetFirmwareMem()->SetFirmware(std::move(firmware)); } - u16 ReadCnt() { return Cnt; } + u16 ReadCnt() const { return Cnt; } void WriteCnt(u16 val); - u8 ReadData(); + u8 ReadData() const; void WriteData(u8 val); void TransferDone(u32 param); diff --git a/src/SPU.cpp b/src/SPU.cpp index f1df9cf3..f0d59464 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -956,7 +956,7 @@ void SPU::InitOutput() Platform::Mutex_Unlock(AudioLock); } -int SPU::GetOutputSize() +int SPU::GetOutputSize() const { Platform::Mutex_Lock(AudioLock); diff --git a/src/SPU.h b/src/SPU.h index 1541c681..b2b05ac7 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -237,7 +237,7 @@ public: void TrimOutput(); void DrainOutput(); void InitOutput(); - int GetOutputSize(); + int GetOutputSize() const; void Sync(bool wait); int ReadOutput(s16* data, int samples); void TransferOutput(); diff --git a/src/TinyVector.h b/src/TinyVector.h index 1904f2ad..5a30ff65 100644 --- a/src/TinyVector.h +++ b/src/TinyVector.h @@ -97,7 +97,7 @@ struct __attribute__((packed)) TinyVector Data[i] = Data[i + 1];*/ } - int Find(T needle) + int Find(T needle) const { for (int i = 0; i < Length; i++) { @@ -125,6 +125,12 @@ struct __attribute__((packed)) TinyVector assert(index >= 0 && index < Length); return Data[index]; } + + const T& operator[](int index) const + { + assert(index >= 0 && index < Length); + return Data[index]; + } }; } diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 9dc696b6..4da253ef 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -78,12 +78,12 @@ const u8 Wifi::MPAckMAC[6] = {0x03, 0x09, 0xBF, 0x00, 0x00, 0x03}; // * TX errors (if applicable) -bool MACEqual(u8* a, const u8* b) +bool MACEqual(const u8* a, const u8* b) { return (*(u32*)&a[0] == *(u32*)&b[0]) && (*(u16*)&a[4] == *(u16*)&b[4]); } -bool MACIsBroadcast(u8* a) +bool MACIsBroadcast(const u8* a) { return (*(u32*)&a[0] == 0xFFFFFFFF) && (*(u16*)&a[4] == 0xFFFF); } @@ -440,14 +440,14 @@ void Wifi::PowerDown() } -int Wifi::PreambleLen(int rate) +int Wifi::PreambleLen(int rate) const { if (rate == 1) return 192; if (IOPORT(W_Preamble) & 0x0004) return 96; return 192; } -u32 Wifi::NumClients(u16 bitmask) +u32 Wifi::NumClients(u16 bitmask) const { u32 ret = 0; for (int i = 1; i < 16; i++) @@ -457,7 +457,7 @@ u32 Wifi::NumClients(u16 bitmask) return ret; } -void Wifi::IncrementTXCount(TXSlot* slot) +void Wifi::IncrementTXCount(const TXSlot* slot) { u8 cnt = RAM[slot->Addr + 0x4]; if (cnt < 0xFF) cnt++; @@ -477,7 +477,7 @@ void Wifi::ReportMPReplyErrors(u16 clientfail) } } -void Wifi::TXSendFrame(TXSlot* slot, int num) +void Wifi::TXSendFrame(const TXSlot* slot, int num) { u32 noseqno = 0; @@ -2258,12 +2258,12 @@ void Wifi::Write(u32 addr, u16 val) } -u8* Wifi::GetMAC() +const u8* Wifi::GetMAC() const { return (u8*)&IOPORT(W_MACAddr0); } -u8* Wifi::GetBSSID() +const u8* Wifi::GetBSSID() const { return (u8*)&IOPORT(W_BSSID0); } diff --git a/src/Wifi.h b/src/Wifi.h index 76fa1463..5553a6f5 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -169,8 +169,8 @@ public: u16 Read(u32 addr); void Write(u32 addr, u16 val); - u8* GetMAC(); - u8* GetBSSID(); + const u8* GetMAC() const; + const u8* GetBSSID() const; private: melonDS::NDS& NDS; @@ -261,12 +261,12 @@ private: void SetStatus(u32 status); void PowerDown(); - int PreambleLen(int rate); - u32 NumClients(u16 bitmask); - void IncrementTXCount(TXSlot* slot); + int PreambleLen(int rate) const; + u32 NumClients(u16 bitmask) const; + void IncrementTXCount(const TXSlot* slot); void ReportMPReplyErrors(u16 clientfail); - void TXSendFrame(TXSlot* slot, int num); + void TXSendFrame(const TXSlot* slot, int num); void StartTX_LocN(int nslot, int loc); void StartTX_Cmd(); void StartTX_Beacon(); diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index efc34a5c..4c645203 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -66,8 +66,8 @@ const u8 WifiAP::APMac[6] = {0x00, 0xF0, 0x77, 0x77, 0x77, 0x77}; #define PALIGN_4(p, base) while (PLEN(p,base) & 0x3) *p++ = 0xFF; -bool MACEqual(u8* a, const u8* b); -bool MACIsBroadcast(u8* a); +bool MACEqual(const u8* a, const u8* b); +bool MACIsBroadcast(const u8* a); WifiAP::WifiAP(Wifi* client) : Client(client) @@ -107,7 +107,7 @@ void WifiAP::MSTimer() } -int WifiAP::HandleManagementFrame(u8* data, int len) +int WifiAP::HandleManagementFrame(const u8* data, int len) { // TODO: perfect this // noting that frames sent pre-auth/assoc don't have a proper BSSID @@ -258,7 +258,7 @@ int WifiAP::HandleManagementFrame(u8* data, int len) } -int WifiAP::SendPacket(u8* data, int len) +int WifiAP::SendPacket(const u8* data, int len) { data += 12; diff --git a/src/WifiAP.h b/src/WifiAP.h index 5d966687..8f3ed111 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -38,7 +38,7 @@ public: void MSTimer(); // packet format: 12-byte TX header + original 802.11 frame - int SendPacket(u8* data, int len); + int SendPacket(const u8* data, int len); int RecvPacket(u8* data); private: @@ -60,7 +60,7 @@ private: // 0=disconnected 1=authenticated 2=associated int ClientStatus; - int HandleManagementFrame(u8* data, int len); + int HandleManagementFrame(const u8* data, int len); }; } From 6f47c9ed4c0e5b1035089805f272c6965343f113 Mon Sep 17 00:00:00 2001 From: Adrian Siekierka Date: Fri, 15 Dec 2023 08:19:53 +0100 Subject: [PATCH 074/157] Support emulating R4 Revolution/M3DS Simply cartridges. (#1854) * Support emulating R4 Revolution/M3DS Simply cartridges. * NDSCartR4: Write state information to savestate file. * NDSCart: Use strncmp instead of strcmp for R4 detection. * NDSCartR4: stylistic improvements * NDSCartR4: rudimentary Ace3DS support * NDSCartR4: fix boot when firmware enabled * NDSCartR4: Fix for namespace changes --------- Co-authored-by: RSDuck --- src/CMakeLists.txt | 1 + src/FATStorage.cpp | 47 ++++++ src/FATStorage.h | 3 + src/NDSCart.cpp | 230 ++++++++++++++-------------- src/NDSCart.h | 87 +++++++++-- src/NDSCartR4.cpp | 371 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 617 insertions(+), 122 deletions(-) create mode 100644 src/NDSCartR4.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ddb1b3c4..b1ae4c47 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,6 +38,7 @@ add_library(core STATIC melonDLDI.h NDS.cpp NDSCart.cpp + NDSCartR4.cpp Platform.h ROMList.h ROMList.cpp diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index 2de42e8f..52011a8e 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -144,6 +144,48 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) return nwrite==len; } +u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data) +{ + if (!File) return false; + if (FF_File) return false; + + FF_File = File; + FF_FileSize = FileSize; + ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9)); + + FRESULT res; + FATFS fs; + + res = f_mount(&fs, "0:", 1); + if (res != FR_OK) + { + ff_disk_close(); + FF_File = nullptr; + return false; + } + + std::string prefixedPath("0:/"); + prefixedPath += path; + FF_FIL file; + res = f_open(&file, prefixedPath.c_str(), FA_READ); + if (res != FR_OK) + { + f_unmount("0:"); + ff_disk_close(); + FF_File = nullptr; + return false; + } + + u32 nread; + f_lseek(&file, start); + f_read(&file, data, len, &nread); + f_close(&file); + + f_unmount("0:"); + ff_disk_close(); + FF_File = nullptr; + return nread; +} u32 FATStorage::ReadSectors(u32 start, u32 num, u8* data) const { @@ -156,6 +198,11 @@ u32 FATStorage::WriteSectors(u32 start, u32 num, const u8* data) return WriteSectorsInternal(File, FileSize, start, num, data); } +u64 FATStorage::GetSectorCount() const +{ + return FileSize / 0x200; +} + FileHandle* FATStorage::FF_File; u64 FATStorage::FF_FileSize; diff --git a/src/FATStorage.h b/src/FATStorage.h index 0774df32..1e89b764 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -57,10 +57,13 @@ public: ~FATStorage(); bool InjectFile(const std::string& path, u8* data, u32 len); + u32 ReadFile(const std::string& path, u32 start, u32 len, u8* data); u32 ReadSectors(u32 start, u32 num, u8* data) const; u32 WriteSectors(u32 start, u32 num, const u8* data); + [[nodiscard]] bool IsReadOnly() const noexcept { return ReadOnly; } + u64 GetSectorCount() const; private: std::string FilePath; diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index c0e1c5ff..4474f97f 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1145,12 +1145,12 @@ u8 CartRetailBT::SPIWrite(u8 val, u32 pos, bool last) return 0; } -CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : - CartHomebrew(CopyToUnique(rom, len), len, chipid, romparams, std::move(sdcard)) -{ -} -CartHomebrew::CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : +CartSD::CartSD(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartSD(CopyToUnique(rom, len), len, chipid, romparams, std::move(sdcard)) +{} + +CartSD::CartSD(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : CartCommon(std::move(rom), len, chipid, false, romparams, CartType::Homebrew), SD(std::move(sdcard)) { @@ -1158,112 +1158,11 @@ CartHomebrew::CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROM // std::move on optionals usually results in an optional with a moved-from object } -CartHomebrew::~CartHomebrew() = default; +CartSD::~CartSD() = default; // The SD card is destroyed by the optional's destructor -void CartHomebrew::Reset() -{ - CartCommon::Reset(); - if (SD) - { - ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly()); - } -} - -void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) -{ - CartCommon::SetupDirectBoot(romname, nds); - - if (SD) - { - // add the ROM to the SD volume - - if (!SD->InjectFile(romname, ROM.get(), ROMLength)) - return; - - // setup argv command line - - char argv[512] = {0}; - int argvlen; - - strncpy(argv, "fat:/", 511); - strncat(argv, romname.c_str(), 511); - argvlen = strlen(argv); - - const NDSHeader& header = GetHeader(); - - u32 argvbase = header.ARM9RAMAddress + header.ARM9Size; - argvbase = (argvbase + 0xF) & ~0xF; - - for (u32 i = 0; i <= argvlen; i+=4) - nds.ARM9Write32(argvbase+i, *(u32*)&argv[i]); - - nds.ARM9Write32(0x02FFFE70, 0x5F617267); - nds.ARM9Write32(0x02FFFE74, argvbase); - nds.ARM9Write32(0x02FFFE78, argvlen+1); - // The DSi version of ARM9Write32 will be called if nds is really a DSi - } -} - -int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) -{ - if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); - - switch (cmd[0]) - { - case 0xB7: - { - u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - memset(data, 0, len); - - if (((addr + len - 1) >> 12) != (addr >> 12)) - { - u32 len1 = 0x1000 - (addr & 0xFFF); - ReadROM_B7(addr, len1, data, 0); - ReadROM_B7(addr+len1, len-len1, data, len1); - } - else - ReadROM_B7(addr, len, data, 0); - } - return 0; - - case 0xC0: // SD read - { - u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - if (SD) SD->ReadSectors(sector, len>>9, data); - } - return 0; - - case 0xC1: // SD write - return 1; - - default: - return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); - } -} - -void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len) -{ - if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); - - // TODO: delayed SD writing? like we have for SRAM - - switch (cmd[0]) - { - case 0xC1: - { - u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; - if (SD && !SD->IsReadOnly()) SD->WriteSectors(sector, len>>9, data); - } - break; - - default: - return CartCommon::ROMCommandFinish(cmd, data, len); - } -} - -void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const +void CartSD::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const { if (patch[0x0D] > binary[dldioffset+0x0F]) { @@ -1364,7 +1263,7 @@ void CartHomebrew::ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, Log(LogLevel::Debug, "applied DLDI patch at %08X\n", dldioffset); } -void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) +void CartSD::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) { if (*(u32*)&patch[0] != 0xBF8DA5ED || *(u32*)&patch[4] != 0x69684320 || @@ -1394,7 +1293,7 @@ void CartHomebrew::ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly) } } -void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const +void CartSD::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const { // TODO: how strict should this be for homebrew? @@ -1403,7 +1302,115 @@ void CartHomebrew::ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const memcpy(data+offset, ROM.get()+addr, len); } +CartHomebrew::CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartSD(rom, len, chipid, romparams, std::move(sdcard)) +{} +CartHomebrew::CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard) : + CartSD(std::move(rom), len, chipid, romparams, std::move(sdcard)) +{} + +CartHomebrew::~CartHomebrew() = default; + +void CartHomebrew::Reset() +{ + CartSD::Reset(); + + if (SD) + ApplyDLDIPatch(melonDLDI, sizeof(melonDLDI), SD->IsReadOnly()); +} + +void CartHomebrew::SetupDirectBoot(const std::string& romname, NDS& nds) +{ + CartCommon::SetupDirectBoot(romname, nds); + + if (SD) + { + // add the ROM to the SD volume + + if (!SD->InjectFile(romname, ROM.get(), ROMLength)) + return; + + // setup argv command line + + char argv[512] = {0}; + int argvlen; + + strncpy(argv, "fat:/", 511); + strncat(argv, romname.c_str(), 511); + argvlen = strlen(argv); + + const NDSHeader& header = GetHeader(); + + u32 argvbase = header.ARM9RAMAddress + header.ARM9Size; + argvbase = (argvbase + 0xF) & ~0xF; + + for (u32 i = 0; i <= argvlen; i+=4) + nds.ARM9Write32(argvbase+i, *(u32*)&argv[i]); + + nds.ARM9Write32(0x02FFFE70, 0x5F617267); + nds.ARM9Write32(0x02FFFE74, argvbase); + nds.ARM9Write32(0x02FFFE78, argvlen+1); + // The DSi version of ARM9Write32 will be called if nds is really a DSi + } +} + +int CartHomebrew::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) +{ + if (CmdEncMode != 2) return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); + + switch (cmd[0]) + { + case 0xB7: + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + memset(data, 0, len); + + if (((addr + len - 1) >> 12) != (addr >> 12)) + { + u32 len1 = 0x1000 - (addr & 0xFFF); + ReadROM_B7(addr, len1, data, 0); + ReadROM_B7(addr+len1, len-len1, data, len1); + } + else + ReadROM_B7(addr, len, data, 0); + } + return 0; + + case 0xC0: // SD read + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + if (SD) SD->ReadSectors(sector, len>>9, data); + } + return 0; + + case 0xC1: // SD write + return 1; + + default: + return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); + } +} + +void CartHomebrew::ROMCommandFinish(const u8* cmd, u8* data, u32 len) +{ + if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); + + // TODO: delayed SD writing? like we have for SRAM + + switch (cmd[0]) + { + case 0xC1: + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + if (SD && !SD->IsReadOnly()) SD->WriteSectors(sector, len>>9, data); + } + break; + + default: + return CartCommon::ROMCommandFinish(cmd, data, len); + } +} NDSCartSlot::NDSCartSlot(melonDS::NDS& nds, std::unique_ptr&& rom) noexcept : NDS(nds) { @@ -1588,6 +1595,7 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen dsi = false; } + const char *gametitle = header.GameTitle; u32 gamecode = header.GameCodeAsU32(); u32 arm9base = header.ARM9ROMOffset; @@ -1645,6 +1653,8 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen auto [sram, sramlen] = args ? std::move(*args->SRAM) : std::make_pair(nullptr, 0); if (homebrew) cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, args ? std::move(args->SDCard) : std::nullopt); + else if (gametitle[0] == 0 && !strncmp("SD/TF-NDS", gametitle + 1, 9) && gamecode == 0x414D5341) + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, CartR4TypeR4, CartR4LanguageEnglish, args ? std::move(args->SDCard) : std::nullopt); else if (cartid & 0x08000000) cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen); else if (irversion != 0) diff --git a/src/NDSCart.h b/src/NDSCart.h index dcbc1eb2..7d482ab0 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -45,6 +45,7 @@ enum CartType RetailIR = 0x103, RetailBT = 0x104, Homebrew = 0x201, + UnlicensedR4 = 0x301 }; class NDSCartSlot; @@ -236,19 +237,13 @@ public: u8 SPIWrite(u8 val, u32 pos, bool last) override; }; -// CartHomebrew -- homebrew 'cart' (no SRAM, DLDI) -class CartHomebrew : public CartCommon +// CartSD -- any 'cart' with an SD card slot +class CartSD : public CartCommon { public: - CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); - CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); - ~CartHomebrew() override; - - void Reset() override; - void SetupDirectBoot(const std::string& romname, NDS& nds) override; - - int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; - void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override; + CartSD(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); + CartSD(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); + ~CartSD() override; [[nodiscard]] const std::optional& GetSDCard() const noexcept { return SD; } void SetSDCard(FATStorage&& sdcard) noexcept { SD = std::move(sdcard); } @@ -260,7 +255,7 @@ public: // it just leaves behind an optional with a moved-from value } -private: +protected: void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const; void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly); void ReadROM_B7(u32 addr, u32 len, u8* data, u32 offset) const; @@ -268,6 +263,74 @@ private: std::optional SD {}; }; +// CartHomebrew -- homebrew 'cart' (no SRAM, DLDI) +class CartHomebrew : public CartSD +{ +public: + CartHomebrew(const u8* rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); + CartHomebrew(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, std::optional&& sdcard = std::nullopt); + ~CartHomebrew() override; + + void Reset() override; + void SetupDirectBoot(const std::string& romname, NDS& nds) override; + + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override; +}; + +// CartR4 -- unlicensed R4 'cart' (NDSCartR4.cpp) +enum CartR4Type +{ + /* non-SDHC carts */ + CartR4TypeM3Simply = 0, + CartR4TypeR4 = 1, + /* SDHC carts */ + CartR4TypeAce3DS = 2 +}; + +enum CartR4Language +{ + CartR4LanguageJapanese = (7 << 3) | 1, + CartR4LanguageEnglish = (7 << 3) | 2, + CartR4LanguageFrench = (2 << 3) | 2, + CartR4LanguageKorean = (4 << 3) | 2, + CartR4LanguageSimplifiedChinese = (6 << 3) | 3, + CartR4LanguageTraditionalChinese = (7 << 3) | 3 +}; + +class CartR4 : public CartSD +{ +public: + CartR4(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, CartR4Type ctype, CartR4Language clanguage, + std::optional&& sdcard = std::nullopt); + ~CartR4() override; + + void Reset() override; + + void DoSavestate(Savestate* file) override; + + int ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) override; + void ROMCommandFinish(const u8* cmd, u8* data, u32 len) override; + +private: + inline u32 GetAdjustedSector(u32 sector) const + { + return R4CartType >= CartR4TypeAce3DS ? sector : sector >> 9; + } + + u16 GetEncryptionKey(u16 sector); + void ReadSDToBuffer(u32 sector, bool rom); + u64 SDFATEntrySectorGet(u32 entry, u32 addr); + + s32 EncryptionKey; + u32 FATEntryOffset[2]; + u8 Buffer[512]; + u8 InitStatus; + CartR4Type R4CartType; + CartR4Language CartLanguage; + bool BufferInitialized; +}; + class NDSCartSlot { public: diff --git a/src/NDSCartR4.cpp b/src/NDSCartR4.cpp new file mode 100644 index 00000000..8497f556 --- /dev/null +++ b/src/NDSCartR4.cpp @@ -0,0 +1,371 @@ +/* + 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 +#include "NDS.h" +#include "DSi.h" +#include "NDSCart.h" +#include "Platform.h" + +namespace melonDS +{ +using Platform::Log; +using Platform::LogLevel; + +namespace NDSCart +{ + +/* + Original algorithm discovered by yasu, 2007 + + http://hp.vector.co.jp/authors/VA013928/ + http://www.usay.jp/ + http://www.yasu.nu/ +*/ +static void DecryptR4Sector(u8* dest, u8* src, u16 key1) +{ + for (int i = 0; i < 512; i++) + { + // Derive key2 from key1. + u8 key2 = ((key1 >> 7) & 0x80) + | ((key1 >> 6) & 0x60) + | ((key1 >> 5) & 0x10) + | ((key1 >> 4) & 0x0C) + | (key1 & 0x03); + + // Decrypt byte. + dest[i] = src[i] ^ key2; + + // Derive next key1 from key2. + u16 tmp = ((src[i] << 8) ^ key1); + u16 tmpXor = 0; + for (int ii = 0; ii < 16; ii++) + tmpXor ^= (tmp >> ii); + + u16 newKey1 = 0; + newKey1 |= ((tmpXor & 0x80) | (tmp & 0x7C)) << 8; + newKey1 |= ((tmp ^ (tmpXor >> 14)) << 8) & 0x0300; + newKey1 |= (((tmp >> 1) ^ tmp) >> 6) & 0xFC; + newKey1 |= ((tmp ^ (tmpXor >> 1)) >> 8) & 0x03; + + key1 = newKey1; + } +} + +CartR4::CartR4(std::unique_ptr&& rom, u32 len, u32 chipid, ROMListEntry romparams, CartR4Type ctype, CartR4Language clanguage, + std::optional&& sdcard) + : CartSD(std::move(rom), len, chipid, romparams, std::move(sdcard)) +{ + InitStatus = 0; + R4CartType = ctype; + CartLanguage = clanguage; +} + +CartR4::~CartR4() +{ +} + +void CartR4::Reset() +{ + CartSD::Reset(); + + BufferInitialized = false; + EncryptionKey = -1; + FATEntryOffset[0] = 0xFFFFFFFF; + FATEntryOffset[1] = 0xFFFFFFFF; + + if (!SD) + InitStatus = 1; + else + { + u8 buffer[512]; + if (!SD->ReadFile("_DS_MENU.DAT", 0, 512, buffer)) + InitStatus = 3; + else + InitStatus = 4; + } +} + +void CartR4::DoSavestate(Savestate* file) +{ + CartCommon::DoSavestate(file); + + file->Var32(&FATEntryOffset[0]); + file->Var32(&FATEntryOffset[1]); + file->VarArray(Buffer, 512); +} + +// FIXME: Ace3DS/clone behavior is only partially verified. +int CartR4::ROMCommandStart(NDS& nds, NDSCart::NDSCartSlot& cartslot, const u8* cmd, u8* data, u32 len) +{ + if (CmdEncMode != 2) + return CartCommon::ROMCommandStart(nds, cartslot, cmd, data, len); + + switch (cmd[0]) + { + case 0xB0: /* Get card information */ + { + u32 info = 0x75A00000 | (((R4CartType >= 1 ? 4 : 0) | CartLanguage) << 3) | InitStatus; + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = info; + return 0; + } + case 0xB4: /* FAT entry */ + { + u8 entryBuffer[512]; + u32 sector = ((cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]) & (~0x1F); + // set FAT entry offset to the starting cluster, to gain a bit of speed + SD->ReadSectors(sector >> 9, 1, entryBuffer); + u16 fileEntryOffset = sector & 0x1FF; + u32 clusterStart = (entryBuffer[fileEntryOffset + 27] << 8) + | entryBuffer[fileEntryOffset + 26] + | (entryBuffer[fileEntryOffset + 21] << 24) + | (entryBuffer[fileEntryOffset + 20] << 16); + FATEntryOffset[cmd[4] & 0x01] = clusterStart; + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } + case 0xB8: /* ? Get chip ID ? */ + { + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = ChipID; + return 0; + } + case 0xB2: /* Save read request */ + case 0xB6: /* ROM read request */ + { + u32 sector = ((cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]); + ReadSDToBuffer(sector, cmd[0] == 0xB6); + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } + case 0xB9: /* SD read request */ + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + if (SD) + SD->ReadSectors(GetAdjustedSector(sector), 1, Buffer); + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } + case 0xBB: /* SD write start */ + case 0xBD: /* Save write start */ + return 1; + case 0xBC: /* SD write status */ + case 0xBE: /* Save write status */ + { + if (R4CartType == CartR4TypeAce3DS && cmd[0] == 0xBC) + { + uint8_t checksum = 0; + for (int i = 0; i < 7; i++) + checksum ^= cmd[i]; + if (checksum != cmd[7]) + Log(LogLevel::Warn, "R4: invalid 0xBC command checksum (%d != %d)", cmd[7], checksum); + } + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } + case 0xB7: /* ROM read data */ + { + /* If the buffer has not been initialized yet, emulate ROM. */ + /* TODO: When does the R4 do this exactly? */ + if (!BufferInitialized) + { + u32 addr = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + memcpy(data, &ROM[addr & (ROMLength-1)], len); + return 0; + } + /* Otherwise, fall through. */ + } + case 0xB3: /* Save read data */ + case 0xBA: /* SD read data */ + { + // TODO: Do these use separate buffers? + for (u32 pos = 0; pos < len; pos++) + data[pos] = Buffer[pos & 0x1FF]; + return 0; + } + case 0xBF: /* ROM read decrypted data */ + { + // TODO: Is decryption done using the sector from 0xBF or 0xB6? + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + if (len >= 512) + DecryptR4Sector(data, Buffer, GetEncryptionKey(sector >> 9)); + return 0; + } + default: + Log(LogLevel::Warn, "R4: unknown command %02X %02X %02X %02X %02X %02X %02X %02X (%d)\n", cmd[0], cmd[1], cmd[2], cmd[3], cmd[4], cmd[5], cmd[6], cmd[7], len); + for (u32 pos = 0; pos < len; pos += 4) + *(u32*)&data[pos] = 0; + return 0; + } +} + +void CartR4::ROMCommandFinish(const u8* cmd, u8* data, u32 len) +{ + if (CmdEncMode != 2) return CartCommon::ROMCommandFinish(cmd, data, len); + + switch (cmd[0]) + { + case 0xBB: /* SD write start */ + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + + // The official R4 firmware sends a superfluous write to card + // (sector 0, byte 1) on boot. TODO: Is this correct? + if (GetAdjustedSector(sector) != sector && (sector & 0x1FF)) break; + + if (SD && !SD->IsReadOnly()) + SD->WriteSectors(GetAdjustedSector(sector), 1, data); + break; + } + case 0xBD: /* Save write start */ + { + u32 sector = (cmd[1]<<24) | (cmd[2]<<16) | (cmd[3]<<8) | cmd[4]; + + if (sector & 0x1FF) break; + + if (SD && !SD->IsReadOnly()) + SD->WriteSectors( + SDFATEntrySectorGet(FATEntryOffset[1], sector) >> 9, + 1, data + ); + break; + } + } +} + +u16 CartR4::GetEncryptionKey(u16 sector) +{ + if (EncryptionKey == -1) + { + u8 encryptedBuffer[512]; + u8 decryptedBuffer[512]; + SD->ReadFile("_DS_MENU.DAT", 0, 512, encryptedBuffer); + for (u32 key = 0; key < 0x10000; key++) + { + DecryptR4Sector(decryptedBuffer, encryptedBuffer, key); + if (decryptedBuffer[12] == '#' && decryptedBuffer[13] == '#' + && decryptedBuffer[14] == '#' && decryptedBuffer[15] == '#') + { + EncryptionKey = key; + break; + } + } + + if (EncryptionKey != -1) + { + Log(LogLevel::Warn, "R4: found cartridge key = %04X\n", EncryptionKey); + } + else + { + EncryptionKey = -2; + Log(LogLevel::Warn, "R4: could not find cartridge key\n"); + } + } + return EncryptionKey ^ sector; +} + +void CartR4::ReadSDToBuffer(u32 sector, bool rom) +{ + if (SD) + { + if (rom && FATEntryOffset[0] == 0xFFFFFFFF) + { + // On first boot, read from _DS_MENU.DAT. + SD->ReadFile("_DS_MENU.DAT", sector & ~0x1FF, 512, Buffer); + } + else + { + // Default mode. + SD->ReadSectors( + SDFATEntrySectorGet(FATEntryOffset[rom ? 0 : 1], sector) >> 9, + 1, Buffer + ); + } + BufferInitialized = true; + } +} + +u64 CartR4::SDFATEntrySectorGet(u32 entry, u32 addr) +{ + u8 buffer[512]; + u32 bufferSector = 0xFFFFFFFF; + + // Parse FAT header. + SD->ReadSectors(0, 1, buffer); + u16 bytesPerSector = (buffer[12] << 8) | buffer[11]; + u8 sectorsPerCluster = buffer[13]; + u16 firstFatSector = (buffer[15] << 8) | buffer[14]; + u8 fatTableCount = buffer[16]; + u32 clustersTotal = SD->GetSectorCount() / sectorsPerCluster; + bool isFat32 = clustersTotal >= 65526; + + u32 fatTableSize = (buffer[23] << 8) | buffer[22]; + if (fatTableSize == 0 && isFat32) + fatTableSize = (buffer[39] << 24) | (buffer[38] << 16) | (buffer[37] << 8) | buffer[36]; + u32 bytesPerCluster = bytesPerSector * sectorsPerCluster; + u32 rootDirSectors = 0; + if (!isFat32) { + u32 rootDirEntries = (buffer[18] << 8) | buffer[17]; + rootDirSectors = ((rootDirEntries * 32) + (bytesPerSector - 1)) / bytesPerSector; + } + u32 firstDataSector = firstFatSector + fatTableCount * fatTableSize + rootDirSectors; + + // Parse file entry (done when processing command 0xB4). + u32 clusterStart = entry; + + // Parse cluster table. + u32 currentCluster = clusterStart; + while (true) + { + currentCluster &= isFat32 ? 0x0FFFFFFF : 0xFFFF; + if (addr < bytesPerCluster) + { + // Read from this cluster. + return (u64) (firstDataSector + ((currentCluster - 2) * sectorsPerCluster)) * bytesPerSector + addr; + } + else if (currentCluster >= 2 && currentCluster <= (isFat32 ? 0x0FFFFFF6 : 0xFFF6)) + { + // Follow into next cluster. + u32 nextClusterOffset = firstFatSector * bytesPerSector + currentCluster * (isFat32 ? 4 : 2); + u32 nextClusterTableSector = nextClusterOffset >> 9; + if (bufferSector != nextClusterTableSector) + { + SD->ReadSectors(nextClusterTableSector, 1, buffer); + bufferSector = nextClusterTableSector; + } + nextClusterOffset &= 0x1FF; + currentCluster = (buffer[nextClusterOffset + 1] << 8) | buffer[nextClusterOffset]; + if (isFat32) + currentCluster |= (buffer[nextClusterOffset + 3] << 24) | (buffer[nextClusterOffset + 2] << 16); + addr -= bytesPerCluster; + } + else + { + // End of cluster table. + return 0; + } + } +} + +} +} From c867a7f1c09b3c5f07e0772fcddabce07bcd7fe7 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Fri, 15 Dec 2023 08:53:31 -0500 Subject: [PATCH 075/157] Make the initial 3D renderer configurable via `NDSArgs` (#1913) * Allow 3D renderers to be created without passing `GPU` to the constructor * Make the initial 3D renderer configurable via `NDSArgs` * Fix a compiler error --- src/Args.h | 6 + src/GPU.cpp | 28 ++-- src/GPU.h | 2 +- src/GPU3D.cpp | 22 ++-- src/GPU3D.h | 22 ++-- src/GPU3D_OpenGL.cpp | 101 ++++++++------- src/GPU3D_OpenGL.h | 17 ++- src/GPU3D_Soft.cpp | 240 ++++++++++++++++++----------------- src/GPU3D_Soft.h | 47 ++++--- src/NDS.cpp | 4 +- src/NDS.h | 8 ++ src/frontend/qt_sdl/main.cpp | 8 +- 12 files changed, 259 insertions(+), 246 deletions(-) diff --git a/src/Args.h b/src/Args.h index c6d131c8..d836b643 100644 --- a/src/Args.h +++ b/src/Args.h @@ -30,6 +30,7 @@ #include "DSi_NAND.h" #include "FATStorage.h" #include "FreeBIOS.h" +#include "GPU3D_Soft.h" #include "SPI_Firmware.h" #include "SPU.h" @@ -118,6 +119,11 @@ struct NDSArgs /// Defaults to disabled. /// Ignored in builds that don't have the GDB stub included. std::optional GDB = std::nullopt; + + /// The 3D renderer to initialize the DS with. + /// Defaults to the software renderer. + /// Can be changed later at any time. + std::unique_ptr Renderer3D = std::make_unique(); }; /// Arguments to pass into the DSi constructor. diff --git a/src/GPU.cpp b/src/GPU.cpp index 2c140a86..c226ccbe 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -67,7 +67,7 @@ GPU::GPU(melonDS::NDS& nds, std::unique_ptr&& renderer3d, std::uniqu NDS(nds), GPU2D_A(0, *this), GPU2D_B(1, *this), - GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique(*this)), + GPU3D(nds, renderer3d ? std::move(renderer3d) : std::make_unique()), GPU2D_Renderer(renderer2d ? std::move(renderer2d) : std::make_unique(*this)) { NDS.RegisterEventFunc(Event_LCD, LCD_StartHBlank, MemberEventFunc(GPU, StartHBlank)); @@ -209,7 +209,7 @@ void GPU::Stop() noexcept memset(Framebuffer[1][0].get(), 0, fbsize*4); memset(Framebuffer[1][1].get(), 0, fbsize*4); - GPU3D.Stop(); + GPU3D.Stop(*this); } void GPU::DoSavestate(Savestate* file) noexcept @@ -294,7 +294,7 @@ void GPU::AssignFramebuffers() noexcept void GPU::SetRenderer3D(std::unique_ptr&& renderer) noexcept { if (renderer == nullptr) - GPU3D.SetCurrentRenderer(std::make_unique(*this)); + GPU3D.SetCurrentRenderer(std::make_unique()); else GPU3D.SetCurrentRenderer(std::move(renderer)); @@ -899,7 +899,7 @@ void GPU::StartHBlank(u32 line) noexcept } else if (VCount == 215) { - GPU3D.VCount215(); + GPU3D.VCount215(*this); } else if (VCount == 262) { @@ -925,7 +925,7 @@ void GPU::FinishFrame(u32 lines) noexcept if (GPU3D.AbortFrame) { - GPU3D.RestartFrame(); + GPU3D.RestartFrame(*this); GPU3D.AbortFrame = false; } } @@ -1018,7 +1018,7 @@ void GPU::StartScanline(u32 line) noexcept // texture memory anyway and only update it before the start //of the next frame. // So we can give the rasteriser a bit more headroom - GPU3D.VCount144(); + GPU3D.VCount144(*this); // VBlank DispStat[0] |= (1<<0); @@ -1038,7 +1038,7 @@ void GPU::StartScanline(u32 line) noexcept // Need a better way to identify the openGL renderer in particular if (GPU3D.IsRendererAccelerated()) - GPU3D.Blit(); + GPU3D.Blit(*this); } } @@ -1068,7 +1068,7 @@ void GPU::SetVCount(u16 val) noexcept } template -NonStupidBitField VRAMTrackingSet::DeriveState(u32* currentMappings, GPU& gpu) +NonStupidBitField VRAMTrackingSet::DeriveState(const u32* currentMappings, GPU& gpu) { NonStupidBitField result; u16 banksToBeZeroed = 0; @@ -1131,12 +1131,12 @@ NonStupidBitField VRAMTrackingSet VRAMTrackingSet<32*1024, 8*1024>::DeriveState(u32*, GPU& gpu); -template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(u32*, GPU& gpu); -template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(u32*, GPU& gpu); -template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(u32*, GPU& gpu); -template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(u32*, GPU& gpu); -template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(u32*, GPU& gpu); +template NonStupidBitField<32*1024/VRAMDirtyGranularity> VRAMTrackingSet<32*1024, 8*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<8*1024/VRAMDirtyGranularity> VRAMTrackingSet<8*1024, 8*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 128*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<128*1024/VRAMDirtyGranularity> VRAMTrackingSet<128*1024, 16*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<256*1024/VRAMDirtyGranularity> VRAMTrackingSet<256*1024, 16*1024>::DeriveState(const u32*, GPU& gpu); +template NonStupidBitField<512*1024/VRAMDirtyGranularity> VRAMTrackingSet<512*1024, 16*1024>::DeriveState(const u32*, GPU& gpu); diff --git a/src/GPU.h b/src/GPU.h index ee7311a6..e070db78 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -49,7 +49,7 @@ struct VRAMTrackingSet Mapping[i] = 0x8000; } } - NonStupidBitField DeriveState(u32* currentMappings, GPU& gpu); + NonStupidBitField DeriveState(const u32* currentMappings, GPU& gpu); }; class GPU diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 7e7df244..ac29a1e4 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -142,7 +142,7 @@ void MatrixLoadIdentity(s32* m); GPU3D::GPU3D(melonDS::NDS& nds, std::unique_ptr&& renderer) noexcept : NDS(nds), - CurrentRenderer(renderer ? std::move(renderer) : std::make_unique(nds.GPU)) + CurrentRenderer(renderer ? std::move(renderer) : std::make_unique()) { } @@ -2367,20 +2367,20 @@ void GPU3D::CheckFIFODMA() noexcept NDS.CheckDMAs(0, 0x07); } -void GPU3D::VCount144() noexcept +void GPU3D::VCount144(GPU& gpu) noexcept { - CurrentRenderer->VCount144(); + CurrentRenderer->VCount144(gpu); } -void GPU3D::RestartFrame() noexcept +void GPU3D::RestartFrame(GPU& gpu) noexcept { - CurrentRenderer->RestartFrame(); + CurrentRenderer->RestartFrame(gpu); } -void GPU3D::Stop() noexcept +void GPU3D::Stop(const GPU& gpu) noexcept { if (CurrentRenderer) - CurrentRenderer->Stop(); + CurrentRenderer->Stop(gpu); } @@ -2473,9 +2473,9 @@ void GPU3D::VBlank() noexcept } } -void GPU3D::VCount215() noexcept +void GPU3D::VCount215(GPU& gpu) noexcept { - CurrentRenderer->RenderFrame(); + CurrentRenderer->RenderFrame(gpu); } void GPU3D::SetRenderXPos(u16 xpos) noexcept @@ -2935,10 +2935,10 @@ void GPU3D::Write32(u32 addr, u32 val) noexcept Log(LogLevel::Debug, "unknown GPU3D write32 %08X %08X\n", addr, val); } -void GPU3D::Blit() noexcept +void GPU3D::Blit(const GPU& gpu) noexcept { if (CurrentRenderer) - CurrentRenderer->Blit(); + CurrentRenderer->Blit(gpu); } Renderer3D::Renderer3D(bool Accelerated) diff --git a/src/GPU3D.h b/src/GPU3D.h index eb975c68..0c900c6c 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -101,12 +101,12 @@ public: void CheckFIFOIRQ() noexcept; void CheckFIFODMA() noexcept; - void VCount144() noexcept; + void VCount144(GPU& gpu) noexcept; void VBlank() noexcept; - void VCount215() noexcept; + void VCount215(GPU& gpu) noexcept; - void RestartFrame() noexcept; - void Stop() noexcept; + void RestartFrame(GPU& gpu) noexcept; + void Stop(const GPU& gpu) noexcept; void SetRenderXPos(u16 xpos) noexcept; [[nodiscard]] u16 GetRenderXPos() const noexcept { return RenderXPos; } @@ -125,7 +125,7 @@ public: void Write8(u32 addr, u8 val) noexcept; void Write16(u32 addr, u16 val) noexcept; void Write32(u32 addr, u32 val) noexcept; - void Blit() noexcept; + void Blit(const GPU& gpu) noexcept; private: melonDS::NDS& NDS; typedef union @@ -334,19 +334,19 @@ public: Renderer3D(const Renderer3D&) = delete; Renderer3D& operator=(const Renderer3D&) = delete; - virtual void Reset() = 0; + virtual void Reset(GPU& gpu) = 0; // This "Accelerated" flag currently communicates if the framebuffer should // be allocated differently and other little misc handlers. Ideally there // are more detailed "traits" that we can ask of the Renderer3D type const bool Accelerated; - virtual void VCount144() {}; - virtual void Stop() {} - virtual void RenderFrame() = 0; - virtual void RestartFrame() {}; + virtual void VCount144(GPU& gpu) {}; + virtual void Stop(const GPU& gpu) {} + virtual void RenderFrame(GPU& gpu) = 0; + virtual void RestartFrame(GPU& gpu) {}; virtual u32* GetLine(int line) = 0; - virtual void Blit() {}; + virtual void Blit(const GPU& gpu) {}; virtual void PrepareCaptureFrame() {} protected: Renderer3D(bool Accelerated); diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 55a034cd..27711a89 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -97,9 +97,8 @@ void SetupDefaultTexParams(GLuint tex) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } -GLRenderer::GLRenderer(GLCompositor&& compositor, melonDS::GPU& gpu) noexcept : +GLRenderer::GLRenderer(GLCompositor&& compositor) noexcept : Renderer3D(true), - GPU(gpu), CurGLCompositor(std::move(compositor)) { // GLRenderer::New() will be used to actually initialize the renderer; @@ -107,7 +106,7 @@ GLRenderer::GLRenderer(GLCompositor&& compositor, melonDS::GPU& gpu) noexcept : // so we can just let the destructor clean up a half-initialized renderer. } -std::unique_ptr GLRenderer::New(melonDS::GPU& gpu) noexcept +std::unique_ptr GLRenderer::New() noexcept { assert(glEnable != nullptr); @@ -117,7 +116,7 @@ std::unique_ptr GLRenderer::New(melonDS::GPU& gpu) noexcept // Will be returned if the initialization succeeds, // or cleaned up via RAII if it fails. - std::unique_ptr result = std::unique_ptr(new GLRenderer(std::move(*compositor), gpu)); + std::unique_ptr result = std::unique_ptr(new GLRenderer(std::move(*compositor))); compositor = std::nullopt; glEnable(GL_DEPTH_TEST); @@ -333,7 +332,7 @@ GLRenderer::~GLRenderer() } } -void GLRenderer::Reset() +void GLRenderer::Reset(GPU& gpu) { // This is where the compositor's Reset() method would be called, // except there's no such method right now. @@ -786,14 +785,14 @@ int GLRenderer::RenderPolygonEdgeBatch(int i) const return numpolys; } -void GLRenderer::RenderSceneChunk(int y, int h) +void GLRenderer::RenderSceneChunk(const GPU3D& gpu3d, int y, int h) { u32 flags = 0; - if (GPU.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; + if (gpu3d.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; if (h != 192) glScissor(0, y<PolyData->IsShadow) { // shadow against clear-plane will only pass if its polyID matches that of the clear plane - u32 clrpolyid = (GPU.GPU3D.RenderClearAttr1 >> 24) & 0x3F; + u32 clrpolyid = (gpu3d.RenderClearAttr1 >> 24) & 0x3F; if (polyid != clrpolyid) { i++; continue; } glEnable(GL_BLEND); @@ -1089,7 +1088,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) } } - if (GPU.GPU3D.RenderDispCnt & 0x00A0) // fog/edge enabled + if (gpu3d.RenderDispCnt & 0x00A0) // fog/edge enabled { glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glColorMaski(1, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); @@ -1111,7 +1110,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) glBindBuffer(GL_ARRAY_BUFFER, ClearVertexBufferID); glBindVertexArray(ClearVertexArrayID); - if (GPU.GPU3D.RenderDispCnt & (1<<5)) + if (gpu3d.RenderDispCnt & (1<<5)) { // edge marking // TODO: depth/polyid values at screen edges @@ -1123,19 +1122,19 @@ void GLRenderer::RenderSceneChunk(int y, int h) glDrawArrays(GL_TRIANGLES, 0, 2*3); } - if (GPU.GPU3D.RenderDispCnt & (1<<7)) + if (gpu3d.RenderDispCnt & (1<<7)) { // fog glUseProgram(FinalPassFogShader[2]); - if (GPU.GPU3D.RenderDispCnt & (1<<6)) + if (gpu3d.RenderDispCnt & (1<<6)) glBlendFuncSeparate(GL_ZERO, GL_ONE, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA); else glBlendFuncSeparate(GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_SRC_ALPHA); { - u32 c = GPU.GPU3D.RenderFogColor; + u32 c = gpu3d.RenderFogColor; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1150,7 +1149,7 @@ void GLRenderer::RenderSceneChunk(int y, int h) } -void GLRenderer::RenderFrame() +void GLRenderer::RenderFrame(GPU& gpu) { CurShaderID = -1; @@ -1159,11 +1158,11 @@ void GLRenderer::RenderFrame() ShaderConfig.uScreenSize[0] = ScreenW; ShaderConfig.uScreenSize[1] = ScreenH; - ShaderConfig.uDispCnt = GPU.GPU3D.RenderDispCnt; + ShaderConfig.uDispCnt = gpu.GPU3D.RenderDispCnt; for (int i = 0; i < 32; i++) { - u16 c = GPU.GPU3D.RenderToonTable[i]; + u16 c = gpu.GPU3D.RenderToonTable[i]; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1175,7 +1174,7 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 8; i++) { - u16 c = GPU.GPU3D.RenderEdgeTable[i]; + u16 c = gpu.GPU3D.RenderEdgeTable[i]; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1186,7 +1185,7 @@ void GLRenderer::RenderFrame() } { - u32 c = GPU.GPU3D.RenderFogColor; + u32 c = gpu.GPU3D.RenderFogColor; u32 r = c & 0x1F; u32 g = (c >> 5) & 0x1F; u32 b = (c >> 10) & 0x1F; @@ -1200,12 +1199,12 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 34; i++) { - u8 d = GPU.GPU3D.RenderFogDensityTable[i]; + u8 d = gpu.GPU3D.RenderFogDensityTable[i]; ShaderConfig.uFogDensity[i][0] = (float)d / 127.0; } - ShaderConfig.uFogOffset = GPU.GPU3D.RenderFogOffset; - ShaderConfig.uFogShift = GPU.GPU3D.RenderFogShift; + ShaderConfig.uFogOffset = gpu.GPU3D.RenderFogOffset; + ShaderConfig.uFogShift = gpu.GPU3D.RenderFogShift; glBindBuffer(GL_UNIFORM_BUFFER, ShaderConfigUBO); void* unibuf = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY); @@ -1218,13 +1217,13 @@ void GLRenderer::RenderFrame() glBindTexture(GL_TEXTURE_2D, TexMemID); for (int i = 0; i < 4; i++) { - u32 mask = GPU.VRAMMap_Texture[i]; + u32 mask = gpu.VRAMMap_Texture[i]; u8* vram; if (!mask) continue; - else if (mask & (1<<0)) vram = GPU.VRAM_A; - else if (mask & (1<<1)) vram = GPU.VRAM_B; - else if (mask & (1<<2)) vram = GPU.VRAM_C; - else if (mask & (1<<3)) vram = GPU.VRAM_D; + else if (mask & (1<<0)) vram = gpu.VRAM_A; + else if (mask & (1<<1)) vram = gpu.VRAM_B; + else if (mask & (1<<2)) vram = gpu.VRAM_C; + else if (mask & (1<<3)) vram = gpu.VRAM_D; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i*128, 1024, 128, GL_RED_INTEGER, GL_UNSIGNED_BYTE, vram); } @@ -1234,12 +1233,12 @@ void GLRenderer::RenderFrame() for (int i = 0; i < 6; i++) { // 6 x 16K chunks - u32 mask = GPU.VRAMMap_TexPal[i]; + u32 mask = gpu.VRAMMap_TexPal[i]; u8* vram; if (!mask) continue; - else if (mask & (1<<4)) vram = &GPU.VRAM_E[(i&3)*0x4000]; - else if (mask & (1<<5)) vram = GPU.VRAM_F; - else if (mask & (1<<6)) vram = GPU.VRAM_G; + else if (mask & (1<<4)) vram = &gpu.VRAM_E[(i&3)*0x4000]; + else if (mask & (1<<5)) vram = gpu.VRAM_F; + else if (mask & (1<<6)) vram = gpu.VRAM_G; glTexSubImage2D(GL_TEXTURE_2D, 0, 0, i*8, 1024, 8, GL_RGBA, GL_UNSIGNED_SHORT_1_5_5_5_REV, vram); } @@ -1264,13 +1263,13 @@ void GLRenderer::RenderFrame() glUseProgram(ClearShaderPlain[2]); glDepthFunc(GL_ALWAYS); - u32 r = GPU.GPU3D.RenderClearAttr1 & 0x1F; - u32 g = (GPU.GPU3D.RenderClearAttr1 >> 5) & 0x1F; - u32 b = (GPU.GPU3D.RenderClearAttr1 >> 10) & 0x1F; - u32 fog = (GPU.GPU3D.RenderClearAttr1 >> 15) & 0x1; - u32 a = (GPU.GPU3D.RenderClearAttr1 >> 16) & 0x1F; - u32 polyid = (GPU.GPU3D.RenderClearAttr1 >> 24) & 0x3F; - u32 z = ((GPU.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; + u32 r = gpu.GPU3D.RenderClearAttr1 & 0x1F; + u32 g = (gpu.GPU3D.RenderClearAttr1 >> 5) & 0x1F; + u32 b = (gpu.GPU3D.RenderClearAttr1 >> 10) & 0x1F; + u32 fog = (gpu.GPU3D.RenderClearAttr1 >> 15) & 0x1; + u32 a = (gpu.GPU3D.RenderClearAttr1 >> 16) & 0x1F; + u32 polyid = (gpu.GPU3D.RenderClearAttr1 >> 24) & 0x3F; + u32 z = ((gpu.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; glStencilFunc(GL_ALWAYS, 0xFF, 0xFF); glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE); @@ -1289,20 +1288,20 @@ void GLRenderer::RenderFrame() glDrawArrays(GL_TRIANGLES, 0, 2*3); } - if (GPU.GPU3D.RenderNumPolygons) + if (gpu.GPU3D.RenderNumPolygons) { // render shit here u32 flags = 0; - if (GPU.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; + if (gpu.GPU3D.RenderPolygonRAM[0]->WBuffer) flags |= RenderFlag_WBuffer; int npolys = 0; int firsttrans = -1; - for (u32 i = 0; i < GPU.GPU3D.RenderNumPolygons; i++) + for (u32 i = 0; i < gpu.GPU3D.RenderNumPolygons; i++) { - if (GPU.GPU3D.RenderPolygonRAM[i]->Degenerate) continue; + if (gpu.GPU3D.RenderPolygonRAM[i]->Degenerate) continue; - SetupPolygon(&PolygonList[npolys], GPU.GPU3D.RenderPolygonRAM[i]); - if (firsttrans < 0 && GPU.GPU3D.RenderPolygonRAM[i]->Translucent) + SetupPolygon(&PolygonList[npolys], gpu.GPU3D.RenderPolygonRAM[i]); + if (firsttrans < 0 && gpu.GPU3D.RenderPolygonRAM[i]->Translucent) firsttrans = npolys; npolys++; @@ -1319,15 +1318,15 @@ void GLRenderer::RenderFrame() glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, NumIndices * 2, IndexBuffer); glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, EdgeIndicesOffset * 2, NumEdgeIndices * 2, IndexBuffer + EdgeIndicesOffset); - RenderSceneChunk(0, 192); + RenderSceneChunk(gpu.GPU3D, 0, 192); } FrontBuffer = FrontBuffer ? 0 : 1; } -void GLRenderer::Stop() +void GLRenderer::Stop(const GPU& gpu) { - CurGLCompositor.Stop(GPU); + CurGLCompositor.Stop(gpu); } void GLRenderer::PrepareCaptureFrame() @@ -1345,9 +1344,9 @@ void GLRenderer::PrepareCaptureFrame() glReadPixels(0, 0, 256, 192, GL_BGRA, GL_UNSIGNED_BYTE, NULL); } -void GLRenderer::Blit() +void GLRenderer::Blit(const GPU& gpu) { - CurGLCompositor.RenderFrame(GPU, *this); + CurGLCompositor.RenderFrame(gpu, *this); } u32* GLRenderer::GetLine(int line) diff --git a/src/GPU3D_OpenGL.h b/src/GPU3D_OpenGL.h index 286d9f58..c30232ca 100644 --- a/src/GPU3D_OpenGL.h +++ b/src/GPU3D_OpenGL.h @@ -31,7 +31,7 @@ class GLRenderer : public Renderer3D { public: ~GLRenderer() override; - void Reset() override; + void Reset(GPU& gpu) override; void SetRenderSettings(bool betterpolygons, int scale) noexcept; void SetBetterPolygons(bool betterpolygons) noexcept; @@ -39,22 +39,22 @@ public: [[nodiscard]] bool GetBetterPolygons() const noexcept { return BetterPolygons; } [[nodiscard]] int GetScaleFactor() const noexcept { return ScaleFactor; } - void VCount144() override {}; - void RenderFrame() override; - void Stop() override; + void VCount144(GPU& gpu) override {}; + void RenderFrame(GPU& gpu) override; + void Stop(const GPU& gpu) override; u32* GetLine(int line) override; void SetupAccelFrame(); void PrepareCaptureFrame() override; - void Blit() override; + void Blit(const GPU& gpu) override; [[nodiscard]] const GLCompositor& GetCompositor() const noexcept { return CurGLCompositor; } GLCompositor& GetCompositor() noexcept { return CurGLCompositor; } - static std::unique_ptr New(melonDS::GPU& gpu) noexcept; + static std::unique_ptr New() noexcept; private: // Used by New() - GLRenderer(GLCompositor&& compositor, GPU& gpu) noexcept; + GLRenderer(GLCompositor&& compositor) noexcept; // GL version requirements // * texelFetch: 3.0 (GLSL 1.30) (3.2/1.50 for MS) @@ -74,7 +74,6 @@ private: u32 RenderKey; }; - melonDS::GPU& GPU; GLCompositor CurGLCompositor; RendererPolygon PolygonList[2048] {}; @@ -86,7 +85,7 @@ private: int RenderSinglePolygon(int i) const; int RenderPolygonBatch(int i) const; int RenderPolygonEdgeBatch(int i) const; - void RenderSceneChunk(int y, int h); + void RenderSceneChunk(const GPU3D& gpu3d, int y, int h); enum { diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index 894ac94a..c96464a6 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -42,14 +42,16 @@ void SoftRenderer::StopRenderThread() } } -void SoftRenderer::SetupRenderThread() +void SoftRenderer::SetupRenderThread(GPU& gpu) { if (Threaded) { if (!RenderThreadRunning.load(std::memory_order_relaxed)) { RenderThreadRunning = true; - RenderThread = Platform::Thread_Create(std::bind(&SoftRenderer::RenderThreadFunc, this)); + RenderThread = Platform::Thread_Create([this, &gpu]() { + RenderThreadFunc(gpu); + }); } // otherwise more than one frame can be queued up at once @@ -71,8 +73,8 @@ void SoftRenderer::SetupRenderThread() } -SoftRenderer::SoftRenderer(melonDS::GPU& gpu, bool threaded) noexcept - : Renderer3D(false), GPU(gpu), Threaded(threaded) +SoftRenderer::SoftRenderer(bool threaded) noexcept + : Renderer3D(false), Threaded(threaded) { Sema_RenderStart = Platform::Semaphore_Create(); Sema_RenderDone = Platform::Semaphore_Create(); @@ -92,7 +94,7 @@ SoftRenderer::~SoftRenderer() Platform::Semaphore_Free(Sema_ScanlineCount); } -void SoftRenderer::Reset() +void SoftRenderer::Reset(GPU& gpu) { memset(ColorBuffer, 0, BufferSize * 2 * 4); memset(DepthBuffer, 0, BufferSize * 2 * 4); @@ -100,19 +102,19 @@ void SoftRenderer::Reset() PrevIsShadowMask = false; - SetupRenderThread(); + SetupRenderThread(gpu); } -void SoftRenderer::SetThreaded(bool threaded) noexcept +void SoftRenderer::SetThreaded(bool threaded, GPU& gpu) noexcept { if (Threaded != threaded) { Threaded = threaded; - SetupRenderThread(); + SetupRenderThread(gpu); } } -void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const +void SoftRenderer::TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const { u32 vramaddr = (texparam & 0xFFFF) << 3; @@ -167,10 +169,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 1: // A3I5 { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + ((pixel&0x1F)<<1)); + *color = ReadVRAM_TexPal(texpal + ((pixel&0x1F)<<1), gpu); *alpha = ((pixel >> 3) & 0x1C) + (pixel >> 6); } break; @@ -178,12 +180,12 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 2: // 4-color { vramaddr += (((t * width) + s) >> 2); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); pixel >>= ((s & 0x3) << 1); pixel &= 0x3; texpal <<= 3; - *color = ReadVRAM_TexPal(texpal + (pixel<<1)); + *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -191,12 +193,12 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 3: // 16-color { vramaddr += (((t * width) + s) >> 1); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); if (s & 0x1) pixel >>= 4; else pixel &= 0xF; texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + (pixel<<1)); + *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -204,10 +206,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 4: // 256-color { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + (pixel<<1)); + *color = ReadVRAM_TexPal(texpal + (pixel<<1), gpu); *alpha = (pixel==0) ? alpha0 : 31; } break; @@ -221,30 +223,30 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co if (vramaddr >= 0x40000) slot1addr += 0x10000; - u8 val = ReadVRAM_Texture(vramaddr); + u8 val = ReadVRAM_Texture(vramaddr, gpu); val >>= (2 * (s & 0x3)); - u16 palinfo = ReadVRAM_Texture(slot1addr); + u16 palinfo = ReadVRAM_Texture(slot1addr, gpu); u32 paloffset = (palinfo & 0x3FFF) << 2; texpal <<= 4; switch (val & 0x3) { case 0: - *color = ReadVRAM_TexPal(texpal + paloffset); + *color = ReadVRAM_TexPal(texpal + paloffset, gpu); *alpha = 31; break; case 1: - *color = ReadVRAM_TexPal(texpal + paloffset + 2); + *color = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); *alpha = 31; break; case 2: if ((palinfo >> 14) == 1) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2); + u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); + u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -261,8 +263,8 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co } else if ((palinfo >> 14) == 3) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2); + u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); + u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -278,20 +280,20 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co *color = r | g | b; } else - *color = ReadVRAM_TexPal(texpal + paloffset + 4); + *color = ReadVRAM_TexPal(texpal + paloffset + 4, gpu); *alpha = 31; break; case 3: if ((palinfo >> 14) == 2) { - *color = ReadVRAM_TexPal(texpal + paloffset + 6); + *color = ReadVRAM_TexPal(texpal + paloffset + 6, gpu); *alpha = 31; } else if ((palinfo >> 14) == 3) { - u16 color0 = ReadVRAM_TexPal(texpal + paloffset); - u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2); + u16 color0 = ReadVRAM_TexPal(texpal + paloffset, gpu); + u16 color1 = ReadVRAM_TexPal(texpal + paloffset + 2, gpu); u32 r0 = color0 & 0x001F; u32 g0 = color0 & 0x03E0; @@ -320,10 +322,10 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 6: // A5I3 { vramaddr += ((t * width) + s); - u8 pixel = ReadVRAM_Texture(vramaddr); + u8 pixel = ReadVRAM_Texture(vramaddr, gpu); texpal <<= 4; - *color = ReadVRAM_TexPal(texpal + ((pixel&0x7)<<1)); + *color = ReadVRAM_TexPal(texpal + ((pixel&0x7)<<1), gpu); *alpha = (pixel >> 3); } break; @@ -331,7 +333,7 @@ void SoftRenderer::TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* co case 7: // direct color { vramaddr += (((t * width) + s) << 1); - *color = ReadVRAM_Texture(vramaddr); + *color = ReadVRAM_Texture(vramaddr, gpu); *alpha = (*color & 0x8000) ? 31 : 0; } break; @@ -388,7 +390,7 @@ bool DepthTest_LessThan_FrontFacing(s32 dstz, s32 z, u32 dstattr) return false; } -u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) const noexcept +u32 SoftRenderer::AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept { u32 dstalpha = dstcolor >> 24; @@ -399,7 +401,7 @@ u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) const noexce u32 srcG = (srccolor >> 8) & 0x3F; u32 srcB = (srccolor >> 16) & 0x3F; - if (GPU.GPU3D.RenderDispCnt & (1<<3)) + if (gpu3d.RenderDispCnt & (1<<3)) { u32 dstR = dstcolor & 0x3F; u32 dstG = (dstcolor >> 8) & 0x3F; @@ -418,7 +420,7 @@ u32 SoftRenderer::AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) const noexce return srcR | (srcG << 8) | (srcB << 16) | (dstalpha << 24); } -u32 SoftRenderer::RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const +u32 SoftRenderer::RenderPixel(const GPU& gpu, const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const { u8 r, g, b, a; @@ -428,7 +430,7 @@ u32 SoftRenderer::RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s if (blendmode == 2) { - if (GPU.GPU3D.RenderDispCnt & (1<<1)) + if (gpu.GPU3D.RenderDispCnt & (1<<1)) { // highlight mode: color is calculated normally // except all vertex color components are set @@ -442,7 +444,7 @@ u32 SoftRenderer::RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s { // toon mode: vertex color is replaced by toon color - u16 tooncolor = GPU.GPU3D.RenderToonTable[vr >> 1]; + u16 tooncolor = gpu.GPU3D.RenderToonTable[vr >> 1]; vr = (tooncolor << 1) & 0x3E; if (vr) vr++; vg = (tooncolor >> 4) & 0x3E; if (vg) vg++; @@ -450,12 +452,12 @@ u32 SoftRenderer::RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s } } - if ((GPU.GPU3D.RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0)) + if ((gpu.GPU3D.RenderDispCnt & (1<<0)) && (((polygon->TexParam >> 26) & 0x7) != 0)) { u8 tr, tg, tb; u16 tcolor; u8 talpha; - TextureLookup(polygon->TexParam, polygon->TexPalette, s, t, &tcolor, &talpha); + TextureLookup(gpu, polygon->TexParam, polygon->TexPalette, s, t, &tcolor, &talpha); tr = (tcolor << 1) & 0x3E; if (tr) tr++; tg = (tcolor >> 4) & 0x3E; if (tg) tg++; @@ -503,9 +505,9 @@ u32 SoftRenderer::RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s a = polyalpha; } - if ((blendmode == 2) && (GPU.GPU3D.RenderDispCnt & (1<<1))) + if ((blendmode == 2) && (gpu.GPU3D.RenderDispCnt & (1<<1))) { - u16 tooncolor = GPU.GPU3D.RenderToonTable[vr >> 1]; + u16 tooncolor = gpu.GPU3D.RenderToonTable[vr >> 1]; vr = (tooncolor << 1) & 0x3E; if (vr) vr++; vg = (tooncolor >> 4) & 0x3E; if (vg) vg++; @@ -526,7 +528,7 @@ u32 SoftRenderer::RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s return r | (g << 8) | (b << 16) | (a << 24); } -void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow) +void SoftRenderer::PlotTranslucentPixel(const GPU3D& gpu3d, u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow) { u32 dstattr = AttrBuffer[pixeladdr]; u32 attr = (polyattr & 0xE0F0) | ((polyattr >> 8) & 0xFF0000) | (1<<22) | (dstattr & 0xFF001F0F); @@ -556,7 +558,7 @@ void SoftRenderer::PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 pol if (!(dstattr & (1<<15))) attr &= ~(1<<15); - color = AlphaBlend(color, ColorBuffer[pixeladdr], color>>24); + color = AlphaBlend(gpu3d, color, ColorBuffer[pixeladdr], color>>24); if (z != -1) DepthBuffer[pixeladdr] = z; @@ -672,7 +674,7 @@ void SoftRenderer::SetupPolygon(SoftRenderer::RendererPolygon* rp, Polygon* poly } } -void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) +void SoftRenderer::RenderShadowMaskScanline(const GPU3D& gpu3d, RendererPolygon* rp, s32 y) { Polygon* polygon = rp->PolyData; @@ -749,7 +751,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) std::swap(zl, zr); // CHECKME: edge fill rules for swapped opaque shadow mask polygons - if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe) + if ((gpu3d.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu3d.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -777,7 +779,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) rp->SlopeR.EdgeParams(&r_edgelen, &r_edgecov); // CHECKME: edge fill rules for unswapped opaque shadow mask polygons - if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe) + if ((gpu3d.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu3d.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -798,7 +800,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) // similarly, we can perform alpha test early (checkme) if (wireframe) polyalpha = 31; - if (polyalpha <= GPU.GPU3D.RenderAlphaRef) return; + if (polyalpha <= gpu3d.RenderAlphaRef) return; // in wireframe mode, there are special rules for equal Z (TODO) @@ -900,7 +902,7 @@ void SoftRenderer::RenderShadowMaskScanline(RendererPolygon* rp, s32 y) rp->XR = rp->SlopeR.Step(); } -void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) +void SoftRenderer::RenderPolygonScanline(const GPU& gpu, RendererPolygon* rp, s32 y) { Polygon* polygon = rp->PolyData; @@ -984,7 +986,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) // edges are always filled if antialiasing/edgemarking are enabled, // if the pixels are translucent and alpha blending is enabled, or if the polygon is wireframe // checkme: do swapped line polygons exist? - if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe) + if ((gpu.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu.GPU3D.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -1019,7 +1021,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) // * edges are filled if both sides are identical and fully overlapping // edges are always filled if antialiasing/edgemarking are enabled, // if the pixels are translucent and alpha blending is enabled, or if the polygon is wireframe - if ((GPU.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (GPU.GPU3D.RenderDispCnt & (1<<3))) || wireframe) + if ((gpu.GPU3D.RenderDispCnt & ((1<<4)|(1<<5))) || ((polyalpha < 31) && (gpu.GPU3D.RenderDispCnt & (1<<3))) || wireframe) { l_filledge = true; r_filledge = true; @@ -1118,17 +1120,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) s16 s = interpX.Interpolate(sl, sr); s16 t = interpX.Interpolate(tl, tr); - u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t); + u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t); u8 alpha = color >> 24; // alpha test - if (alpha <= GPU.GPU3D.RenderAlphaRef) continue; + if (alpha <= gpu.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - if (GPU.GPU3D.RenderDispCnt & (1<<4)) + if (gpu.GPU3D.RenderDispCnt & (1<<4)) { // anti-aliasing: all edges are rendered @@ -1158,11 +1160,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) else { if (!(polygon->Attr & (1<<11))) z = -1; - PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow); // blend with bottom pixel too, if needed if ((dstattr & 0xF) && (pixeladdr < BufferSize)) - PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); } } @@ -1214,17 +1216,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) s16 s = interpX.Interpolate(sl, sr); s16 t = interpX.Interpolate(tl, tr); - u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t); + u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t); u8 alpha = color >> 24; // alpha test - if (alpha <= GPU.GPU3D.RenderAlphaRef) continue; + if (alpha <= gpu.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - if ((GPU.GPU3D.RenderDispCnt & (1<<4)) && (attr & 0xF)) + if ((gpu.GPU3D.RenderDispCnt & (1<<4)) && (attr & 0xF)) { // anti-aliasing: all edges are rendered @@ -1247,11 +1249,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) else { if (!(polygon->Attr & (1<<11))) z = -1; - PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow); // blend with bottom pixel too, if needed if ((dstattr & 0xF) && (pixeladdr < BufferSize)) - PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); } } @@ -1306,17 +1308,17 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) s16 s = interpX.Interpolate(sl, sr); s16 t = interpX.Interpolate(tl, tr); - u32 color = RenderPixel(polygon, vr>>3, vg>>3, vb>>3, s, t); + u32 color = RenderPixel(gpu, polygon, vr>>3, vg>>3, vb>>3, s, t); u8 alpha = color >> 24; // alpha test - if (alpha <= GPU.GPU3D.RenderAlphaRef) continue; + if (alpha <= gpu.GPU3D.RenderAlphaRef) continue; if (alpha == 31) { u32 attr = polyattr | edge; - if (GPU.GPU3D.RenderDispCnt & (1<<4)) + if (gpu.GPU3D.RenderDispCnt & (1<<4)) { // anti-aliasing: all edges are rendered @@ -1346,11 +1348,11 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) else { if (!(polygon->Attr & (1<<11))) z = -1; - PlotTranslucentPixel(pixeladdr, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr, color, z, polyattr, polygon->IsShadow); // blend with bottom pixel too, if needed if ((dstattr & 0xF) && (pixeladdr < BufferSize)) - PlotTranslucentPixel(pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); + PlotTranslucentPixel(gpu.GPU3D, pixeladdr+BufferSize, color, z, polyattr, polygon->IsShadow); } } @@ -1358,7 +1360,7 @@ void SoftRenderer::RenderPolygonScanline(RendererPolygon* rp, s32 y) rp->XR = rp->SlopeR.Step(); } -void SoftRenderer::RenderScanline(s32 y, int npolys) +void SoftRenderer::RenderScanline(const GPU& gpu, s32 y, int npolys) { for (int i = 0; i < npolys; i++) { @@ -1368,19 +1370,19 @@ void SoftRenderer::RenderScanline(s32 y, int npolys) if (y >= polygon->YTop && (y < polygon->YBottom || (y == polygon->YTop && polygon->YBottom == polygon->YTop))) { if (polygon->IsShadowMask) - RenderShadowMaskScanline(rp, y); + RenderShadowMaskScanline(gpu.GPU3D, rp, y); else - RenderPolygonScanline(rp, y); + RenderPolygonScanline(gpu, rp, y); } } } -u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) const +u32 SoftRenderer::CalculateFogDensity(const GPU3D& gpu3d, u32 pixeladdr) const { u32 z = DepthBuffer[pixeladdr]; u32 densityid, densityfrac; - if (z < GPU.GPU3D.RenderFogOffset) + if (z < gpu3d.RenderFogOffset) { densityid = 0; densityfrac = 0; @@ -1392,8 +1394,8 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) const // on hardware, the final value can overflow the 32-bit range with a shift big enough, // causing fog to 'wrap around' and accidentally apply to larger Z ranges - z -= GPU.GPU3D.RenderFogOffset; - z = (z >> 2) << GPU.GPU3D.RenderFogShift; + z -= gpu3d.RenderFogOffset; + z = (z >> 2) << gpu3d.RenderFogShift; densityid = z >> 17; if (densityid >= 32) @@ -1407,20 +1409,20 @@ u32 SoftRenderer::CalculateFogDensity(u32 pixeladdr) const // checkme (may be too precise?) u32 density = - ((GPU.GPU3D.RenderFogDensityTable[densityid] * (0x20000-densityfrac)) + - (GPU.GPU3D.RenderFogDensityTable[densityid+1] * densityfrac)) >> 17; + ((gpu3d.RenderFogDensityTable[densityid] * (0x20000-densityfrac)) + + (gpu3d.RenderFogDensityTable[densityid+1] * densityfrac)) >> 17; if (density >= 127) density = 128; return density; } -void SoftRenderer::ScanlineFinalPass(s32 y) +void SoftRenderer::ScanlineFinalPass(const GPU3D& gpu3d, s32 y) { // to consider: // clearing all polygon fog flags if the master flag isn't set? // merging all final pass loops into one? - if (GPU.GPU3D.RenderDispCnt & (1<<5)) + if (gpu3d.RenderDispCnt & (1<<5)) { // edge marking // only applied to topmost pixels @@ -1440,7 +1442,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) ((polyid != (AttrBuffer[pixeladdr-ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr-ScanlineWidth])) || ((polyid != (AttrBuffer[pixeladdr+ScanlineWidth] >> 24)) && (z < DepthBuffer[pixeladdr+ScanlineWidth]))) { - u16 edgecolor = GPU.GPU3D.RenderEdgeTable[polyid >> 3]; + u16 edgecolor = gpu3d.RenderEdgeTable[polyid >> 3]; u32 edgeR = (edgecolor << 1) & 0x3E; if (edgeR) edgeR++; u32 edgeG = (edgecolor >> 4) & 0x3E; if (edgeG) edgeG++; u32 edgeB = (edgecolor >> 9) & 0x3E; if (edgeB) edgeB++; @@ -1453,7 +1455,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) } } - if (GPU.GPU3D.RenderDispCnt & (1<<7)) + if (gpu3d.RenderDispCnt & (1<<7)) { // fog @@ -1466,12 +1468,12 @@ void SoftRenderer::ScanlineFinalPass(s32 y) // TODO: check the 'fog alpha glitch with small Z' GBAtek talks about - bool fogcolor = !(GPU.GPU3D.RenderDispCnt & (1<<6)); + bool fogcolor = !(gpu3d.RenderDispCnt & (1<<6)); - u32 fogR = (GPU.GPU3D.RenderFogColor << 1) & 0x3E; if (fogR) fogR++; - u32 fogG = (GPU.GPU3D.RenderFogColor >> 4) & 0x3E; if (fogG) fogG++; - u32 fogB = (GPU.GPU3D.RenderFogColor >> 9) & 0x3E; if (fogB) fogB++; - u32 fogA = (GPU.GPU3D.RenderFogColor >> 16) & 0x1F; + u32 fogR = (gpu3d.RenderFogColor << 1) & 0x3E; if (fogR) fogR++; + u32 fogG = (gpu3d.RenderFogColor >> 4) & 0x3E; if (fogG) fogG++; + u32 fogB = (gpu3d.RenderFogColor >> 9) & 0x3E; if (fogB) fogB++; + u32 fogA = (gpu3d.RenderFogColor >> 16) & 0x1F; for (int x = 0; x < 256; x++) { @@ -1481,7 +1483,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) u32 attr = AttrBuffer[pixeladdr]; if (attr & (1<<15)) { - density = CalculateFogDensity(pixeladdr); + density = CalculateFogDensity(gpu3d, pixeladdr); srccolor = ColorBuffer[pixeladdr]; srcR = srccolor & 0x3F; @@ -1510,7 +1512,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) attr = AttrBuffer[pixeladdr]; if (!(attr & (1<<15))) continue; - density = CalculateFogDensity(pixeladdr); + density = CalculateFogDensity(gpu3d, pixeladdr); srccolor = ColorBuffer[pixeladdr]; srcR = srccolor & 0x3F; @@ -1531,7 +1533,7 @@ void SoftRenderer::ScanlineFinalPass(s32 y) } } - if (GPU.GPU3D.RenderDispCnt & (1<<4)) + if (gpu3d.RenderDispCnt & (1<<4)) { // anti-aliasing @@ -1584,10 +1586,10 @@ void SoftRenderer::ScanlineFinalPass(s32 y) } } -void SoftRenderer::ClearBuffers() +void SoftRenderer::ClearBuffers(const GPU& gpu) { - u32 clearz = ((GPU.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; - u32 polyid = GPU.GPU3D.RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID + u32 clearz = ((gpu.GPU3D.RenderClearAttr2 & 0x7FFF) * 0x200) + 0x1FF; + u32 polyid = gpu.GPU3D.RenderClearAttr1 & 0x3F000000; // this sets the opaque polygonID // fill screen borders for edge marking @@ -1617,17 +1619,17 @@ void SoftRenderer::ClearBuffers() // clear the screen - if (GPU.GPU3D.RenderDispCnt & (1<<14)) + if (gpu.GPU3D.RenderDispCnt & (1<<14)) { - u8 xoff = (GPU.GPU3D.RenderClearAttr2 >> 16) & 0xFF; - u8 yoff = (GPU.GPU3D.RenderClearAttr2 >> 24) & 0xFF; + u8 xoff = (gpu.GPU3D.RenderClearAttr2 >> 16) & 0xFF; + u8 yoff = (gpu.GPU3D.RenderClearAttr2 >> 24) & 0xFF; for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth) { for (int x = 0; x < 256; x++) { - u16 val2 = ReadVRAM_Texture(0x40000 + (yoff << 9) + (xoff << 1)); - u16 val3 = ReadVRAM_Texture(0x60000 + (yoff << 9) + (xoff << 1)); + u16 val2 = ReadVRAM_Texture(0x40000 + (yoff << 9) + (xoff << 1), gpu); + u16 val3 = ReadVRAM_Texture(0x60000 + (yoff << 9) + (xoff << 1), gpu); // TODO: confirm color conversion u32 r = (val2 << 1) & 0x3E; if (r) r++; @@ -1652,13 +1654,13 @@ void SoftRenderer::ClearBuffers() else { // TODO: confirm color conversion - u32 r = (GPU.GPU3D.RenderClearAttr1 << 1) & 0x3E; if (r) r++; - u32 g = (GPU.GPU3D.RenderClearAttr1 >> 4) & 0x3E; if (g) g++; - u32 b = (GPU.GPU3D.RenderClearAttr1 >> 9) & 0x3E; if (b) b++; - u32 a = (GPU.GPU3D.RenderClearAttr1 >> 16) & 0x1F; + u32 r = (gpu.GPU3D.RenderClearAttr1 << 1) & 0x3E; if (r) r++; + u32 g = (gpu.GPU3D.RenderClearAttr1 >> 4) & 0x3E; if (g) g++; + u32 b = (gpu.GPU3D.RenderClearAttr1 >> 9) & 0x3E; if (b) b++; + u32 a = (gpu.GPU3D.RenderClearAttr1 >> 16) & 0x1F; u32 color = r | (g << 8) | (b << 16) | (a << 24); - polyid |= (GPU.GPU3D.RenderClearAttr1 & 0x8000); + polyid |= (gpu.GPU3D.RenderClearAttr1 & 0x8000); for (int y = 0; y < ScanlineWidth*192; y+=ScanlineWidth) { @@ -1673,7 +1675,7 @@ void SoftRenderer::ClearBuffers() } } -void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys) +void SoftRenderer::RenderPolygons(const GPU& gpu, bool threaded, Polygon** polygons, int npolys) { int j = 0; for (int i = 0; i < npolys; i++) @@ -1682,38 +1684,38 @@ void SoftRenderer::RenderPolygons(bool threaded, Polygon** polygons, int npolys) SetupPolygon(&PolygonList[j++], polygons[i]); } - RenderScanline(0, j); + RenderScanline(gpu, 0, j); for (s32 y = 1; y < 192; y++) { - RenderScanline(y, j); - ScanlineFinalPass(y-1); + RenderScanline(gpu, y, j); + ScanlineFinalPass(gpu.GPU3D, y-1); if (threaded) Platform::Semaphore_Post(Sema_ScanlineCount); } - ScanlineFinalPass(191); + ScanlineFinalPass(gpu.GPU3D, 191); if (threaded) Platform::Semaphore_Post(Sema_ScanlineCount); } -void SoftRenderer::VCount144() +void SoftRenderer::VCount144(GPU& gpu) { - if (RenderThreadRunning.load(std::memory_order_relaxed) && !GPU.GPU3D.AbortFrame) + if (RenderThreadRunning.load(std::memory_order_relaxed) && !gpu.GPU3D.AbortFrame) Platform::Semaphore_Wait(Sema_RenderDone); } -void SoftRenderer::RenderFrame() +void SoftRenderer::RenderFrame(GPU& gpu) { - auto textureDirty = GPU.VRAMDirty_Texture.DeriveState(GPU.VRAMMap_Texture, GPU); - auto texPalDirty = GPU.VRAMDirty_TexPal.DeriveState(GPU.VRAMMap_TexPal, GPU); + auto textureDirty = gpu.VRAMDirty_Texture.DeriveState(gpu.VRAMMap_Texture, gpu); + auto texPalDirty = gpu.VRAMDirty_TexPal.DeriveState(gpu.VRAMMap_TexPal, gpu); - bool textureChanged = GPU.MakeVRAMFlat_TextureCoherent(textureDirty); - bool texPalChanged = GPU.MakeVRAMFlat_TexPalCoherent(texPalDirty); + bool textureChanged = gpu.MakeVRAMFlat_TextureCoherent(textureDirty); + bool texPalChanged = gpu.MakeVRAMFlat_TexPalCoherent(texPalDirty); - FrameIdentical = !(textureChanged || texPalChanged) && GPU.GPU3D.RenderFrameIdentical; + FrameIdentical = !(textureChanged || texPalChanged) && gpu.GPU3D.RenderFrameIdentical; if (RenderThreadRunning.load(std::memory_order_relaxed)) { @@ -1721,17 +1723,17 @@ void SoftRenderer::RenderFrame() } else if (!FrameIdentical) { - ClearBuffers(); - RenderPolygons(false, &GPU.GPU3D.RenderPolygonRAM[0], GPU.GPU3D.RenderNumPolygons); + ClearBuffers(gpu); + RenderPolygons(gpu, false, &gpu.GPU3D.RenderPolygonRAM[0], gpu.GPU3D.RenderNumPolygons); } } -void SoftRenderer::RestartFrame() +void SoftRenderer::RestartFrame(GPU& gpu) { - SetupRenderThread(); + SetupRenderThread(gpu); } -void SoftRenderer::RenderThreadFunc() +void SoftRenderer::RenderThreadFunc(GPU& gpu) { for (;;) { @@ -1745,8 +1747,8 @@ void SoftRenderer::RenderThreadFunc() } else { - ClearBuffers(); - RenderPolygons(true, &GPU.GPU3D.RenderPolygonRAM[0], GPU.GPU3D.RenderNumPolygons); + ClearBuffers(gpu); + RenderPolygons(gpu, true, &gpu.GPU3D.RenderPolygonRAM[0], gpu.GPU3D.RenderNumPolygons); } Platform::Semaphore_Post(Sema_RenderDone); diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index f405b2d8..8fb42013 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -29,19 +29,19 @@ namespace melonDS class SoftRenderer : public Renderer3D { public: - SoftRenderer(melonDS::GPU& gpu, bool threaded = false) noexcept; + SoftRenderer(bool threaded = false) noexcept; ~SoftRenderer() override; - void Reset() override; + void Reset(GPU& gpu) override; - void SetThreaded(bool threaded) noexcept; + void SetThreaded(bool threaded, GPU& gpu) noexcept; [[nodiscard]] bool IsThreaded() const noexcept { return Threaded; } - void VCount144() override; - void RenderFrame() override; - void RestartFrame() override; + void VCount144(GPU& gpu) override; + void RenderFrame(GPU& gpu) override; + void RestartFrame(GPU& gpu) override; u32* GetLine(int line) override; - void SetupRenderThread(); + void SetupRenderThread(GPU& gpu); void StopRenderThread(); private: // Notes on the interpolator: @@ -429,16 +429,16 @@ private: }; template - inline T ReadVRAM_Texture(u32 addr) const + inline T ReadVRAM_Texture(u32 addr, const GPU& gpu) const { - return *(T*)&GPU.VRAMFlat_Texture[addr & 0x7FFFF]; + return *(T*)&gpu.VRAMFlat_Texture[addr & 0x7FFFF]; } template - inline T ReadVRAM_TexPal(u32 addr) const + inline T ReadVRAM_TexPal(u32 addr, const GPU& gpu) const { - return *(T*)&GPU.VRAMFlat_TexPal[addr & 0x1FFFF]; + return *(T*)&gpu.VRAMFlat_TexPal[addr & 0x1FFFF]; } - u32 AlphaBlend(u32 srccolor, u32 dstcolor, u32 alpha) const noexcept; + u32 AlphaBlend(const GPU3D& gpu3d, u32 srccolor, u32 dstcolor, u32 alpha) const noexcept; struct RendererPolygon { @@ -452,23 +452,22 @@ private: }; - melonDS::GPU& GPU; RendererPolygon PolygonList[2048]; - void TextureLookup(u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const; - u32 RenderPixel(const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const; - void PlotTranslucentPixel(u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow); + void TextureLookup(const GPU& gpu, u32 texparam, u32 texpal, s16 s, s16 t, u16* color, u8* alpha) const; + u32 RenderPixel(const GPU& gpu, const Polygon* polygon, u8 vr, u8 vg, u8 vb, s16 s, s16 t) const; + void PlotTranslucentPixel(const GPU3D& gpu3d, u32 pixeladdr, u32 color, u32 z, u32 polyattr, u32 shadow); void SetupPolygonLeftEdge(RendererPolygon* rp, s32 y) const; void SetupPolygonRightEdge(RendererPolygon* rp, s32 y) const; void SetupPolygon(RendererPolygon* rp, Polygon* polygon) const; - void RenderShadowMaskScanline(RendererPolygon* rp, s32 y); - void RenderPolygonScanline(RendererPolygon* rp, s32 y); - void RenderScanline(s32 y, int npolys); - u32 CalculateFogDensity(u32 pixeladdr) const; - void ScanlineFinalPass(s32 y); - void ClearBuffers(); - void RenderPolygons(bool threaded, Polygon** polygons, int npolys); + void RenderShadowMaskScanline(const GPU3D& gpu3d, RendererPolygon* rp, s32 y); + void RenderPolygonScanline(const GPU& gpu, RendererPolygon* rp, s32 y); + void RenderScanline(const GPU& gpu, s32 y, int npolys); + u32 CalculateFogDensity(const GPU3D& gpu3d, u32 pixeladdr) const; + void ScanlineFinalPass(const GPU3D& gpu3d, s32 y); + void ClearBuffers(const GPU& gpu); + void RenderPolygons(const GPU& gpu, bool threaded, Polygon** polygons, int npolys); - void RenderThreadFunc(); + void RenderThreadFunc(GPU& gpu); // buffer dimensions are 258x194 to add a offscreen 1px border // which simplifies edge marking tests diff --git a/src/NDS.cpp b/src/NDS.cpp index 5d2a1ce2..4fa5eefd 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -94,7 +94,7 @@ NDS::NDS(NDSArgs&& args, int type) noexcept : ARM9BIOS(args.ARM9BIOS), JIT(*this, args.JIT), SPU(*this, args.BitDepth, args.Interpolation), - GPU(*this), + GPU(*this, std::move(args.Renderer3D)), SPI(*this, std::move(args.Firmware)), RTC(*this), Wifi(*this), @@ -1322,7 +1322,7 @@ void NDS::SetIRQ(u32 cpu, u32 irq) { CPUStop &= ~CPUStop_Sleep; CPUStop |= CPUStop_Wakeup; - GPU.GPU3D.RestartFrame(); + GPU.GPU3D.RestartFrame(GPU); } } } diff --git a/src/NDS.h b/src/NDS.h index e178c4a2..d485bd3c 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -328,6 +328,14 @@ public: Firmware& GetFirmware() { return SPI.GetFirmwareMem()->GetFirmware(); } void SetFirmware(Firmware&& firmware) { SPI.GetFirmwareMem()->SetFirmware(std::move(firmware)); } + const Renderer3D& GetRenderer3D() const noexcept { return GPU.GetRenderer3D(); } + Renderer3D& GetRenderer3D() noexcept { return GPU.GetRenderer3D(); } + void SetRenderer3D(std::unique_ptr&& renderer) noexcept + { + if (renderer != nullptr) + GPU.SetRenderer3D(std::move(renderer)); + } + virtual bool NeedsDirectBoot() const; void SetupDirectBoot(const std::string& romname); virtual void SetupDirectBoot(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 45dc4e06..3eb5db09 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -544,11 +544,11 @@ void EmuThread::run() if (videoRenderer == 0) { // If we're using the software renderer... - NDS->GPU.SetRenderer3D(std::make_unique(NDS->GPU, Config::Threaded3D != 0)); + NDS->GPU.SetRenderer3D(std::make_unique(Config::Threaded3D != 0)); } else { - auto glrenderer = melonDS::GLRenderer::New(NDS->GPU); + auto glrenderer = melonDS::GLRenderer::New(); glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); NDS->GPU.SetRenderer3D(std::move(glrenderer)); } @@ -677,11 +677,11 @@ void EmuThread::run() if (videoRenderer == 0) { // If we're using the software renderer... - NDS->GPU.SetRenderer3D(std::make_unique(NDS->GPU, Config::Threaded3D != 0)); + NDS->GPU.SetRenderer3D(std::make_unique(Config::Threaded3D != 0)); } else { - auto glrenderer = melonDS::GLRenderer::New(NDS->GPU); + auto glrenderer = melonDS::GLRenderer::New(); glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); NDS->GPU.SetRenderer3D(std::move(glrenderer)); } From 24c402af51fe9c0537582173fc48d1ad3daff459 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Fri, 15 Dec 2023 08:54:41 -0500 Subject: [PATCH 076/157] Fix detection of native NDS ARM BIOS images (#1910) * Fix detection of native NDS ARM BIOS images - Instead of checking for built-in BIOS images, now the altered methods check for native ones - The CRC32 must match exactly; patched BIOS images will result in `false` * Encapsulate `NDS::ARM9BIOS` and `ARM7BIOS` - Also compute the checksum only when setting the BIOS --- src/ARMJIT.cpp | 4 ++-- src/MemConstants.h | 2 ++ src/NDS.cpp | 18 ++++++++++++++++-- src/NDS.h | 24 +++++++++++++++++++++--- src/NDSCart.cpp | 12 ++++++------ src/frontend/qt_sdl/main.cpp | 4 ++-- 6 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/ARMJIT.cpp b/src/ARMJIT.cpp index 5e0e2079..c3fcba26 100644 --- a/src/ARMJIT.cpp +++ b/src/ARMJIT.cpp @@ -63,12 +63,12 @@ const u32 CodeRegionSizes[ARMJIT_Memory::memregions_Count] = 0, ITCMPhysicalSize, 0, - sizeof(NDS::ARM9BIOS), + ARM9BIOSSize, MainRAMMaxSize, SharedWRAMSize, 0, 0x100000, - sizeof(NDS::ARM7BIOS), + ARM7BIOSSize, ARM7WRAMSize, 0, 0, diff --git a/src/MemConstants.h b/src/MemConstants.h index 4cf2d36c..e9aa6b2b 100644 --- a/src/MemConstants.h +++ b/src/MemConstants.h @@ -32,6 +32,8 @@ constexpr u32 ARM7BIOSSize = 0x4000; constexpr u32 DSiBIOSSize = 0x10000; constexpr u32 ITCMPhysicalSize = 0x8000; constexpr u32 DTCMPhysicalSize = 0x4000; +constexpr u32 ARM7BIOSCRC32 = 0x1280f0d5; +constexpr u32 ARM9BIOSCRC32 = 0x2ab23573; } #endif // MELONDS_MEMCONSTANTS_H \ No newline at end of file diff --git a/src/NDS.cpp b/src/NDS.cpp index 4fa5eefd..ed68b685 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -92,6 +92,8 @@ NDS::NDS(NDSArgs&& args, int type) noexcept : ConsoleType(type), ARM7BIOS(args.ARM7BIOS), ARM9BIOS(args.ARM9BIOS), + ARM7BIOSNative(CRC32(ARM7BIOS.data(), ARM7BIOS.size()) == ARM7BIOSCRC32), + ARM9BIOSNative(CRC32(ARM9BIOS.data(), ARM9BIOS.size()) == ARM9BIOSCRC32), JIT(*this, args.JIT), SPU(*this, args.BitDepth, args.Interpolation), GPU(*this, std::move(args.Renderer3D)), @@ -270,7 +272,7 @@ bool NDS::NeedsDirectBoot() const return true; // FreeBIOS requires direct boot (it can't boot firmware) - if (IsLoadedARM7BIOSBuiltIn() || IsLoadedARM9BIOSBuiltIn()) + if (!IsLoadedARM9BIOSKnownNative() || !IsLoadedARM7BIOSKnownNative()) return true; return false; @@ -286,7 +288,7 @@ void NDS::SetupDirectBoot() // Copy the Nintendo logo from the NDS ROM header to the ARM9 BIOS if using FreeBIOS // Games need this for DS<->GBA comm to work - if (IsLoadedARM9BIOSBuiltIn()) + if (!IsLoadedARM9BIOSKnownNative()) { memcpy(ARM9BIOS.data() + 0x20, header.NintendoLogo, 0x9C); } @@ -756,6 +758,18 @@ void NDS::LoadBIOS() Reset(); } +void NDS::SetARM7BIOS(const std::array& bios) noexcept +{ + ARM7BIOS = bios; + ARM7BIOSNative = CRC32(ARM7BIOS.data(), ARM7BIOS.size()) == ARM7BIOSCRC32; +} + +void NDS::SetARM9BIOS(const std::array& bios) noexcept +{ + ARM9BIOS = bios; + ARM9BIOSNative = CRC32(ARM9BIOS.data(), ARM9BIOS.size()) == ARM9BIOSCRC32; +} + u64 NDS::NextTarget() { u64 minEvent = UINT64_MAX; diff --git a/src/NDS.h b/src/NDS.h index d485bd3c..23b1f888 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -39,6 +39,7 @@ #include "MemRegion.h" #include "ARMJIT_Memory.h" #include "ARM.h" +#include "CRC32.h" #include "DMA.h" #include "FreeBIOS.h" @@ -227,7 +228,7 @@ private: bool EnableJIT; #endif -public: +public: // TODO: Encapsulate the rest of these members int ConsoleType; int CurCPU; @@ -261,8 +262,14 @@ public: u8 ROMSeed0[2*8]; u8 ROMSeed1[2*8]; +protected: + // These BIOS arrays should be declared *before* the component objects (JIT, SPI, etc.) + // so that they're initialized before the component objects' constructors run. std::array ARM9BIOS; std::array ARM7BIOS; + bool ARM9BIOSNative; + bool ARM7BIOSNative; +public: // TODO: Encapsulate the rest of these members u16 ARM7BIOSProt; u8* MainRAM; @@ -310,8 +317,19 @@ public: void SetARM7RegionTimings(u32 addrstart, u32 addrend, u32 region, int buswidth, int nonseq, int seq); void LoadBIOS(); - [[nodiscard]] bool IsLoadedARM9BIOSBuiltIn() const noexcept { return ARM9BIOS == bios_arm9_bin; } - [[nodiscard]] bool IsLoadedARM7BIOSBuiltIn() const noexcept { return ARM7BIOS == bios_arm7_bin; } + + /// @return \c true if the loaded ARM9 BIOS image is a known dump + /// of a native DS-compatible ARM9 BIOS. + [[nodiscard]] bool IsLoadedARM9BIOSKnownNative() const noexcept { return ARM9BIOSNative; } + [[nodiscard]] const std::array& GetARM9BIOS() const noexcept { return ARM9BIOS; } + void SetARM9BIOS(const std::array& bios) noexcept; + + [[nodiscard]] const std::array& GetARM7BIOS() const noexcept { return ARM7BIOS; } + void SetARM7BIOS(const std::array& bios) noexcept; + + /// @return \c true if the loaded ARM7 BIOS image is a known dump + /// of a native DS-compatible ARM9 BIOS. + [[nodiscard]] bool IsLoadedARM7BIOSKnownNative() const noexcept { return ARM7BIOSNative; } [[nodiscard]] NDSCart::CartCommon* GetNDSCart() { return NDSCartSlot.GetCart(); } [[nodiscard]] const NDSCart::CartCommon* GetNDSCart() const { return NDSCartSlot.GetCart(); } diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 4474f97f..1f2fe692 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -111,7 +111,7 @@ void NDSCartSlot::Key1_ApplyKeycode(u32* keycode, u32 mod) noexcept void NDSCartSlot::Key1_LoadKeyBuf(bool dsi, const u8 *bios, u32 biosLength) noexcept { - if (!NDS.IsLoadedARM7BIOSBuiltIn()) + if (NDS.IsLoadedARM7BIOSKnownNative()) { u32 expected_bios_length = dsi ? 0x10000 : 0x4000; if (biosLength != expected_bios_length) @@ -261,7 +261,7 @@ int CartCommon::ROMCommandStart(NDS& nds, NDSCartSlot& cartslot, const u8* cmd, case 0x3C: CmdEncMode = 1; - cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, &nds.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); + cartslot.Key1_InitKeycode(false, *(u32*)&ROM[0xC], 2, 2, nds.GetARM7BIOS().data(), ARM7BIOSSize); DSiMode = false; return 0; @@ -1540,10 +1540,10 @@ void NDSCartSlot::DecryptSecureArea(u8* out) noexcept memcpy(out, &cartrom[arm9base], 0x800); - Key1_InitKeycode(false, gamecode, 2, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, gamecode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); Key1_Decrypt((u32*)&out[0]); - Key1_InitKeycode(false, gamecode, 3, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, gamecode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); for (u32 i = 0; i < 0x800; i += 8) Key1_Decrypt((u32*)&out[i]); @@ -1695,11 +1695,11 @@ void NDSCartSlot::SetCart(std::unique_ptr&& cart) noexcept strncpy((char*)&cartrom[header.ARM9ROMOffset], "encryObj", 8); - Key1_InitKeycode(false, romparams.GameCode, 3, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, romparams.GameCode, 3, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); for (u32 i = 0; i < 0x800; i += 8) Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset + i]); - Key1_InitKeycode(false, romparams.GameCode, 2, 2, &NDS.ARM7BIOS[0], sizeof(NDS::ARM7BIOS)); + Key1_InitKeycode(false, romparams.GameCode, 2, 2, NDS.GetARM7BIOS().data(), ARM7BIOSSize); Key1_Encrypt((u32*)&cartrom[header.ARM9ROMOffset]); Log(LogLevel::Debug, "Re-encrypted cart secure area\n"); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 3eb5db09..ba5fecb9 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -395,8 +395,8 @@ bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAAr }; NDS->SetJITArgs(Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt); #endif - NDS->ARM7BIOS = *arm7bios; - NDS->ARM9BIOS = *arm9bios; + NDS->SetARM7BIOS(*arm7bios); + NDS->SetARM9BIOS(*arm9bios); NDS->SetFirmware(std::move(*firmware)); NDS->SetNDSCart(std::move(nextndscart)); NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); From e1821d00235d33b2fe384fffa7e34b00b7af3ec9 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Fri, 15 Dec 2023 08:56:10 -0500 Subject: [PATCH 077/157] Simplify the SRAM's representation in `NDSCartArgs` (#1914) * Simplify the SRAM's representation in `NDSCartArgs` - I overthought this one. - I could've just checked `args && args->SRAM`, but then some other poor bastard might make this mistake. - Don't mix `pair`, `optional`, and `unique_ptr` all at once, kids. * Fix a `nullptr` read --- src/NDSCart.cpp | 9 +++++++-- src/NDSCart.h | 9 +++++++-- src/frontend/qt_sdl/ROMManager.cpp | 4 ++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index 1f2fe692..a5fe3180 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -404,7 +404,11 @@ CartRetail::CartRetail(std::unique_ptr&& rom, u32 len, u32 chipid, bool ba { // Copy in what we can, truncate the rest. SRAM = std::make_unique(SRAMLength); memset(SRAM.get(), 0xFF, SRAMLength); - memcpy(SRAM.get(), sram.get(), std::min(sramlen, SRAMLength)); + + if (sram) + { // If we have anything to copy, that is. + memcpy(SRAM.get(), sram.get(), std::min(sramlen, SRAMLength)); + } } } @@ -1650,7 +1654,8 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen } std::unique_ptr cart; - auto [sram, sramlen] = args ? std::move(*args->SRAM) : std::make_pair(nullptr, 0); + std::unique_ptr sram = args ? std::move(args->SRAM) : nullptr; + u32 sramlen = args ? args->SRAMLength : 0; if (homebrew) cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, args ? std::move(args->SDCard) : std::nullopt); else if (gametitle[0] == 0 && !strncmp("SD/TF-NDS", gametitle + 1, 9) && gamecode == 0x414D5341) diff --git a/src/NDSCart.h b/src/NDSCart.h index 7d482ab0..78439a2c 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -62,9 +62,14 @@ struct NDSCartArgs std::optional SDCard = std::nullopt; /// Save RAM to load into the cartridge. - /// If \c nullopt, then the cart's SRAM buffer will be empty. + /// If \c nullptr, then the cart's SRAM buffer will be empty. /// Ignored for homebrew ROMs. - std::optional, u32>> SRAM = std::nullopt; + std::unique_ptr SRAM = nullptr; + + /// The length of the buffer in SRAM. + /// If 0, then the cart's SRAM buffer will be empty. + /// Ignored for homebrew ROMs. + u32 SRAMLength = 0; }; // CartCommon -- base code shared by all cart types diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index b065ad1a..a20af206 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -1316,8 +1316,8 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) // the ROM is homebrew or not. // So this is the card we *would* load if the ROM were homebrew. .SDCard = GetDLDISDCardArgs(), - - .SRAM = std::make_pair(std::move(savedata), savelen), + .SRAM = std::move(savedata), + .SRAMLength = savelen, }; auto cart = NDSCart::ParseROM(std::move(filedata), filelen, std::move(cartargs)); From eedb0ba478dea7ffebd347a37acdbf19b095066b Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Fri, 15 Dec 2023 14:52:35 -0500 Subject: [PATCH 078/157] Add a call to `std::move` that I missed (#1917) --- src/GBACart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 6cd6e39d..826444b6 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -758,7 +758,7 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen std::unique_ptr cartsram; try { - cartsram = sramdata ? std::make_unique(sramlen) : nullptr; + cartsram = std::move(sramdata) ? std::make_unique(sramlen) : nullptr; } catch (const std::bad_alloc& e) { From 1bec2a92935d7cb2e47088d00d89b69f25c82967 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Fri, 15 Dec 2023 18:05:43 -0500 Subject: [PATCH 079/157] Fix an incorrect use of `std::move` (#1919) - When I adapted `GBACart::ParseROM` to use `unique_ptr` instead of a plain pointer, I forgot to remove the code that copied the SRAM data - That code was made unnecessary because of the move --- src/GBACart.cpp | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 826444b6..37fa0449 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -755,24 +755,6 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen auto [cartrom, cartromsize] = PadToPowerOf2(std::move(romdata), romlen); - std::unique_ptr cartsram; - try - { - cartsram = std::move(sramdata) ? std::make_unique(sramlen) : nullptr; - } - catch (const std::bad_alloc& e) - { - Log(LogLevel::Error, "GBACart: failed to allocate memory for ROM (%d bytes)\n", cartromsize); - - return nullptr; - } - - if (cartsram) - { - memset(cartsram.get(), 0, sramlen); - memcpy(cartsram.get(), sramdata.get(), sramlen); - } - char gamecode[5] = { '\0' }; memcpy(&gamecode, cartrom.get() + 0xAC, 4); @@ -790,9 +772,9 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen std::unique_ptr cart; if (solarsensor) - cart = std::make_unique(std::move(cartrom), cartromsize, std::move(cartsram), sramlen); + cart = std::make_unique(std::move(cartrom), cartromsize, std::move(sramdata), sramlen); else - cart = std::make_unique(std::move(cartrom), cartromsize, std::move(cartsram), sramlen); + cart = std::make_unique(std::move(cartrom), cartromsize, std::move(sramdata), sramlen); cart->Reset(); From 4b4239de62b4ca0ab3c52c36227380eacb657813 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Tue, 19 Dec 2023 09:15:35 -0500 Subject: [PATCH 080/157] Set `NDS::EnableJIT` in the constructor (#1921) --- src/NDS.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NDS.cpp b/src/NDS.cpp index ed68b685..3582a3b5 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -105,6 +105,7 @@ NDS::NDS(NDSArgs&& args, int type) noexcept : AREngine(*this), ARM9(*this, args.GDB, args.JIT.has_value()), ARM7(*this, args.GDB, args.JIT.has_value()), + EnableJIT(args.JIT.has_value()), DMAs { DMA(0, 0, *this), DMA(0, 1, *this), From 01f8ad009ef3610fa4b58f57bc0a544b7793c5bd Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Wed, 20 Dec 2023 08:25:49 -0500 Subject: [PATCH 081/157] Wrap the `EnableJIT` initialization in an `#ifdef` (#1922) --- src/NDS.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/NDS.cpp b/src/NDS.cpp index 3582a3b5..7c176fae 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -105,7 +105,9 @@ NDS::NDS(NDSArgs&& args, int type) noexcept : AREngine(*this), ARM9(*this, args.GDB, args.JIT.has_value()), ARM7(*this, args.GDB, args.JIT.has_value()), +#ifdef JIT_ENABLED EnableJIT(args.JIT.has_value()), +#endif DMAs { DMA(0, 0, *this), DMA(0, 1, *this), From 6c6318b63bdaec9dedf5bf3e8a9ae1db1df8058f Mon Sep 17 00:00:00 2001 From: Daniel Simon Date: Wed, 20 Dec 2023 00:00:13 +0100 Subject: [PATCH 082/157] Fix generic icon when using Wayland --- src/frontend/qt_sdl/main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index ba5fecb9..646777cc 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -3477,6 +3477,10 @@ MelonApplication::MelonApplication(int& argc, char** argv) #ifndef __APPLE__ setWindowIcon(QIcon(":/melon-icon")); #endif + +#ifdef !defined(__APPLE__) && !defined(__WIN32__) + setDesktopFileName(QString("net.kuribo64.melonDS")); +#endif } bool MelonApplication::event(QEvent *event) From fd3c34973595573d5dea9aeba0a251a37264d30d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 20 Dec 2023 21:33:48 +0100 Subject: [PATCH 083/157] Check Q_OS_UNIX define instead --- src/frontend/qt_sdl/main.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 646777cc..9ada19b4 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -3474,12 +3474,11 @@ void emuStop() MelonApplication::MelonApplication(int& argc, char** argv) : QApplication(argc, argv) { -#ifndef __APPLE__ +#if !defined(Q_OS_APPLE) setWindowIcon(QIcon(":/melon-icon")); -#endif - -#ifdef !defined(__APPLE__) && !defined(__WIN32__) - setDesktopFileName(QString("net.kuribo64.melonDS")); + #if defined(Q_OS_UNIX) + setDesktopFileName(QString("net.kuribo64.melonDS")); + #endif #endif } From ed650f2b468ab951c7606d232f0a34e97c51eb77 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Thu, 21 Dec 2023 21:43:57 +0100 Subject: [PATCH 084/157] call Reset on 3D renderer object --- src/GPU3D.cpp | 9 +++++++++ src/GPU3D.h | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index ac29a1e4..47877021 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -146,6 +146,12 @@ GPU3D::GPU3D(melonDS::NDS& nds, std::unique_ptr&& renderer) noexcept { } +void GPU3D::SetCurrentRenderer(std::unique_ptr&& renderer) noexcept +{ + CurrentRenderer = std::move(renderer); + CurrentRenderer->Reset(NDS.GPU); +} + void GPU3D::ResetRenderingState() noexcept { RenderNumPolygons = 0; @@ -282,6 +288,9 @@ void GPU3D::Reset() noexcept FlushAttributes = 0; RenderXPos = 0; + + if (CurrentRenderer) + CurrentRenderer->Reset(NDS.GPU); } void GPU3D::DoSavestate(Savestate* file) noexcept diff --git a/src/GPU3D.h b/src/GPU3D.h index 0c900c6c..7c42eeb5 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -117,7 +117,7 @@ public: [[nodiscard]] bool IsRendererAccelerated() const noexcept; [[nodiscard]] Renderer3D& GetCurrentRenderer() noexcept { return *CurrentRenderer; } [[nodiscard]] const Renderer3D& GetCurrentRenderer() const noexcept { return *CurrentRenderer; } - void SetCurrentRenderer(std::unique_ptr&& renderer) noexcept { CurrentRenderer = std::move(renderer); } + void SetCurrentRenderer(std::unique_ptr&& renderer) noexcept; u8 Read8(u32 addr) noexcept; u16 Read16(u32 addr) noexcept; From 084747abc52b22ace7ae02b895b343a789af7c67 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Thu, 21 Dec 2023 22:15:12 +0100 Subject: [PATCH 085/157] Reset DS object directly after creation --- src/frontend/qt_sdl/main.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 9ada19b4..0d775a58 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -335,6 +335,7 @@ bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAAr NDS::Current = nullptr; NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart)); + NDS->Reset(); NDS::Current = NDS.get(); return NDS != nullptr; From 752b37ed824ceb521f61f1a0c79b022efd9cef12 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 22 Dec 2023 01:35:45 +0100 Subject: [PATCH 086/157] Attempt to get rid of leftover QSharedMemory instance after crash --- src/frontend/qt_sdl/Platform.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 46305821..6fe87ac4 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -66,12 +66,25 @@ void IPCInit() IPCBuffer = new QSharedMemory("melonIPC"); +#if !defined(Q_OS_WINDOWS) + // QSharedMemory instances can be left over from crashed processes on UNIX platforms. + // To prevent melonDS thinking there's another instance, we attach and then immediately detach from the + // shared memory. If no other process was actually using it, it'll be destroyed and we'll have a clean + // shared memory buffer after creating it again below. + if (IPCBuffer->attach()) + { + IPCBuffer->detach(); + delete IPCBuffer; + IPCBuffer = new QSharedMemory("melonIPC"); + } +#endif + if (!IPCBuffer->attach()) { Log(LogLevel::Info, "IPC sharedmem doesn't exist. creating\n"); if (!IPCBuffer->create(1024)) { - Log(LogLevel::Error, "IPC sharedmem create failed :(\n"); + Log(LogLevel::Error, "IPC sharedmem create failed: %s\n", IPCBuffer->errorString().toStdString().c_str()); delete IPCBuffer; IPCBuffer = nullptr; return; From 521fc249a3b95b66147be848a63d29e4425d4c5d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 22 Dec 2023 02:17:26 +0100 Subject: [PATCH 087/157] Don't try to call transferLayout on non-GL ScreenPanel --- src/frontend/qt_sdl/main.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 0d775a58..cd84988a 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -202,7 +202,8 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); - static_cast(mainWindow->panel)->transferLayout(this); + auto glPanel = dynamic_cast(mainWindow->panel); + if (glPanel) glPanel->transferLayout(this); } std::unique_ptr EmuThread::CreateConsole( From 989b93c92ab03985426619f6fc4d0076fa973406 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Fri, 22 Dec 2023 23:30:59 +0100 Subject: [PATCH 088/157] do not access NDS object emulation is paused --- src/frontend/qt_sdl/main.cpp | 116 +++++++++++++++++------------------ 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index cd84988a..725a75f0 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -592,69 +592,69 @@ void EmuThread::run() if (Input::HotkeyPressed(HK_SwapScreens)) emit swapScreensToggle(); if (Input::HotkeyPressed(HK_SwapScreenEmphasis)) emit screenEmphasisToggle(); - if (Input::HotkeyPressed(HK_SolarSensorDecrease)) - { - int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorDown, true); - if (level != -1) - { - char msg[64]; - sprintf(msg, "Solar sensor level: %d", level); - OSD::AddMessage(0, msg); - } - } - if (Input::HotkeyPressed(HK_SolarSensorIncrease)) - { - int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorUp, true); - if (level != -1) - { - char msg[64]; - sprintf(msg, "Solar sensor level: %d", level); - OSD::AddMessage(0, msg); - } - } - - if (NDS->ConsoleType == 1) - { - DSi& dsi = static_cast(*NDS); - double currentTime = SDL_GetPerformanceCounter() * perfCountsSec; - - // Handle power button - if (Input::HotkeyDown(HK_PowerButton)) - { - dsi.I2C.GetBPTWL()->SetPowerButtonHeld(currentTime); - } - else if (Input::HotkeyReleased(HK_PowerButton)) - { - dsi.I2C.GetBPTWL()->SetPowerButtonReleased(currentTime); - } - - // Handle volume buttons - if (Input::HotkeyDown(HK_VolumeUp)) - { - dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); - } - else if (Input::HotkeyReleased(HK_VolumeUp)) - { - dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); - } - - if (Input::HotkeyDown(HK_VolumeDown)) - { - dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); - } - else if (Input::HotkeyReleased(HK_VolumeDown)) - { - dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); - } - - dsi.I2C.GetBPTWL()->ProcessVolumeSwitchInput(currentTime); - } - if (EmuRunning == emuStatus_Running || EmuRunning == emuStatus_FrameStep) { EmuStatus = emuStatus_Running; if (EmuRunning == emuStatus_FrameStep) EmuRunning = emuStatus_Paused; + if (Input::HotkeyPressed(HK_SolarSensorDecrease)) + { + int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorDown, true); + if (level != -1) + { + char msg[64]; + sprintf(msg, "Solar sensor level: %d", level); + OSD::AddMessage(0, msg); + } + } + if (Input::HotkeyPressed(HK_SolarSensorIncrease)) + { + int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorUp, true); + if (level != -1) + { + char msg[64]; + sprintf(msg, "Solar sensor level: %d", level); + OSD::AddMessage(0, msg); + } + } + + if (NDS->ConsoleType == 1) + { + DSi& dsi = static_cast(*NDS); + double currentTime = SDL_GetPerformanceCounter() * perfCountsSec; + + // Handle power button + if (Input::HotkeyDown(HK_PowerButton)) + { + dsi.I2C.GetBPTWL()->SetPowerButtonHeld(currentTime); + } + else if (Input::HotkeyReleased(HK_PowerButton)) + { + dsi.I2C.GetBPTWL()->SetPowerButtonReleased(currentTime); + } + + // Handle volume buttons + if (Input::HotkeyDown(HK_VolumeUp)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); + } + else if (Input::HotkeyReleased(HK_VolumeUp)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); + } + + if (Input::HotkeyDown(HK_VolumeDown)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); + } + else if (Input::HotkeyReleased(HK_VolumeDown)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); + } + + dsi.I2C.GetBPTWL()->ProcessVolumeSwitchInput(currentTime); + } + // update render settings if needed // HACK: // once the fast forward hotkey is released, we need to update vsync From de4ae9dd92761b552f23401a3b1336a0b4ab8641 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sat, 23 Dec 2023 23:59:59 +0100 Subject: [PATCH 089/157] fix possible crash --- src/frontend/qt_sdl/LocalMP.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/LocalMP.cpp b/src/frontend/qt_sdl/LocalMP.cpp index 466f90cf..7ea98686 100644 --- a/src/frontend/qt_sdl/LocalMP.cpp +++ b/src/frontend/qt_sdl/LocalMP.cpp @@ -248,7 +248,9 @@ bool Init() Log(LogLevel::Info, "MP sharedmem doesn't exist. creating\n"); if (!MPQueue->create(kQueueSize)) { - Log(LogLevel::Error, "MP sharedmem create failed :(\n"); + Log(LogLevel::Error, "MP sharedmem create failed :( (%d)\n", MPQueue->error()); + delete MPQueue; + MPQueue = nullptr; return false; } @@ -328,6 +330,7 @@ void SetRecvTimeout(int timeout) void Begin() { + if (!MPQueue) return; MPQueue->lock(); MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); PacketReadOffset = header->PacketWriteOffset; @@ -340,6 +343,7 @@ void Begin() void End() { + if (!MPQueue) return; MPQueue->lock(); MPQueueHeader* header = (MPQueueHeader*)MPQueue->data(); //SemReset(InstanceID); @@ -421,6 +425,7 @@ void FIFOWrite(int fifo, void* buf, int len) int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) { + if (!MPQueue) return 0; MPQueue->lock(); u8* data = (u8*)MPQueue->data(); MPQueueHeader* header = (MPQueueHeader*)&data[0]; @@ -476,6 +481,7 @@ int SendPacketGeneric(u32 type, u8* packet, int len, u64 timestamp) int RecvPacketGeneric(u8* packet, bool block, u64* timestamp) { + if (!MPQueue) return 0; for (;;) { if (!SemWait(InstanceID, block ? RecvTimeout : 0)) @@ -552,6 +558,8 @@ int SendAck(u8* packet, int len, u64 timestamp) int RecvHostPacket(u8* packet, u64* timestamp) { + if (!MPQueue) return -1; + if (LastHostID != -1) { // check if the host is still connected @@ -571,6 +579,8 @@ int RecvHostPacket(u8* packet, u64* timestamp) u16 RecvReplies(u8* packets, u64 timestamp, u16 aidmask) { + if (!MPQueue) return 0; + u16 ret = 0; u16 myinstmask = (1 << InstanceID); u16 curinstmask; From f580d20a7b39f4698d0c5f32ac64ede8a5ed3915 Mon Sep 17 00:00:00 2001 From: Samuel Magnan Date: Sat, 23 Dec 2023 19:09:43 -0500 Subject: [PATCH 090/157] Support GBA .sav file with appended .rtc (#1927) * Support GBA .sav file with appended .rtc * Change comment --- src/GBACart.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GBACart.cpp b/src/GBACart.cpp index 37fa0449..1be50e75 100644 --- a/src/GBACart.cpp +++ b/src/GBACart.cpp @@ -193,6 +193,7 @@ void CartGame::SetupSave(u32 type) SRAMType = S_FLASH512K; break; case 128*1024: + case (128*1024 + 0x10): // .sav file with appended real time clock data (ex: emulator mGBA) SRAMType = S_FLASH1M; break; case 0: From 6a1232b9a9791f7cb54d0bbde6b7d5bb06399c59 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Sun, 24 Dec 2023 15:11:30 +0100 Subject: [PATCH 091/157] move MainWindow and Screen stuff to separate files; WIP --- src/frontend/qt_sdl/CMakeLists.txt | 4 +- src/frontend/qt_sdl/Input.h | 2 + src/frontend/qt_sdl/Screen.cpp | 564 +++++++ src/frontend/qt_sdl/Screen.h | 155 ++ src/frontend/qt_sdl/Window.cpp | 2047 ++++++++++++++++++++++++ src/frontend/qt_sdl/Window.h | 239 +++ src/frontend/qt_sdl/main.cpp | 2373 +--------------------------- src/frontend/qt_sdl/main.h | 300 +--- 8 files changed, 3019 insertions(+), 2665 deletions(-) create mode 100644 src/frontend/qt_sdl/Screen.cpp create mode 100644 src/frontend/qt_sdl/Screen.h create mode 100644 src/frontend/qt_sdl/Window.cpp create mode 100644 src/frontend/qt_sdl/Window.h diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 40815638..9da2c91d 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -5,6 +5,8 @@ include(FixInterfaceIncludes) set(SOURCES_QT_SDL main.cpp main_shaders.h + Screen.cpp + Window.cpp CheatsDialog.cpp Config.cpp DateTimeDialog.cpp @@ -38,7 +40,7 @@ set(SOURCES_QT_SDL SaveManager.cpp CameraManager.cpp AudioInOut.cpp - + ArchiveUtil.h ArchiveUtil.cpp diff --git a/src/frontend/qt_sdl/Input.h b/src/frontend/qt_sdl/Input.h index 366c9c39..2dfa4a77 100644 --- a/src/frontend/qt_sdl/Input.h +++ b/src/frontend/qt_sdl/Input.h @@ -19,6 +19,8 @@ #ifndef INPUT_H #define INPUT_H +#include + #include "types.h" namespace Input diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp new file mode 100644 index 00000000..6de4d78e --- /dev/null +++ b/src/frontend/qt_sdl/Screen.cpp @@ -0,0 +1,564 @@ +/* + 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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#ifndef _WIN32 +#ifndef APPLE +#include +#endif +#endif + +#include "main.h" + +#include "NDS.h" +#include "Platform.h" +#include "Config.h" + +//#include "main_shaders.h" + +#include "OSD.h" + +using namespace melonDS; + + +/*const struct { int id; float ratio; const char* label; } aspectRatios[] = +{ + { 0, 1, "4:3 (native)" }, + { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"}, + { 1, (16.f / 9) / (4.f / 3), "16:9" }, + { 2, (21.f / 9) / (4.f / 3), "21:9" }, + { 3, 0, "window" } +}; +int AspectRatiosNum = sizeof(aspectRatios) / sizeof(aspectRatios[0]);*/ + +// TEMP +extern MainWindow* mainWindow; +extern EmuThread* emuThread; +extern bool RunningSomething; +extern int autoScreenSizing; + +extern int videoRenderer; +extern bool videoSettingsDirty; + + +ScreenHandler::ScreenHandler(QWidget* widget) +{ + widget->setMouseTracking(true); + widget->setAttribute(Qt::WA_AcceptTouchEvents); + QTimer* mouseTimer = setupMouseTimer(); + widget->connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) widget->setCursor(Qt::BlankCursor);}); +} + +ScreenHandler::~ScreenHandler() +{ + mouseTimer->stop(); + delete mouseTimer; +} + +void ScreenHandler::screenSetupLayout(int w, int h) +{ + int sizing = Config::ScreenSizing; + if (sizing == 3) sizing = autoScreenSizing; + + float aspectTop, aspectBot; + + for (auto ratio : aspectRatios) + { + if (ratio.id == Config::ScreenAspectTop) + aspectTop = ratio.ratio; + if (ratio.id == Config::ScreenAspectBot) + aspectBot = ratio.ratio; + } + + if (aspectTop == 0) + aspectTop = ((float) w / h) / (4.f / 3.f); + + if (aspectBot == 0) + aspectBot = ((float) w / h) / (4.f / 3.f); + + Frontend::SetupScreenLayout(w, h, + static_cast(Config::ScreenLayout), + static_cast(Config::ScreenRotation), + static_cast(sizing), + Config::ScreenGap, + Config::IntegerScaling != 0, + Config::ScreenSwap != 0, + aspectTop, + aspectBot); + + numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind); +} + +QSize ScreenHandler::screenGetMinSize(int factor = 1) +{ + bool isHori = (Config::ScreenRotation == Frontend::screenRot_90Deg + || Config::ScreenRotation == Frontend::screenRot_270Deg); + int gap = Config::ScreenGap * factor; + + int w = 256 * factor; + int h = 192 * factor; + + if (Config::ScreenSizing == Frontend::screenSizing_TopOnly + || Config::ScreenSizing == Frontend::screenSizing_BotOnly) + { + return QSize(w, h); + } + + if (Config::ScreenLayout == Frontend::screenLayout_Natural) + { + if (isHori) + return QSize(h+gap+h, w); + else + return QSize(w, h+gap+h); + } + else if (Config::ScreenLayout == Frontend::screenLayout_Vertical) + { + if (isHori) + return QSize(h, w+gap+w); + else + return QSize(w, h+gap+h); + } + else if (Config::ScreenLayout == Frontend::screenLayout_Horizontal) + { + if (isHori) + return QSize(h+gap+h, w); + else + return QSize(w+gap+w, h); + } + else // hybrid + { + if (isHori) + return QSize(h+gap+h, 3*w + (int)ceil((4*gap) / 3.0)); + else + return QSize(3*w + (int)ceil((4*gap) / 3.0), h+gap+h); + } +} + +void ScreenHandler::screenOnMousePress(QMouseEvent* event) +{ + event->accept(); + if (event->button() != Qt::LeftButton) return; + + int x = event->pos().x(); + int y = event->pos().y(); + + if (Frontend::GetTouchCoords(x, y, false)) + { + touching = true; + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } +} + +void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) +{ + event->accept(); + if (event->button() != Qt::LeftButton) return; + + if (touching) + { + touching = false; + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); + } +} + +void ScreenHandler::screenOnMouseMove(QMouseEvent* event) +{ + event->accept(); + + showCursor(); + + if (!(event->buttons() & Qt::LeftButton)) return; + if (!touching) return; + + int x = event->pos().x(); + int y = event->pos().y(); + + if (Frontend::GetTouchCoords(x, y, true)) + { + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } +} + +void ScreenHandler::screenHandleTablet(QTabletEvent* event) +{ + event->accept(); + + switch(event->type()) + { + case QEvent::TabletPress: + case QEvent::TabletMove: + { + int x = event->x(); + int y = event->y(); + + if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) + { + touching = true; + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } + } + break; + case QEvent::TabletRelease: + if (touching) + { + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); + touching = false; + } + break; + default: + break; + } +} + +void ScreenHandler::screenHandleTouch(QTouchEvent* event) +{ + event->accept(); + + switch(event->type()) + { + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + if (event->touchPoints().length() > 0) + { + QPointF lastPosition = event->touchPoints().first().lastPos(); + int x = (int)lastPosition.x(); + int y = (int)lastPosition.y(); + + if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) + { + touching = true; + assert(emuThread->NDS != nullptr); + emuThread->NDS->TouchScreen(x, y); + } + } + break; + case QEvent::TouchEnd: + if (touching) + { + assert(emuThread->NDS != nullptr); + emuThread->NDS->ReleaseScreen(); + touching = false; + } + break; + default: + break; + } +} + +void ScreenHandler::showCursor() +{ + mainWindow->panelWidget->setCursor(Qt::ArrowCursor); + mouseTimer->start(); +} + +QTimer* ScreenHandler::setupMouseTimer() +{ + mouseTimer = new QTimer(); + mouseTimer->setSingleShot(true); + mouseTimer->setInterval(Config::MouseHideSeconds*1000); + mouseTimer->start(); + + return mouseTimer; +} + +ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenHandler(this) +{ + screen[0] = QImage(256, 192, QImage::Format_RGB32); + screen[1] = QImage(256, 192, QImage::Format_RGB32); + + screenTrans[0].reset(); + screenTrans[1].reset(); + + OSD::Init(false); +} + +ScreenPanelNative::~ScreenPanelNative() +{ + OSD::DeInit(); +} + +void ScreenPanelNative::setupScreenLayout() +{ + int w = width(); + int h = height(); + + screenSetupLayout(w, h); + + for (int i = 0; i < numScreens; i++) + { + float* mtx = screenMatrix[i]; + screenTrans[i].setMatrix(mtx[0], mtx[1], 0.f, + mtx[2], mtx[3], 0.f, + mtx[4], mtx[5], 1.f); + } +} + +void ScreenPanelNative::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + + // fill background + painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0)); + + if (emuThread->emuIsActive()) + { + assert(emuThread->NDS != nullptr); + emuThread->FrontBufferLock.lock(); + int frontbuf = emuThread->FrontBuffer; + if (!emuThread->NDS->GPU.Framebuffer[frontbuf][0] || !emuThread->NDS->GPU.Framebuffer[frontbuf][1]) + { + emuThread->FrontBufferLock.unlock(); + return; + } + + memcpy(screen[0].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4); + memcpy(screen[1].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4); + emuThread->FrontBufferLock.unlock(); + + QRect screenrc(0, 0, 256, 192); + + for (int i = 0; i < numScreens; i++) + { + painter.setTransform(screenTrans[i]); + painter.drawImage(screenrc, screen[screenKind[i]]); + } + } + + OSD::Update(); + OSD::DrawNative(painter); +} + +void ScreenPanelNative::resizeEvent(QResizeEvent* event) +{ + setupScreenLayout(); +} + +void ScreenPanelNative::mousePressEvent(QMouseEvent* event) +{ + screenOnMousePress(event); +} + +void ScreenPanelNative::mouseReleaseEvent(QMouseEvent* event) +{ + screenOnMouseRelease(event); +} + +void ScreenPanelNative::mouseMoveEvent(QMouseEvent* event) +{ + screenOnMouseMove(event); +} + +void ScreenPanelNative::tabletEvent(QTabletEvent* event) +{ + screenHandleTablet(event); +} + +bool ScreenPanelNative::event(QEvent* event) +{ + if (event->type() == QEvent::TouchBegin + || event->type() == QEvent::TouchEnd + || event->type() == QEvent::TouchUpdate) + { + screenHandleTouch((QTouchEvent*)event); + return true; + } + return QWidget::event(event); +} + +void ScreenPanelNative::onScreenLayoutChanged() +{ + setMinimumSize(screenGetMinSize()); + setupScreenLayout(); +} + + +ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QWidget(parent), ScreenHandler(this) +{ + setAutoFillBackground(false); + setAttribute(Qt::WA_NativeWindow, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAttribute(Qt::WA_PaintOnScreen, true); + setAttribute(Qt::WA_KeyCompression, false); + setFocusPolicy(Qt::StrongFocus); + setMinimumSize(screenGetMinSize()); +} + +ScreenPanelGL::~ScreenPanelGL() +{} + +bool ScreenPanelGL::createContext() +{ + std::optional windowInfo = getWindowInfo(); + std::array versionsToTry = { + GL::Context::Version{GL::Context::Profile::Core, 4, 3}, + GL::Context::Version{GL::Context::Profile::Core, 3, 2}}; + if (windowInfo.has_value()) + { + glContext = GL::Context::Create(*getWindowInfo(), versionsToTry); + glContext->DoneCurrent(); + } + + return glContext != nullptr; +} + +qreal ScreenPanelGL::devicePixelRatioFromScreen() const +{ + const QScreen* screen_for_ratio = window()->windowHandle()->screen(); + if (!screen_for_ratio) + screen_for_ratio = QGuiApplication::primaryScreen(); + + return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); +} + +int ScreenPanelGL::scaledWindowWidth() const +{ + return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())), 1); +} + +int ScreenPanelGL::scaledWindowHeight() const +{ + return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())), 1); +} + +std::optional ScreenPanelGL::getWindowInfo() +{ + WindowInfo wi; + + // Windows and Apple are easy here since there's no display connection. + #if defined(_WIN32) + wi.type = WindowInfo::Type::Win32; + wi.window_handle = reinterpret_cast(winId()); + #elif defined(__APPLE__) + wi.type = WindowInfo::Type::MacOS; + wi.window_handle = reinterpret_cast(winId()); + #else + QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); + const QString platform_name = QGuiApplication::platformName(); + if (platform_name == QStringLiteral("xcb")) + { + wi.type = WindowInfo::Type::X11; + wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); + wi.window_handle = reinterpret_cast(winId()); + } + else if (platform_name == QStringLiteral("wayland")) + { + wi.type = WindowInfo::Type::Wayland; + QWindow* handle = windowHandle(); + if (handle == nullptr) + return std::nullopt; + + wi.display_connection = pni->nativeResourceForWindow("display", handle); + wi.window_handle = pni->nativeResourceForWindow("surface", handle); + } + else + { + qCritical() << "Unknown PNI platform " << platform_name; + return std::nullopt; + } + #endif + + wi.surface_width = static_cast(scaledWindowWidth()); + wi.surface_height = static_cast(scaledWindowHeight()); + wi.surface_scale = static_cast(devicePixelRatioFromScreen()); + + return wi; +} + + +QPaintEngine* ScreenPanelGL::paintEngine() const +{ + return nullptr; +} + +void ScreenPanelGL::setupScreenLayout() +{ + int w = width(); + int h = height(); + + screenSetupLayout(w, h); + if (emuThread) + transferLayout(emuThread); +} + +void ScreenPanelGL::resizeEvent(QResizeEvent* event) +{ + setupScreenLayout(); + + QWidget::resizeEvent(event); +} + +void ScreenPanelGL::mousePressEvent(QMouseEvent* event) +{ + screenOnMousePress(event); +} + +void ScreenPanelGL::mouseReleaseEvent(QMouseEvent* event) +{ + screenOnMouseRelease(event); +} + +void ScreenPanelGL::mouseMoveEvent(QMouseEvent* event) +{ + screenOnMouseMove(event); +} + +void ScreenPanelGL::tabletEvent(QTabletEvent* event) +{ + screenHandleTablet(event); +} + +bool ScreenPanelGL::event(QEvent* event) +{ + if (event->type() == QEvent::TouchBegin + || event->type() == QEvent::TouchEnd + || event->type() == QEvent::TouchUpdate) + { + screenHandleTouch((QTouchEvent*)event); + return true; + } + return QWidget::event(event); +} + +void ScreenPanelGL::transferLayout(EmuThread* thread) +{ + std::optional windowInfo = getWindowInfo(); + if (windowInfo.has_value()) + thread->updateScreenSettings(Config::ScreenFilter, *windowInfo, numScreens, screenKind, &screenMatrix[0][0]); +} + +void ScreenPanelGL::onScreenLayoutChanged() +{ + setMinimumSize(screenGetMinSize()); + setupScreenLayout(); +} diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h new file mode 100644 index 00000000..955eb2e6 --- /dev/null +++ b/src/frontend/qt_sdl/Screen.h @@ -0,0 +1,155 @@ +/* + 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 SCREEN_H +#define SCREEN_H + +#include "glad/glad.h" +#include "FrontendUtil.h" +#include "duckstation/gl/context.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +class EmuThread; + + +const struct { int id; float ratio; const char* label; } aspectRatios[] = +{ + { 0, 1, "4:3 (native)" }, + { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"}, + { 1, (16.f / 9) / (4.f / 3), "16:9" }, + { 2, (21.f / 9) / (4.f / 3), "21:9" }, + { 3, 0, "window" } +}; +constexpr int AspectRatiosNum = sizeof(aspectRatios) / sizeof(aspectRatios[0]); + + +class ScreenHandler +{ + Q_GADGET + +public: + ScreenHandler(QWidget* widget); + virtual ~ScreenHandler(); + QTimer* setupMouseTimer(); + void updateMouseTimer(); + QTimer* mouseTimer; + QSize screenGetMinSize(int factor); + +protected: + void screenSetupLayout(int w, int h); + + void screenOnMousePress(QMouseEvent* event); + void screenOnMouseRelease(QMouseEvent* event); + void screenOnMouseMove(QMouseEvent* event); + + void screenHandleTablet(QTabletEvent* event); + void screenHandleTouch(QTouchEvent* event); + + float screenMatrix[Frontend::MaxScreenTransforms][6]; + int screenKind[Frontend::MaxScreenTransforms]; + int numScreens; + + bool touching = false; + + void showCursor(); +}; + + +class ScreenPanelNative : public QWidget, public ScreenHandler +{ + Q_OBJECT + +public: + explicit ScreenPanelNative(QWidget* parent); + virtual ~ScreenPanelNative(); + +protected: + void paintEvent(QPaintEvent* event) override; + + void resizeEvent(QResizeEvent* event) override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + + void tabletEvent(QTabletEvent* event) override; + bool event(QEvent* event) override; +private slots: + void onScreenLayoutChanged(); + +private: + void setupScreenLayout(); + + QImage screen[2]; + QTransform screenTrans[Frontend::MaxScreenTransforms]; +}; + + +class ScreenPanelGL : public QWidget, public ScreenHandler +{ + Q_OBJECT + +public: + explicit ScreenPanelGL(QWidget* parent); + virtual ~ScreenPanelGL(); + + std::optional getWindowInfo(); + + bool createContext(); + + GL::Context* getContext() { return glContext.get(); } + + void transferLayout(EmuThread* thread); +protected: + + qreal devicePixelRatioFromScreen() const; + int scaledWindowWidth() const; + int scaledWindowHeight() const; + + QPaintEngine* paintEngine() const override; + + void resizeEvent(QResizeEvent* event) override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + + void tabletEvent(QTabletEvent* event) override; + bool event(QEvent* event) override; + +private slots: + void onScreenLayoutChanged(); + +private: + void setupScreenLayout(); + + std::unique_ptr glContext; +}; + +#endif // SCREEN_H + diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp new file mode 100644 index 00000000..2a8f00c3 --- /dev/null +++ b/src/frontend/qt_sdl/Window.cpp @@ -0,0 +1,2047 @@ +/* + 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 +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#include +#include +#ifndef APPLE +#include +#endif +#endif + +#include "main.h" +#include "Input.h" +#include "CheatsDialog.h" +#include "DateTimeDialog.h" +#include "EmuSettingsDialog.h" +#include "InputConfig/InputConfigDialog.h" +#include "VideoSettingsDialog.h" +#include "CameraSettingsDialog.h" +#include "AudioSettingsDialog.h" +#include "FirmwareSettingsDialog.h" +#include "PathSettingsDialog.h" +#include "MPSettingsDialog.h" +#include "WifiSettingsDialog.h" +#include "InterfaceSettingsDialog.h" +#include "ROMInfoDialog.h" +#include "RAMInfoDialog.h" +#include "TitleManagerDialog.h" +#include "PowerManagement/PowerManagementDialog.h" +#include "AudioInOut.h" + +#include "Platform.h" +#include "Config.h" +#include "Savestate.h" +#include "LocalMP.h" + +//#include "main_shaders.h" + +#include "ROMManager.h" +#include "ArchiveUtil.h" +#include "CameraManager.h" + +#include "OSD.h" + +using namespace melonDS; + +// TEMP +extern MainWindow* mainWindow; +extern EmuThread* emuThread; +extern bool RunningSomething; +extern int autoScreenSizing; +extern QString NdsRomMimeType; +extern QStringList NdsRomExtensions; +extern QString GbaRomMimeType; +extern QStringList GbaRomExtensions; +extern QStringList ArchiveMimeTypes; +extern QStringList ArchiveExtensions; +/*static bool FileExtensionInList(const QString& filename, const QStringList& extensions, Qt::CaseSensitivity cs); +static bool MimeTypeInList(const QMimeType& mimetype, const QStringList& superTypeNames); +static bool NdsRomByExtension(const QString& filename); +static bool GbaRomByExtension(const QString& filename); +static bool SupportedArchiveByExtension(const QString& filename); +static bool NdsRomByMimetype(const QMimeType& mimetype); +static bool GbaRomByMimetype(const QMimeType& mimetype); +static bool SupportedArchiveByMimetype(const QMimeType& mimetype); +static bool ZstdNdsRomByExtension(const QString& filename); +static bool ZstdGbaRomByExtension(const QString& filename); +static bool FileIsSupportedFiletype(const QString& filename, bool insideArchive);*/ + +extern CameraManager* camManager[2]; +extern bool camStarted[2]; + +extern int videoRenderer; +extern bool videoSettingsDirty; + + +// AAAAAAA +static bool FileExtensionInList(const QString& filename, const QStringList& extensions, Qt::CaseSensitivity cs = Qt::CaseInsensitive) +{ + return std::any_of(extensions.cbegin(), extensions.cend(), [&](const auto& ext) { + return filename.endsWith(ext, cs); + }); +} + +static bool MimeTypeInList(const QMimeType& mimetype, const QStringList& superTypeNames) +{ + return std::any_of(superTypeNames.cbegin(), superTypeNames.cend(), [&](const auto& superTypeName) { + return mimetype.inherits(superTypeName); + }); +} + + +static bool NdsRomByExtension(const QString& filename) +{ + return FileExtensionInList(filename, NdsRomExtensions); +} + +static bool GbaRomByExtension(const QString& filename) +{ + return FileExtensionInList(filename, GbaRomExtensions); +} + +static bool SupportedArchiveByExtension(const QString& filename) +{ + return FileExtensionInList(filename, ArchiveExtensions); +} + + +static bool NdsRomByMimetype(const QMimeType& mimetype) +{ + return mimetype.inherits(NdsRomMimeType); +} + +static bool GbaRomByMimetype(const QMimeType& mimetype) +{ + return mimetype.inherits(GbaRomMimeType); +} + +static bool SupportedArchiveByMimetype(const QMimeType& mimetype) +{ + return MimeTypeInList(mimetype, ArchiveMimeTypes); +} + +static bool ZstdNdsRomByExtension(const QString& filename) +{ + return filename.endsWith(".zst", Qt::CaseInsensitive) && + NdsRomByExtension(filename.left(filename.size() - 4)); +} + +static bool ZstdGbaRomByExtension(const QString& filename) +{ + return filename.endsWith(".zst", Qt::CaseInsensitive) && + GbaRomByExtension(filename.left(filename.size() - 4)); +} + +static bool FileIsSupportedFiletype(const QString& filename, bool insideArchive = false) +{ + if (ZstdNdsRomByExtension(filename) || ZstdGbaRomByExtension(filename)) + return true; + + if (NdsRomByExtension(filename) || GbaRomByExtension(filename) || SupportedArchiveByExtension(filename)) + return true; + + const auto matchmode = insideArchive ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; + const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, matchmode); + return NdsRomByMimetype(mimetype) || GbaRomByMimetype(mimetype) || SupportedArchiveByMimetype(mimetype); +} + + +#ifndef _WIN32 +static int signalFd[2]; +QSocketNotifier *signalSn; + +static void signalHandler(int) +{ + char a = 1; + write(signalFd[0], &a, sizeof(a)); +} +#endif + +MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) +{ +#ifndef _WIN32 + if (socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) + { + qFatal("Couldn't create socketpair"); + } + + signalSn = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); + connect(signalSn, SIGNAL(activated(int)), this, SLOT(onQuit())); + + struct sigaction sa; + + sa.sa_handler = signalHandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_flags |= SA_RESTART; + sigaction(SIGINT, &sa, 0); +#endif + + oldW = Config::WindowWidth; + oldH = Config::WindowHeight; + oldMax = Config::WindowMaximized; + + setWindowTitle("melonDS " MELONDS_VERSION); + setAttribute(Qt::WA_DeleteOnClose); + setAcceptDrops(true); + setFocusPolicy(Qt::ClickFocus); + + int inst = Platform::InstanceID(); + + QMenuBar* menubar = new QMenuBar(); + { + QMenu* menu = menubar->addMenu("File"); + + actOpenROM = menu->addAction("Open ROM..."); + connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); + actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open)); + + /*actOpenROMArchive = menu->addAction("Open ROM inside archive..."); + connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive); + actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ + + recentMenu = menu->addMenu("Open recent"); + for (int i = 0; i < 10; ++i) + { + std::string item = Config::RecentROMList[i]; + if (!item.empty()) + recentFileList.push_back(QString::fromStdString(item)); + } + updateRecentFilesMenu(); + + //actBootFirmware = menu->addAction("Launch DS menu"); + actBootFirmware = menu->addAction("Boot firmware"); + connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); + + menu->addSeparator(); + + actCurrentCart = menu->addAction("DS slot: " + ROMManager::CartLabel()); + actCurrentCart->setEnabled(false); + + actInsertCart = menu->addAction("Insert cart..."); + connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart); + + actEjectCart = menu->addAction("Eject cart"); + connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart); + + menu->addSeparator(); + + actCurrentGBACart = menu->addAction("GBA slot: " + ROMManager::GBACartLabel()); + actCurrentGBACart->setEnabled(false); + + actInsertGBACart = menu->addAction("Insert ROM cart..."); + connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart); + + { + QMenu* submenu = menu->addMenu("Insert add-on cart"); + + actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); + actInsertGBAAddon[0]->setData(QVariant(GBAAddon_RAMExpansion)); + connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); + } + + actEjectGBACart = menu->addAction("Eject cart"); + connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart); + + menu->addSeparator(); + + actImportSavefile = menu->addAction("Import savefile"); + connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); + + menu->addSeparator(); + + { + QMenu* submenu = menu->addMenu("Save state"); + + for (int i = 1; i < 9; i++) + { + actSaveState[i] = submenu->addAction(QString("%1").arg(i)); + actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1+i-1))); + actSaveState[i]->setData(QVariant(i)); + connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); + } + + actSaveState[0] = submenu->addAction("File..."); + actSaveState[0]->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_F9)); + actSaveState[0]->setData(QVariant(0)); + connect(actSaveState[0], &QAction::triggered, this, &MainWindow::onSaveState); + } + { + QMenu* submenu = menu->addMenu("Load state"); + + for (int i = 1; i < 9; i++) + { + actLoadState[i] = submenu->addAction(QString("%1").arg(i)); + actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1+i-1)); + actLoadState[i]->setData(QVariant(i)); + connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actLoadState[0] = submenu->addAction("File..."); + actLoadState[0]->setShortcut(QKeySequence(Qt::Key_F9)); + actLoadState[0]->setData(QVariant(0)); + connect(actLoadState[0], &QAction::triggered, this, &MainWindow::onLoadState); + } + + actUndoStateLoad = menu->addAction("Undo state load"); + actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); + connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); + + menu->addSeparator(); + + actQuit = menu->addAction("Quit"); + connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); + actQuit->setShortcut(QKeySequence(QKeySequence::StandardKey::Quit)); + } + { + QMenu* menu = menubar->addMenu("System"); + + actPause = menu->addAction("Pause"); + actPause->setCheckable(true); + connect(actPause, &QAction::triggered, this, &MainWindow::onPause); + + actReset = menu->addAction("Reset"); + connect(actReset, &QAction::triggered, this, &MainWindow::onReset); + + actStop = menu->addAction("Stop"); + connect(actStop, &QAction::triggered, this, &MainWindow::onStop); + + actFrameStep = menu->addAction("Frame step"); + connect(actFrameStep, &QAction::triggered, this, &MainWindow::onFrameStep); + + menu->addSeparator(); + + actPowerManagement = menu->addAction("Power management"); + connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); + + actDateTime = menu->addAction("Date and time"); + connect(actDateTime, &QAction::triggered, this, &MainWindow::onOpenDateTime); + + menu->addSeparator(); + + actEnableCheats = menu->addAction("Enable cheats"); + actEnableCheats->setCheckable(true); + connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats); + + //if (inst == 0) + { + actSetupCheats = menu->addAction("Setup cheat codes"); + actSetupCheats->setMenuRole(QAction::NoRole); + connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats); + + menu->addSeparator(); + actROMInfo = menu->addAction("ROM info"); + connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); + + actRAMInfo = menu->addAction("RAM search"); + connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo); + + actTitleManager = menu->addAction("Manage DSi titles"); + connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); + } + + { + menu->addSeparator(); + QMenu* submenu = menu->addMenu("Multiplayer"); + + actMPNewInstance = submenu->addAction("Launch new instance"); + connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance); + } + } + { + QMenu* menu = menubar->addMenu("Config"); + + actEmuSettings = menu->addAction("Emu settings"); + connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + +#ifdef __APPLE__ + actPreferences = menu->addAction("Preferences..."); + connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); + actPreferences->setMenuRole(QAction::PreferencesRole); +#endif + + actInputConfig = menu->addAction("Input and hotkeys"); + connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); + + actVideoSettings = menu->addAction("Video settings"); + connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); + + actCameraSettings = menu->addAction("Camera settings"); + connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); + + actAudioSettings = menu->addAction("Audio settings"); + connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); + + actMPSettings = menu->addAction("Multiplayer settings"); + connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); + + actWifiSettings = menu->addAction("Wifi settings"); + connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); + + actFirmwareSettings = menu->addAction("Firmware settings"); + connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); + + actInterfaceSettings = menu->addAction("Interface settings"); + connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); + + actPathSettings = menu->addAction("Path settings"); + connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); + + { + QMenu* submenu = menu->addMenu("Savestate settings"); + + actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); + actSavestateSRAMReloc->setCheckable(true); + connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); + } + + menu->addSeparator(); + + { + QMenu* submenu = menu->addMenu("Screen size"); + + for (int i = 0; i < 4; i++) + { + int data = i+1; + actScreenSize[i] = submenu->addAction(QString("%1x").arg(data)); + actScreenSize[i]->setData(QVariant(data)); + connect(actScreenSize[i], &QAction::triggered, this, &MainWindow::onChangeScreenSize); + } + } + { + QMenu* submenu = menu->addMenu("Screen rotation"); + grpScreenRotation = new QActionGroup(submenu); + + for (int i = 0; i < Frontend::screenRot_MAX; i++) + { + int data = i*90; + actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); + actScreenRotation[i]->setActionGroup(grpScreenRotation); + actScreenRotation[i]->setData(QVariant(i)); + actScreenRotation[i]->setCheckable(true); + } + + connect(grpScreenRotation, &QActionGroup::triggered, this, &MainWindow::onChangeScreenRotation); + } + { + QMenu* submenu = menu->addMenu("Screen gap"); + grpScreenGap = new QActionGroup(submenu); + + const int screengap[] = {0, 1, 8, 64, 90, 128}; + + for (int i = 0; i < 6; i++) + { + int data = screengap[i]; + actScreenGap[i] = submenu->addAction(QString("%1 px").arg(data)); + actScreenGap[i]->setActionGroup(grpScreenGap); + actScreenGap[i]->setData(QVariant(data)); + actScreenGap[i]->setCheckable(true); + } + + connect(grpScreenGap, &QActionGroup::triggered, this, &MainWindow::onChangeScreenGap); + } + { + QMenu* submenu = menu->addMenu("Screen layout"); + grpScreenLayout = new QActionGroup(submenu); + + const char* screenlayout[] = {"Natural", "Vertical", "Horizontal", "Hybrid"}; + + for (int i = 0; i < Frontend::screenLayout_MAX; i++) + { + actScreenLayout[i] = submenu->addAction(QString(screenlayout[i])); + actScreenLayout[i]->setActionGroup(grpScreenLayout); + actScreenLayout[i]->setData(QVariant(i)); + actScreenLayout[i]->setCheckable(true); + } + + connect(grpScreenLayout, &QActionGroup::triggered, this, &MainWindow::onChangeScreenLayout); + + submenu->addSeparator(); + + actScreenSwap = submenu->addAction("Swap screens"); + actScreenSwap->setCheckable(true); + connect(actScreenSwap, &QAction::triggered, this, &MainWindow::onChangeScreenSwap); + } + { + QMenu* submenu = menu->addMenu("Screen sizing"); + grpScreenSizing = new QActionGroup(submenu); + + const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"}; + + for (int i = 0; i < Frontend::screenSizing_MAX; i++) + { + actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); + actScreenSizing[i]->setActionGroup(grpScreenSizing); + actScreenSizing[i]->setData(QVariant(i)); + actScreenSizing[i]->setCheckable(true); + } + + connect(grpScreenSizing, &QActionGroup::triggered, this, &MainWindow::onChangeScreenSizing); + + submenu->addSeparator(); + + actIntegerScaling = submenu->addAction("Force integer scaling"); + actIntegerScaling->setCheckable(true); + connect(actIntegerScaling, &QAction::triggered, this, &MainWindow::onChangeIntegerScaling); + } + { + QMenu* submenu = menu->addMenu("Aspect ratio"); + grpScreenAspectTop = new QActionGroup(submenu); + grpScreenAspectBot = new QActionGroup(submenu); + actScreenAspectTop = new QAction*[AspectRatiosNum]; + actScreenAspectBot = new QAction*[AspectRatiosNum]; + + for (int i = 0; i < 2; i++) + { + QActionGroup* group = grpScreenAspectTop; + QAction** actions = actScreenAspectTop; + + if (i == 1) + { + group = grpScreenAspectBot; + submenu->addSeparator(); + actions = actScreenAspectBot; + } + + for (int j = 0; j < AspectRatiosNum; j++) + { + auto ratio = aspectRatios[j]; + QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label); + actions[j] = submenu->addAction(label); + actions[j]->setActionGroup(group); + actions[j]->setData(QVariant(ratio.id)); + actions[j]->setCheckable(true); + } + + connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect); + } + } + + actScreenFiltering = menu->addAction("Screen filtering"); + actScreenFiltering->setCheckable(true); + connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); + + actShowOSD = menu->addAction("Show OSD"); + actShowOSD->setCheckable(true); + connect(actShowOSD, &QAction::triggered, this, &MainWindow::onChangeShowOSD); + + menu->addSeparator(); + + actLimitFramerate = menu->addAction("Limit framerate"); + actLimitFramerate->setCheckable(true); + connect(actLimitFramerate, &QAction::triggered, this, &MainWindow::onChangeLimitFramerate); + + actAudioSync = menu->addAction("Audio sync"); + actAudioSync->setCheckable(true); + connect(actAudioSync, &QAction::triggered, this, &MainWindow::onChangeAudioSync); + } + setMenuBar(menubar); + + resize(Config::WindowWidth, Config::WindowHeight); + + if (Config::FirmwareUsername == "Arisotura") + actMPNewInstance->setText("Fart"); + +#ifdef Q_OS_MAC + QPoint screenCenter = screen()->availableGeometry().center(); + QRect frameGeo = frameGeometry(); + frameGeo.moveCenter(screenCenter); + move(frameGeo.topLeft()); +#endif + + if (oldMax) + showMaximized(); + else + show(); + + createScreenPanel(); + + actEjectCart->setEnabled(false); + actEjectGBACart->setEnabled(false); + + if (Config::ConsoleType == 1) + { + actInsertGBACart->setEnabled(false); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(false); + } + + for (int i = 0; i < 9; i++) + { + actSaveState[i]->setEnabled(false); + actLoadState[i]->setEnabled(false); + } + actUndoStateLoad->setEnabled(false); + actImportSavefile->setEnabled(false); + + actPause->setEnabled(false); + actReset->setEnabled(false); + actStop->setEnabled(false); + actFrameStep->setEnabled(false); + + actDateTime->setEnabled(true); + actPowerManagement->setEnabled(false); + + actSetupCheats->setEnabled(false); + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); + + actEnableCheats->setChecked(Config::EnableCheats); + + actROMInfo->setEnabled(false); + actRAMInfo->setEnabled(false); + + actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM); + + actScreenRotation[Config::ScreenRotation]->setChecked(true); + + for (int i = 0; i < 6; i++) + { + if (actScreenGap[i]->data().toInt() == Config::ScreenGap) + { + actScreenGap[i]->setChecked(true); + break; + } + } + + actScreenLayout[Config::ScreenLayout]->setChecked(true); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + actIntegerScaling->setChecked(Config::IntegerScaling); + + actScreenSwap->setChecked(Config::ScreenSwap); + + for (int i = 0; i < AspectRatiosNum; i++) + { + if (Config::ScreenAspectTop == aspectRatios[i].id) + actScreenAspectTop[i]->setChecked(true); + if (Config::ScreenAspectBot == aspectRatios[i].id) + actScreenAspectBot[i]->setChecked(true); + } + + actScreenFiltering->setChecked(Config::ScreenFilter); + actShowOSD->setChecked(Config::ShowOSD); + + actLimitFramerate->setChecked(Config::LimitFPS); + actAudioSync->setChecked(Config::AudioSync); + + if (inst > 0) + { + actEmuSettings->setEnabled(false); + actVideoSettings->setEnabled(false); + actMPSettings->setEnabled(false); + actWifiSettings->setEnabled(false); + actInterfaceSettings->setEnabled(false); + +#ifdef __APPLE__ + actPreferences->setEnabled(false); +#endif // __APPLE__ + } +} + +MainWindow::~MainWindow() +{ + delete[] actScreenAspectTop; + delete[] actScreenAspectBot; +} + +void MainWindow::closeEvent(QCloseEvent* event) +{ + if (hasOGL) + { + // we intentionally don't unpause here + emuThread->emuPause(); + emuThread->deinitContext(); + } + + QMainWindow::closeEvent(event); +} + +void MainWindow::createScreenPanel() +{ + hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); + + if (hasOGL) + { + ScreenPanelGL* panelGL = new ScreenPanelGL(this); + panelGL->show(); + + panel = panelGL; + panelWidget = panelGL; + + panelGL->createContext(); + } + + if (!hasOGL) + { + ScreenPanelNative* panelNative = new ScreenPanelNative(this); + panel = panelNative; + panelWidget = panelNative; + panelWidget->show(); + } + setCentralWidget(panelWidget); + + actScreenFiltering->setEnabled(hasOGL); + + connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged())); + emit screenLayoutChange(); +} + +GL::Context* MainWindow::getOGLContext() +{ + if (!hasOGL) return nullptr; + + ScreenPanelGL* glpanel = static_cast(panel); + return glpanel->getContext(); +} + +void MainWindow::resizeEvent(QResizeEvent* event) +{ + int w = event->size().width(); + int h = event->size().height(); + + if (!isFullScreen()) + { + // this is ugly + // thing is, when maximizing the window, we first receive the resizeEvent + // with a new size matching the screen, then the changeEvent telling us that + // the maximized flag was updated + oldW = Config::WindowWidth; + oldH = Config::WindowHeight; + oldMax = isMaximized(); + + Config::WindowWidth = w; + Config::WindowHeight = h; + } +} + +void MainWindow::changeEvent(QEvent* event) +{ + if (isMaximized() && !oldMax) + { + Config::WindowWidth = oldW; + Config::WindowHeight = oldH; + } + + Config::WindowMaximized = isMaximized() ? 1:0; +} + +void MainWindow::keyPressEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) return; + + // TODO!! REMOVE ME IN RELEASE BUILDS!! + //if (event->key() == Qt::Key_F11) NDS::debug(0); + + Input::KeyPress(event); +} + +void MainWindow::keyReleaseEvent(QKeyEvent* event) +{ + if (event->isAutoRepeat()) return; + + Input::KeyRelease(event); +} + + +void MainWindow::dragEnterEvent(QDragEnterEvent* event) +{ + if (!event->mimeData()->hasUrls()) return; + + QList urls = event->mimeData()->urls(); + if (urls.count() > 1) return; // not handling more than one file at once + + QString filename = urls.at(0).toLocalFile(); + + if (FileIsSupportedFiletype(filename)) + event->acceptProposedAction(); +} + +void MainWindow::dropEvent(QDropEvent* event) +{ + if (!event->mimeData()->hasUrls()) return; + + QList urls = event->mimeData()->urls(); + if (urls.count() > 1) return; // not handling more than one file at once + + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + const QStringList file = splitArchivePath(urls.at(0).toLocalFile(), false); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + const QString filename = file.last(); + const bool romInsideArchive = file.size() > 1; + const auto matchMode = romInsideArchive ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; + const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, matchMode); + + bool isNdsRom = NdsRomByExtension(filename) || NdsRomByMimetype(mimetype); + bool isGbaRom = GbaRomByExtension(filename) || GbaRomByMimetype(mimetype); + isNdsRom |= ZstdNdsRomByExtension(filename); + isGbaRom |= ZstdGbaRomByExtension(filename); + + if (isNdsRom) + { + if (!ROMManager::LoadROM(emuThread, file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the DS ROM."); + emuThread->emuUnpause(); + return; + } + + const QString barredFilename = file.join('|'); + recentFileList.removeAll(barredFilename); + recentFileList.prepend(barredFilename); + updateRecentFilesMenu(); + + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); + emuThread->emuRun(); + + updateCartInserted(false); + } + else if (isGbaRom) + { + if (!ROMManager::LoadGBAROM(*emuThread->NDS, file)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); + emuThread->emuUnpause(); + return; + } + + emuThread->emuUnpause(); + + updateCartInserted(true); + } + else + { + QMessageBox::critical(this, "melonDS", "The file could not be recognized as a DS or GBA ROM."); + emuThread->emuUnpause(); + return; + } +} + +void MainWindow::focusInEvent(QFocusEvent* event) +{ + AudioInOut::AudioMute(mainWindow); +} + +void MainWindow::focusOutEvent(QFocusEvent* event) +{ + AudioInOut::AudioMute(mainWindow); +} + +void MainWindow::onAppStateChanged(Qt::ApplicationState state) +{ + if (state == Qt::ApplicationInactive) + { + if (Config::PauseLostFocus && emuThread->emuIsRunning()) + emuThread->emuPause(); + } + else if (state == Qt::ApplicationActive) + { + if (Config::PauseLostFocus && !pausedManually) + emuThread->emuUnpause(); + } +} + +bool MainWindow::verifySetup() +{ + QString res = ROMManager::VerifySetup(); + if (!res.isEmpty()) + { + QMessageBox::critical(this, "melonDS", res); + return false; + } + + return true; +} + +bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) +{ + if (!verifySetup()) + { + return false; + } + + bool gbaloaded = false; + if (!gbafile.isEmpty()) + { + if (!ROMManager::LoadGBAROM(*emuThread->NDS, gbafile)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); + return false; + } + + gbaloaded = true; + } + + bool ndsloaded = false; + if (!file.isEmpty()) + { + if (!ROMManager::LoadROM(emuThread, file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + return false; + } + recentFileList.removeAll(file.join("|")); + recentFileList.prepend(file.join("|")); + updateRecentFilesMenu(); + ndsloaded = true; + } + + if (boot) + { + if (ndsloaded) + { + emuThread->NDS->Start(); + emuThread->emuRun(); + } + else + { + onBootFirmware(); + } + } + + updateCartInserted(false); + + if (gbaloaded) + { + updateCartInserted(true); + } + + return true; +} + +QStringList MainWindow::splitArchivePath(const QString& filename, bool useMemberSyntax) +{ + if (filename.isEmpty()) return {}; + +#ifdef ARCHIVE_SUPPORT_ENABLED + if (useMemberSyntax) + { + const QStringList filenameParts = filename.split('|'); + if (filenameParts.size() > 2) + { + QMessageBox::warning(this, "melonDS", "This path contains too many '|'."); + return {}; + } + + if (filenameParts.size() == 2) + { + const QString archive = filenameParts.at(0); + if (!QFileInfo(archive).exists()) + { + QMessageBox::warning(this, "melonDS", "This archive does not exist."); + return {}; + } + + const QString subfile = filenameParts.at(1); + if (!Archive::ListArchive(archive).contains(subfile)) + { + QMessageBox::warning(this, "melonDS", "This archive does not contain the desired file."); + return {}; + } + + return filenameParts; + } + } +#endif + + if (!QFileInfo(filename).exists()) + { + QMessageBox::warning(this, "melonDS", "This ROM file does not exist."); + return {}; + } + +#ifdef ARCHIVE_SUPPORT_ENABLED + if (SupportedArchiveByExtension(filename) + || SupportedArchiveByMimetype(QMimeDatabase().mimeTypeForFile(filename))) + { + const QString subfile = pickFileFromArchive(filename); + if (subfile.isEmpty()) + return {}; + + return { filename, subfile }; + } +#endif + + return { filename }; +} + +QString MainWindow::pickFileFromArchive(QString archiveFileName) +{ + QVector archiveROMList = Archive::ListArchive(archiveFileName); + + if (archiveROMList.size() <= 1) + { + if (!archiveROMList.isEmpty() && archiveROMList.at(0) == "OK") + QMessageBox::warning(this, "melonDS", "This archive is empty."); + else + QMessageBox::critical(this, "melonDS", "This archive could not be read. It may be corrupt or you don't have the permissions."); + return QString(); + } + + archiveROMList.removeFirst(); + + const auto notSupportedRom = [&](const auto& filename){ + if (NdsRomByExtension(filename) || GbaRomByExtension(filename)) + return false; + const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, QMimeDatabase::MatchExtension); + return !(NdsRomByMimetype(mimetype) || GbaRomByMimetype(mimetype)); + }; + + archiveROMList.erase(std::remove_if(archiveROMList.begin(), archiveROMList.end(), notSupportedRom), + archiveROMList.end()); + + if (archiveROMList.isEmpty()) + { + QMessageBox::warning(this, "melonDS", "This archive does not contain any supported ROMs."); + return QString(); + } + + if (archiveROMList.size() == 1) + return archiveROMList.first(); + + bool ok; + const QString toLoad = QInputDialog::getItem( + this, "melonDS", + "This archive contains multiple files. Select which ROM you want to load.", + archiveROMList.toList(), 0, false, &ok + ); + + if (ok) return toLoad; + + // User clicked on cancel + + return QString(); +} + +QStringList MainWindow::pickROM(bool gba) +{ + const QString console = gba ? "GBA" : "DS"; + const QStringList& romexts = gba ? GbaRomExtensions : NdsRomExtensions; + + QString rawROMs = romexts.join(" *"); + QString extraFilters = ";;" + console + " ROMs (*" + rawROMs; + QString allROMs = rawROMs; + + QString zstdROMs = "*" + romexts.join(".zst *") + ".zst"; + extraFilters += ");;Zstandard-compressed " + console + " ROMs (" + zstdROMs + ")"; + allROMs += " " + zstdROMs; + +#ifdef ARCHIVE_SUPPORT_ENABLED + QString archives = "*" + ArchiveExtensions.join(" *"); + extraFilters += ";;Archives (" + archives + ")"; + allROMs += " " + archives; +#endif + extraFilters += ";;All files (*.*)"; + + const QString filename = QFileDialog::getOpenFileName( + this, "Open " + console + " ROM", + QString::fromStdString(Config::LastROMFolder), + "All supported files (*" + allROMs + ")" + extraFilters + ); + + if (filename.isEmpty()) return {}; + + Config::LastROMFolder = QFileInfo(filename).dir().path().toStdString(); + return splitArchivePath(filename, false); +} + +void MainWindow::updateCartInserted(bool gba) +{ + bool inserted; + if (gba) + { + inserted = ROMManager::GBACartInserted() && (Config::ConsoleType == 0); + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + actEjectGBACart->setEnabled(inserted); + } + else + { + inserted = ROMManager::CartInserted(); + actCurrentCart->setText("DS slot: " + ROMManager::CartLabel()); + actEjectCart->setEnabled(inserted); + actImportSavefile->setEnabled(inserted); + actSetupCheats->setEnabled(inserted); + actROMInfo->setEnabled(inserted); + actRAMInfo->setEnabled(inserted); + } +} + +void MainWindow::onOpenFile() +{ + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + QStringList file = pickROM(false); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadROM(emuThread, file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + QString filename = file.join('|'); + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); + emuThread->emuRun(); + + updateCartInserted(false); +} + +void MainWindow::onClearRecentFiles() +{ + recentFileList.clear(); + for (int i = 0; i < 10; i++) + Config::RecentROMList[i] = ""; + updateRecentFilesMenu(); +} + +void MainWindow::updateRecentFilesMenu() +{ + recentMenu->clear(); + + for (int i = 0; i < recentFileList.size(); ++i) + { + if (i >= 10) break; + + QString item_full = recentFileList.at(i); + QString item_display = item_full; + int itemlen = item_full.length(); + const int maxlen = 100; + if (itemlen > maxlen) + { + int cut_start = 0; + while (item_full[cut_start] != '/' && item_full[cut_start] != '\\' && + cut_start < itemlen) + cut_start++; + + int cut_end = itemlen-1; + while (((item_full[cut_end] != '/' && item_full[cut_end] != '\\') || + (cut_start+4+(itemlen-cut_end) < maxlen)) && + cut_end > 0) + cut_end--; + + item_display.truncate(cut_start+1); + item_display += "..."; + item_display += QString(item_full).remove(0, cut_end); + } + + QAction *actRecentFile_i = recentMenu->addAction(QString("%1. %2").arg(i+1).arg(item_display)); + actRecentFile_i->setData(item_full); + connect(actRecentFile_i, &QAction::triggered, this, &MainWindow::onClickRecentFile); + + Config::RecentROMList[i] = recentFileList.at(i).toStdString(); + } + + while (recentFileList.size() > 10) + recentFileList.removeLast(); + + recentMenu->addSeparator(); + + QAction *actClearRecentList = recentMenu->addAction("Clear"); + connect(actClearRecentList, &QAction::triggered, this, &MainWindow::onClearRecentFiles); + + if (recentFileList.empty()) + actClearRecentList->setEnabled(false); + + Config::Save(); +} + +void MainWindow::onClickRecentFile() +{ + QAction *act = (QAction *)sender(); + QString filename = act->data().toString(); + + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + const QStringList file = splitArchivePath(filename, true); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadROM(emuThread, file, true)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + recentFileList.removeAll(filename); + recentFileList.prepend(filename); + updateRecentFilesMenu(); + + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); + emuThread->emuRun(); + + updateCartInserted(false); +} + +void MainWindow::onBootFirmware() +{ + emuThread->emuPause(); + + if (!verifySetup()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::BootToMenu(emuThread)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); + emuThread->emuUnpause(); + return; + } + + assert(emuThread->NDS != nullptr); + emuThread->NDS->Start(); + emuThread->emuRun(); +} + +void MainWindow::onInsertCart() +{ + emuThread->emuPause(); + + QStringList file = pickROM(false); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadROM(emuThread, file, false)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + emuThread->emuUnpause(); + + updateCartInserted(false); +} + +void MainWindow::onEjectCart() +{ + emuThread->emuPause(); + + ROMManager::EjectCart(*emuThread->NDS); + + emuThread->emuUnpause(); + + updateCartInserted(false); +} + +void MainWindow::onInsertGBACart() +{ + emuThread->emuPause(); + + QStringList file = pickROM(true); + if (file.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + if (!ROMManager::LoadGBAROM(*emuThread->NDS, file)) + { + // TODO: better error reporting? + QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); + emuThread->emuUnpause(); + return; + } + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onInsertGBAAddon() +{ + QAction* act = (QAction*)sender(); + int type = act->data().toInt(); + + emuThread->emuPause(); + + ROMManager::LoadGBAAddon(*emuThread->NDS, type); + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onEjectGBACart() +{ + emuThread->emuPause(); + + ROMManager::EjectGBACart(*emuThread->NDS); + + emuThread->emuUnpause(); + + updateCartInserted(true); +} + +void MainWindow::onSaveState() +{ + int slot = ((QAction*)sender())->data().toInt(); + + emuThread->emuPause(); + + std::string filename; + if (slot > 0) + { + filename = ROMManager::GetSavestateName(slot); + } + else + { + // TODO: specific 'last directory' for savestate files? + QString qfilename = QFileDialog::getSaveFileName(this, + "Save state", + QString::fromStdString(Config::LastROMFolder), + "melonDS savestates (*.mln);;Any file (*.*)"); + if (qfilename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + filename = qfilename.toStdString(); + } + + if (ROMManager::SaveState(*emuThread->NDS, filename)) + { + char msg[64]; + if (slot > 0) sprintf(msg, "State saved to slot %d", slot); + else sprintf(msg, "State saved to file"); + OSD::AddMessage(0, msg); + + actLoadState[slot]->setEnabled(true); + } + else + { + OSD::AddMessage(0xFFA0A0, "State save failed"); + } + + emuThread->emuUnpause(); +} + +void MainWindow::onLoadState() +{ + int slot = ((QAction*)sender())->data().toInt(); + + emuThread->emuPause(); + + std::string filename; + if (slot > 0) + { + filename = ROMManager::GetSavestateName(slot); + } + else + { + // TODO: specific 'last directory' for savestate files? + QString qfilename = QFileDialog::getOpenFileName(this, + "Load state", + QString::fromStdString(Config::LastROMFolder), + "melonDS savestates (*.ml*);;Any file (*.*)"); + if (qfilename.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + filename = qfilename.toStdString(); + } + + if (!Platform::FileExists(filename)) + { + char msg[64]; + if (slot > 0) sprintf(msg, "State slot %d is empty", slot); + else sprintf(msg, "State file does not exist"); + OSD::AddMessage(0xFFA0A0, msg); + + emuThread->emuUnpause(); + return; + } + + if (ROMManager::LoadState(*emuThread->NDS, filename)) + { + char msg[64]; + if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); + else sprintf(msg, "State loaded from file"); + OSD::AddMessage(0, msg); + + actUndoStateLoad->setEnabled(true); + } + else + { + OSD::AddMessage(0xFFA0A0, "State load failed"); + } + + emuThread->emuUnpause(); +} + +void MainWindow::onUndoStateLoad() +{ + emuThread->emuPause(); + ROMManager::UndoStateLoad(*emuThread->NDS); + emuThread->emuUnpause(); + + OSD::AddMessage(0, "State load undone"); +} + +void MainWindow::onImportSavefile() +{ + emuThread->emuPause(); + QString path = QFileDialog::getOpenFileName(this, + "Select savefile", + QString::fromStdString(Config::LastROMFolder), + "Savefiles (*.sav *.bin *.dsv);;Any file (*.*)"); + + if (path.isEmpty()) + { + emuThread->emuUnpause(); + return; + } + + Platform::FileHandle* f = Platform::OpenFile(path.toStdString(), Platform::FileMode::Read); + if (!f) + { + QMessageBox::critical(this, "melonDS", "Could not open the given savefile."); + emuThread->emuUnpause(); + return; + } + + if (RunningSomething) + { + if (QMessageBox::warning(this, + "melonDS", + "The emulation will be reset and the current savefile overwritten.", + QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) + { + emuThread->emuUnpause(); + return; + } + + ROMManager::Reset(emuThread); + } + + u32 len = FileLength(f); + + std::unique_ptr data = std::make_unique(len); + Platform::FileRewind(f); + Platform::FileRead(data.get(), len, 1, f); + + assert(emuThread->NDS != nullptr); + emuThread->NDS->SetNDSSave(data.get(), len); + + CloseFile(f); + emuThread->emuUnpause(); +} + +void MainWindow::onQuit() +{ +#ifndef _WIN32 + signalSn->setEnabled(false); +#endif + QApplication::quit(); +} + + +void MainWindow::onPause(bool checked) +{ + if (!RunningSomething) return; + + if (checked) + { + emuThread->emuPause(); + OSD::AddMessage(0, "Paused"); + pausedManually = true; + } + else + { + emuThread->emuUnpause(); + OSD::AddMessage(0, "Resumed"); + pausedManually = false; + } +} + +void MainWindow::onReset() +{ + if (!RunningSomething) return; + + emuThread->emuPause(); + + actUndoStateLoad->setEnabled(false); + + ROMManager::Reset(emuThread); + + OSD::AddMessage(0, "Reset"); + emuThread->emuRun(); +} + +void MainWindow::onStop() +{ + if (!RunningSomething) return; + + emuThread->emuPause(); + emuThread->NDS->Stop(); +} + +void MainWindow::onFrameStep() +{ + if (!RunningSomething) return; + + emuThread->emuFrameStep(); +} + +void MainWindow::onOpenDateTime() +{ + DateTimeDialog* dlg = DateTimeDialog::openDlg(this); +} + +void MainWindow::onOpenPowerManagement() +{ + PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this, emuThread); +} + +void MainWindow::onEnableCheats(bool checked) +{ + Config::EnableCheats = checked?1:0; + ROMManager::EnableCheats(*emuThread->NDS, Config::EnableCheats != 0); +} + +void MainWindow::onSetupCheats() +{ + emuThread->emuPause(); + + CheatsDialog* dlg = CheatsDialog::openDlg(this); + connect(dlg, &CheatsDialog::finished, this, &MainWindow::onCheatsDialogFinished); +} + +void MainWindow::onCheatsDialogFinished(int res) +{ + emuThread->emuUnpause(); +} + +void MainWindow::onROMInfo() +{ + auto cart = emuThread->NDS->NDSCartSlot.GetCart(); + if (cart) + ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this, *cart); +} + +void MainWindow::onRAMInfo() +{ + RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this, emuThread); +} + +void MainWindow::onOpenTitleManager() +{ + TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this); +} + +void MainWindow::onMPNewInstance() +{ + //QProcess::startDetached(QApplication::applicationFilePath()); + QProcess newinst; + newinst.setProgram(QApplication::applicationFilePath()); + newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1)); + +#ifdef __WIN32__ + newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args) + { + args->flags |= CREATE_NEW_CONSOLE; + }); +#endif + + newinst.startDetached(); +} + +void MainWindow::onOpenEmuSettings() +{ + emuThread->emuPause(); + + EmuSettingsDialog* dlg = EmuSettingsDialog::openDlg(this); + connect(dlg, &EmuSettingsDialog::finished, this, &MainWindow::onEmuSettingsDialogFinished); +} + +void MainWindow::onEmuSettingsDialogFinished(int res) +{ + emuThread->emuUnpause(); + + if (Config::ConsoleType == 1) + { + actInsertGBACart->setEnabled(false); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(false); + actEjectGBACart->setEnabled(false); + } + else + { + actInsertGBACart->setEnabled(true); + for (int i = 0; i < 1; i++) + actInsertGBAAddon[i]->setEnabled(true); + actEjectGBACart->setEnabled(ROMManager::GBACartInserted()); + } + + if (EmuSettingsDialog::needsReset) + onReset(); + + actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); + + if (!RunningSomething) + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); +} + +void MainWindow::onOpenInputConfig() +{ + emuThread->emuPause(); + + InputConfigDialog* dlg = InputConfigDialog::openDlg(this); + connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); +} + +void MainWindow::onInputConfigFinished(int res) +{ + emuThread->emuUnpause(); +} + +void MainWindow::onOpenVideoSettings() +{ + VideoSettingsDialog* dlg = VideoSettingsDialog::openDlg(this); + connect(dlg, &VideoSettingsDialog::updateVideoSettings, this, &MainWindow::onUpdateVideoSettings); +} + +void MainWindow::onOpenCameraSettings() +{ + emuThread->emuPause(); + + camStarted[0] = camManager[0]->isStarted(); + camStarted[1] = camManager[1]->isStarted(); + if (camStarted[0]) camManager[0]->stop(); + if (camStarted[1]) camManager[1]->stop(); + + CameraSettingsDialog* dlg = CameraSettingsDialog::openDlg(this); + connect(dlg, &CameraSettingsDialog::finished, this, &MainWindow::onCameraSettingsFinished); +} + +void MainWindow::onCameraSettingsFinished(int res) +{ + if (camStarted[0]) camManager[0]->start(); + if (camStarted[1]) camManager[1]->start(); + + emuThread->emuUnpause(); +} + +void MainWindow::onOpenAudioSettings() +{ + AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this, emuThread->emuIsActive(), emuThread); + connect(emuThread, &EmuThread::syncVolumeLevel, dlg, &AudioSettingsDialog::onSyncVolumeLevel); + connect(emuThread, &EmuThread::windowEmuStart, dlg, &AudioSettingsDialog::onConsoleReset); + connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings); + connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished); +} + +void MainWindow::onOpenFirmwareSettings() +{ + emuThread->emuPause(); + + FirmwareSettingsDialog* dlg = FirmwareSettingsDialog::openDlg(this); + connect(dlg, &FirmwareSettingsDialog::finished, this, &MainWindow::onFirmwareSettingsFinished); +} + +void MainWindow::onFirmwareSettingsFinished(int res) +{ + if (FirmwareSettingsDialog::needsReset) + onReset(); + + emuThread->emuUnpause(); +} + +void MainWindow::onOpenPathSettings() +{ + emuThread->emuPause(); + + PathSettingsDialog* dlg = PathSettingsDialog::openDlg(this); + connect(dlg, &PathSettingsDialog::finished, this, &MainWindow::onPathSettingsFinished); +} + +void MainWindow::onPathSettingsFinished(int res) +{ + if (PathSettingsDialog::needsReset) + onReset(); + + emuThread->emuUnpause(); +} + +void MainWindow::onUpdateAudioSettings() +{ + assert(emuThread->NDS != nullptr); + emuThread->NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); + + if (Config::AudioBitDepth == 0) + emuThread->NDS->SPU.SetDegrade10Bit(emuThread->NDS->ConsoleType == 0); + else + emuThread->NDS->SPU.SetDegrade10Bit(Config::AudioBitDepth == 1); +} + +void MainWindow::onAudioSettingsFinished(int res) +{ + AudioInOut::UpdateSettings(*emuThread->NDS); +} + +void MainWindow::onOpenMPSettings() +{ + emuThread->emuPause(); + + MPSettingsDialog* dlg = MPSettingsDialog::openDlg(this); + connect(dlg, &MPSettingsDialog::finished, this, &MainWindow::onMPSettingsFinished); +} + +void MainWindow::onMPSettingsFinished(int res) +{ + AudioInOut::AudioMute(mainWindow); + LocalMP::SetRecvTimeout(Config::MPRecvTimeout); + + emuThread->emuUnpause(); +} + +void MainWindow::onOpenWifiSettings() +{ + emuThread->emuPause(); + + WifiSettingsDialog* dlg = WifiSettingsDialog::openDlg(this); + connect(dlg, &WifiSettingsDialog::finished, this, &MainWindow::onWifiSettingsFinished); +} + +void MainWindow::onWifiSettingsFinished(int res) +{ + Platform::LAN_DeInit(); + Platform::LAN_Init(); + + if (WifiSettingsDialog::needsReset) + onReset(); + + emuThread->emuUnpause(); +} + +void MainWindow::onOpenInterfaceSettings() +{ + emuThread->emuPause(); + InterfaceSettingsDialog* dlg = InterfaceSettingsDialog::openDlg(this); + connect(dlg, &InterfaceSettingsDialog::finished, this, &MainWindow::onInterfaceSettingsFinished); + connect(dlg, &InterfaceSettingsDialog::updateMouseTimer, this, &MainWindow::onUpdateMouseTimer); +} + +void MainWindow::onUpdateMouseTimer() +{ + panel->mouseTimer->setInterval(Config::MouseHideSeconds*1000); +} + +void MainWindow::onInterfaceSettingsFinished(int res) +{ + emuThread->emuUnpause(); +} + +void MainWindow::onChangeSavestateSRAMReloc(bool checked) +{ + Config::SavestateRelocSRAM = checked?1:0; +} + +void MainWindow::onChangeScreenSize() +{ + int factor = ((QAction*)sender())->data().toInt(); + QSize diff = size() - panelWidget->size(); + resize(panel->screenGetMinSize(factor) + diff); +} + +void MainWindow::onChangeScreenRotation(QAction* act) +{ + int rot = act->data().toInt(); + Config::ScreenRotation = rot; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenGap(QAction* act) +{ + int gap = act->data().toInt(); + Config::ScreenGap = gap; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenLayout(QAction* act) +{ + int layout = act->data().toInt(); + Config::ScreenLayout = layout; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenSwap(bool checked) +{ + Config::ScreenSwap = checked?1:0; + + // Swap between top and bottom screen when displaying one screen. + if (Config::ScreenSizing == Frontend::screenSizing_TopOnly) + { + // Bottom Screen. + Config::ScreenSizing = Frontend::screenSizing_BotOnly; + actScreenSizing[Frontend::screenSizing_TopOnly]->setChecked(false); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + } + else if (Config::ScreenSizing == Frontend::screenSizing_BotOnly) + { + // Top Screen. + Config::ScreenSizing = Frontend::screenSizing_TopOnly; + actScreenSizing[Frontend::screenSizing_BotOnly]->setChecked(false); + actScreenSizing[Config::ScreenSizing]->setChecked(true); + } + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenSizing(QAction* act) +{ + int sizing = act->data().toInt(); + Config::ScreenSizing = sizing; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenAspect(QAction* act) +{ + int aspect = act->data().toInt(); + QActionGroup* group = act->actionGroup(); + + if (group == grpScreenAspectTop) + { + Config::ScreenAspectTop = aspect; + } + else + { + Config::ScreenAspectBot = aspect; + } + + emit screenLayoutChange(); +} + +void MainWindow::onChangeIntegerScaling(bool checked) +{ + Config::IntegerScaling = checked?1:0; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeScreenFiltering(bool checked) +{ + Config::ScreenFilter = checked?1:0; + + emit screenLayoutChange(); +} + +void MainWindow::onChangeShowOSD(bool checked) +{ + Config::ShowOSD = checked?1:0; +} +void MainWindow::onChangeLimitFramerate(bool checked) +{ + Config::LimitFPS = checked?1:0; +} + +void MainWindow::onChangeAudioSync(bool checked) +{ + Config::AudioSync = checked?1:0; +} + + +void MainWindow::onTitleUpdate(QString title) +{ + setWindowTitle(title); +} + +void ToggleFullscreen(MainWindow* mainWindow) +{ + if (!mainWindow->isFullScreen()) + { + mainWindow->showFullScreen(); + mainWindow->menuBar()->setFixedHeight(0); // Don't use hide() as menubar actions stop working + } + else + { + mainWindow->showNormal(); + int menuBarHeight = mainWindow->menuBar()->sizeHint().height(); + mainWindow->menuBar()->setFixedHeight(menuBarHeight); + } +} + +void MainWindow::onFullscreenToggled() +{ + ToggleFullscreen(this); +} + +void MainWindow::onScreenEmphasisToggled() +{ + int currentSizing = Config::ScreenSizing; + if (currentSizing == Frontend::screenSizing_EmphTop) + { + Config::ScreenSizing = Frontend::screenSizing_EmphBot; + } + else if (currentSizing == Frontend::screenSizing_EmphBot) + { + Config::ScreenSizing = Frontend::screenSizing_EmphTop; + } + + emit screenLayoutChange(); +} + +void MainWindow::onEmuStart() +{ + for (int i = 1; i < 9; i++) + { + actSaveState[i]->setEnabled(true); + actLoadState[i]->setEnabled(ROMManager::SavestateExists(i)); + } + actSaveState[0]->setEnabled(true); + actLoadState[0]->setEnabled(true); + actUndoStateLoad->setEnabled(false); + + actPause->setEnabled(true); + actPause->setChecked(false); + actReset->setEnabled(true); + actStop->setEnabled(true); + actFrameStep->setEnabled(true); + + actDateTime->setEnabled(false); + actPowerManagement->setEnabled(true); + + actTitleManager->setEnabled(false); +} + +void MainWindow::onEmuStop() +{ + emuThread->emuPause(); + + for (int i = 0; i < 9; i++) + { + actSaveState[i]->setEnabled(false); + actLoadState[i]->setEnabled(false); + } + actUndoStateLoad->setEnabled(false); + + actPause->setEnabled(false); + actReset->setEnabled(false); + actStop->setEnabled(false); + actFrameStep->setEnabled(false); + + actDateTime->setEnabled(true); + actPowerManagement->setEnabled(false); + + actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); +} + +void MainWindow::onUpdateVideoSettings(bool glchange) +{ + if (glchange) + { + emuThread->emuPause(); + if (hasOGL) emuThread->deinitContext(); + + delete panel; + createScreenPanel(); + connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint())); + } + + videoSettingsDirty = true; + + if (glchange) + { + if (hasOGL) emuThread->initContext(); + emuThread->emuUnpause(); + } +} diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h new file mode 100644 index 00000000..a84bc1d4 --- /dev/null +++ b/src/frontend/qt_sdl/Window.h @@ -0,0 +1,239 @@ +/* + 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 WINDOW_H +#define WINDOW_H + +#include "glad/glad.h" +#include "FrontendUtil.h" +#include "duckstation/gl/context.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Screen.h" + + +class EmuThread; + + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = nullptr); + ~MainWindow(); + + bool hasOGL; + GL::Context* getOGLContext(); + + bool preloadROMs(QStringList file, QStringList gbafile, bool boot); + QStringList splitArchivePath(const QString& filename, bool useMemberSyntax); + + void onAppStateChanged(Qt::ApplicationState state); + +protected: + void resizeEvent(QResizeEvent* event) override; + void changeEvent(QEvent* event) override; + + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; + + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; + + void focusInEvent(QFocusEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; + +signals: + void screenLayoutChange(); + +private slots: + void onOpenFile(); + void onClickRecentFile(); + void onClearRecentFiles(); + void onBootFirmware(); + void onInsertCart(); + void onEjectCart(); + void onInsertGBACart(); + void onInsertGBAAddon(); + void onEjectGBACart(); + void onSaveState(); + void onLoadState(); + void onUndoStateLoad(); + void onImportSavefile(); + void onQuit(); + + void onPause(bool checked); + void onReset(); + void onStop(); + void onFrameStep(); + void onOpenPowerManagement(); + void onOpenDateTime(); + void onEnableCheats(bool checked); + void onSetupCheats(); + void onCheatsDialogFinished(int res); + void onROMInfo(); + void onRAMInfo(); + void onOpenTitleManager(); + void onMPNewInstance(); + + void onOpenEmuSettings(); + void onEmuSettingsDialogFinished(int res); + void onOpenInputConfig(); + void onInputConfigFinished(int res); + void onOpenVideoSettings(); + void onOpenCameraSettings(); + void onCameraSettingsFinished(int res); + void onOpenAudioSettings(); + void onUpdateAudioSettings(); + void onAudioSettingsFinished(int res); + void onOpenMPSettings(); + void onMPSettingsFinished(int res); + void onOpenWifiSettings(); + void onWifiSettingsFinished(int res); + void onOpenFirmwareSettings(); + void onFirmwareSettingsFinished(int res); + void onOpenPathSettings(); + void onPathSettingsFinished(int res); + void onOpenInterfaceSettings(); + void onInterfaceSettingsFinished(int res); + void onUpdateMouseTimer(); + void onChangeSavestateSRAMReloc(bool checked); + void onChangeScreenSize(); + void onChangeScreenRotation(QAction* act); + void onChangeScreenGap(QAction* act); + void onChangeScreenLayout(QAction* act); + void onChangeScreenSwap(bool checked); + void onChangeScreenSizing(QAction* act); + void onChangeScreenAspect(QAction* act); + void onChangeIntegerScaling(bool checked); + void onChangeScreenFiltering(bool checked); + void onChangeShowOSD(bool checked); + void onChangeLimitFramerate(bool checked); + void onChangeAudioSync(bool checked); + + void onTitleUpdate(QString title); + + void onEmuStart(); + void onEmuStop(); + + void onUpdateVideoSettings(bool glchange); + + void onFullscreenToggled(); + void onScreenEmphasisToggled(); + +private: + virtual void closeEvent(QCloseEvent* event) override; + + QStringList currentROM; + QStringList currentGBAROM; + QList recentFileList; + QMenu *recentMenu; + void updateRecentFilesMenu(); + + bool verifySetup(); + QString pickFileFromArchive(QString archiveFileName); + QStringList pickROM(bool gba); + void updateCartInserted(bool gba); + + void createScreenPanel(); + + bool pausedManually = false; + + int oldW, oldH; + bool oldMax; + +public: + ScreenHandler* panel; + QWidget* panelWidget; + + QAction* actOpenROM; + QAction* actBootFirmware; + QAction* actCurrentCart; + QAction* actInsertCart; + QAction* actEjectCart; + QAction* actCurrentGBACart; + QAction* actInsertGBACart; + QAction* actInsertGBAAddon[1]; + QAction* actEjectGBACart; + QAction* actImportSavefile; + QAction* actSaveState[9]; + QAction* actLoadState[9]; + QAction* actUndoStateLoad; + QAction* actQuit; + + QAction* actPause; + QAction* actReset; + QAction* actStop; + QAction* actFrameStep; + QAction* actPowerManagement; + QAction* actDateTime; + QAction* actEnableCheats; + QAction* actSetupCheats; + QAction* actROMInfo; + QAction* actRAMInfo; + QAction* actTitleManager; + QAction* actMPNewInstance; + + QAction* actEmuSettings; +#ifdef __APPLE__ + QAction* actPreferences; +#endif + QAction* actInputConfig; + QAction* actVideoSettings; + QAction* actCameraSettings; + QAction* actAudioSettings; + QAction* actMPSettings; + QAction* actWifiSettings; + QAction* actFirmwareSettings; + QAction* actPathSettings; + QAction* actInterfaceSettings; + QAction* actSavestateSRAMReloc; + QAction* actScreenSize[4]; + QActionGroup* grpScreenRotation; + QAction* actScreenRotation[Frontend::screenRot_MAX]; + QActionGroup* grpScreenGap; + QAction* actScreenGap[6]; + QActionGroup* grpScreenLayout; + QAction* actScreenLayout[Frontend::screenLayout_MAX]; + QAction* actScreenSwap; + QActionGroup* grpScreenSizing; + QAction* actScreenSizing[Frontend::screenSizing_MAX]; + QAction* actIntegerScaling; + QActionGroup* grpScreenAspectTop; + QAction** actScreenAspectTop; + QActionGroup* grpScreenAspectBot; + QAction** actScreenAspectBot; + QAction* actScreenFiltering; + QAction* actShowOSD; + QAction* actLimitFramerate; + QAction* actAudioSync; +}; + +void ToggleFullscreen(MainWindow* mainWindow); + +#endif // WINDOW_H diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 725a75f0..3f53f3cc 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -109,14 +109,15 @@ // TODO: uniform variable spelling using namespace melonDS; -const QString NdsRomMimeType = "application/x-nintendo-ds-rom"; -const QStringList NdsRomExtensions { ".nds", ".srl", ".dsi", ".ids" }; +QString NdsRomMimeType = "application/x-nintendo-ds-rom"; +QStringList NdsRomExtensions { ".nds", ".srl", ".dsi", ".ids" }; + +QString GbaRomMimeType = "application/x-gba-rom"; +QStringList GbaRomExtensions { ".gba", ".agb" }; -const QString GbaRomMimeType = "application/x-gba-rom"; -const QStringList GbaRomExtensions { ".gba", ".agb" }; // This list of supported archive formats is based on libarchive(3) version 3.6.2 (2022-12-09). -const QStringList ArchiveMimeTypes +QStringList ArchiveMimeTypes { #ifdef ARCHIVE_SUPPORT_ENABLED "application/zip", @@ -138,7 +139,7 @@ const QStringList ArchiveMimeTypes #endif }; -const QStringList ArchiveExtensions +QStringList ArchiveExtensions { #ifdef ARCHIVE_SUPPORT_ENABLED ".zip", ".7z", ".rar", ".tar", @@ -171,15 +172,7 @@ bool videoSettingsDirty; CameraManager* camManager[2]; bool camStarted[2]; -const struct { int id; float ratio; const char* label; } aspectRatios[] = -{ - { 0, 1, "4:3 (native)" }, - { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"}, - { 1, (16.f / 9) / (4.f / 3), "16:9" }, - { 2, (21.f / 9) / (4.f / 3), "21:9" }, - { 3, 0, "window" } -}; -constexpr int AspectRatiosNum = sizeof(aspectRatios) / sizeof(aspectRatios[0]); +//extern int AspectRatiosNum; EmuThread::EmuThread(QObject* parent) : QThread(parent) @@ -1038,501 +1031,7 @@ void EmuThread::drawScreenGL() oglContext->SwapBuffers(); } -ScreenHandler::ScreenHandler(QWidget* widget) -{ - widget->setMouseTracking(true); - widget->setAttribute(Qt::WA_AcceptTouchEvents); - QTimer* mouseTimer = setupMouseTimer(); - widget->connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) widget->setCursor(Qt::BlankCursor);}); -} -ScreenHandler::~ScreenHandler() -{ - mouseTimer->stop(); - delete mouseTimer; -} - -void ScreenHandler::screenSetupLayout(int w, int h) -{ - int sizing = Config::ScreenSizing; - if (sizing == 3) sizing = autoScreenSizing; - - float aspectTop, aspectBot; - - for (auto ratio : aspectRatios) - { - if (ratio.id == Config::ScreenAspectTop) - aspectTop = ratio.ratio; - if (ratio.id == Config::ScreenAspectBot) - aspectBot = ratio.ratio; - } - - if (aspectTop == 0) - aspectTop = ((float) w / h) / (4.f / 3.f); - - if (aspectBot == 0) - aspectBot = ((float) w / h) / (4.f / 3.f); - - Frontend::SetupScreenLayout(w, h, - static_cast(Config::ScreenLayout), - static_cast(Config::ScreenRotation), - static_cast(sizing), - Config::ScreenGap, - Config::IntegerScaling != 0, - Config::ScreenSwap != 0, - aspectTop, - aspectBot); - - numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind); -} - -QSize ScreenHandler::screenGetMinSize(int factor = 1) -{ - bool isHori = (Config::ScreenRotation == Frontend::screenRot_90Deg - || Config::ScreenRotation == Frontend::screenRot_270Deg); - int gap = Config::ScreenGap * factor; - - int w = 256 * factor; - int h = 192 * factor; - - if (Config::ScreenSizing == Frontend::screenSizing_TopOnly - || Config::ScreenSizing == Frontend::screenSizing_BotOnly) - { - return QSize(w, h); - } - - if (Config::ScreenLayout == Frontend::screenLayout_Natural) - { - if (isHori) - return QSize(h+gap+h, w); - else - return QSize(w, h+gap+h); - } - else if (Config::ScreenLayout == Frontend::screenLayout_Vertical) - { - if (isHori) - return QSize(h, w+gap+w); - else - return QSize(w, h+gap+h); - } - else if (Config::ScreenLayout == Frontend::screenLayout_Horizontal) - { - if (isHori) - return QSize(h+gap+h, w); - else - return QSize(w+gap+w, h); - } - else // hybrid - { - if (isHori) - return QSize(h+gap+h, 3*w + (int)ceil((4*gap) / 3.0)); - else - return QSize(3*w + (int)ceil((4*gap) / 3.0), h+gap+h); - } -} - -void ScreenHandler::screenOnMousePress(QMouseEvent* event) -{ - event->accept(); - if (event->button() != Qt::LeftButton) return; - - int x = event->pos().x(); - int y = event->pos().y(); - - if (Frontend::GetTouchCoords(x, y, false)) - { - touching = true; - assert(emuThread->NDS != nullptr); - emuThread->NDS->TouchScreen(x, y); - } -} - -void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) -{ - event->accept(); - if (event->button() != Qt::LeftButton) return; - - if (touching) - { - touching = false; - assert(emuThread->NDS != nullptr); - emuThread->NDS->ReleaseScreen(); - } -} - -void ScreenHandler::screenOnMouseMove(QMouseEvent* event) -{ - event->accept(); - - showCursor(); - - if (!(event->buttons() & Qt::LeftButton)) return; - if (!touching) return; - - int x = event->pos().x(); - int y = event->pos().y(); - - if (Frontend::GetTouchCoords(x, y, true)) - { - assert(emuThread->NDS != nullptr); - emuThread->NDS->TouchScreen(x, y); - } -} - -void ScreenHandler::screenHandleTablet(QTabletEvent* event) -{ - event->accept(); - - switch(event->type()) - { - case QEvent::TabletPress: - case QEvent::TabletMove: - { - int x = event->x(); - int y = event->y(); - - if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TabletMove)) - { - touching = true; - assert(emuThread->NDS != nullptr); - emuThread->NDS->TouchScreen(x, y); - } - } - break; - case QEvent::TabletRelease: - if (touching) - { - assert(emuThread->NDS != nullptr); - emuThread->NDS->ReleaseScreen(); - touching = false; - } - break; - default: - break; - } -} - -void ScreenHandler::screenHandleTouch(QTouchEvent* event) -{ - event->accept(); - - switch(event->type()) - { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - if (event->touchPoints().length() > 0) - { - QPointF lastPosition = event->touchPoints().first().lastPos(); - int x = (int)lastPosition.x(); - int y = (int)lastPosition.y(); - - if (Frontend::GetTouchCoords(x, y, event->type()==QEvent::TouchUpdate)) - { - touching = true; - assert(emuThread->NDS != nullptr); - emuThread->NDS->TouchScreen(x, y); - } - } - break; - case QEvent::TouchEnd: - if (touching) - { - assert(emuThread->NDS != nullptr); - emuThread->NDS->ReleaseScreen(); - touching = false; - } - break; - default: - break; - } -} - -void ScreenHandler::showCursor() -{ - mainWindow->panelWidget->setCursor(Qt::ArrowCursor); - mouseTimer->start(); -} - -QTimer* ScreenHandler::setupMouseTimer() -{ - mouseTimer = new QTimer(); - mouseTimer->setSingleShot(true); - mouseTimer->setInterval(Config::MouseHideSeconds*1000); - mouseTimer->start(); - - return mouseTimer; -} - -ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenHandler(this) -{ - screen[0] = QImage(256, 192, QImage::Format_RGB32); - screen[1] = QImage(256, 192, QImage::Format_RGB32); - - screenTrans[0].reset(); - screenTrans[1].reset(); - - OSD::Init(false); -} - -ScreenPanelNative::~ScreenPanelNative() -{ - OSD::DeInit(); -} - -void ScreenPanelNative::setupScreenLayout() -{ - int w = width(); - int h = height(); - - screenSetupLayout(w, h); - - for (int i = 0; i < numScreens; i++) - { - float* mtx = screenMatrix[i]; - screenTrans[i].setMatrix(mtx[0], mtx[1], 0.f, - mtx[2], mtx[3], 0.f, - mtx[4], mtx[5], 1.f); - } -} - -void ScreenPanelNative::paintEvent(QPaintEvent* event) -{ - QPainter painter(this); - - // fill background - painter.fillRect(event->rect(), QColor::fromRgb(0, 0, 0)); - - if (emuThread->emuIsActive()) - { - assert(emuThread->NDS != nullptr); - emuThread->FrontBufferLock.lock(); - int frontbuf = emuThread->FrontBuffer; - if (!emuThread->NDS->GPU.Framebuffer[frontbuf][0] || !emuThread->NDS->GPU.Framebuffer[frontbuf][1]) - { - emuThread->FrontBufferLock.unlock(); - return; - } - - memcpy(screen[0].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][0].get(), 256 * 192 * 4); - memcpy(screen[1].scanLine(0), emuThread->NDS->GPU.Framebuffer[frontbuf][1].get(), 256 * 192 * 4); - emuThread->FrontBufferLock.unlock(); - - QRect screenrc(0, 0, 256, 192); - - for (int i = 0; i < numScreens; i++) - { - painter.setTransform(screenTrans[i]); - painter.drawImage(screenrc, screen[screenKind[i]]); - } - } - - OSD::Update(); - OSD::DrawNative(painter); -} - -void ScreenPanelNative::resizeEvent(QResizeEvent* event) -{ - setupScreenLayout(); -} - -void ScreenPanelNative::mousePressEvent(QMouseEvent* event) -{ - screenOnMousePress(event); -} - -void ScreenPanelNative::mouseReleaseEvent(QMouseEvent* event) -{ - screenOnMouseRelease(event); -} - -void ScreenPanelNative::mouseMoveEvent(QMouseEvent* event) -{ - screenOnMouseMove(event); -} - -void ScreenPanelNative::tabletEvent(QTabletEvent* event) -{ - screenHandleTablet(event); -} - -bool ScreenPanelNative::event(QEvent* event) -{ - if (event->type() == QEvent::TouchBegin - || event->type() == QEvent::TouchEnd - || event->type() == QEvent::TouchUpdate) - { - screenHandleTouch((QTouchEvent*)event); - return true; - } - return QWidget::event(event); -} - -void ScreenPanelNative::onScreenLayoutChanged() -{ - setMinimumSize(screenGetMinSize()); - setupScreenLayout(); -} - - -ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QWidget(parent), ScreenHandler(this) -{ - setAutoFillBackground(false); - setAttribute(Qt::WA_NativeWindow, true); - setAttribute(Qt::WA_NoSystemBackground, true); - setAttribute(Qt::WA_PaintOnScreen, true); - setAttribute(Qt::WA_KeyCompression, false); - setFocusPolicy(Qt::StrongFocus); - setMinimumSize(screenGetMinSize()); -} - -ScreenPanelGL::~ScreenPanelGL() -{} - -bool ScreenPanelGL::createContext() -{ - std::optional windowInfo = getWindowInfo(); - std::array versionsToTry = { - GL::Context::Version{GL::Context::Profile::Core, 4, 3}, - GL::Context::Version{GL::Context::Profile::Core, 3, 2}}; - if (windowInfo.has_value()) - { - glContext = GL::Context::Create(*getWindowInfo(), versionsToTry); - glContext->DoneCurrent(); - } - - return glContext != nullptr; -} - -qreal ScreenPanelGL::devicePixelRatioFromScreen() const -{ - const QScreen* screen_for_ratio = window()->windowHandle()->screen(); - if (!screen_for_ratio) - screen_for_ratio = QGuiApplication::primaryScreen(); - - return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); -} - -int ScreenPanelGL::scaledWindowWidth() const -{ - return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())), 1); -} - -int ScreenPanelGL::scaledWindowHeight() const -{ - return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())), 1); -} - -std::optional ScreenPanelGL::getWindowInfo() -{ - WindowInfo wi; - - // Windows and Apple are easy here since there's no display connection. - #if defined(_WIN32) - wi.type = WindowInfo::Type::Win32; - wi.window_handle = reinterpret_cast(winId()); - #elif defined(__APPLE__) - wi.type = WindowInfo::Type::MacOS; - wi.window_handle = reinterpret_cast(winId()); - #else - QPlatformNativeInterface* pni = QGuiApplication::platformNativeInterface(); - const QString platform_name = QGuiApplication::platformName(); - if (platform_name == QStringLiteral("xcb")) - { - wi.type = WindowInfo::Type::X11; - wi.display_connection = pni->nativeResourceForWindow("display", windowHandle()); - wi.window_handle = reinterpret_cast(winId()); - } - else if (platform_name == QStringLiteral("wayland")) - { - wi.type = WindowInfo::Type::Wayland; - QWindow* handle = windowHandle(); - if (handle == nullptr) - return std::nullopt; - - wi.display_connection = pni->nativeResourceForWindow("display", handle); - wi.window_handle = pni->nativeResourceForWindow("surface", handle); - } - else - { - qCritical() << "Unknown PNI platform " << platform_name; - return std::nullopt; - } - #endif - - wi.surface_width = static_cast(scaledWindowWidth()); - wi.surface_height = static_cast(scaledWindowHeight()); - wi.surface_scale = static_cast(devicePixelRatioFromScreen()); - - return wi; -} - - -QPaintEngine* ScreenPanelGL::paintEngine() const -{ - return nullptr; -} - -void ScreenPanelGL::setupScreenLayout() -{ - int w = width(); - int h = height(); - - screenSetupLayout(w, h); - if (emuThread) - transferLayout(emuThread); -} - -void ScreenPanelGL::resizeEvent(QResizeEvent* event) -{ - setupScreenLayout(); - - QWidget::resizeEvent(event); -} - -void ScreenPanelGL::mousePressEvent(QMouseEvent* event) -{ - screenOnMousePress(event); -} - -void ScreenPanelGL::mouseReleaseEvent(QMouseEvent* event) -{ - screenOnMouseRelease(event); -} - -void ScreenPanelGL::mouseMoveEvent(QMouseEvent* event) -{ - screenOnMouseMove(event); -} - -void ScreenPanelGL::tabletEvent(QTabletEvent* event) -{ - screenHandleTablet(event); -} - -bool ScreenPanelGL::event(QEvent* event) -{ - if (event->type() == QEvent::TouchBegin - || event->type() == QEvent::TouchEnd - || event->type() == QEvent::TouchUpdate) - { - screenHandleTouch((QTouchEvent*)event); - return true; - } - return QWidget::event(event); -} - -void ScreenPanelGL::transferLayout(EmuThread* thread) -{ - std::optional windowInfo = getWindowInfo(); - if (windowInfo.has_value()) - thread->updateScreenSettings(Config::ScreenFilter, *windowInfo, numScreens, screenKind, &screenMatrix[0][0]); -} - -void ScreenPanelGL::onScreenLayoutChanged() -{ - setMinimumSize(screenGetMinSize()); - setupScreenLayout(); -} static bool FileExtensionInList(const QString& filename, const QStringList& extensions, Qt::CaseSensitivity cs = Qt::CaseInsensitive) @@ -1607,1863 +1106,7 @@ static bool FileIsSupportedFiletype(const QString& filename, bool insideArchive } -#ifndef _WIN32 -static int signalFd[2]; -QSocketNotifier *signalSn; -static void signalHandler(int) -{ - char a = 1; - write(signalFd[0], &a, sizeof(a)); -} -#endif - -MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) -{ -#ifndef _WIN32 - if (socketpair(AF_UNIX, SOCK_STREAM, 0, signalFd)) - { - qFatal("Couldn't create socketpair"); - } - - signalSn = new QSocketNotifier(signalFd[1], QSocketNotifier::Read, this); - connect(signalSn, SIGNAL(activated(int)), this, SLOT(onQuit())); - - struct sigaction sa; - - sa.sa_handler = signalHandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_flags |= SA_RESTART; - sigaction(SIGINT, &sa, 0); -#endif - - oldW = Config::WindowWidth; - oldH = Config::WindowHeight; - oldMax = Config::WindowMaximized; - - setWindowTitle("melonDS " MELONDS_VERSION); - setAttribute(Qt::WA_DeleteOnClose); - setAcceptDrops(true); - setFocusPolicy(Qt::ClickFocus); - - int inst = Platform::InstanceID(); - - QMenuBar* menubar = new QMenuBar(); - { - QMenu* menu = menubar->addMenu("File"); - - actOpenROM = menu->addAction("Open ROM..."); - connect(actOpenROM, &QAction::triggered, this, &MainWindow::onOpenFile); - actOpenROM->setShortcut(QKeySequence(QKeySequence::StandardKey::Open)); - - /*actOpenROMArchive = menu->addAction("Open ROM inside archive..."); - connect(actOpenROMArchive, &QAction::triggered, this, &MainWindow::onOpenFileArchive); - actOpenROMArchive->setShortcut(QKeySequence(Qt::Key_O | Qt::CTRL | Qt::SHIFT));*/ - - recentMenu = menu->addMenu("Open recent"); - for (int i = 0; i < 10; ++i) - { - std::string item = Config::RecentROMList[i]; - if (!item.empty()) - recentFileList.push_back(QString::fromStdString(item)); - } - updateRecentFilesMenu(); - - //actBootFirmware = menu->addAction("Launch DS menu"); - actBootFirmware = menu->addAction("Boot firmware"); - connect(actBootFirmware, &QAction::triggered, this, &MainWindow::onBootFirmware); - - menu->addSeparator(); - - actCurrentCart = menu->addAction("DS slot: " + ROMManager::CartLabel()); - actCurrentCart->setEnabled(false); - - actInsertCart = menu->addAction("Insert cart..."); - connect(actInsertCart, &QAction::triggered, this, &MainWindow::onInsertCart); - - actEjectCart = menu->addAction("Eject cart"); - connect(actEjectCart, &QAction::triggered, this, &MainWindow::onEjectCart); - - menu->addSeparator(); - - actCurrentGBACart = menu->addAction("GBA slot: " + ROMManager::GBACartLabel()); - actCurrentGBACart->setEnabled(false); - - actInsertGBACart = menu->addAction("Insert ROM cart..."); - connect(actInsertGBACart, &QAction::triggered, this, &MainWindow::onInsertGBACart); - - { - QMenu* submenu = menu->addMenu("Insert add-on cart"); - - actInsertGBAAddon[0] = submenu->addAction("Memory expansion"); - actInsertGBAAddon[0]->setData(QVariant(GBAAddon_RAMExpansion)); - connect(actInsertGBAAddon[0], &QAction::triggered, this, &MainWindow::onInsertGBAAddon); - } - - actEjectGBACart = menu->addAction("Eject cart"); - connect(actEjectGBACart, &QAction::triggered, this, &MainWindow::onEjectGBACart); - - menu->addSeparator(); - - actImportSavefile = menu->addAction("Import savefile"); - connect(actImportSavefile, &QAction::triggered, this, &MainWindow::onImportSavefile); - - menu->addSeparator(); - - { - QMenu* submenu = menu->addMenu("Save state"); - - for (int i = 1; i < 9; i++) - { - actSaveState[i] = submenu->addAction(QString("%1").arg(i)); - actSaveState[i]->setShortcut(QKeySequence(Qt::ShiftModifier | (Qt::Key_F1+i-1))); - actSaveState[i]->setData(QVariant(i)); - connect(actSaveState[i], &QAction::triggered, this, &MainWindow::onSaveState); - } - - actSaveState[0] = submenu->addAction("File..."); - actSaveState[0]->setShortcut(QKeySequence(Qt::ShiftModifier | Qt::Key_F9)); - actSaveState[0]->setData(QVariant(0)); - connect(actSaveState[0], &QAction::triggered, this, &MainWindow::onSaveState); - } - { - QMenu* submenu = menu->addMenu("Load state"); - - for (int i = 1; i < 9; i++) - { - actLoadState[i] = submenu->addAction(QString("%1").arg(i)); - actLoadState[i]->setShortcut(QKeySequence(Qt::Key_F1+i-1)); - actLoadState[i]->setData(QVariant(i)); - connect(actLoadState[i], &QAction::triggered, this, &MainWindow::onLoadState); - } - - actLoadState[0] = submenu->addAction("File..."); - actLoadState[0]->setShortcut(QKeySequence(Qt::Key_F9)); - actLoadState[0]->setData(QVariant(0)); - connect(actLoadState[0], &QAction::triggered, this, &MainWindow::onLoadState); - } - - actUndoStateLoad = menu->addAction("Undo state load"); - actUndoStateLoad->setShortcut(QKeySequence(Qt::Key_F12)); - connect(actUndoStateLoad, &QAction::triggered, this, &MainWindow::onUndoStateLoad); - - menu->addSeparator(); - - actQuit = menu->addAction("Quit"); - connect(actQuit, &QAction::triggered, this, &MainWindow::onQuit); - actQuit->setShortcut(QKeySequence(QKeySequence::StandardKey::Quit)); - } - { - QMenu* menu = menubar->addMenu("System"); - - actPause = menu->addAction("Pause"); - actPause->setCheckable(true); - connect(actPause, &QAction::triggered, this, &MainWindow::onPause); - - actReset = menu->addAction("Reset"); - connect(actReset, &QAction::triggered, this, &MainWindow::onReset); - - actStop = menu->addAction("Stop"); - connect(actStop, &QAction::triggered, this, &MainWindow::onStop); - - actFrameStep = menu->addAction("Frame step"); - connect(actFrameStep, &QAction::triggered, this, &MainWindow::onFrameStep); - - menu->addSeparator(); - - actPowerManagement = menu->addAction("Power management"); - connect(actPowerManagement, &QAction::triggered, this, &MainWindow::onOpenPowerManagement); - - actDateTime = menu->addAction("Date and time"); - connect(actDateTime, &QAction::triggered, this, &MainWindow::onOpenDateTime); - - menu->addSeparator(); - - actEnableCheats = menu->addAction("Enable cheats"); - actEnableCheats->setCheckable(true); - connect(actEnableCheats, &QAction::triggered, this, &MainWindow::onEnableCheats); - - //if (inst == 0) - { - actSetupCheats = menu->addAction("Setup cheat codes"); - actSetupCheats->setMenuRole(QAction::NoRole); - connect(actSetupCheats, &QAction::triggered, this, &MainWindow::onSetupCheats); - - menu->addSeparator(); - actROMInfo = menu->addAction("ROM info"); - connect(actROMInfo, &QAction::triggered, this, &MainWindow::onROMInfo); - - actRAMInfo = menu->addAction("RAM search"); - connect(actRAMInfo, &QAction::triggered, this, &MainWindow::onRAMInfo); - - actTitleManager = menu->addAction("Manage DSi titles"); - connect(actTitleManager, &QAction::triggered, this, &MainWindow::onOpenTitleManager); - } - - { - menu->addSeparator(); - QMenu* submenu = menu->addMenu("Multiplayer"); - - actMPNewInstance = submenu->addAction("Launch new instance"); - connect(actMPNewInstance, &QAction::triggered, this, &MainWindow::onMPNewInstance); - } - } - { - QMenu* menu = menubar->addMenu("Config"); - - actEmuSettings = menu->addAction("Emu settings"); - connect(actEmuSettings, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); - -#ifdef __APPLE__ - actPreferences = menu->addAction("Preferences..."); - connect(actPreferences, &QAction::triggered, this, &MainWindow::onOpenEmuSettings); - actPreferences->setMenuRole(QAction::PreferencesRole); -#endif - - actInputConfig = menu->addAction("Input and hotkeys"); - connect(actInputConfig, &QAction::triggered, this, &MainWindow::onOpenInputConfig); - - actVideoSettings = menu->addAction("Video settings"); - connect(actVideoSettings, &QAction::triggered, this, &MainWindow::onOpenVideoSettings); - - actCameraSettings = menu->addAction("Camera settings"); - connect(actCameraSettings, &QAction::triggered, this, &MainWindow::onOpenCameraSettings); - - actAudioSettings = menu->addAction("Audio settings"); - connect(actAudioSettings, &QAction::triggered, this, &MainWindow::onOpenAudioSettings); - - actMPSettings = menu->addAction("Multiplayer settings"); - connect(actMPSettings, &QAction::triggered, this, &MainWindow::onOpenMPSettings); - - actWifiSettings = menu->addAction("Wifi settings"); - connect(actWifiSettings, &QAction::triggered, this, &MainWindow::onOpenWifiSettings); - - actFirmwareSettings = menu->addAction("Firmware settings"); - connect(actFirmwareSettings, &QAction::triggered, this, &MainWindow::onOpenFirmwareSettings); - - actInterfaceSettings = menu->addAction("Interface settings"); - connect(actInterfaceSettings, &QAction::triggered, this, &MainWindow::onOpenInterfaceSettings); - - actPathSettings = menu->addAction("Path settings"); - connect(actPathSettings, &QAction::triggered, this, &MainWindow::onOpenPathSettings); - - { - QMenu* submenu = menu->addMenu("Savestate settings"); - - actSavestateSRAMReloc = submenu->addAction("Separate savefiles"); - actSavestateSRAMReloc->setCheckable(true); - connect(actSavestateSRAMReloc, &QAction::triggered, this, &MainWindow::onChangeSavestateSRAMReloc); - } - - menu->addSeparator(); - - { - QMenu* submenu = menu->addMenu("Screen size"); - - for (int i = 0; i < 4; i++) - { - int data = i+1; - actScreenSize[i] = submenu->addAction(QString("%1x").arg(data)); - actScreenSize[i]->setData(QVariant(data)); - connect(actScreenSize[i], &QAction::triggered, this, &MainWindow::onChangeScreenSize); - } - } - { - QMenu* submenu = menu->addMenu("Screen rotation"); - grpScreenRotation = new QActionGroup(submenu); - - for (int i = 0; i < Frontend::screenRot_MAX; i++) - { - int data = i*90; - actScreenRotation[i] = submenu->addAction(QString("%1°").arg(data)); - actScreenRotation[i]->setActionGroup(grpScreenRotation); - actScreenRotation[i]->setData(QVariant(i)); - actScreenRotation[i]->setCheckable(true); - } - - connect(grpScreenRotation, &QActionGroup::triggered, this, &MainWindow::onChangeScreenRotation); - } - { - QMenu* submenu = menu->addMenu("Screen gap"); - grpScreenGap = new QActionGroup(submenu); - - const int screengap[] = {0, 1, 8, 64, 90, 128}; - - for (int i = 0; i < 6; i++) - { - int data = screengap[i]; - actScreenGap[i] = submenu->addAction(QString("%1 px").arg(data)); - actScreenGap[i]->setActionGroup(grpScreenGap); - actScreenGap[i]->setData(QVariant(data)); - actScreenGap[i]->setCheckable(true); - } - - connect(grpScreenGap, &QActionGroup::triggered, this, &MainWindow::onChangeScreenGap); - } - { - QMenu* submenu = menu->addMenu("Screen layout"); - grpScreenLayout = new QActionGroup(submenu); - - const char* screenlayout[] = {"Natural", "Vertical", "Horizontal", "Hybrid"}; - - for (int i = 0; i < Frontend::screenLayout_MAX; i++) - { - actScreenLayout[i] = submenu->addAction(QString(screenlayout[i])); - actScreenLayout[i]->setActionGroup(grpScreenLayout); - actScreenLayout[i]->setData(QVariant(i)); - actScreenLayout[i]->setCheckable(true); - } - - connect(grpScreenLayout, &QActionGroup::triggered, this, &MainWindow::onChangeScreenLayout); - - submenu->addSeparator(); - - actScreenSwap = submenu->addAction("Swap screens"); - actScreenSwap->setCheckable(true); - connect(actScreenSwap, &QAction::triggered, this, &MainWindow::onChangeScreenSwap); - } - { - QMenu* submenu = menu->addMenu("Screen sizing"); - grpScreenSizing = new QActionGroup(submenu); - - const char* screensizing[] = {"Even", "Emphasize top", "Emphasize bottom", "Auto", "Top only", "Bottom only"}; - - for (int i = 0; i < Frontend::screenSizing_MAX; i++) - { - actScreenSizing[i] = submenu->addAction(QString(screensizing[i])); - actScreenSizing[i]->setActionGroup(grpScreenSizing); - actScreenSizing[i]->setData(QVariant(i)); - actScreenSizing[i]->setCheckable(true); - } - - connect(grpScreenSizing, &QActionGroup::triggered, this, &MainWindow::onChangeScreenSizing); - - submenu->addSeparator(); - - actIntegerScaling = submenu->addAction("Force integer scaling"); - actIntegerScaling->setCheckable(true); - connect(actIntegerScaling, &QAction::triggered, this, &MainWindow::onChangeIntegerScaling); - } - { - QMenu* submenu = menu->addMenu("Aspect ratio"); - grpScreenAspectTop = new QActionGroup(submenu); - grpScreenAspectBot = new QActionGroup(submenu); - actScreenAspectTop = new QAction*[AspectRatiosNum]; - actScreenAspectBot = new QAction*[AspectRatiosNum]; - - for (int i = 0; i < 2; i++) - { - QActionGroup* group = grpScreenAspectTop; - QAction** actions = actScreenAspectTop; - - if (i == 1) - { - group = grpScreenAspectBot; - submenu->addSeparator(); - actions = actScreenAspectBot; - } - - for (int j = 0; j < AspectRatiosNum; j++) - { - auto ratio = aspectRatios[j]; - QString label = QString("%1 %2").arg(i ? "Bottom" : "Top", ratio.label); - actions[j] = submenu->addAction(label); - actions[j]->setActionGroup(group); - actions[j]->setData(QVariant(ratio.id)); - actions[j]->setCheckable(true); - } - - connect(group, &QActionGroup::triggered, this, &MainWindow::onChangeScreenAspect); - } - } - - actScreenFiltering = menu->addAction("Screen filtering"); - actScreenFiltering->setCheckable(true); - connect(actScreenFiltering, &QAction::triggered, this, &MainWindow::onChangeScreenFiltering); - - actShowOSD = menu->addAction("Show OSD"); - actShowOSD->setCheckable(true); - connect(actShowOSD, &QAction::triggered, this, &MainWindow::onChangeShowOSD); - - menu->addSeparator(); - - actLimitFramerate = menu->addAction("Limit framerate"); - actLimitFramerate->setCheckable(true); - connect(actLimitFramerate, &QAction::triggered, this, &MainWindow::onChangeLimitFramerate); - - actAudioSync = menu->addAction("Audio sync"); - actAudioSync->setCheckable(true); - connect(actAudioSync, &QAction::triggered, this, &MainWindow::onChangeAudioSync); - } - setMenuBar(menubar); - - resize(Config::WindowWidth, Config::WindowHeight); - - if (Config::FirmwareUsername == "Arisotura") - actMPNewInstance->setText("Fart"); - -#ifdef Q_OS_MAC - QPoint screenCenter = screen()->availableGeometry().center(); - QRect frameGeo = frameGeometry(); - frameGeo.moveCenter(screenCenter); - move(frameGeo.topLeft()); -#endif - - if (oldMax) - showMaximized(); - else - show(); - - createScreenPanel(); - - actEjectCart->setEnabled(false); - actEjectGBACart->setEnabled(false); - - if (Config::ConsoleType == 1) - { - actInsertGBACart->setEnabled(false); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(false); - } - - for (int i = 0; i < 9; i++) - { - actSaveState[i]->setEnabled(false); - actLoadState[i]->setEnabled(false); - } - actUndoStateLoad->setEnabled(false); - actImportSavefile->setEnabled(false); - - actPause->setEnabled(false); - actReset->setEnabled(false); - actStop->setEnabled(false); - actFrameStep->setEnabled(false); - - actDateTime->setEnabled(true); - actPowerManagement->setEnabled(false); - - actSetupCheats->setEnabled(false); - actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); - - actEnableCheats->setChecked(Config::EnableCheats); - - actROMInfo->setEnabled(false); - actRAMInfo->setEnabled(false); - - actSavestateSRAMReloc->setChecked(Config::SavestateRelocSRAM); - - actScreenRotation[Config::ScreenRotation]->setChecked(true); - - for (int i = 0; i < 6; i++) - { - if (actScreenGap[i]->data().toInt() == Config::ScreenGap) - { - actScreenGap[i]->setChecked(true); - break; - } - } - - actScreenLayout[Config::ScreenLayout]->setChecked(true); - actScreenSizing[Config::ScreenSizing]->setChecked(true); - actIntegerScaling->setChecked(Config::IntegerScaling); - - actScreenSwap->setChecked(Config::ScreenSwap); - - for (int i = 0; i < AspectRatiosNum; i++) - { - if (Config::ScreenAspectTop == aspectRatios[i].id) - actScreenAspectTop[i]->setChecked(true); - if (Config::ScreenAspectBot == aspectRatios[i].id) - actScreenAspectBot[i]->setChecked(true); - } - - actScreenFiltering->setChecked(Config::ScreenFilter); - actShowOSD->setChecked(Config::ShowOSD); - - actLimitFramerate->setChecked(Config::LimitFPS); - actAudioSync->setChecked(Config::AudioSync); - - if (inst > 0) - { - actEmuSettings->setEnabled(false); - actVideoSettings->setEnabled(false); - actMPSettings->setEnabled(false); - actWifiSettings->setEnabled(false); - actInterfaceSettings->setEnabled(false); - -#ifdef __APPLE__ - actPreferences->setEnabled(false); -#endif // __APPLE__ - } -} - -MainWindow::~MainWindow() -{ - delete[] actScreenAspectTop; - delete[] actScreenAspectBot; -} - -void MainWindow::closeEvent(QCloseEvent* event) -{ - if (hasOGL) - { - // we intentionally don't unpause here - emuThread->emuPause(); - emuThread->deinitContext(); - } - - QMainWindow::closeEvent(event); -} - -void MainWindow::createScreenPanel() -{ - hasOGL = (Config::ScreenUseGL != 0) || (Config::_3DRenderer != 0); - - if (hasOGL) - { - ScreenPanelGL* panelGL = new ScreenPanelGL(this); - panelGL->show(); - - panel = panelGL; - panelWidget = panelGL; - - panelGL->createContext(); - } - - if (!hasOGL) - { - ScreenPanelNative* panelNative = new ScreenPanelNative(this); - panel = panelNative; - panelWidget = panelNative; - panelWidget->show(); - } - setCentralWidget(panelWidget); - - actScreenFiltering->setEnabled(hasOGL); - - connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged())); - emit screenLayoutChange(); -} - -GL::Context* MainWindow::getOGLContext() -{ - if (!hasOGL) return nullptr; - - ScreenPanelGL* glpanel = static_cast(panel); - return glpanel->getContext(); -} - -void MainWindow::resizeEvent(QResizeEvent* event) -{ - int w = event->size().width(); - int h = event->size().height(); - - if (!isFullScreen()) - { - // this is ugly - // thing is, when maximizing the window, we first receive the resizeEvent - // with a new size matching the screen, then the changeEvent telling us that - // the maximized flag was updated - oldW = Config::WindowWidth; - oldH = Config::WindowHeight; - oldMax = isMaximized(); - - Config::WindowWidth = w; - Config::WindowHeight = h; - } -} - -void MainWindow::changeEvent(QEvent* event) -{ - if (isMaximized() && !oldMax) - { - Config::WindowWidth = oldW; - Config::WindowHeight = oldH; - } - - Config::WindowMaximized = isMaximized() ? 1:0; -} - -void MainWindow::keyPressEvent(QKeyEvent* event) -{ - if (event->isAutoRepeat()) return; - - // TODO!! REMOVE ME IN RELEASE BUILDS!! - //if (event->key() == Qt::Key_F11) NDS::debug(0); - - Input::KeyPress(event); -} - -void MainWindow::keyReleaseEvent(QKeyEvent* event) -{ - if (event->isAutoRepeat()) return; - - Input::KeyRelease(event); -} - - -void MainWindow::dragEnterEvent(QDragEnterEvent* event) -{ - if (!event->mimeData()->hasUrls()) return; - - QList urls = event->mimeData()->urls(); - if (urls.count() > 1) return; // not handling more than one file at once - - QString filename = urls.at(0).toLocalFile(); - - if (FileIsSupportedFiletype(filename)) - event->acceptProposedAction(); -} - -void MainWindow::dropEvent(QDropEvent* event) -{ - if (!event->mimeData()->hasUrls()) return; - - QList urls = event->mimeData()->urls(); - if (urls.count() > 1) return; // not handling more than one file at once - - emuThread->emuPause(); - - if (!verifySetup()) - { - emuThread->emuUnpause(); - return; - } - - const QStringList file = splitArchivePath(urls.at(0).toLocalFile(), false); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - const QString filename = file.last(); - const bool romInsideArchive = file.size() > 1; - const auto matchMode = romInsideArchive ? QMimeDatabase::MatchExtension : QMimeDatabase::MatchDefault; - const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, matchMode); - - bool isNdsRom = NdsRomByExtension(filename) || NdsRomByMimetype(mimetype); - bool isGbaRom = GbaRomByExtension(filename) || GbaRomByMimetype(mimetype); - isNdsRom |= ZstdNdsRomByExtension(filename); - isGbaRom |= ZstdGbaRomByExtension(filename); - - if (isNdsRom) - { - if (!ROMManager::LoadROM(emuThread, file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the DS ROM."); - emuThread->emuUnpause(); - return; - } - - const QString barredFilename = file.join('|'); - recentFileList.removeAll(barredFilename); - recentFileList.prepend(barredFilename); - updateRecentFilesMenu(); - - assert(emuThread->NDS != nullptr); - emuThread->NDS->Start(); - emuThread->emuRun(); - - updateCartInserted(false); - } - else if (isGbaRom) - { - if (!ROMManager::LoadGBAROM(*emuThread->NDS, file)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); - emuThread->emuUnpause(); - return; - } - - emuThread->emuUnpause(); - - updateCartInserted(true); - } - else - { - QMessageBox::critical(this, "melonDS", "The file could not be recognized as a DS or GBA ROM."); - emuThread->emuUnpause(); - return; - } -} - -void MainWindow::focusInEvent(QFocusEvent* event) -{ - AudioInOut::AudioMute(mainWindow); -} - -void MainWindow::focusOutEvent(QFocusEvent* event) -{ - AudioInOut::AudioMute(mainWindow); -} - -void MainWindow::onAppStateChanged(Qt::ApplicationState state) -{ - if (state == Qt::ApplicationInactive) - { - if (Config::PauseLostFocus && emuThread->emuIsRunning()) - emuThread->emuPause(); - } - else if (state == Qt::ApplicationActive) - { - if (Config::PauseLostFocus && !pausedManually) - emuThread->emuUnpause(); - } -} - -bool MainWindow::verifySetup() -{ - QString res = ROMManager::VerifySetup(); - if (!res.isEmpty()) - { - QMessageBox::critical(this, "melonDS", res); - return false; - } - - return true; -} - -bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) -{ - if (!verifySetup()) - { - return false; - } - - bool gbaloaded = false; - if (!gbafile.isEmpty()) - { - if (!ROMManager::LoadGBAROM(*emuThread->NDS, gbafile)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); - return false; - } - - gbaloaded = true; - } - - bool ndsloaded = false; - if (!file.isEmpty()) - { - if (!ROMManager::LoadROM(emuThread, file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - return false; - } - recentFileList.removeAll(file.join("|")); - recentFileList.prepend(file.join("|")); - updateRecentFilesMenu(); - ndsloaded = true; - } - - if (boot) - { - if (ndsloaded) - { - emuThread->NDS->Start(); - emuThread->emuRun(); - } - else - { - onBootFirmware(); - } - } - - updateCartInserted(false); - - if (gbaloaded) - { - updateCartInserted(true); - } - - return true; -} - -QStringList MainWindow::splitArchivePath(const QString& filename, bool useMemberSyntax) -{ - if (filename.isEmpty()) return {}; - -#ifdef ARCHIVE_SUPPORT_ENABLED - if (useMemberSyntax) - { - const QStringList filenameParts = filename.split('|'); - if (filenameParts.size() > 2) - { - QMessageBox::warning(this, "melonDS", "This path contains too many '|'."); - return {}; - } - - if (filenameParts.size() == 2) - { - const QString archive = filenameParts.at(0); - if (!QFileInfo(archive).exists()) - { - QMessageBox::warning(this, "melonDS", "This archive does not exist."); - return {}; - } - - const QString subfile = filenameParts.at(1); - if (!Archive::ListArchive(archive).contains(subfile)) - { - QMessageBox::warning(this, "melonDS", "This archive does not contain the desired file."); - return {}; - } - - return filenameParts; - } - } -#endif - - if (!QFileInfo(filename).exists()) - { - QMessageBox::warning(this, "melonDS", "This ROM file does not exist."); - return {}; - } - -#ifdef ARCHIVE_SUPPORT_ENABLED - if (SupportedArchiveByExtension(filename) - || SupportedArchiveByMimetype(QMimeDatabase().mimeTypeForFile(filename))) - { - const QString subfile = pickFileFromArchive(filename); - if (subfile.isEmpty()) - return {}; - - return { filename, subfile }; - } -#endif - - return { filename }; -} - -QString MainWindow::pickFileFromArchive(QString archiveFileName) -{ - QVector archiveROMList = Archive::ListArchive(archiveFileName); - - if (archiveROMList.size() <= 1) - { - if (!archiveROMList.isEmpty() && archiveROMList.at(0) == "OK") - QMessageBox::warning(this, "melonDS", "This archive is empty."); - else - QMessageBox::critical(this, "melonDS", "This archive could not be read. It may be corrupt or you don't have the permissions."); - return QString(); - } - - archiveROMList.removeFirst(); - - const auto notSupportedRom = [&](const auto& filename){ - if (NdsRomByExtension(filename) || GbaRomByExtension(filename)) - return false; - const QMimeType mimetype = QMimeDatabase().mimeTypeForFile(filename, QMimeDatabase::MatchExtension); - return !(NdsRomByMimetype(mimetype) || GbaRomByMimetype(mimetype)); - }; - - archiveROMList.erase(std::remove_if(archiveROMList.begin(), archiveROMList.end(), notSupportedRom), - archiveROMList.end()); - - if (archiveROMList.isEmpty()) - { - QMessageBox::warning(this, "melonDS", "This archive does not contain any supported ROMs."); - return QString(); - } - - if (archiveROMList.size() == 1) - return archiveROMList.first(); - - bool ok; - const QString toLoad = QInputDialog::getItem( - this, "melonDS", - "This archive contains multiple files. Select which ROM you want to load.", - archiveROMList.toList(), 0, false, &ok - ); - - if (ok) return toLoad; - - // User clicked on cancel - - return QString(); -} - -QStringList MainWindow::pickROM(bool gba) -{ - const QString console = gba ? "GBA" : "DS"; - const QStringList& romexts = gba ? GbaRomExtensions : NdsRomExtensions; - - QString rawROMs = romexts.join(" *"); - QString extraFilters = ";;" + console + " ROMs (*" + rawROMs; - QString allROMs = rawROMs; - - QString zstdROMs = "*" + romexts.join(".zst *") + ".zst"; - extraFilters += ");;Zstandard-compressed " + console + " ROMs (" + zstdROMs + ")"; - allROMs += " " + zstdROMs; - -#ifdef ARCHIVE_SUPPORT_ENABLED - QString archives = "*" + ArchiveExtensions.join(" *"); - extraFilters += ";;Archives (" + archives + ")"; - allROMs += " " + archives; -#endif - extraFilters += ";;All files (*.*)"; - - const QString filename = QFileDialog::getOpenFileName( - this, "Open " + console + " ROM", - QString::fromStdString(Config::LastROMFolder), - "All supported files (*" + allROMs + ")" + extraFilters - ); - - if (filename.isEmpty()) return {}; - - Config::LastROMFolder = QFileInfo(filename).dir().path().toStdString(); - return splitArchivePath(filename, false); -} - -void MainWindow::updateCartInserted(bool gba) -{ - bool inserted; - if (gba) - { - inserted = ROMManager::GBACartInserted() && (Config::ConsoleType == 0); - actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); - actEjectGBACart->setEnabled(inserted); - } - else - { - inserted = ROMManager::CartInserted(); - actCurrentCart->setText("DS slot: " + ROMManager::CartLabel()); - actEjectCart->setEnabled(inserted); - actImportSavefile->setEnabled(inserted); - actSetupCheats->setEnabled(inserted); - actROMInfo->setEnabled(inserted); - actRAMInfo->setEnabled(inserted); - } -} - -void MainWindow::onOpenFile() -{ - emuThread->emuPause(); - - if (!verifySetup()) - { - emuThread->emuUnpause(); - return; - } - - QStringList file = pickROM(false); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadROM(emuThread, file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - emuThread->emuUnpause(); - return; - } - - QString filename = file.join('|'); - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); - - assert(emuThread->NDS != nullptr); - emuThread->NDS->Start(); - emuThread->emuRun(); - - updateCartInserted(false); -} - -void MainWindow::onClearRecentFiles() -{ - recentFileList.clear(); - for (int i = 0; i < 10; i++) - Config::RecentROMList[i] = ""; - updateRecentFilesMenu(); -} - -void MainWindow::updateRecentFilesMenu() -{ - recentMenu->clear(); - - for (int i = 0; i < recentFileList.size(); ++i) - { - if (i >= 10) break; - - QString item_full = recentFileList.at(i); - QString item_display = item_full; - int itemlen = item_full.length(); - const int maxlen = 100; - if (itemlen > maxlen) - { - int cut_start = 0; - while (item_full[cut_start] != '/' && item_full[cut_start] != '\\' && - cut_start < itemlen) - cut_start++; - - int cut_end = itemlen-1; - while (((item_full[cut_end] != '/' && item_full[cut_end] != '\\') || - (cut_start+4+(itemlen-cut_end) < maxlen)) && - cut_end > 0) - cut_end--; - - item_display.truncate(cut_start+1); - item_display += "..."; - item_display += QString(item_full).remove(0, cut_end); - } - - QAction *actRecentFile_i = recentMenu->addAction(QString("%1. %2").arg(i+1).arg(item_display)); - actRecentFile_i->setData(item_full); - connect(actRecentFile_i, &QAction::triggered, this, &MainWindow::onClickRecentFile); - - Config::RecentROMList[i] = recentFileList.at(i).toStdString(); - } - - while (recentFileList.size() > 10) - recentFileList.removeLast(); - - recentMenu->addSeparator(); - - QAction *actClearRecentList = recentMenu->addAction("Clear"); - connect(actClearRecentList, &QAction::triggered, this, &MainWindow::onClearRecentFiles); - - if (recentFileList.empty()) - actClearRecentList->setEnabled(false); - - Config::Save(); -} - -void MainWindow::onClickRecentFile() -{ - QAction *act = (QAction *)sender(); - QString filename = act->data().toString(); - - emuThread->emuPause(); - - if (!verifySetup()) - { - emuThread->emuUnpause(); - return; - } - - const QStringList file = splitArchivePath(filename, true); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadROM(emuThread, file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - emuThread->emuUnpause(); - return; - } - - recentFileList.removeAll(filename); - recentFileList.prepend(filename); - updateRecentFilesMenu(); - - assert(emuThread->NDS != nullptr); - emuThread->NDS->Start(); - emuThread->emuRun(); - - updateCartInserted(false); -} - -void MainWindow::onBootFirmware() -{ - emuThread->emuPause(); - - if (!verifySetup()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::BootToMenu(emuThread)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "This firmware is not bootable."); - emuThread->emuUnpause(); - return; - } - - assert(emuThread->NDS != nullptr); - emuThread->NDS->Start(); - emuThread->emuRun(); -} - -void MainWindow::onInsertCart() -{ - emuThread->emuPause(); - - QStringList file = pickROM(false); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadROM(emuThread, file, false)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - emuThread->emuUnpause(); - return; - } - - emuThread->emuUnpause(); - - updateCartInserted(false); -} - -void MainWindow::onEjectCart() -{ - emuThread->emuPause(); - - ROMManager::EjectCart(*emuThread->NDS); - - emuThread->emuUnpause(); - - updateCartInserted(false); -} - -void MainWindow::onInsertGBACart() -{ - emuThread->emuPause(); - - QStringList file = pickROM(true); - if (file.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - if (!ROMManager::LoadGBAROM(*emuThread->NDS, file)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - emuThread->emuUnpause(); - return; - } - - emuThread->emuUnpause(); - - updateCartInserted(true); -} - -void MainWindow::onInsertGBAAddon() -{ - QAction* act = (QAction*)sender(); - int type = act->data().toInt(); - - emuThread->emuPause(); - - ROMManager::LoadGBAAddon(*emuThread->NDS, type); - - emuThread->emuUnpause(); - - updateCartInserted(true); -} - -void MainWindow::onEjectGBACart() -{ - emuThread->emuPause(); - - ROMManager::EjectGBACart(*emuThread->NDS); - - emuThread->emuUnpause(); - - updateCartInserted(true); -} - -void MainWindow::onSaveState() -{ - int slot = ((QAction*)sender())->data().toInt(); - - emuThread->emuPause(); - - std::string filename; - if (slot > 0) - { - filename = ROMManager::GetSavestateName(slot); - } - else - { - // TODO: specific 'last directory' for savestate files? - QString qfilename = QFileDialog::getSaveFileName(this, - "Save state", - QString::fromStdString(Config::LastROMFolder), - "melonDS savestates (*.mln);;Any file (*.*)"); - if (qfilename.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - filename = qfilename.toStdString(); - } - - if (ROMManager::SaveState(*emuThread->NDS, filename)) - { - char msg[64]; - if (slot > 0) sprintf(msg, "State saved to slot %d", slot); - else sprintf(msg, "State saved to file"); - OSD::AddMessage(0, msg); - - actLoadState[slot]->setEnabled(true); - } - else - { - OSD::AddMessage(0xFFA0A0, "State save failed"); - } - - emuThread->emuUnpause(); -} - -void MainWindow::onLoadState() -{ - int slot = ((QAction*)sender())->data().toInt(); - - emuThread->emuPause(); - - std::string filename; - if (slot > 0) - { - filename = ROMManager::GetSavestateName(slot); - } - else - { - // TODO: specific 'last directory' for savestate files? - QString qfilename = QFileDialog::getOpenFileName(this, - "Load state", - QString::fromStdString(Config::LastROMFolder), - "melonDS savestates (*.ml*);;Any file (*.*)"); - if (qfilename.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - filename = qfilename.toStdString(); - } - - if (!Platform::FileExists(filename)) - { - char msg[64]; - if (slot > 0) sprintf(msg, "State slot %d is empty", slot); - else sprintf(msg, "State file does not exist"); - OSD::AddMessage(0xFFA0A0, msg); - - emuThread->emuUnpause(); - return; - } - - if (ROMManager::LoadState(*emuThread->NDS, filename)) - { - char msg[64]; - if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); - else sprintf(msg, "State loaded from file"); - OSD::AddMessage(0, msg); - - actUndoStateLoad->setEnabled(true); - } - else - { - OSD::AddMessage(0xFFA0A0, "State load failed"); - } - - emuThread->emuUnpause(); -} - -void MainWindow::onUndoStateLoad() -{ - emuThread->emuPause(); - ROMManager::UndoStateLoad(*emuThread->NDS); - emuThread->emuUnpause(); - - OSD::AddMessage(0, "State load undone"); -} - -void MainWindow::onImportSavefile() -{ - emuThread->emuPause(); - QString path = QFileDialog::getOpenFileName(this, - "Select savefile", - QString::fromStdString(Config::LastROMFolder), - "Savefiles (*.sav *.bin *.dsv);;Any file (*.*)"); - - if (path.isEmpty()) - { - emuThread->emuUnpause(); - return; - } - - Platform::FileHandle* f = Platform::OpenFile(path.toStdString(), Platform::FileMode::Read); - if (!f) - { - QMessageBox::critical(this, "melonDS", "Could not open the given savefile."); - emuThread->emuUnpause(); - return; - } - - if (RunningSomething) - { - if (QMessageBox::warning(this, - "melonDS", - "The emulation will be reset and the current savefile overwritten.", - QMessageBox::Ok, QMessageBox::Cancel) != QMessageBox::Ok) - { - emuThread->emuUnpause(); - return; - } - - ROMManager::Reset(emuThread); - } - - u32 len = FileLength(f); - - std::unique_ptr data = std::make_unique(len); - Platform::FileRewind(f); - Platform::FileRead(data.get(), len, 1, f); - - assert(emuThread->NDS != nullptr); - emuThread->NDS->SetNDSSave(data.get(), len); - - CloseFile(f); - emuThread->emuUnpause(); -} - -void MainWindow::onQuit() -{ -#ifndef _WIN32 - signalSn->setEnabled(false); -#endif - QApplication::quit(); -} - - -void MainWindow::onPause(bool checked) -{ - if (!RunningSomething) return; - - if (checked) - { - emuThread->emuPause(); - OSD::AddMessage(0, "Paused"); - pausedManually = true; - } - else - { - emuThread->emuUnpause(); - OSD::AddMessage(0, "Resumed"); - pausedManually = false; - } -} - -void MainWindow::onReset() -{ - if (!RunningSomething) return; - - emuThread->emuPause(); - - actUndoStateLoad->setEnabled(false); - - ROMManager::Reset(emuThread); - - OSD::AddMessage(0, "Reset"); - emuThread->emuRun(); -} - -void MainWindow::onStop() -{ - if (!RunningSomething) return; - - emuThread->emuPause(); - emuThread->NDS->Stop(); -} - -void MainWindow::onFrameStep() -{ - if (!RunningSomething) return; - - emuThread->emuFrameStep(); -} - -void MainWindow::onOpenDateTime() -{ - DateTimeDialog* dlg = DateTimeDialog::openDlg(this); -} - -void MainWindow::onOpenPowerManagement() -{ - PowerManagementDialog* dlg = PowerManagementDialog::openDlg(this, emuThread); -} - -void MainWindow::onEnableCheats(bool checked) -{ - Config::EnableCheats = checked?1:0; - ROMManager::EnableCheats(*emuThread->NDS, Config::EnableCheats != 0); -} - -void MainWindow::onSetupCheats() -{ - emuThread->emuPause(); - - CheatsDialog* dlg = CheatsDialog::openDlg(this); - connect(dlg, &CheatsDialog::finished, this, &MainWindow::onCheatsDialogFinished); -} - -void MainWindow::onCheatsDialogFinished(int res) -{ - emuThread->emuUnpause(); -} - -void MainWindow::onROMInfo() -{ - auto cart = emuThread->NDS->NDSCartSlot.GetCart(); - if (cart) - ROMInfoDialog* dlg = ROMInfoDialog::openDlg(this, *cart); -} - -void MainWindow::onRAMInfo() -{ - RAMInfoDialog* dlg = RAMInfoDialog::openDlg(this, emuThread); -} - -void MainWindow::onOpenTitleManager() -{ - TitleManagerDialog* dlg = TitleManagerDialog::openDlg(this); -} - -void MainWindow::onMPNewInstance() -{ - //QProcess::startDetached(QApplication::applicationFilePath()); - QProcess newinst; - newinst.setProgram(QApplication::applicationFilePath()); - newinst.setArguments(QApplication::arguments().mid(1, QApplication::arguments().length()-1)); - -#ifdef __WIN32__ - newinst.setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments *args) - { - args->flags |= CREATE_NEW_CONSOLE; - }); -#endif - - newinst.startDetached(); -} - -void MainWindow::onOpenEmuSettings() -{ - emuThread->emuPause(); - - EmuSettingsDialog* dlg = EmuSettingsDialog::openDlg(this); - connect(dlg, &EmuSettingsDialog::finished, this, &MainWindow::onEmuSettingsDialogFinished); -} - -void MainWindow::onEmuSettingsDialogFinished(int res) -{ - emuThread->emuUnpause(); - - if (Config::ConsoleType == 1) - { - actInsertGBACart->setEnabled(false); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(false); - actEjectGBACart->setEnabled(false); - } - else - { - actInsertGBACart->setEnabled(true); - for (int i = 0; i < 1; i++) - actInsertGBAAddon[i]->setEnabled(true); - actEjectGBACart->setEnabled(ROMManager::GBACartInserted()); - } - - if (EmuSettingsDialog::needsReset) - onReset(); - - actCurrentGBACart->setText("GBA slot: " + ROMManager::GBACartLabel()); - - if (!RunningSomething) - actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); -} - -void MainWindow::onOpenInputConfig() -{ - emuThread->emuPause(); - - InputConfigDialog* dlg = InputConfigDialog::openDlg(this); - connect(dlg, &InputConfigDialog::finished, this, &MainWindow::onInputConfigFinished); -} - -void MainWindow::onInputConfigFinished(int res) -{ - emuThread->emuUnpause(); -} - -void MainWindow::onOpenVideoSettings() -{ - VideoSettingsDialog* dlg = VideoSettingsDialog::openDlg(this); - connect(dlg, &VideoSettingsDialog::updateVideoSettings, this, &MainWindow::onUpdateVideoSettings); -} - -void MainWindow::onOpenCameraSettings() -{ - emuThread->emuPause(); - - camStarted[0] = camManager[0]->isStarted(); - camStarted[1] = camManager[1]->isStarted(); - if (camStarted[0]) camManager[0]->stop(); - if (camStarted[1]) camManager[1]->stop(); - - CameraSettingsDialog* dlg = CameraSettingsDialog::openDlg(this); - connect(dlg, &CameraSettingsDialog::finished, this, &MainWindow::onCameraSettingsFinished); -} - -void MainWindow::onCameraSettingsFinished(int res) -{ - if (camStarted[0]) camManager[0]->start(); - if (camStarted[1]) camManager[1]->start(); - - emuThread->emuUnpause(); -} - -void MainWindow::onOpenAudioSettings() -{ - AudioSettingsDialog* dlg = AudioSettingsDialog::openDlg(this, emuThread->emuIsActive(), emuThread); - connect(emuThread, &EmuThread::syncVolumeLevel, dlg, &AudioSettingsDialog::onSyncVolumeLevel); - connect(emuThread, &EmuThread::windowEmuStart, dlg, &AudioSettingsDialog::onConsoleReset); - connect(dlg, &AudioSettingsDialog::updateAudioSettings, this, &MainWindow::onUpdateAudioSettings); - connect(dlg, &AudioSettingsDialog::finished, this, &MainWindow::onAudioSettingsFinished); -} - -void MainWindow::onOpenFirmwareSettings() -{ - emuThread->emuPause(); - - FirmwareSettingsDialog* dlg = FirmwareSettingsDialog::openDlg(this); - connect(dlg, &FirmwareSettingsDialog::finished, this, &MainWindow::onFirmwareSettingsFinished); -} - -void MainWindow::onFirmwareSettingsFinished(int res) -{ - if (FirmwareSettingsDialog::needsReset) - onReset(); - - emuThread->emuUnpause(); -} - -void MainWindow::onOpenPathSettings() -{ - emuThread->emuPause(); - - PathSettingsDialog* dlg = PathSettingsDialog::openDlg(this); - connect(dlg, &PathSettingsDialog::finished, this, &MainWindow::onPathSettingsFinished); -} - -void MainWindow::onPathSettingsFinished(int res) -{ - if (PathSettingsDialog::needsReset) - onReset(); - - emuThread->emuUnpause(); -} - -void MainWindow::onUpdateAudioSettings() -{ - assert(emuThread->NDS != nullptr); - emuThread->NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); - - if (Config::AudioBitDepth == 0) - emuThread->NDS->SPU.SetDegrade10Bit(emuThread->NDS->ConsoleType == 0); - else - emuThread->NDS->SPU.SetDegrade10Bit(Config::AudioBitDepth == 1); -} - -void MainWindow::onAudioSettingsFinished(int res) -{ - AudioInOut::UpdateSettings(*emuThread->NDS); -} - -void MainWindow::onOpenMPSettings() -{ - emuThread->emuPause(); - - MPSettingsDialog* dlg = MPSettingsDialog::openDlg(this); - connect(dlg, &MPSettingsDialog::finished, this, &MainWindow::onMPSettingsFinished); -} - -void MainWindow::onMPSettingsFinished(int res) -{ - AudioInOut::AudioMute(mainWindow); - LocalMP::SetRecvTimeout(Config::MPRecvTimeout); - - emuThread->emuUnpause(); -} - -void MainWindow::onOpenWifiSettings() -{ - emuThread->emuPause(); - - WifiSettingsDialog* dlg = WifiSettingsDialog::openDlg(this); - connect(dlg, &WifiSettingsDialog::finished, this, &MainWindow::onWifiSettingsFinished); -} - -void MainWindow::onWifiSettingsFinished(int res) -{ - Platform::LAN_DeInit(); - Platform::LAN_Init(); - - if (WifiSettingsDialog::needsReset) - onReset(); - - emuThread->emuUnpause(); -} - -void MainWindow::onOpenInterfaceSettings() -{ - emuThread->emuPause(); - InterfaceSettingsDialog* dlg = InterfaceSettingsDialog::openDlg(this); - connect(dlg, &InterfaceSettingsDialog::finished, this, &MainWindow::onInterfaceSettingsFinished); - connect(dlg, &InterfaceSettingsDialog::updateMouseTimer, this, &MainWindow::onUpdateMouseTimer); -} - -void MainWindow::onUpdateMouseTimer() -{ - panel->mouseTimer->setInterval(Config::MouseHideSeconds*1000); -} - -void MainWindow::onInterfaceSettingsFinished(int res) -{ - emuThread->emuUnpause(); -} - -void MainWindow::onChangeSavestateSRAMReloc(bool checked) -{ - Config::SavestateRelocSRAM = checked?1:0; -} - -void MainWindow::onChangeScreenSize() -{ - int factor = ((QAction*)sender())->data().toInt(); - QSize diff = size() - panelWidget->size(); - resize(panel->screenGetMinSize(factor) + diff); -} - -void MainWindow::onChangeScreenRotation(QAction* act) -{ - int rot = act->data().toInt(); - Config::ScreenRotation = rot; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenGap(QAction* act) -{ - int gap = act->data().toInt(); - Config::ScreenGap = gap; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenLayout(QAction* act) -{ - int layout = act->data().toInt(); - Config::ScreenLayout = layout; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenSwap(bool checked) -{ - Config::ScreenSwap = checked?1:0; - - // Swap between top and bottom screen when displaying one screen. - if (Config::ScreenSizing == Frontend::screenSizing_TopOnly) - { - // Bottom Screen. - Config::ScreenSizing = Frontend::screenSizing_BotOnly; - actScreenSizing[Frontend::screenSizing_TopOnly]->setChecked(false); - actScreenSizing[Config::ScreenSizing]->setChecked(true); - } - else if (Config::ScreenSizing == Frontend::screenSizing_BotOnly) - { - // Top Screen. - Config::ScreenSizing = Frontend::screenSizing_TopOnly; - actScreenSizing[Frontend::screenSizing_BotOnly]->setChecked(false); - actScreenSizing[Config::ScreenSizing]->setChecked(true); - } - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenSizing(QAction* act) -{ - int sizing = act->data().toInt(); - Config::ScreenSizing = sizing; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenAspect(QAction* act) -{ - int aspect = act->data().toInt(); - QActionGroup* group = act->actionGroup(); - - if (group == grpScreenAspectTop) - { - Config::ScreenAspectTop = aspect; - } - else - { - Config::ScreenAspectBot = aspect; - } - - emit screenLayoutChange(); -} - -void MainWindow::onChangeIntegerScaling(bool checked) -{ - Config::IntegerScaling = checked?1:0; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeScreenFiltering(bool checked) -{ - Config::ScreenFilter = checked?1:0; - - emit screenLayoutChange(); -} - -void MainWindow::onChangeShowOSD(bool checked) -{ - Config::ShowOSD = checked?1:0; -} -void MainWindow::onChangeLimitFramerate(bool checked) -{ - Config::LimitFPS = checked?1:0; -} - -void MainWindow::onChangeAudioSync(bool checked) -{ - Config::AudioSync = checked?1:0; -} - - -void MainWindow::onTitleUpdate(QString title) -{ - setWindowTitle(title); -} - -void ToggleFullscreen(MainWindow* mainWindow) -{ - if (!mainWindow->isFullScreen()) - { - mainWindow->showFullScreen(); - mainWindow->menuBar()->setFixedHeight(0); // Don't use hide() as menubar actions stop working - } - else - { - mainWindow->showNormal(); - int menuBarHeight = mainWindow->menuBar()->sizeHint().height(); - mainWindow->menuBar()->setFixedHeight(menuBarHeight); - } -} - -void MainWindow::onFullscreenToggled() -{ - ToggleFullscreen(this); -} - -void MainWindow::onScreenEmphasisToggled() -{ - int currentSizing = Config::ScreenSizing; - if (currentSizing == Frontend::screenSizing_EmphTop) - { - Config::ScreenSizing = Frontend::screenSizing_EmphBot; - } - else if (currentSizing == Frontend::screenSizing_EmphBot) - { - Config::ScreenSizing = Frontend::screenSizing_EmphTop; - } - - emit screenLayoutChange(); -} - -void MainWindow::onEmuStart() -{ - for (int i = 1; i < 9; i++) - { - actSaveState[i]->setEnabled(true); - actLoadState[i]->setEnabled(ROMManager::SavestateExists(i)); - } - actSaveState[0]->setEnabled(true); - actLoadState[0]->setEnabled(true); - actUndoStateLoad->setEnabled(false); - - actPause->setEnabled(true); - actPause->setChecked(false); - actReset->setEnabled(true); - actStop->setEnabled(true); - actFrameStep->setEnabled(true); - - actDateTime->setEnabled(false); - actPowerManagement->setEnabled(true); - - actTitleManager->setEnabled(false); -} - -void MainWindow::onEmuStop() -{ - emuThread->emuPause(); - - for (int i = 0; i < 9; i++) - { - actSaveState[i]->setEnabled(false); - actLoadState[i]->setEnabled(false); - } - actUndoStateLoad->setEnabled(false); - - actPause->setEnabled(false); - actReset->setEnabled(false); - actStop->setEnabled(false); - actFrameStep->setEnabled(false); - - actDateTime->setEnabled(true); - actPowerManagement->setEnabled(false); - - actTitleManager->setEnabled(!Config::DSiNANDPath.empty()); -} - -void MainWindow::onUpdateVideoSettings(bool glchange) -{ - if (glchange) - { - emuThread->emuPause(); - if (hasOGL) emuThread->deinitContext(); - - delete panel; - createScreenPanel(); - connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint())); - } - - videoSettingsDirty = true; - - if (glchange) - { - if (hasOGL) emuThread->initContext(); - emuThread->emuUnpause(); - } -} void emuStop() diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index ee2f7201..9703c178 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -37,6 +37,7 @@ #include #include +#include "Window.h" #include "FrontendUtil.h" #include "duckstation/gl/context.h" @@ -156,110 +157,6 @@ private: int lastScreenWidth = -1, lastScreenHeight = -1; }; - -class ScreenHandler -{ - Q_GADGET - -public: - ScreenHandler(QWidget* widget); - virtual ~ScreenHandler(); - QTimer* setupMouseTimer(); - void updateMouseTimer(); - QTimer* mouseTimer; - QSize screenGetMinSize(int factor); - -protected: - void screenSetupLayout(int w, int h); - - void screenOnMousePress(QMouseEvent* event); - void screenOnMouseRelease(QMouseEvent* event); - void screenOnMouseMove(QMouseEvent* event); - - void screenHandleTablet(QTabletEvent* event); - void screenHandleTouch(QTouchEvent* event); - - float screenMatrix[Frontend::MaxScreenTransforms][6]; - int screenKind[Frontend::MaxScreenTransforms]; - int numScreens; - - bool touching = false; - - void showCursor(); -}; - - -class ScreenPanelNative : public QWidget, public ScreenHandler -{ - Q_OBJECT - -public: - explicit ScreenPanelNative(QWidget* parent); - virtual ~ScreenPanelNative(); - -protected: - void paintEvent(QPaintEvent* event) override; - - void resizeEvent(QResizeEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - - void tabletEvent(QTabletEvent* event) override; - bool event(QEvent* event) override; -private slots: - void onScreenLayoutChanged(); - -private: - void setupScreenLayout(); - - QImage screen[2]; - QTransform screenTrans[Frontend::MaxScreenTransforms]; -}; - - -class ScreenPanelGL : public QWidget, public ScreenHandler -{ - Q_OBJECT - -public: - explicit ScreenPanelGL(QWidget* parent); - virtual ~ScreenPanelGL(); - - std::optional getWindowInfo(); - - bool createContext(); - - GL::Context* getContext() { return glContext.get(); } - - void transferLayout(EmuThread* thread); -protected: - - qreal devicePixelRatioFromScreen() const; - int scaledWindowWidth() const; - int scaledWindowHeight() const; - - QPaintEngine* paintEngine() const override; - - void resizeEvent(QResizeEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - - void tabletEvent(QTabletEvent* event) override; - bool event(QEvent* event) override; - -private slots: - void onScreenLayoutChanged(); - -private: - void setupScreenLayout(); - - std::unique_ptr glContext; -}; - class MelonApplication : public QApplication { Q_OBJECT @@ -269,199 +166,4 @@ public: bool event(QEvent* event) override; }; -class MainWindow : public QMainWindow -{ - Q_OBJECT - -public: - explicit MainWindow(QWidget* parent = nullptr); - ~MainWindow(); - - bool hasOGL; - GL::Context* getOGLContext(); - - bool preloadROMs(QStringList file, QStringList gbafile, bool boot); - QStringList splitArchivePath(const QString& filename, bool useMemberSyntax); - - void onAppStateChanged(Qt::ApplicationState state); - -protected: - void resizeEvent(QResizeEvent* event) override; - void changeEvent(QEvent* event) override; - - void keyPressEvent(QKeyEvent* event) override; - void keyReleaseEvent(QKeyEvent* event) override; - - void dragEnterEvent(QDragEnterEvent* event) override; - void dropEvent(QDropEvent* event) override; - - void focusInEvent(QFocusEvent* event) override; - void focusOutEvent(QFocusEvent* event) override; - -signals: - void screenLayoutChange(); - -private slots: - void onOpenFile(); - void onClickRecentFile(); - void onClearRecentFiles(); - void onBootFirmware(); - void onInsertCart(); - void onEjectCart(); - void onInsertGBACart(); - void onInsertGBAAddon(); - void onEjectGBACart(); - void onSaveState(); - void onLoadState(); - void onUndoStateLoad(); - void onImportSavefile(); - void onQuit(); - - void onPause(bool checked); - void onReset(); - void onStop(); - void onFrameStep(); - void onOpenPowerManagement(); - void onOpenDateTime(); - void onEnableCheats(bool checked); - void onSetupCheats(); - void onCheatsDialogFinished(int res); - void onROMInfo(); - void onRAMInfo(); - void onOpenTitleManager(); - void onMPNewInstance(); - - void onOpenEmuSettings(); - void onEmuSettingsDialogFinished(int res); - void onOpenInputConfig(); - void onInputConfigFinished(int res); - void onOpenVideoSettings(); - void onOpenCameraSettings(); - void onCameraSettingsFinished(int res); - void onOpenAudioSettings(); - void onUpdateAudioSettings(); - void onAudioSettingsFinished(int res); - void onOpenMPSettings(); - void onMPSettingsFinished(int res); - void onOpenWifiSettings(); - void onWifiSettingsFinished(int res); - void onOpenFirmwareSettings(); - void onFirmwareSettingsFinished(int res); - void onOpenPathSettings(); - void onPathSettingsFinished(int res); - void onOpenInterfaceSettings(); - void onInterfaceSettingsFinished(int res); - void onUpdateMouseTimer(); - void onChangeSavestateSRAMReloc(bool checked); - void onChangeScreenSize(); - void onChangeScreenRotation(QAction* act); - void onChangeScreenGap(QAction* act); - void onChangeScreenLayout(QAction* act); - void onChangeScreenSwap(bool checked); - void onChangeScreenSizing(QAction* act); - void onChangeScreenAspect(QAction* act); - void onChangeIntegerScaling(bool checked); - void onChangeScreenFiltering(bool checked); - void onChangeShowOSD(bool checked); - void onChangeLimitFramerate(bool checked); - void onChangeAudioSync(bool checked); - - void onTitleUpdate(QString title); - - void onEmuStart(); - void onEmuStop(); - - void onUpdateVideoSettings(bool glchange); - - void onFullscreenToggled(); - void onScreenEmphasisToggled(); - -private: - virtual void closeEvent(QCloseEvent* event) override; - - QStringList currentROM; - QStringList currentGBAROM; - QList recentFileList; - QMenu *recentMenu; - void updateRecentFilesMenu(); - - bool verifySetup(); - QString pickFileFromArchive(QString archiveFileName); - QStringList pickROM(bool gba); - void updateCartInserted(bool gba); - - void createScreenPanel(); - - bool pausedManually = false; - - int oldW, oldH; - bool oldMax; - -public: - ScreenHandler* panel; - QWidget* panelWidget; - - QAction* actOpenROM; - QAction* actBootFirmware; - QAction* actCurrentCart; - QAction* actInsertCart; - QAction* actEjectCart; - QAction* actCurrentGBACart; - QAction* actInsertGBACart; - QAction* actInsertGBAAddon[1]; - QAction* actEjectGBACart; - QAction* actImportSavefile; - QAction* actSaveState[9]; - QAction* actLoadState[9]; - QAction* actUndoStateLoad; - QAction* actQuit; - - QAction* actPause; - QAction* actReset; - QAction* actStop; - QAction* actFrameStep; - QAction* actPowerManagement; - QAction* actDateTime; - QAction* actEnableCheats; - QAction* actSetupCheats; - QAction* actROMInfo; - QAction* actRAMInfo; - QAction* actTitleManager; - QAction* actMPNewInstance; - - QAction* actEmuSettings; -#ifdef __APPLE__ - QAction* actPreferences; -#endif - QAction* actInputConfig; - QAction* actVideoSettings; - QAction* actCameraSettings; - QAction* actAudioSettings; - QAction* actMPSettings; - QAction* actWifiSettings; - QAction* actFirmwareSettings; - QAction* actPathSettings; - QAction* actInterfaceSettings; - QAction* actSavestateSRAMReloc; - QAction* actScreenSize[4]; - QActionGroup* grpScreenRotation; - QAction* actScreenRotation[Frontend::screenRot_MAX]; - QActionGroup* grpScreenGap; - QAction* actScreenGap[6]; - QActionGroup* grpScreenLayout; - QAction* actScreenLayout[Frontend::screenLayout_MAX]; - QAction* actScreenSwap; - QActionGroup* grpScreenSizing; - QAction* actScreenSizing[Frontend::screenSizing_MAX]; - QAction* actIntegerScaling; - QActionGroup* grpScreenAspectTop; - QAction** actScreenAspectTop; - QActionGroup* grpScreenAspectBot; - QAction** actScreenAspectBot; - QAction* actScreenFiltering; - QAction* actShowOSD; - QAction* actLimitFramerate; - QAction* actAudioSync; -}; - #endif // MAIN_H From 80c6dd524b04b9d94a362037fad140bbc1455d15 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 25 Dec 2023 16:34:29 +0100 Subject: [PATCH 092/157] add convenience method to Window class for OSD messages --- src/frontend/qt_sdl/OSD.cpp | 2 +- src/frontend/qt_sdl/Window.cpp | 44 ++++++++++++++----------- src/frontend/qt_sdl/Window.h | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 19 deletions(-) diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp index 25072df7..f845412d 100644 --- a/src/frontend/qt_sdl/OSD.cpp +++ b/src/frontend/qt_sdl/OSD.cpp @@ -69,7 +69,7 @@ QMutex Rendering; bool Init(bool openGL) -{ +{printf("%d\n", sizeof(Item)); if (openGL) { OpenGL::BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, Shader, "OSDShader"); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 2a8f00c3..f373450b 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -685,6 +685,20 @@ MainWindow::~MainWindow() delete[] actScreenAspectBot; } +void MainWindow::osdAddMessage(unsigned int color, const char* fmt, ...) +{ + if (fmt == nullptr) + return; + + char msg[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(msg, 1024, fmt, args); + va_end(args); + + OSD::AddMessage(color, msg); +} + void MainWindow::closeEvent(QCloseEvent* event) { if (hasOGL) @@ -1394,16 +1408,14 @@ void MainWindow::onSaveState() if (ROMManager::SaveState(*emuThread->NDS, filename)) { - char msg[64]; - if (slot > 0) sprintf(msg, "State saved to slot %d", slot); - else sprintf(msg, "State saved to file"); - OSD::AddMessage(0, msg); + if (slot > 0) osdAddMessage(0, "State saved to slot %d", slot); + else osdAddMessage(0, "State saved to file"); actLoadState[slot]->setEnabled(true); } else { - OSD::AddMessage(0xFFA0A0, "State save failed"); + osdAddMessage(0xFFA0A0, "State save failed"); } emuThread->emuUnpause(); @@ -1438,10 +1450,8 @@ void MainWindow::onLoadState() if (!Platform::FileExists(filename)) { - char msg[64]; - if (slot > 0) sprintf(msg, "State slot %d is empty", slot); - else sprintf(msg, "State file does not exist"); - OSD::AddMessage(0xFFA0A0, msg); + if (slot > 0) osdAddMessage(0xFFA0A0, "State slot %d is empty", slot); + else osdAddMessage(0xFFA0A0, "State file does not exist"); emuThread->emuUnpause(); return; @@ -1449,16 +1459,14 @@ void MainWindow::onLoadState() if (ROMManager::LoadState(*emuThread->NDS, filename)) { - char msg[64]; - if (slot > 0) sprintf(msg, "State loaded from slot %d", slot); - else sprintf(msg, "State loaded from file"); - OSD::AddMessage(0, msg); + if (slot > 0) osdAddMessage(0, "State loaded from slot %d", slot); + else osdAddMessage(0, "State loaded from file"); actUndoStateLoad->setEnabled(true); } else { - OSD::AddMessage(0xFFA0A0, "State load failed"); + osdAddMessage(0xFFA0A0, "State load failed"); } emuThread->emuUnpause(); @@ -1470,7 +1478,7 @@ void MainWindow::onUndoStateLoad() ROMManager::UndoStateLoad(*emuThread->NDS); emuThread->emuUnpause(); - OSD::AddMessage(0, "State load undone"); + osdAddMessage(0, "State load undone"); } void MainWindow::onImportSavefile() @@ -1538,13 +1546,13 @@ void MainWindow::onPause(bool checked) if (checked) { emuThread->emuPause(); - OSD::AddMessage(0, "Paused"); + osdAddMessage(0, "Paused"); pausedManually = true; } else { emuThread->emuUnpause(); - OSD::AddMessage(0, "Resumed"); + osdAddMessage(0, "Resumed"); pausedManually = false; } } @@ -1559,7 +1567,7 @@ void MainWindow::onReset() ROMManager::Reset(emuThread); - OSD::AddMessage(0, "Reset"); + osdAddMessage(0, "Reset"); emuThread->emuRun(); } diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index a84bc1d4..445d5e28 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -38,6 +38,63 @@ class EmuThread; +/* +class WindowBase : public QMainWindow +{ + Q_OBJECT + +public: + explicit WindowBase(QWidget* parent = nullptr); + ~WindowBase(); + + bool hasOGL; + GL::Context* getOGLContext(); + + //void onAppStateChanged(Qt::ApplicationState state); + +protected: + void resizeEvent(QResizeEvent* event) override; + void changeEvent(QEvent* event) override; + + void keyPressEvent(QKeyEvent* event) override; + void keyReleaseEvent(QKeyEvent* event) override; + + void dragEnterEvent(QDragEnterEvent* event) override; + void dropEvent(QDropEvent* event) override; + + void focusInEvent(QFocusEvent* event) override; + void focusOutEvent(QFocusEvent* event) override; + +signals: + void screenLayoutChange(); + +private slots: + //void onQuit(); + + //void onTitleUpdate(QString title); + + //void onEmuStart(); + //void onEmuStop(); + + //void onUpdateVideoSettings(bool glchange); + + void onFullscreenToggled(); + void onScreenEmphasisToggled(); + +private: + virtual void closeEvent(QCloseEvent* event) override; + + void createScreenPanel(); + + //bool pausedManually = false; + + int oldW, oldH; + bool oldMax; + +public: + ScreenHandler* panel; + QWidget* panelWidget; +};*/ class MainWindow : public QMainWindow { @@ -55,6 +112,8 @@ public: void onAppStateChanged(Qt::ApplicationState state); + void osdAddMessage(unsigned int color, const char* fmt, ...); + protected: void resizeEvent(QResizeEvent* event) override; void changeEvent(QEvent* event) override; From 5c90cb939d729e524a8eee3ccad705e4cb2bf42e Mon Sep 17 00:00:00 2001 From: Arisotura Date: Mon, 25 Dec 2023 22:52:44 +0100 Subject: [PATCH 093/157] oops --- src/frontend/qt_sdl/OSD.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp index f845412d..25072df7 100644 --- a/src/frontend/qt_sdl/OSD.cpp +++ b/src/frontend/qt_sdl/OSD.cpp @@ -69,7 +69,7 @@ QMutex Rendering; bool Init(bool openGL) -{printf("%d\n", sizeof(Item)); +{ if (openGL) { OpenGL::BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, Shader, "OSDShader"); From f16de402cf976c8a2f364bcd36126588c1232204 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 26 Dec 2023 04:26:38 +0100 Subject: [PATCH 094/157] Screen.cpp: include missing headers --- src/frontend/qt_sdl/Screen.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 6de4d78e..61b6891d 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -25,9 +25,11 @@ #include #include #include +#include #include #include +#include #ifndef _WIN32 #ifndef APPLE #include From 4d3af0d91529e7230398d02e7a9e8491b5d8a74d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 26 Dec 2023 05:12:26 +0100 Subject: [PATCH 095/157] Make that FindWayland warning shut up Hopefully "stable" distros use a not-ancient-enough CMake version that this should be okay. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 337c4a16..2f80767e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.15) +cmake_minimum_required(VERSION 3.16) cmake_policy(VERSION 3.15) if (POLICY CMP0076) From 27ac8dbc146cd4e3c8fa34c78918297a73065a56 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 26 Dec 2023 06:51:49 +0100 Subject: [PATCH 096/157] Integrate support for building with dependencies from vcpkg (#1880) * Integrate support for building with dependencies from vcpkg Configure the build using -DUSE_VCPKG=ON to use vcpkg. By default recommended triplets targeting the OS versions official builds support are used. You can opt out of this with -DUSE_RECOMMENDED_TRIPLETS=OFF. * Add the vcpkg manifest * Fetch vcpkg with FetchContent if we don't have it * macOS cross compiling fixes - can't use the x86_64 one as host triplet on arm64 because building Qt fails for whatever reason. Because of course it does :D - vcpkg doesn't always like periods in triplet names so removed those * x86_64 macOS should also use its recommended target when building arm64 builds --- CMakeLists.txt | 5 ++ cmake/ConfigureVcpkg.cmake | 90 +++++++++++++++++++ .../arm64-osx-11-release.cmake | 12 +++ .../x64-osx-1015-release.cmake | 12 +++ vcpkg.json | 18 ++++ 5 files changed, 137 insertions(+) create mode 100644 cmake/ConfigureVcpkg.cmake create mode 100644 cmake/overlay-triplets/arm64-osx-11-release.cmake create mode 100644 cmake/overlay-triplets/x64-osx-1015-release.cmake create mode 100644 vcpkg.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f80767e..97dfc5bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,11 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) +option(USE_VCPKG "Use vcpkg for dependency packages" OFF) +if (USE_VCPKG) + include(ConfigureVcpkg) +endif() + project(melonDS VERSION 0.9.5 DESCRIPTION "DS emulator, sorta" diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake new file mode 100644 index 00000000..f7f7f62f --- /dev/null +++ b/cmake/ConfigureVcpkg.cmake @@ -0,0 +1,90 @@ +include(FetchContent) + +set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg") +set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository") + +if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") + FetchContent_Declare(vcpkg + GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" + GIT_TAG 2023.10.19 + SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") + FetchContent_MakeAvailable(vcpkg) +endif() + +set(VCPKG_OVERLAY_TRIPLETS "${CMAKE_SOURCE_DIR}/cmake/overlay-triplets") + +option(USE_RECOMMENDED_TRIPLETS "Use the recommended triplets that are used for official builds" ON) + +if (CMAKE_OSX_ARCHITECTURES MATCHES ";") + message(FATAL_ERROR "macOS universal builds are not supported. Build them individually and combine afterwards instead.") +endif() + +if (USE_RECOMMENDED_TRIPLETS) + execute_process( + COMMAND uname -m + OUTPUT_VARIABLE _HOST_PROCESSOR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(_CAN_TARGET_AS_HOST OFF) + + if (APPLE) + if (NOT CMAKE_OSX_ARCHITECTURES) + if (_HOST_PROCESSOR STREQUAL arm64) + set(CMAKE_OSX_ARCHITECTURES arm64) + else() + set(CMAKE_OSX_ARCHITECTURES x86_64) + endif() + endif() + + if (CMAKE_OSX_ARCHITECTURES STREQUAL arm64) + set(_WANTED_TRIPLET arm64-osx-11-release) + set(CMAKE_OSX_DEPLOYMENT_TARGET 11.0) + else() + set(_WANTED_TRIPLET x64-osx-1015-release) + set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15) + endif() + elseif(WIN32) + # TODO Windows arm64 if possible + set(_CAN_TARGET_AS_HOST ON) + set(_WANTED_TRIPLET x64-mingw-static) + endif() + + # Don't override it if the user set something else + if (NOT VCPKG_TARGET_TRIPLET) + set(VCPKG_TARGET_TRIPLET "${_WANTED_TRIPLET}") + else() + set(_WANTED_TRIPLET "${VCPKG_TARGET_TRIPLET}") + endif() + + if (APPLE) + if (_HOST_PROCESSOR MATCHES arm64) + if (_WANTED_TRIPLET MATCHES "^arm64-osx-") + set(_CAN_TARGET_AS_HOST ON) + elseif (_WANTED_TRIPLET STREQUAL "x64-osx-1015-release") + # Use the default triplet for when building for arm64 + # because we're probably making a universal build + set(VCPKG_HOST_TRIPLET arm64-osx-11-release) + endif() + else() + if (_WANTED_TRIPLET MATCHES "^x64-osx-") + set(_CAN_TARGET_AS_HOST ON) + elseif (_WANTED_TRIPLET STREQUAL "arm64-osx-11-release") + set(VCPKG_HOST_TRIPLET x64-osx-1015-release) + endif() + endif() + endif() + + # If host and target triplet differ, vcpkg seems to always assume that the host can't run the target's binaries. + # In cases like cross compiling from ARM -> Intel macOS, or target being an older version of the host OS, we *can* do that so the packages built targeting the host are redundant. + if (_CAN_TARGET_AS_HOST AND NOT VCPKG_HOST_TRIPLET) + option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" ON) + else() + option(VCPKG_TARGET_AS_HOST "Use the target as host triplet to speed up builds" OFF) + endif() + + if (VCPKG_TARGET_AS_HOST) + set(VCPKG_HOST_TRIPLET "${VCPKG_TARGET_TRIPLET}" CACHE STRING "Host triplet to use for vcpkg") + endif() +endif() + +set(CMAKE_TOOLCHAIN_FILE "${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") diff --git a/cmake/overlay-triplets/arm64-osx-11-release.cmake b/cmake/overlay-triplets/arm64-osx-11-release.cmake new file mode 100644 index 00000000..7c4b43ae --- /dev/null +++ b/cmake/overlay-triplets/arm64-osx-11-release.cmake @@ -0,0 +1,12 @@ +set(VCPKG_TARGET_ARCHITECTURE arm64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) + +set(VCPKG_CMAKE_SYSTEM_NAME Darwin) +set(VCPKG_CMAKE_SYSTEM_VERSION 11.0) +set(VCPKG_OSX_ARCHITECTURES arm64) +set(VCPKG_BUILD_TYPE release) +set(VCPKG_OSX_DEPLOYMENT_TARGET 11.0) + +set(VCPKG_C_FLAGS -mmacosx-version-min=11.0) +set(VCPKG_CXX_FLAGS -mmacosx-version-min=11.0) diff --git a/cmake/overlay-triplets/x64-osx-1015-release.cmake b/cmake/overlay-triplets/x64-osx-1015-release.cmake new file mode 100644 index 00000000..fcb67a7a --- /dev/null +++ b/cmake/overlay-triplets/x64-osx-1015-release.cmake @@ -0,0 +1,12 @@ +set(VCPKG_TARGET_ARCHITECTURE x64) +set(VCPKG_CRT_LINKAGE dynamic) +set(VCPKG_LIBRARY_LINKAGE static) + +set(VCPKG_CMAKE_SYSTEM_NAME Darwin) +set(VCPKG_CMAKE_SYSTEM_VERSION 10.15) +set(VCPKG_OSX_ARCHITECTURES x86_64) +set(VCPKG_BUILD_TYPE release) +set(VCPKG_OSX_DEPLOYMENT_TARGET 10.15) + +set(VCPKG_C_FLAGS -mmacosx-version-min=10.15) +set(VCPKG_CXX_FLAGS -mmacosx-version-min=10.15) diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 00000000..2ff0c549 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,18 @@ +{ + "dependencies": [ + "sdl2", + "libarchive", + "libslirp", + "zstd", + { + "name": "qtbase", + "default-features": false, + "features": ["gui", "png", "thread", "widgets", "opengl", "zstd"] + }, + { + "name": "qtmultimedia", + "default-features": false + }, + "qtsvg" + ] +} From 65780e6ba2bd1eb567290db33cf3de1d4fec9c3f Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 26 Dec 2023 08:14:20 +0100 Subject: [PATCH 097/157] Update vcpkg --- cmake/ConfigureVcpkg.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index f7f7f62f..95cbcdb9 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -6,7 +6,7 @@ set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repo if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") FetchContent_Declare(vcpkg GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" - GIT_TAG 2023.10.19 + GIT_TAG 2023.12.12 SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") FetchContent_MakeAvailable(vcpkg) endif() From ac3153d86bc66e5839e45b3a1fbc91f7e8b64a91 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 26 Dec 2023 08:14:30 +0100 Subject: [PATCH 098/157] "Incorrectly" link libslirp to stop its broken build system fucking up our linker flags --- src/frontend/qt_sdl/CMakeLists.txt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 9da2c91d..c2fa5b52 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -84,11 +84,11 @@ if (BUILD_STATIC) endif() pkg_check_modules(SDL2 REQUIRED IMPORTED_TARGET sdl2) -pkg_check_modules(Slirp REQUIRED IMPORTED_TARGET slirp) +pkg_check_modules(Slirp REQUIRED slirp) pkg_check_modules(LibArchive REQUIRED IMPORTED_TARGET libarchive) pkg_check_modules(Zstd REQUIRED IMPORTED_TARGET libzstd) -fix_interface_includes(PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive) +fix_interface_includes(PkgConfig::SDL2 PkgConfig::LibArchive) add_compile_definitions(ARCHIVE_SUPPORT_ENABLED) @@ -160,9 +160,12 @@ else() target_include_directories(melonDS PUBLIC ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() target_link_libraries(melonDS PRIVATE core) -target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::Slirp PkgConfig::LibArchive PkgConfig::Zstd) +target_link_libraries(melonDS PRIVATE PkgConfig::SDL2 PkgConfig::LibArchive PkgConfig::Zstd) target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS}) +target_include_directories(melonDS PRIVATE "${Slirp_INCLUDE_DIRS}") +target_link_libraries(melonDS PRIVATE "${Slirp_LINK_LIBRARIES}") + if (UNIX) option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF) elseif (WIN32) From 740489f7a4a848bd3f96f1c911a6c7db5f6cc27f Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 26 Dec 2023 08:49:02 +0100 Subject: [PATCH 099/157] Don't call Reset on nullptr --- src/frontend/qt_sdl/main.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 3f53f3cc..7ba37c17 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -329,10 +329,14 @@ bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAAr NDS::Current = nullptr; NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart)); + + if (NDS == nullptr) + return false; + NDS->Reset(); NDS::Current = NDS.get(); - return NDS != nullptr; + return true; } auto arm9bios = ROMManager::LoadARM9BIOS(); From d55a384c8807cd0962399dac69e8ee1a9d5c53fd Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Tue, 26 Dec 2023 10:34:04 -0500 Subject: [PATCH 100/157] Apply some quick hotfixes (#1931) --- src/DSi_NAND.cpp | 4 +++- src/FATStorage.cpp | 10 ++-------- src/FATStorage.h | 4 ++-- src/NDSCart.cpp | 10 ++++++++-- 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/DSi_NAND.cpp b/src/DSi_NAND.cpp index 5f767142..8da02540 100644 --- a/src/DSi_NAND.cpp +++ b/src/DSi_NAND.cpp @@ -122,7 +122,8 @@ NANDImage::NANDImage(NANDImage&& other) noexcept : ConsoleID(other.ConsoleID), FATIV(other.FATIV), FATKey(other.FATKey), - ESKey(other.ESKey) + ESKey(other.ESKey), + Length(other.Length) { other.CurFile = nullptr; } @@ -140,6 +141,7 @@ NANDImage& NANDImage::operator=(NANDImage&& other) noexcept FATIV = other.FATIV; FATKey = other.FATKey; ESKey = other.ESKey; + Length = other.Length; other.CurFile = nullptr; } diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index 52011a8e..9a1a9ad4 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -32,14 +32,8 @@ using namespace Platform; using std::string; FATStorage::FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional& sourcedir) : - FilePath(filename), - FileSize(size), - ReadOnly(readonly), - SourceDir(sourcedir) + FATStorage(FATStorageArgs { filename, size, readonly, sourcedir }) { - Load(filename, size, sourcedir); - - File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); } FATStorage::FATStorage(const FATStorageArgs& args) noexcept : @@ -55,7 +49,7 @@ FATStorage::FATStorage(FATStorageArgs&& args) noexcept : { Load(FilePath, FileSize, SourceDir); - File = nullptr; + File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); } FATStorage::FATStorage(FATStorage&& other) noexcept diff --git a/src/FATStorage.h b/src/FATStorage.h index 1e89b764..48a411b0 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -48,8 +48,8 @@ class FATStorage { public: FATStorage(const std::string& filename, u64 size, bool readonly, const std::optional& sourcedir = std::nullopt); - FATStorage(const FATStorageArgs& args) noexcept; - FATStorage(FATStorageArgs&& args) noexcept; + explicit FATStorage(const FATStorageArgs& args) noexcept; + explicit FATStorage(FATStorageArgs&& args) noexcept; FATStorage(FATStorage&& other) noexcept; FATStorage(const FATStorage& other) = delete; FATStorage& operator=(const FATStorage& other) = delete; diff --git a/src/NDSCart.cpp b/src/NDSCart.cpp index a5fe3180..a64d8a27 100644 --- a/src/NDSCart.cpp +++ b/src/NDSCart.cpp @@ -1657,9 +1657,15 @@ std::unique_ptr ParseROM(std::unique_ptr&& romdata, u32 romlen std::unique_ptr sram = args ? std::move(args->SRAM) : nullptr; u32 sramlen = args ? args->SRAMLength : 0; if (homebrew) - cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, args ? std::move(args->SDCard) : std::nullopt); + { + std::optional sdcard = args && args->SDCard ? std::make_optional(std::move(*args->SDCard)) : std::nullopt; + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, std::move(sdcard)); + } else if (gametitle[0] == 0 && !strncmp("SD/TF-NDS", gametitle + 1, 9) && gamecode == 0x414D5341) - cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, CartR4TypeR4, CartR4LanguageEnglish, args ? std::move(args->SDCard) : std::nullopt); + { + std::optional sdcard = args && args->SDCard ? std::make_optional(std::move(*args->SDCard)) : std::nullopt; + cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, CartR4TypeR4, CartR4LanguageEnglish, std::move(sdcard)); + } else if (cartid & 0x08000000) cart = std::make_unique(std::move(cartrom), cartromsize, cartid, romparams, std::move(sram), sramlen); else if (irversion != 0) From 7f437d48db554a739dd1cab53fb0536783c87432 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 26 Dec 2023 19:04:01 +0100 Subject: [PATCH 101/157] start cleaning up: move OpenGL stuff out of EmuThread --- src/frontend/qt_sdl/Screen.cpp | 206 ++++++++++++++++++++++++++++++--- src/frontend/qt_sdl/Screen.h | 19 ++- src/frontend/qt_sdl/Window.cpp | 24 ++++ src/frontend/qt_sdl/Window.h | 3 + src/frontend/qt_sdl/main.cpp | 149 ++++-------------------- src/frontend/qt_sdl/main.h | 13 ++- 6 files changed, 263 insertions(+), 151 deletions(-) diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 6de4d78e..a86147c1 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -34,29 +34,25 @@ #endif #endif +#include "OpenGLSupport.h" +#include "duckstation/gl/context.h" + #include "main.h" #include "NDS.h" +#include "GPU.h" +#include "GPU3D_Soft.h" +#include "GPU3D_OpenGL.h" #include "Platform.h" #include "Config.h" -//#include "main_shaders.h" +#include "main_shaders.h" #include "OSD.h" using namespace melonDS; -/*const struct { int id; float ratio; const char* label; } aspectRatios[] = -{ - { 0, 1, "4:3 (native)" }, - { 4, (5.f / 3) / (4.f / 3), "5:3 (3DS)"}, - { 1, (16.f / 9) / (4.f / 3), "16:9" }, - { 2, (21.f / 9) / (4.f / 3), "21:9" }, - { 3, 0, "window" } -}; -int AspectRatiosNum = sizeof(aspectRatios) / sizeof(aspectRatios[0]);*/ - // TEMP extern MainWindow* mainWindow; extern EmuThread* emuThread; @@ -432,6 +428,172 @@ bool ScreenPanelGL::createContext() return glContext != nullptr; } +void ScreenPanelGL::setSwapInterval(int intv) +{ + if (!glContext) return; + + glContext->SetSwapInterval(intv); +} + +void ScreenPanelGL::initOpenGL() +{ + if (!glContext) return; + + glContext->MakeCurrent(); + + OpenGL::BuildShaderProgram(kScreenVS, kScreenFS, screenShaderProgram, "ScreenShader"); + GLuint pid = screenShaderProgram[2]; + glBindAttribLocation(pid, 0, "vPosition"); + glBindAttribLocation(pid, 1, "vTexcoord"); + glBindFragDataLocation(pid, 0, "oColor"); + + OpenGL::LinkShaderProgram(screenShaderProgram); + + glUseProgram(pid); + glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0); + + screenShaderScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize"); + screenShaderTransformULoc = glGetUniformLocation(pid, "uTransform"); + + // to prevent bleeding between both parts of the screen + // with bilinear filtering enabled + const int paddedHeight = 192*2+2; + const float padPixels = 1.f / paddedHeight; + + const float vertices[] = + { + 0.f, 0.f, 0.f, 0.f, + 0.f, 192.f, 0.f, 0.5f - padPixels, + 256.f, 192.f, 1.f, 0.5f - padPixels, + 0.f, 0.f, 0.f, 0.f, + 256.f, 192.f, 1.f, 0.5f - padPixels, + 256.f, 0.f, 1.f, 0.f, + + 0.f, 0.f, 0.f, 0.5f + padPixels, + 0.f, 192.f, 0.f, 1.f, + 256.f, 192.f, 1.f, 1.f, + 0.f, 0.f, 0.f, 0.5f + padPixels, + 256.f, 192.f, 1.f, 1.f, + 256.f, 0.f, 1.f, 0.5f + padPixels + }; + + glGenBuffers(1, &screenVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glGenVertexArrays(1, &screenVertexArray); + glBindVertexArray(screenVertexArray); + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(0)); + glEnableVertexAttribArray(1); // texcoord + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(2*4)); + + glGenTextures(1, &screenTexture); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, screenTexture); + 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); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, paddedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + // fill the padding + u8 zeroData[256*4*4]; + memset(zeroData, 0, sizeof(zeroData)); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData); + + OSD::Init(true); + + glContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); + transferLayout(); +} + +void ScreenPanelGL::deinitOpenGL() +{ + if (!glContext) return; + + glDeleteTextures(1, &screenTexture); + + glDeleteVertexArrays(1, &screenVertexArray); + glDeleteBuffers(1, &screenVertexBuffer); + + OpenGL::DeleteShaderProgram(screenShaderProgram); + + OSD::DeInit(); + + glContext->DoneCurrent(); + + lastScreenWidth = lastScreenHeight = -1; +} + +void ScreenPanelGL::drawScreenGL() +{ + if (!glContext) return; + if (!emuThread->NDS) return; + + int w = windowInfo.surface_width; + int h = windowInfo.surface_height; + float factor = windowInfo.surface_scale; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDisable(GL_DEPTH_TEST); + glDepthMask(false); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); + glDisable(GL_STENCIL_TEST); + glClear(GL_COLOR_BUFFER_BIT); + + glViewport(0, 0, w, h); + + glUseProgram(screenShaderProgram[2]); + glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); + + int frontbuf = emuThread->FrontBuffer; + glActiveTexture(GL_TEXTURE0); + +#ifdef OGLRENDERER_ENABLED + if (emuThread->NDS->GPU.GetRenderer3D().Accelerated) + { + // hardware-accelerated render + static_cast(emuThread->NDS->GPU.GetRenderer3D()).GetCompositor().BindOutputTexture(frontbuf); + } + else +#endif + { + // regular render + glBindTexture(GL_TEXTURE_2D, screenTexture); + + if (emuThread->NDS->GPU.Framebuffer[frontbuf][0] && emuThread->NDS->GPU.Framebuffer[frontbuf][1]) + { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, emuThread->NDS->GPU.Framebuffer[frontbuf][0].get()); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, + GL_UNSIGNED_BYTE, emuThread->NDS->GPU.Framebuffer[frontbuf][1].get()); + } + } + + screenSettingsLock.lock(); + + GLint filter = this->filter ? GL_LINEAR : GL_NEAREST; + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + + glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); + glBindVertexArray(screenVertexArray); + + for (int i = 0; i < numScreens; i++) + { + glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]); + glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3); + } + + screenSettingsLock.unlock(); + + OSD::Update(); + OSD::DrawGL(w, h); + + glContext->SwapBuffers(); +} + qreal ScreenPanelGL::devicePixelRatioFromScreen() const { const QScreen* screen_for_ratio = window()->windowHandle()->screen(); @@ -507,8 +669,7 @@ void ScreenPanelGL::setupScreenLayout() int h = height(); screenSetupLayout(w, h); - if (emuThread) - transferLayout(emuThread); + transferLayout(); } void ScreenPanelGL::resizeEvent(QResizeEvent* event) @@ -550,11 +711,26 @@ bool ScreenPanelGL::event(QEvent* event) return QWidget::event(event); } -void ScreenPanelGL::transferLayout(EmuThread* thread) +void ScreenPanelGL::transferLayout() { std::optional windowInfo = getWindowInfo(); if (windowInfo.has_value()) - thread->updateScreenSettings(Config::ScreenFilter, *windowInfo, numScreens, screenKind, &screenMatrix[0][0]); + { + screenSettingsLock.lock(); + + if (lastScreenWidth != windowInfo->surface_width || lastScreenHeight != windowInfo->surface_height) + { + if (glContext) + glContext->ResizeSurface(windowInfo->surface_width, windowInfo->surface_height); + lastScreenWidth = windowInfo->surface_width; + lastScreenHeight = windowInfo->surface_height; + } + + this->filter = Config::ScreenFilter; + this->windowInfo = *windowInfo; + + screenSettingsLock.unlock(); + } } void ScreenPanelGL::onScreenLayoutChanged() diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h index 955eb2e6..b11df29b 100644 --- a/src/frontend/qt_sdl/Screen.h +++ b/src/frontend/qt_sdl/Screen.h @@ -122,9 +122,15 @@ public: bool createContext(); + void setSwapInterval(int intv); + + void initOpenGL(); + void deinitOpenGL(); + void drawScreenGL(); + GL::Context* getContext() { return glContext.get(); } - void transferLayout(EmuThread* thread); + void transferLayout(); protected: qreal devicePixelRatioFromScreen() const; @@ -149,6 +155,17 @@ private: void setupScreenLayout(); std::unique_ptr glContext; + + GLuint screenVertexBuffer, screenVertexArray; + GLuint screenTexture; + GLuint screenShaderProgram[3]; + GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc; + + QMutex screenSettingsLock; + WindowInfo windowInfo; + bool filter; + + int lastScreenWidth = -1, lastScreenHeight = -1; }; #endif // SCREEN_H diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index f373450b..7e86354e 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -749,6 +749,30 @@ GL::Context* MainWindow::getOGLContext() return glpanel->getContext(); } +/*void MainWindow::initOpenGL() +{ + if (!hasOGL) return; + + ScreenPanelGL* glpanel = static_cast(panel); + return glpanel->initOpenGL(); +} + +void MainWindow::deinitOpenGL() +{ + if (!hasOGL) return; + + ScreenPanelGL* glpanel = static_cast(panel); + return glpanel->deinitOpenGL(); +} + +void MainWindow::drawScreenGL() +{ + if (!hasOGL) return; + + ScreenPanelGL* glpanel = static_cast(panel); + return glpanel->drawScreenGL(); +}*/ + void MainWindow::resizeEvent(QResizeEvent* event) { int w = event->size().width(); diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index 445d5e28..f0f66c26 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -106,6 +106,9 @@ public: bool hasOGL; GL::Context* getOGLContext(); + /*void initOpenGL(); + void deinitOpenGL(); + void drawScreenGL();*/ bool preloadROMs(QStringList file, QStringList gbafile, bool boot); QStringList splitArchivePath(const QString& filename, bool useMemberSyntax); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 3f53f3cc..22cb0344 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -99,7 +99,7 @@ #include "Savestate.h" -#include "main_shaders.h" +//#include "main_shaders.h" #include "ROMManager.h" #include "ArchiveUtil.h" @@ -194,9 +194,6 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); - - auto glPanel = dynamic_cast(mainWindow->panel); - if (glPanel) glPanel->transferLayout(this); } std::unique_ptr EmuThread::CreateConsole( @@ -402,116 +399,6 @@ bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAAr return true; } -void EmuThread::updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix) -{ - screenSettingsLock.lock(); - - if (lastScreenWidth != windowInfo.surface_width || lastScreenHeight != windowInfo.surface_height) - { - if (oglContext) - oglContext->ResizeSurface(windowInfo.surface_width, windowInfo.surface_height); - lastScreenWidth = windowInfo.surface_width; - lastScreenHeight = windowInfo.surface_height; - } - - this->filter = filter; - this->windowInfo = windowInfo; - this->numScreens = numScreens; - memcpy(this->screenKind, screenKind, sizeof(int)*numScreens); - memcpy(this->screenMatrix, screenMatrix, sizeof(float)*numScreens*6); - - screenSettingsLock.unlock(); -} - -void EmuThread::initOpenGL() -{ - GL::Context* windowctx = mainWindow->getOGLContext(); - - oglContext = windowctx; - oglContext->MakeCurrent(); - - OpenGL::BuildShaderProgram(kScreenVS, kScreenFS, screenShaderProgram, "ScreenShader"); - GLuint pid = screenShaderProgram[2]; - glBindAttribLocation(pid, 0, "vPosition"); - glBindAttribLocation(pid, 1, "vTexcoord"); - glBindFragDataLocation(pid, 0, "oColor"); - - OpenGL::LinkShaderProgram(screenShaderProgram); - - glUseProgram(pid); - glUniform1i(glGetUniformLocation(pid, "ScreenTex"), 0); - - screenShaderScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize"); - screenShaderTransformULoc = glGetUniformLocation(pid, "uTransform"); - - // to prevent bleeding between both parts of the screen - // with bilinear filtering enabled - const int paddedHeight = 192*2+2; - const float padPixels = 1.f / paddedHeight; - - const float vertices[] = - { - 0.f, 0.f, 0.f, 0.f, - 0.f, 192.f, 0.f, 0.5f - padPixels, - 256.f, 192.f, 1.f, 0.5f - padPixels, - 0.f, 0.f, 0.f, 0.f, - 256.f, 192.f, 1.f, 0.5f - padPixels, - 256.f, 0.f, 1.f, 0.f, - - 0.f, 0.f, 0.f, 0.5f + padPixels, - 0.f, 192.f, 0.f, 1.f, - 256.f, 192.f, 1.f, 1.f, - 0.f, 0.f, 0.f, 0.5f + padPixels, - 256.f, 192.f, 1.f, 1.f, - 256.f, 0.f, 1.f, 0.5f + padPixels - }; - - glGenBuffers(1, &screenVertexBuffer); - glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glGenVertexArrays(1, &screenVertexArray); - glBindVertexArray(screenVertexArray); - glEnableVertexAttribArray(0); // position - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(0)); - glEnableVertexAttribArray(1); // texcoord - glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*4, (void*)(2*4)); - - glGenTextures(1, &screenTexture); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, screenTexture); - 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); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, paddedHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - // fill the padding - u8 zeroData[256*4*4]; - memset(zeroData, 0, sizeof(zeroData)); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData); - - OSD::Init(true); - - oglContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); -} - -void EmuThread::deinitOpenGL() -{ - glDeleteTextures(1, &screenTexture); - - glDeleteVertexArrays(1, &screenVertexArray); - glDeleteBuffers(1, &screenVertexBuffer); - - OpenGL::DeleteShaderProgram(screenShaderProgram); - - OSD::DeInit(); - - oglContext->DoneCurrent(); - oglContext = nullptr; - - lastScreenWidth = lastScreenHeight = -1; -} - void EmuThread::run() { u32 mainScreenPos[3]; @@ -529,11 +416,13 @@ void EmuThread::run() if (mainWindow->hasOGL) { - initOpenGL(); + screenGL = static_cast(mainWindow->panel); + screenGL->initOpenGL(); videoRenderer = Config::_3DRenderer; } else { + screenGL = nullptr; videoRenderer = 0; } @@ -654,9 +543,9 @@ void EmuThread::run() // to the old setting again if (videoSettingsDirty || Input::HotkeyReleased(HK_FastForward)) { - if (oglContext) + if (screenGL) { - oglContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); + screenGL->setSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); videoRenderer = Config::_3DRenderer; } #ifdef OGLRENDERER_ENABLED @@ -666,7 +555,7 @@ void EmuThread::run() videoRenderer = 0; } - videoRenderer = oglContext ? Config::_3DRenderer : 0; + videoRenderer = screenGL ? Config::_3DRenderer : 0; videoSettingsDirty = false; @@ -738,7 +627,7 @@ void EmuThread::run() if (ROMManager::FirmwareSave) ROMManager::FirmwareSave->CheckFlush(); - if (!oglContext) + if (!screenGL) { FrontBufferLock.lock(); FrontBuffer = NDS->GPU.FrontBuffer; @@ -747,7 +636,7 @@ void EmuThread::run() else { FrontBuffer = NDS->GPU.FrontBuffer; - drawScreenGL(); + screenGL->drawScreenGL(); } #ifdef MELONCAP @@ -757,7 +646,7 @@ void EmuThread::run() if (EmuRunning == emuStatus_Exit) break; winUpdateCount++; - if (winUpdateCount >= winUpdateFreq && !oglContext) + if (winUpdateCount >= winUpdateFreq && !screenGL) { emit windowUpdate(); winUpdateCount = 0; @@ -765,9 +654,9 @@ void EmuThread::run() bool fastforward = Input::HotkeyDown(HK_FastForward); - if (fastforward && oglContext && Config::ScreenVSync) + if (fastforward && screenGL && Config::ScreenVSync) { - oglContext->SetSwapInterval(0); + screenGL->setSwapInterval(0); } if (Config::DSiVolumeSync && NDS->ConsoleType == 1) @@ -856,18 +745,20 @@ void EmuThread::run() SDL_Delay(75); - if (oglContext) - drawScreenGL(); + if (screenGL) + screenGL->drawScreenGL(); ContextRequestKind contextRequest = ContextRequest; if (contextRequest == contextRequest_InitGL) { - initOpenGL(); + screenGL = static_cast(mainWindow->panel); + screenGL->initOpenGL(); ContextRequest = contextRequest_None; } else if (contextRequest == contextRequest_DeInitGL) { - deinitOpenGL(); + screenGL->deinitOpenGL(); + screenGL = nullptr; ContextRequest = contextRequest_None; } } @@ -964,7 +855,7 @@ bool EmuThread::emuIsActive() return (RunningSomething == 1); } -void EmuThread::drawScreenGL() +/*void EmuThread::drawScreenGL() { if (!NDS) return; int w = windowInfo.surface_width; @@ -1029,7 +920,7 @@ void EmuThread::drawScreenGL() OSD::DrawGL(w, h); oglContext->SwapBuffers(); -} +}*/ diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 9703c178..4034ba34 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -78,7 +78,7 @@ public: int FrontBuffer = 0; QMutex FrontBufferLock; - void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); + //void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); /// Applies the config in args. /// Creates a new NDS console if needed, @@ -113,9 +113,9 @@ private: std::unique_ptr&& ndscart, std::unique_ptr&& gbacart ) noexcept; - void drawScreenGL(); - void initOpenGL(); - void deinitOpenGL(); + //void drawScreenGL(); + //void initOpenGL(); + //void deinitOpenGL(); enum EmuStatusKind { @@ -141,7 +141,7 @@ private: }; std::atomic ContextRequest = contextRequest_None; - GL::Context* oglContext = nullptr; + /*GL::Context* oglContext = nullptr; GLuint screenVertexBuffer, screenVertexArray; GLuint screenTexture; GLuint screenShaderProgram[3]; @@ -154,7 +154,8 @@ private: int numScreens; bool filter; - int lastScreenWidth = -1, lastScreenHeight = -1; + int lastScreenWidth = -1, lastScreenHeight = -1;*/ + ScreenPanelGL* screenGL; }; class MelonApplication : public QApplication From f905b6fb93e1ee35ad39f78eef88d221dda26537 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 26 Dec 2023 19:24:14 +0100 Subject: [PATCH 102/157] separate EmuThread to its own file --- src/frontend/qt_sdl/CMakeLists.txt | 1 + src/frontend/qt_sdl/EmuThread.cpp | 758 +++++++++++++++++++++++++++++ src/frontend/qt_sdl/EmuThread.h | 134 +++++ src/frontend/qt_sdl/Window.cpp | 1 - src/frontend/qt_sdl/main.cpp | 750 ---------------------------- src/frontend/qt_sdl/main.h | 125 +---- 6 files changed, 894 insertions(+), 875 deletions(-) create mode 100644 src/frontend/qt_sdl/EmuThread.cpp create mode 100644 src/frontend/qt_sdl/EmuThread.h diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index c2fa5b52..92810210 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES_QT_SDL main_shaders.h Screen.cpp Window.cpp + EmuThread.cpp CheatsDialog.cpp Config.cpp DateTimeDialog.cpp diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp new file mode 100644 index 00000000..f86d30b7 --- /dev/null +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -0,0 +1,758 @@ +/* + 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 +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "main.h" +#include "Input.h" +#include "AudioInOut.h" + +#include "types.h" +#include "version.h" + +#include "FrontendUtil.h" +#include "OSD.h" + +#include "Args.h" +#include "NDS.h" +#include "NDSCart.h" +#include "GBACart.h" +#include "GPU.h" +#include "SPU.h" +#include "Wifi.h" +#include "Platform.h" +#include "LocalMP.h" +#include "Config.h" +#include "RTC.h" +#include "DSi.h" +#include "DSi_I2C.h" +#include "GPU3D_Soft.h" +#include "GPU3D_OpenGL.h" + +#include "Savestate.h" + +#include "ROMManager.h" +//#include "ArchiveUtil.h" +//#include "CameraManager.h" + +//#include "CLI.h" + +// TODO: uniform variable spelling +using namespace melonDS; + +// TEMP +extern bool RunningSomething; +extern MainWindow* mainWindow; +extern int autoScreenSizing; +extern int videoRenderer; +extern bool videoSettingsDirty; + + +EmuThread::EmuThread(QObject* parent) : QThread(parent) +{ + EmuStatus = emuStatus_Exit; + EmuRunning = emuStatus_Paused; + EmuPauseStack = EmuPauseStackRunning; + RunningSomething = false; + + connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint())); + connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); + connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); + connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); + connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); + connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); + connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger())); + connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger())); + connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged())); + connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); + connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); + connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); +} + +std::unique_ptr EmuThread::CreateConsole( + std::unique_ptr&& ndscart, + std::unique_ptr&& gbacart +) noexcept +{ + auto arm7bios = ROMManager::LoadARM7BIOS(); + if (!arm7bios) + return nullptr; + + auto arm9bios = ROMManager::LoadARM9BIOS(); + if (!arm9bios) + return nullptr; + + auto firmware = ROMManager::LoadFirmware(Config::ConsoleType); + if (!firmware) + return nullptr; + +#ifdef JIT_ENABLED + JITArgs jitargs { + static_cast(Config::JIT_MaxBlockSize), + Config::JIT_LiteralOptimisations, + Config::JIT_BranchOptimisations, + Config::JIT_FastMemory, + }; +#endif + +#ifdef GDBSTUB_ENABLED + GDBArgs gdbargs { + static_cast(Config::GdbPortARM7), + static_cast(Config::GdbPortARM9), + Config::GdbARM7BreakOnStartup, + Config::GdbARM9BreakOnStartup, + }; +#endif + + NDSArgs ndsargs { + std::move(ndscart), + std::move(gbacart), + *arm9bios, + *arm7bios, + std::move(*firmware), +#ifdef JIT_ENABLED + Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt, +#else + std::nullopt, +#endif + static_cast(Config::AudioBitDepth), + static_cast(Config::AudioInterp), +#ifdef GDBSTUB_ENABLED + Config::GdbEnabled ? std::make_optional(gdbargs) : std::nullopt, +#else + std::nullopt, +#endif + }; + + if (Config::ConsoleType == 1) + { + auto arm7ibios = ROMManager::LoadDSiARM7BIOS(); + if (!arm7ibios) + return nullptr; + + auto arm9ibios = ROMManager::LoadDSiARM9BIOS(); + if (!arm9ibios) + return nullptr; + + auto nand = ROMManager::LoadNAND(*arm7ibios); + if (!nand) + return nullptr; + + auto sdcard = ROMManager::LoadDSiSDCard(); + DSiArgs args { + std::move(ndsargs), + *arm9ibios, + *arm7ibios, + std::move(*nand), + std::move(sdcard), + Config::DSiFullBIOSBoot, + }; + + args.GBAROM = nullptr; + + return std::make_unique(std::move(args)); + } + + return std::make_unique(std::move(ndsargs)); +} + +bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept +{ + // Let's get the cart we want to use; + // if we wnat to keep the cart, we'll eject it from the existing console first. + std::unique_ptr nextndscart; + if (std::holds_alternative(ndsargs)) + { // If we want to keep the existing cart (if any)... + nextndscart = NDS ? NDS->EjectCart() : nullptr; + ndsargs = {}; + } + else if (const auto ptr = std::get_if>(&ndsargs)) + { + nextndscart = std::move(*ptr); + ndsargs = {}; + } + + if (nextndscart && nextndscart->Type() == NDSCart::Homebrew) + { + // Load DLDISDCard will return nullopt if the SD card is disabled; + // SetSDCard will accept nullopt, which means no SD card + auto& homebrew = static_cast(*nextndscart); + homebrew.SetSDCard(ROMManager::LoadDLDISDCard()); + } + + std::unique_ptr nextgbacart; + if (std::holds_alternative(gbaargs)) + { + nextgbacart = NDS ? NDS->EjectGBACart() : nullptr; + } + else if (const auto ptr = std::get_if>(&gbaargs)) + { + nextgbacart = std::move(*ptr); + gbaargs = {}; + } + + if (!NDS || NDS->ConsoleType != Config::ConsoleType) + { // If we're switching between DS and DSi mode, or there's no console... + // To ensure the destructor is called before a new one is created, + // as the presence of global signal handlers still complicates things a bit + NDS = nullptr; + NDS::Current = nullptr; + + NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart)); + + if (NDS == nullptr) + return false; + + NDS->Reset(); + NDS::Current = NDS.get(); + + return true; + } + + auto arm9bios = ROMManager::LoadARM9BIOS(); + if (!arm9bios) + return false; + + auto arm7bios = ROMManager::LoadARM7BIOS(); + if (!arm7bios) + return false; + + auto firmware = ROMManager::LoadFirmware(NDS->ConsoleType); + if (!firmware) + return false; + + if (NDS->ConsoleType == 1) + { // If the console we're updating is a DSi... + DSi& dsi = static_cast(*NDS); + + auto arm9ibios = ROMManager::LoadDSiARM9BIOS(); + if (!arm9ibios) + return false; + + auto arm7ibios = ROMManager::LoadDSiARM7BIOS(); + if (!arm7ibios) + return false; + + auto nandimage = ROMManager::LoadNAND(*arm7ibios); + if (!nandimage) + return false; + + auto dsisdcard = ROMManager::LoadDSiSDCard(); + + dsi.SetFullBIOSBoot(Config::DSiFullBIOSBoot); + dsi.ARM7iBIOS = *arm7ibios; + dsi.ARM9iBIOS = *arm9ibios; + dsi.SetNAND(std::move(*nandimage)); + dsi.SetSDCard(std::move(dsisdcard)); + // We're moving the optional, not the card + // (inserting std::nullopt here is okay, it means no card) + + dsi.EjectGBACart(); + } + + if (NDS->ConsoleType == 0) + { + NDS->SetGBACart(std::move(nextgbacart)); + } + +#ifdef JIT_ENABLED + JITArgs jitargs { + static_cast(Config::JIT_MaxBlockSize), + Config::JIT_LiteralOptimisations, + Config::JIT_BranchOptimisations, + Config::JIT_FastMemory, + }; + NDS->SetJITArgs(Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt); +#endif + NDS->SetARM7BIOS(*arm7bios); + NDS->SetARM9BIOS(*arm9bios); + NDS->SetFirmware(std::move(*firmware)); + NDS->SetNDSCart(std::move(nextndscart)); + NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); + NDS->SPU.SetDegrade10Bit(static_cast(Config::AudioBitDepth)); + + NDS::Current = NDS.get(); + + return true; +} + +void EmuThread::run() +{ + u32 mainScreenPos[3]; + Platform::FileHandle* file; + + UpdateConsole(nullptr, nullptr); + // No carts are inserted when melonDS first boots + + mainScreenPos[0] = 0; + mainScreenPos[1] = 0; + mainScreenPos[2] = 0; + autoScreenSizing = 0; + + videoSettingsDirty = false; + + if (mainWindow->hasOGL) + { + screenGL = static_cast(mainWindow->panel); + screenGL->initOpenGL(); + videoRenderer = Config::_3DRenderer; + } + else + { + screenGL = nullptr; + videoRenderer = 0; + } + + if (videoRenderer == 0) + { // If we're using the software renderer... + NDS->GPU.SetRenderer3D(std::make_unique(Config::Threaded3D != 0)); + } + else + { + auto glrenderer = melonDS::GLRenderer::New(); + glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); + NDS->GPU.SetRenderer3D(std::move(glrenderer)); + } + + Input::Init(); + + u32 nframes = 0; + double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency(); + double lastTime = SDL_GetPerformanceCounter() * perfCountsSec; + double frameLimitError = 0.0; + double lastMeasureTime = lastTime; + + u32 winUpdateCount = 0, winUpdateFreq = 1; + u8 dsiVolumeLevel = 0x1F; + + file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read); + if (file) + { + RTC::StateData state; + Platform::FileRead(&state, sizeof(state), 1, file); + Platform::CloseFile(file); + NDS->RTC.SetState(state); + } + + char melontitle[100]; + + while (EmuRunning != emuStatus_Exit) + { + Input::Process(); + + if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); + + if (Input::HotkeyPressed(HK_Pause)) emit windowEmuPause(); + if (Input::HotkeyPressed(HK_Reset)) emit windowEmuReset(); + if (Input::HotkeyPressed(HK_FrameStep)) emit windowEmuFrameStep(); + + if (Input::HotkeyPressed(HK_FullscreenToggle)) emit windowFullscreenToggle(); + + if (Input::HotkeyPressed(HK_SwapScreens)) emit swapScreensToggle(); + if (Input::HotkeyPressed(HK_SwapScreenEmphasis)) emit screenEmphasisToggle(); + + if (EmuRunning == emuStatus_Running || EmuRunning == emuStatus_FrameStep) + { + EmuStatus = emuStatus_Running; + if (EmuRunning == emuStatus_FrameStep) EmuRunning = emuStatus_Paused; + + if (Input::HotkeyPressed(HK_SolarSensorDecrease)) + { + int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorDown, true); + if (level != -1) + { + char msg[64]; + sprintf(msg, "Solar sensor level: %d", level); + OSD::AddMessage(0, msg); + } + } + if (Input::HotkeyPressed(HK_SolarSensorIncrease)) + { + int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorUp, true); + if (level != -1) + { + char msg[64]; + sprintf(msg, "Solar sensor level: %d", level); + OSD::AddMessage(0, msg); + } + } + + if (NDS->ConsoleType == 1) + { + DSi& dsi = static_cast(*NDS); + double currentTime = SDL_GetPerformanceCounter() * perfCountsSec; + + // Handle power button + if (Input::HotkeyDown(HK_PowerButton)) + { + dsi.I2C.GetBPTWL()->SetPowerButtonHeld(currentTime); + } + else if (Input::HotkeyReleased(HK_PowerButton)) + { + dsi.I2C.GetBPTWL()->SetPowerButtonReleased(currentTime); + } + + // Handle volume buttons + if (Input::HotkeyDown(HK_VolumeUp)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); + } + else if (Input::HotkeyReleased(HK_VolumeUp)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); + } + + if (Input::HotkeyDown(HK_VolumeDown)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); + } + else if (Input::HotkeyReleased(HK_VolumeDown)) + { + dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); + } + + dsi.I2C.GetBPTWL()->ProcessVolumeSwitchInput(currentTime); + } + + // update render settings if needed + // HACK: + // once the fast forward hotkey is released, we need to update vsync + // to the old setting again + if (videoSettingsDirty || Input::HotkeyReleased(HK_FastForward)) + { + if (screenGL) + { + screenGL->setSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); + videoRenderer = Config::_3DRenderer; + } +#ifdef OGLRENDERER_ENABLED + else +#endif + { + videoRenderer = 0; + } + + videoRenderer = screenGL ? Config::_3DRenderer : 0; + + videoSettingsDirty = false; + + if (videoRenderer == 0) + { // If we're using the software renderer... + NDS->GPU.SetRenderer3D(std::make_unique(Config::Threaded3D != 0)); + } + else + { + auto glrenderer = melonDS::GLRenderer::New(); + glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); + NDS->GPU.SetRenderer3D(std::move(glrenderer)); + } + } + + // process input and hotkeys + NDS->SetKeyMask(Input::InputMask); + + if (Input::HotkeyPressed(HK_Lid)) + { + bool lid = !NDS->IsLidClosed(); + NDS->SetLidClosed(lid); + OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); + } + + // microphone input + AudioInOut::MicProcess(*NDS); + + // auto screen layout + if (Config::ScreenSizing == Frontend::screenSizing_Auto) + { + mainScreenPos[2] = mainScreenPos[1]; + mainScreenPos[1] = mainScreenPos[0]; + mainScreenPos[0] = NDS->PowerControl9 >> 15; + + int guess; + if (mainScreenPos[0] == mainScreenPos[2] && + mainScreenPos[0] != mainScreenPos[1]) + { + // constant flickering, likely displaying 3D on both screens + // TODO: when both screens are used for 2D only...??? + guess = Frontend::screenSizing_Even; + } + else + { + if (mainScreenPos[0] == 1) + guess = Frontend::screenSizing_EmphTop; + else + guess = Frontend::screenSizing_EmphBot; + } + + if (guess != autoScreenSizing) + { + autoScreenSizing = guess; + emit screenLayoutChange(); + } + } + + + // emulate + u32 nlines = NDS->RunFrame(); + + if (ROMManager::NDSSave) + ROMManager::NDSSave->CheckFlush(); + + if (ROMManager::GBASave) + ROMManager::GBASave->CheckFlush(); + + if (ROMManager::FirmwareSave) + ROMManager::FirmwareSave->CheckFlush(); + + if (!screenGL) + { + FrontBufferLock.lock(); + FrontBuffer = NDS->GPU.FrontBuffer; + FrontBufferLock.unlock(); + } + else + { + FrontBuffer = NDS->GPU.FrontBuffer; + screenGL->drawScreenGL(); + } + +#ifdef MELONCAP + MelonCap::Update(); +#endif // MELONCAP + + if (EmuRunning == emuStatus_Exit) break; + + winUpdateCount++; + if (winUpdateCount >= winUpdateFreq && !screenGL) + { + emit windowUpdate(); + winUpdateCount = 0; + } + + bool fastforward = Input::HotkeyDown(HK_FastForward); + + if (fastforward && screenGL && Config::ScreenVSync) + { + screenGL->setSwapInterval(0); + } + + if (Config::DSiVolumeSync && NDS->ConsoleType == 1) + { + DSi& dsi = static_cast(*NDS); + u8 volumeLevel = dsi.I2C.GetBPTWL()->GetVolumeLevel(); + if (volumeLevel != dsiVolumeLevel) + { + dsiVolumeLevel = volumeLevel; + emit syncVolumeLevel(); + } + + Config::AudioVolume = volumeLevel * (256.0 / 31.0); + } + + if (Config::AudioSync && !fastforward) + AudioInOut::AudioSync(*this->NDS); + + double frametimeStep = nlines / (60.0 * 263.0); + + { + bool limitfps = Config::LimitFPS && !fastforward; + + double practicalFramelimit = limitfps ? frametimeStep : 1.0 / 1000.0; + + double curtime = SDL_GetPerformanceCounter() * perfCountsSec; + + frameLimitError += practicalFramelimit - (curtime - lastTime); + if (frameLimitError < -practicalFramelimit) + frameLimitError = -practicalFramelimit; + if (frameLimitError > practicalFramelimit) + frameLimitError = practicalFramelimit; + + if (round(frameLimitError * 1000.0) > 0.0) + { + SDL_Delay(round(frameLimitError * 1000.0)); + double timeBeforeSleep = curtime; + curtime = SDL_GetPerformanceCounter() * perfCountsSec; + frameLimitError -= curtime - timeBeforeSleep; + } + + lastTime = curtime; + } + + nframes++; + if (nframes >= 30) + { + double time = SDL_GetPerformanceCounter() * perfCountsSec; + double dt = time - lastMeasureTime; + lastMeasureTime = time; + + u32 fps = round(nframes / dt); + nframes = 0; + + float fpstarget = 1.0/frametimeStep; + + winUpdateFreq = fps / (u32)round(fpstarget); + if (winUpdateFreq < 1) + winUpdateFreq = 1; + + int inst = Platform::InstanceID(); + if (inst == 0) + sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); + else + sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); + changeWindowTitle(melontitle); + } + } + else + { + // paused + nframes = 0; + lastTime = SDL_GetPerformanceCounter() * perfCountsSec; + lastMeasureTime = lastTime; + + emit windowUpdate(); + + EmuStatus = EmuRunning; + + int inst = Platform::InstanceID(); + if (inst == 0) + sprintf(melontitle, "melonDS " MELONDS_VERSION); + else + sprintf(melontitle, "melonDS (%d)", inst+1); + changeWindowTitle(melontitle); + + SDL_Delay(75); + + if (screenGL) + screenGL->drawScreenGL(); + + ContextRequestKind contextRequest = ContextRequest; + if (contextRequest == contextRequest_InitGL) + { + screenGL = static_cast(mainWindow->panel); + screenGL->initOpenGL(); + ContextRequest = contextRequest_None; + } + else if (contextRequest == contextRequest_DeInitGL) + { + screenGL->deinitOpenGL(); + screenGL = nullptr; + ContextRequest = contextRequest_None; + } + } + } + + file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write); + if (file) + { + RTC::StateData state; + NDS->RTC.GetState(state); + Platform::FileWrite(&state, sizeof(state), 1, file); + Platform::CloseFile(file); + } + + EmuStatus = emuStatus_Exit; + + NDS::Current = nullptr; + // nds is out of scope, so unique_ptr cleans it up for us +} + +void EmuThread::changeWindowTitle(char* title) +{ + emit windowTitleChange(QString(title)); +} + +void EmuThread::emuRun() +{ + EmuRunning = emuStatus_Running; + EmuPauseStack = EmuPauseStackRunning; + RunningSomething = true; + + // checkme + emit windowEmuStart(); + AudioInOut::Enable(); +} + +void EmuThread::initContext() +{ + ContextRequest = contextRequest_InitGL; + while (ContextRequest != contextRequest_None); +} + +void EmuThread::deinitContext() +{ + ContextRequest = contextRequest_DeInitGL; + while (ContextRequest != contextRequest_None); +} + +void EmuThread::emuPause() +{ + EmuPauseStack++; + if (EmuPauseStack > EmuPauseStackPauseThreshold) return; + + PrevEmuStatus = EmuRunning; + EmuRunning = emuStatus_Paused; + while (EmuStatus != emuStatus_Paused); + + AudioInOut::Disable(); +} + +void EmuThread::emuUnpause() +{ + if (EmuPauseStack < EmuPauseStackPauseThreshold) return; + + EmuPauseStack--; + if (EmuPauseStack >= EmuPauseStackPauseThreshold) return; + + EmuRunning = PrevEmuStatus; + + AudioInOut::Enable(); +} + +void EmuThread::emuStop() +{ + EmuRunning = emuStatus_Exit; + EmuPauseStack = EmuPauseStackRunning; + + AudioInOut::Disable(); +} + +void EmuThread::emuFrameStep() +{ + if (EmuPauseStack < EmuPauseStackPauseThreshold) emit windowEmuPause(); + EmuRunning = emuStatus_FrameStep; +} + +bool EmuThread::emuIsRunning() +{ + return EmuRunning == emuStatus_Running; +} + +bool EmuThread::emuIsActive() +{ + return (RunningSomething == 1); +} diff --git a/src/frontend/qt_sdl/EmuThread.h b/src/frontend/qt_sdl/EmuThread.h new file mode 100644 index 00000000..4950ebbf --- /dev/null +++ b/src/frontend/qt_sdl/EmuThread.h @@ -0,0 +1,134 @@ +/* + 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 EMUTHREAD_H +#define EMUTHREAD_H + +#include +#include + +#include +#include +#include + +#include "NDSCart.h" +#include "GBACart.h" + +using Keep = std::monostate; +using UpdateConsoleNDSArgs = std::variant>; +using UpdateConsoleGBAArgs = std::variant>; +namespace melonDS +{ +class NDS; +} + +class ScreenPanelGL; + +class EmuThread : public QThread +{ + Q_OBJECT + void run() override; + +public: + explicit EmuThread(QObject* parent = nullptr); + + void changeWindowTitle(char* title); + + // to be called from the UI thread + void emuRun(); + void emuPause(); + void emuUnpause(); + void emuStop(); + void emuFrameStep(); + + bool emuIsRunning(); + bool emuIsActive(); + + void initContext(); + void deinitContext(); + + int FrontBuffer = 0; + QMutex FrontBufferLock; + + /// Applies the config in args. + /// Creates a new NDS console if needed, + /// modifies the existing one if possible. + /// @return \c true if the console was updated. + /// If this returns \c false, then the existing NDS console is not modified. + bool UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept; + std::unique_ptr NDS; // TODO: Proper encapsulation and synchronization +signals: + void windowUpdate(); + void windowTitleChange(QString title); + + void windowEmuStart(); + void windowEmuStop(); + void windowEmuPause(); + void windowEmuReset(); + void windowEmuFrameStep(); + + void windowLimitFPSChange(); + + void screenLayoutChange(); + + void windowFullscreenToggle(); + + void swapScreensToggle(); + void screenEmphasisToggle(); + + void syncVolumeLevel(); + +private: + std::unique_ptr CreateConsole( + std::unique_ptr&& ndscart, + std::unique_ptr&& gbacart + ) noexcept; + + enum EmuStatusKind + { + emuStatus_Exit, + emuStatus_Running, + emuStatus_Paused, + emuStatus_FrameStep, + }; + std::atomic EmuStatus; + + EmuStatusKind PrevEmuStatus; + EmuStatusKind EmuRunning; + + constexpr static int EmuPauseStackRunning = 0; + constexpr static int EmuPauseStackPauseThreshold = 1; + int EmuPauseStack; + + enum ContextRequestKind + { + contextRequest_None = 0, + contextRequest_InitGL, + contextRequest_DeInitGL + }; + std::atomic ContextRequest = contextRequest_None; + + ScreenPanelGL* screenGL; + + int autoScreenSizing; + + int videoRenderer; + bool videoSettingsDirty; +}; + +#endif // EMUTHREAD_H diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 7e86354e..7b67ec52 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -89,7 +89,6 @@ using namespace melonDS; extern MainWindow* mainWindow; extern EmuThread* emuThread; extern bool RunningSomething; -extern int autoScreenSizing; extern QString NdsRomMimeType; extern QStringList NdsRomExtensions; extern QString GbaRomMimeType; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index e8859ef9..2f08c374 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -175,756 +175,6 @@ bool camStarted[2]; //extern int AspectRatiosNum; -EmuThread::EmuThread(QObject* parent) : QThread(parent) -{ - EmuStatus = emuStatus_Exit; - EmuRunning = emuStatus_Paused; - EmuPauseStack = EmuPauseStackRunning; - RunningSomething = false; - - connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint())); - connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); - connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); - connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); - connect(this, SIGNAL(windowEmuPause()), mainWindow->actPause, SLOT(trigger())); - connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); - connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger())); - connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger())); - connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged())); - connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); - connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); - connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); -} - -std::unique_ptr EmuThread::CreateConsole( - std::unique_ptr&& ndscart, - std::unique_ptr&& gbacart -) noexcept -{ - auto arm7bios = ROMManager::LoadARM7BIOS(); - if (!arm7bios) - return nullptr; - - auto arm9bios = ROMManager::LoadARM9BIOS(); - if (!arm9bios) - return nullptr; - - auto firmware = ROMManager::LoadFirmware(Config::ConsoleType); - if (!firmware) - return nullptr; - -#ifdef JIT_ENABLED - JITArgs jitargs { - static_cast(Config::JIT_MaxBlockSize), - Config::JIT_LiteralOptimisations, - Config::JIT_BranchOptimisations, - Config::JIT_FastMemory, - }; -#endif - -#ifdef GDBSTUB_ENABLED - GDBArgs gdbargs { - static_cast(Config::GdbPortARM7), - static_cast(Config::GdbPortARM9), - Config::GdbARM7BreakOnStartup, - Config::GdbARM9BreakOnStartup, - }; -#endif - - NDSArgs ndsargs { - std::move(ndscart), - std::move(gbacart), - *arm9bios, - *arm7bios, - std::move(*firmware), -#ifdef JIT_ENABLED - Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt, -#else - std::nullopt, -#endif - static_cast(Config::AudioBitDepth), - static_cast(Config::AudioInterp), -#ifdef GDBSTUB_ENABLED - Config::GdbEnabled ? std::make_optional(gdbargs) : std::nullopt, -#else - std::nullopt, -#endif - }; - - if (Config::ConsoleType == 1) - { - auto arm7ibios = ROMManager::LoadDSiARM7BIOS(); - if (!arm7ibios) - return nullptr; - - auto arm9ibios = ROMManager::LoadDSiARM9BIOS(); - if (!arm9ibios) - return nullptr; - - auto nand = ROMManager::LoadNAND(*arm7ibios); - if (!nand) - return nullptr; - - auto sdcard = ROMManager::LoadDSiSDCard(); - DSiArgs args { - std::move(ndsargs), - *arm9ibios, - *arm7ibios, - std::move(*nand), - std::move(sdcard), - Config::DSiFullBIOSBoot, - }; - - args.GBAROM = nullptr; - - return std::make_unique(std::move(args)); - } - - return std::make_unique(std::move(ndsargs)); -} - -bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept -{ - // Let's get the cart we want to use; - // if we wnat to keep the cart, we'll eject it from the existing console first. - std::unique_ptr nextndscart; - if (std::holds_alternative(ndsargs)) - { // If we want to keep the existing cart (if any)... - nextndscart = NDS ? NDS->EjectCart() : nullptr; - ndsargs = {}; - } - else if (const auto ptr = std::get_if>(&ndsargs)) - { - nextndscart = std::move(*ptr); - ndsargs = {}; - } - - if (nextndscart && nextndscart->Type() == NDSCart::Homebrew) - { - // Load DLDISDCard will return nullopt if the SD card is disabled; - // SetSDCard will accept nullopt, which means no SD card - auto& homebrew = static_cast(*nextndscart); - homebrew.SetSDCard(ROMManager::LoadDLDISDCard()); - } - - std::unique_ptr nextgbacart; - if (std::holds_alternative(gbaargs)) - { - nextgbacart = NDS ? NDS->EjectGBACart() : nullptr; - } - else if (const auto ptr = std::get_if>(&gbaargs)) - { - nextgbacart = std::move(*ptr); - gbaargs = {}; - } - - if (!NDS || NDS->ConsoleType != Config::ConsoleType) - { // If we're switching between DS and DSi mode, or there's no console... - // To ensure the destructor is called before a new one is created, - // as the presence of global signal handlers still complicates things a bit - NDS = nullptr; - NDS::Current = nullptr; - - NDS = CreateConsole(std::move(nextndscart), std::move(nextgbacart)); - - if (NDS == nullptr) - return false; - - NDS->Reset(); - NDS::Current = NDS.get(); - - return true; - } - - auto arm9bios = ROMManager::LoadARM9BIOS(); - if (!arm9bios) - return false; - - auto arm7bios = ROMManager::LoadARM7BIOS(); - if (!arm7bios) - return false; - - auto firmware = ROMManager::LoadFirmware(NDS->ConsoleType); - if (!firmware) - return false; - - if (NDS->ConsoleType == 1) - { // If the console we're updating is a DSi... - DSi& dsi = static_cast(*NDS); - - auto arm9ibios = ROMManager::LoadDSiARM9BIOS(); - if (!arm9ibios) - return false; - - auto arm7ibios = ROMManager::LoadDSiARM7BIOS(); - if (!arm7ibios) - return false; - - auto nandimage = ROMManager::LoadNAND(*arm7ibios); - if (!nandimage) - return false; - - auto dsisdcard = ROMManager::LoadDSiSDCard(); - - dsi.SetFullBIOSBoot(Config::DSiFullBIOSBoot); - dsi.ARM7iBIOS = *arm7ibios; - dsi.ARM9iBIOS = *arm9ibios; - dsi.SetNAND(std::move(*nandimage)); - dsi.SetSDCard(std::move(dsisdcard)); - // We're moving the optional, not the card - // (inserting std::nullopt here is okay, it means no card) - - dsi.EjectGBACart(); - } - - if (NDS->ConsoleType == 0) - { - NDS->SetGBACart(std::move(nextgbacart)); - } - -#ifdef JIT_ENABLED - JITArgs jitargs { - static_cast(Config::JIT_MaxBlockSize), - Config::JIT_LiteralOptimisations, - Config::JIT_BranchOptimisations, - Config::JIT_FastMemory, - }; - NDS->SetJITArgs(Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt); -#endif - NDS->SetARM7BIOS(*arm7bios); - NDS->SetARM9BIOS(*arm9bios); - NDS->SetFirmware(std::move(*firmware)); - NDS->SetNDSCart(std::move(nextndscart)); - NDS->SPU.SetInterpolation(static_cast(Config::AudioInterp)); - NDS->SPU.SetDegrade10Bit(static_cast(Config::AudioBitDepth)); - - NDS::Current = NDS.get(); - - return true; -} - -void EmuThread::run() -{ - u32 mainScreenPos[3]; - Platform::FileHandle* file; - - UpdateConsole(nullptr, nullptr); - // No carts are inserted when melonDS first boots - - mainScreenPos[0] = 0; - mainScreenPos[1] = 0; - mainScreenPos[2] = 0; - autoScreenSizing = 0; - - videoSettingsDirty = false; - - if (mainWindow->hasOGL) - { - screenGL = static_cast(mainWindow->panel); - screenGL->initOpenGL(); - videoRenderer = Config::_3DRenderer; - } - else - { - screenGL = nullptr; - videoRenderer = 0; - } - - if (videoRenderer == 0) - { // If we're using the software renderer... - NDS->GPU.SetRenderer3D(std::make_unique(Config::Threaded3D != 0)); - } - else - { - auto glrenderer = melonDS::GLRenderer::New(); - glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); - NDS->GPU.SetRenderer3D(std::move(glrenderer)); - } - - Input::Init(); - - u32 nframes = 0; - double perfCountsSec = 1.0 / SDL_GetPerformanceFrequency(); - double lastTime = SDL_GetPerformanceCounter() * perfCountsSec; - double frameLimitError = 0.0; - double lastMeasureTime = lastTime; - - u32 winUpdateCount = 0, winUpdateFreq = 1; - u8 dsiVolumeLevel = 0x1F; - - file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Read); - if (file) - { - RTC::StateData state; - Platform::FileRead(&state, sizeof(state), 1, file); - Platform::CloseFile(file); - NDS->RTC.SetState(state); - } - - char melontitle[100]; - - while (EmuRunning != emuStatus_Exit) - { - Input::Process(); - - if (Input::HotkeyPressed(HK_FastForwardToggle)) emit windowLimitFPSChange(); - - if (Input::HotkeyPressed(HK_Pause)) emit windowEmuPause(); - if (Input::HotkeyPressed(HK_Reset)) emit windowEmuReset(); - if (Input::HotkeyPressed(HK_FrameStep)) emit windowEmuFrameStep(); - - if (Input::HotkeyPressed(HK_FullscreenToggle)) emit windowFullscreenToggle(); - - if (Input::HotkeyPressed(HK_SwapScreens)) emit swapScreensToggle(); - if (Input::HotkeyPressed(HK_SwapScreenEmphasis)) emit screenEmphasisToggle(); - - if (EmuRunning == emuStatus_Running || EmuRunning == emuStatus_FrameStep) - { - EmuStatus = emuStatus_Running; - if (EmuRunning == emuStatus_FrameStep) EmuRunning = emuStatus_Paused; - - if (Input::HotkeyPressed(HK_SolarSensorDecrease)) - { - int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorDown, true); - if (level != -1) - { - char msg[64]; - sprintf(msg, "Solar sensor level: %d", level); - OSD::AddMessage(0, msg); - } - } - if (Input::HotkeyPressed(HK_SolarSensorIncrease)) - { - int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorUp, true); - if (level != -1) - { - char msg[64]; - sprintf(msg, "Solar sensor level: %d", level); - OSD::AddMessage(0, msg); - } - } - - if (NDS->ConsoleType == 1) - { - DSi& dsi = static_cast(*NDS); - double currentTime = SDL_GetPerformanceCounter() * perfCountsSec; - - // Handle power button - if (Input::HotkeyDown(HK_PowerButton)) - { - dsi.I2C.GetBPTWL()->SetPowerButtonHeld(currentTime); - } - else if (Input::HotkeyReleased(HK_PowerButton)) - { - dsi.I2C.GetBPTWL()->SetPowerButtonReleased(currentTime); - } - - // Handle volume buttons - if (Input::HotkeyDown(HK_VolumeUp)) - { - dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Up); - } - else if (Input::HotkeyReleased(HK_VolumeUp)) - { - dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Up); - } - - if (Input::HotkeyDown(HK_VolumeDown)) - { - dsi.I2C.GetBPTWL()->SetVolumeSwitchHeld(DSi_BPTWL::volumeKey_Down); - } - else if (Input::HotkeyReleased(HK_VolumeDown)) - { - dsi.I2C.GetBPTWL()->SetVolumeSwitchReleased(DSi_BPTWL::volumeKey_Down); - } - - dsi.I2C.GetBPTWL()->ProcessVolumeSwitchInput(currentTime); - } - - // update render settings if needed - // HACK: - // once the fast forward hotkey is released, we need to update vsync - // to the old setting again - if (videoSettingsDirty || Input::HotkeyReleased(HK_FastForward)) - { - if (screenGL) - { - screenGL->setSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); - videoRenderer = Config::_3DRenderer; - } -#ifdef OGLRENDERER_ENABLED - else -#endif - { - videoRenderer = 0; - } - - videoRenderer = screenGL ? Config::_3DRenderer : 0; - - videoSettingsDirty = false; - - if (videoRenderer == 0) - { // If we're using the software renderer... - NDS->GPU.SetRenderer3D(std::make_unique(Config::Threaded3D != 0)); - } - else - { - auto glrenderer = melonDS::GLRenderer::New(); - glrenderer->SetRenderSettings(Config::GL_BetterPolygons, Config::GL_ScaleFactor); - NDS->GPU.SetRenderer3D(std::move(glrenderer)); - } - } - - // process input and hotkeys - NDS->SetKeyMask(Input::InputMask); - - if (Input::HotkeyPressed(HK_Lid)) - { - bool lid = !NDS->IsLidClosed(); - NDS->SetLidClosed(lid); - OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); - } - - // microphone input - AudioInOut::MicProcess(*NDS); - - // auto screen layout - if (Config::ScreenSizing == Frontend::screenSizing_Auto) - { - mainScreenPos[2] = mainScreenPos[1]; - mainScreenPos[1] = mainScreenPos[0]; - mainScreenPos[0] = NDS->PowerControl9 >> 15; - - int guess; - if (mainScreenPos[0] == mainScreenPos[2] && - mainScreenPos[0] != mainScreenPos[1]) - { - // constant flickering, likely displaying 3D on both screens - // TODO: when both screens are used for 2D only...??? - guess = Frontend::screenSizing_Even; - } - else - { - if (mainScreenPos[0] == 1) - guess = Frontend::screenSizing_EmphTop; - else - guess = Frontend::screenSizing_EmphBot; - } - - if (guess != autoScreenSizing) - { - autoScreenSizing = guess; - emit screenLayoutChange(); - } - } - - - // emulate - u32 nlines = NDS->RunFrame(); - - if (ROMManager::NDSSave) - ROMManager::NDSSave->CheckFlush(); - - if (ROMManager::GBASave) - ROMManager::GBASave->CheckFlush(); - - if (ROMManager::FirmwareSave) - ROMManager::FirmwareSave->CheckFlush(); - - if (!screenGL) - { - FrontBufferLock.lock(); - FrontBuffer = NDS->GPU.FrontBuffer; - FrontBufferLock.unlock(); - } - else - { - FrontBuffer = NDS->GPU.FrontBuffer; - screenGL->drawScreenGL(); - } - -#ifdef MELONCAP - MelonCap::Update(); -#endif // MELONCAP - - if (EmuRunning == emuStatus_Exit) break; - - winUpdateCount++; - if (winUpdateCount >= winUpdateFreq && !screenGL) - { - emit windowUpdate(); - winUpdateCount = 0; - } - - bool fastforward = Input::HotkeyDown(HK_FastForward); - - if (fastforward && screenGL && Config::ScreenVSync) - { - screenGL->setSwapInterval(0); - } - - if (Config::DSiVolumeSync && NDS->ConsoleType == 1) - { - DSi& dsi = static_cast(*NDS); - u8 volumeLevel = dsi.I2C.GetBPTWL()->GetVolumeLevel(); - if (volumeLevel != dsiVolumeLevel) - { - dsiVolumeLevel = volumeLevel; - emit syncVolumeLevel(); - } - - Config::AudioVolume = volumeLevel * (256.0 / 31.0); - } - - if (Config::AudioSync && !fastforward) - AudioInOut::AudioSync(*emuThread->NDS); - - double frametimeStep = nlines / (60.0 * 263.0); - - { - bool limitfps = Config::LimitFPS && !fastforward; - - double practicalFramelimit = limitfps ? frametimeStep : 1.0 / 1000.0; - - double curtime = SDL_GetPerformanceCounter() * perfCountsSec; - - frameLimitError += practicalFramelimit - (curtime - lastTime); - if (frameLimitError < -practicalFramelimit) - frameLimitError = -practicalFramelimit; - if (frameLimitError > practicalFramelimit) - frameLimitError = practicalFramelimit; - - if (round(frameLimitError * 1000.0) > 0.0) - { - SDL_Delay(round(frameLimitError * 1000.0)); - double timeBeforeSleep = curtime; - curtime = SDL_GetPerformanceCounter() * perfCountsSec; - frameLimitError -= curtime - timeBeforeSleep; - } - - lastTime = curtime; - } - - nframes++; - if (nframes >= 30) - { - double time = SDL_GetPerformanceCounter() * perfCountsSec; - double dt = time - lastMeasureTime; - lastMeasureTime = time; - - u32 fps = round(nframes / dt); - nframes = 0; - - float fpstarget = 1.0/frametimeStep; - - winUpdateFreq = fps / (u32)round(fpstarget); - if (winUpdateFreq < 1) - winUpdateFreq = 1; - - int inst = Platform::InstanceID(); - if (inst == 0) - sprintf(melontitle, "[%d/%.0f] melonDS " MELONDS_VERSION, fps, fpstarget); - else - sprintf(melontitle, "[%d/%.0f] melonDS (%d)", fps, fpstarget, inst+1); - changeWindowTitle(melontitle); - } - } - else - { - // paused - nframes = 0; - lastTime = SDL_GetPerformanceCounter() * perfCountsSec; - lastMeasureTime = lastTime; - - emit windowUpdate(); - - EmuStatus = EmuRunning; - - int inst = Platform::InstanceID(); - if (inst == 0) - sprintf(melontitle, "melonDS " MELONDS_VERSION); - else - sprintf(melontitle, "melonDS (%d)", inst+1); - changeWindowTitle(melontitle); - - SDL_Delay(75); - - if (screenGL) - screenGL->drawScreenGL(); - - ContextRequestKind contextRequest = ContextRequest; - if (contextRequest == contextRequest_InitGL) - { - screenGL = static_cast(mainWindow->panel); - screenGL->initOpenGL(); - ContextRequest = contextRequest_None; - } - else if (contextRequest == contextRequest_DeInitGL) - { - screenGL->deinitOpenGL(); - screenGL = nullptr; - ContextRequest = contextRequest_None; - } - } - } - - file = Platform::OpenLocalFile("rtc.bin", Platform::FileMode::Write); - if (file) - { - RTC::StateData state; - NDS->RTC.GetState(state); - Platform::FileWrite(&state, sizeof(state), 1, file); - Platform::CloseFile(file); - } - - EmuStatus = emuStatus_Exit; - - NDS::Current = nullptr; - // nds is out of scope, so unique_ptr cleans it up for us -} - -void EmuThread::changeWindowTitle(char* title) -{ - emit windowTitleChange(QString(title)); -} - -void EmuThread::emuRun() -{ - EmuRunning = emuStatus_Running; - EmuPauseStack = EmuPauseStackRunning; - RunningSomething = true; - - // checkme - emit windowEmuStart(); - AudioInOut::Enable(); -} - -void EmuThread::initContext() -{ - ContextRequest = contextRequest_InitGL; - while (ContextRequest != contextRequest_None); -} - -void EmuThread::deinitContext() -{ - ContextRequest = contextRequest_DeInitGL; - while (ContextRequest != contextRequest_None); -} - -void EmuThread::emuPause() -{ - EmuPauseStack++; - if (EmuPauseStack > EmuPauseStackPauseThreshold) return; - - PrevEmuStatus = EmuRunning; - EmuRunning = emuStatus_Paused; - while (EmuStatus != emuStatus_Paused); - - AudioInOut::Disable(); -} - -void EmuThread::emuUnpause() -{ - if (EmuPauseStack < EmuPauseStackPauseThreshold) return; - - EmuPauseStack--; - if (EmuPauseStack >= EmuPauseStackPauseThreshold) return; - - EmuRunning = PrevEmuStatus; - - AudioInOut::Enable(); -} - -void EmuThread::emuStop() -{ - EmuRunning = emuStatus_Exit; - EmuPauseStack = EmuPauseStackRunning; - - AudioInOut::Disable(); -} - -void EmuThread::emuFrameStep() -{ - if (EmuPauseStack < EmuPauseStackPauseThreshold) emit windowEmuPause(); - EmuRunning = emuStatus_FrameStep; -} - -bool EmuThread::emuIsRunning() -{ - return EmuRunning == emuStatus_Running; -} - -bool EmuThread::emuIsActive() -{ - return (RunningSomething == 1); -} - -/*void EmuThread::drawScreenGL() -{ - if (!NDS) return; - int w = windowInfo.surface_width; - int h = windowInfo.surface_height; - float factor = windowInfo.surface_scale; - - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glDisable(GL_DEPTH_TEST); - glDepthMask(false); - glDisable(GL_BLEND); - glDisable(GL_SCISSOR_TEST); - glDisable(GL_STENCIL_TEST); - glClear(GL_COLOR_BUFFER_BIT); - - glViewport(0, 0, w, h); - - glUseProgram(screenShaderProgram[2]); - glUniform2f(screenShaderScreenSizeULoc, w / factor, h / factor); - - int frontbuf = FrontBuffer; - glActiveTexture(GL_TEXTURE0); - -#ifdef OGLRENDERER_ENABLED - if (NDS->GPU.GetRenderer3D().Accelerated) - { - // hardware-accelerated render - static_cast(NDS->GPU.GetRenderer3D()).GetCompositor().BindOutputTexture(frontbuf); - } - else -#endif - { - // regular render - glBindTexture(GL_TEXTURE_2D, screenTexture); - - if (NDS->GPU.Framebuffer[frontbuf][0] && NDS->GPU.Framebuffer[frontbuf][1]) - { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][0].get()); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192+2, 256, 192, GL_RGBA, - GL_UNSIGNED_BYTE, NDS->GPU.Framebuffer[frontbuf][1].get()); - } - } - - screenSettingsLock.lock(); - - GLint filter = this->filter ? GL_LINEAR : GL_NEAREST; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); - - glBindBuffer(GL_ARRAY_BUFFER, screenVertexBuffer); - glBindVertexArray(screenVertexArray); - - for (int i = 0; i < numScreens; i++) - { - glUniformMatrix2x3fv(screenShaderTransformULoc, 1, GL_TRUE, screenMatrix[i]); - glDrawArrays(GL_TRIANGLES, screenKind[i] == 0 ? 0 : 2*3, 2*3); - } - - screenSettingsLock.unlock(); - - OSD::Update(); - OSD::DrawGL(w, h); - - oglContext->SwapBuffers(); -}*/ diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 4034ba34..51157c6c 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -22,141 +22,18 @@ #include "glad/glad.h" #include -#include #include #include #include #include #include #include -#include #include #include -#include -#include -#include - #include "Window.h" +#include "EmuThread.h" #include "FrontendUtil.h" -#include "duckstation/gl/context.h" - -#include "NDSCart.h" -#include "GBACart.h" - -using Keep = std::monostate; -using UpdateConsoleNDSArgs = std::variant>; -using UpdateConsoleGBAArgs = std::variant>; -namespace melonDS -{ -class NDS; -} - -class EmuThread : public QThread -{ - Q_OBJECT - void run() override; - -public: - explicit EmuThread(QObject* parent = nullptr); - - void changeWindowTitle(char* title); - - // to be called from the UI thread - void emuRun(); - void emuPause(); - void emuUnpause(); - void emuStop(); - void emuFrameStep(); - - bool emuIsRunning(); - bool emuIsActive(); - - void initContext(); - void deinitContext(); - - int FrontBuffer = 0; - QMutex FrontBufferLock; - - //void updateScreenSettings(bool filter, const WindowInfo& windowInfo, int numScreens, int* screenKind, float* screenMatrix); - - /// Applies the config in args. - /// Creates a new NDS console if needed, - /// modifies the existing one if possible. - /// @return \c true if the console was updated. - /// If this returns \c false, then the existing NDS console is not modified. - bool UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAArgs&& gbaargs) noexcept; - std::unique_ptr NDS; // TODO: Proper encapsulation and synchronization -signals: - void windowUpdate(); - void windowTitleChange(QString title); - - void windowEmuStart(); - void windowEmuStop(); - void windowEmuPause(); - void windowEmuReset(); - void windowEmuFrameStep(); - - void windowLimitFPSChange(); - - void screenLayoutChange(); - - void windowFullscreenToggle(); - - void swapScreensToggle(); - void screenEmphasisToggle(); - - void syncVolumeLevel(); - -private: - std::unique_ptr CreateConsole( - std::unique_ptr&& ndscart, - std::unique_ptr&& gbacart - ) noexcept; - //void drawScreenGL(); - //void initOpenGL(); - //void deinitOpenGL(); - - enum EmuStatusKind - { - emuStatus_Exit, - emuStatus_Running, - emuStatus_Paused, - emuStatus_FrameStep, - }; - std::atomic EmuStatus; - - EmuStatusKind PrevEmuStatus; - EmuStatusKind EmuRunning; - - constexpr static int EmuPauseStackRunning = 0; - constexpr static int EmuPauseStackPauseThreshold = 1; - int EmuPauseStack; - - enum ContextRequestKind - { - contextRequest_None = 0, - contextRequest_InitGL, - contextRequest_DeInitGL - }; - std::atomic ContextRequest = contextRequest_None; - - /*GL::Context* oglContext = nullptr; - GLuint screenVertexBuffer, screenVertexArray; - GLuint screenTexture; - GLuint screenShaderProgram[3]; - GLuint screenShaderTransformULoc, screenShaderScreenSizeULoc; - - QMutex screenSettingsLock; - WindowInfo windowInfo; - float screenMatrix[Frontend::MaxScreenTransforms][6]; - int screenKind[Frontend::MaxScreenTransforms]; - int numScreens; - bool filter; - - int lastScreenWidth = -1, lastScreenHeight = -1;*/ - ScreenPanelGL* screenGL; -}; class MelonApplication : public QApplication { From ab8938a6950f613c0f255788b483ef683e5e0851 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 26 Dec 2023 19:32:38 +0100 Subject: [PATCH 103/157] fix OSD scaling on hiDPI screens --- src/frontend/qt_sdl/OSD.cpp | 4 ++-- src/frontend/qt_sdl/OSD.h | 2 +- src/frontend/qt_sdl/Screen.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp index 25072df7..3734b76b 100644 --- a/src/frontend/qt_sdl/OSD.cpp +++ b/src/frontend/qt_sdl/OSD.cpp @@ -418,7 +418,7 @@ void DrawNative(QPainter& painter) Rendering.unlock(); } -void DrawGL(float w, float h) +void DrawGL(float w, float h, float factor) { if (!Config::ShowOSD) return; if (!mainWindow || !mainWindow->panel) return; @@ -430,7 +430,7 @@ void DrawGL(float w, float h) glUseProgram(Shader[2]); glUniform2f(uScreenSize, w, h); - glUniform1f(uScaleFactor, mainWindow->devicePixelRatioF()); + glUniform1f(uScaleFactor, factor); glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); glBindVertexArray(OSDVertexArray); diff --git a/src/frontend/qt_sdl/OSD.h b/src/frontend/qt_sdl/OSD.h index 64131d5b..c907a0bb 100644 --- a/src/frontend/qt_sdl/OSD.h +++ b/src/frontend/qt_sdl/OSD.h @@ -32,7 +32,7 @@ void AddMessage(u32 color, const char* text); void Update(); void DrawNative(QPainter& painter); -void DrawGL(float w, float h); +void DrawGL(float w, float h, float factor); } diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index 35be7e6a..d8af6624 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -591,7 +591,7 @@ void ScreenPanelGL::drawScreenGL() screenSettingsLock.unlock(); OSD::Update(); - OSD::DrawGL(w, h); + OSD::DrawGL(w, h, factor); glContext->SwapBuffers(); } From cbd65a131ed4eb6938964e57de3385ca890b6091 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Tue, 26 Dec 2023 16:09:39 -0500 Subject: [PATCH 104/157] Add `alignas` specifiers to some arrays based on how they're accessed (#1933) * Align some two-element `u32` arrays as `u64`s - To pacify "unaligned read/write" warnings from UBSan * Align some more arrays based on how they're accessed --- src/GPU.h | 34 +++++++++++++++++----------------- src/NDS.h | 14 +++++++------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/GPU.h b/src/GPU.h index e070db78..780d5e01 100644 --- a/src/GPU.h +++ b/src/GPU.h @@ -536,18 +536,18 @@ public: u8 VRAMCNT[9] {}; u8 VRAMSTAT = 0; - u8 Palette[2*1024] {}; - u8 OAM[2*1024] {}; + alignas(u64) u8 Palette[2*1024] {}; + alignas(u64) u8 OAM[2*1024] {}; - u8 VRAM_A[128*1024] {}; - u8 VRAM_B[128*1024] {}; - u8 VRAM_C[128*1024] {}; - u8 VRAM_D[128*1024] {}; - u8 VRAM_E[ 64*1024] {}; - u8 VRAM_F[ 16*1024] {}; - u8 VRAM_G[ 16*1024] {}; - u8 VRAM_H[ 32*1024] {}; - u8 VRAM_I[ 16*1024] {}; + alignas(u64) u8 VRAM_A[128*1024] {}; + alignas(u64) u8 VRAM_B[128*1024] {}; + alignas(u64) u8 VRAM_C[128*1024] {}; + alignas(u64) u8 VRAM_D[128*1024] {}; + alignas(u64) u8 VRAM_E[ 64*1024] {}; + alignas(u64) u8 VRAM_F[ 16*1024] {}; + alignas(u64) u8 VRAM_G[ 16*1024] {}; + alignas(u64) u8 VRAM_H[ 32*1024] {}; + alignas(u64) u8 VRAM_I[ 16*1024] {}; u8* const VRAM[9] = {VRAM_A, VRAM_B, VRAM_C, VRAM_D, VRAM_E, VRAM_F, VRAM_G, VRAM_H, VRAM_I}; u32 const VRAMMask[9] = {0x1FFFF, 0x1FFFF, 0x1FFFF, 0x1FFFF, 0xFFFF, 0x3FFF, 0x3FFF, 0x7FFF, 0x3FFF}; @@ -596,14 +596,14 @@ public: u8 VRAMFlat_AOBJ[256*1024] {}; u8 VRAMFlat_BOBJ[128*1024] {}; - u8 VRAMFlat_ABGExtPal[32*1024] {}; - u8 VRAMFlat_BBGExtPal[32*1024] {}; + alignas(u16) u8 VRAMFlat_ABGExtPal[32*1024] {}; + alignas(u16) u8 VRAMFlat_BBGExtPal[32*1024] {}; - u8 VRAMFlat_AOBJExtPal[8*1024] {}; - u8 VRAMFlat_BOBJExtPal[8*1024] {}; + alignas(u16) u8 VRAMFlat_AOBJExtPal[8*1024] {}; + alignas(u16) u8 VRAMFlat_BOBJExtPal[8*1024] {}; - u8 VRAMFlat_Texture[512*1024] {}; - u8 VRAMFlat_TexPal[128*1024] {}; + alignas(u64) u8 VRAMFlat_Texture[512*1024] {}; + alignas(u64) u8 VRAMFlat_TexPal[128*1024] {}; private: void ResetVRAMCache() noexcept; void AssignFramebuffers() noexcept; diff --git a/src/NDS.h b/src/NDS.h index 23b1f888..f9df2d69 100644 --- a/src/NDS.h +++ b/src/NDS.h @@ -259,8 +259,8 @@ public: // TODO: Encapsulate the rest of these members u16 PowerControl9; u16 ExMemCnt[2]; - u8 ROMSeed0[2*8]; - u8 ROMSeed1[2*8]; + alignas(u32) u8 ROMSeed0[2*8]; + alignas(u32) u8 ROMSeed1[2*8]; protected: // These BIOS arrays should be declared *before* the component objects (JIT, SPI, etc.) @@ -489,12 +489,12 @@ private: FIFO IPCFIFO9; // FIFO in which the ARM9 writes FIFO IPCFIFO7; u16 DivCnt; - u32 DivNumerator[2]; - u32 DivDenominator[2]; - u32 DivQuotient[2]; - u32 DivRemainder[2]; + alignas(u64) u32 DivNumerator[2]; + alignas(u64) u32 DivDenominator[2]; + alignas(u64) u32 DivQuotient[2]; + alignas(u64) u32 DivRemainder[2]; u16 SqrtCnt; - u32 SqrtVal[2]; + alignas(u64) u32 SqrtVal[2]; u32 SqrtRes; u16 KeyCnt[2]; bool Running; From 5a08118c877efb5407a08f48cf31865faf813d7c Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 27 Dec 2023 21:28:03 +0100 Subject: [PATCH 105/157] sfjsh --- qt-wayland-scale-hack.patch | 80 ++++++++++++++++++++++++++++++++++++ src/frontend/qt_sdl/Screen.h | 2 + 2 files changed, 82 insertions(+) create mode 100644 qt-wayland-scale-hack.patch diff --git a/qt-wayland-scale-hack.patch b/qt-wayland-scale-hack.patch new file mode 100644 index 00000000..a8740354 --- /dev/null +++ b/qt-wayland-scale-hack.patch @@ -0,0 +1,80 @@ +diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp +index d8af6624..019ce246 100644 +--- a/src/frontend/qt_sdl/Screen.cpp ++++ b/src/frontend/qt_sdl/Screen.cpp +@@ -30,6 +30,7 @@ + #include + #include + #include ++#include + #ifndef _WIN32 + #ifndef APPLE + #include +@@ -596,23 +597,20 @@ void ScreenPanelGL::drawScreenGL() + glContext->SwapBuffers(); + } + +-qreal ScreenPanelGL::devicePixelRatioFromScreen() const ++qreal ScreenPanelGL::devicePixelRatioFromWindow() const + { +- const QScreen* screen_for_ratio = window()->windowHandle()->screen(); +- if (!screen_for_ratio) +- screen_for_ratio = QGuiApplication::primaryScreen(); +- +- return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); ++ QWindow* window = windowHandle(); ++ return window->devicePixelRatio(); + } + + int ScreenPanelGL::scaledWindowWidth() const + { +- return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())), 1); ++ return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromWindow())), 1); + } + + int ScreenPanelGL::scaledWindowHeight() const + { +- return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())), 1); ++ return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromWindow())), 1); + } + + std::optional ScreenPanelGL::getWindowInfo() +@@ -644,6 +642,7 @@ std::optional ScreenPanelGL::getWindowInfo() + + wi.display_connection = pni->nativeResourceForWindow("display", handle); + wi.window_handle = pni->nativeResourceForWindow("surface", handle); ++ qApp->processEvents(); + } + else + { +@@ -654,7 +653,7 @@ std::optional ScreenPanelGL::getWindowInfo() + + wi.surface_width = static_cast(scaledWindowWidth()); + wi.surface_height = static_cast(scaledWindowHeight()); +- wi.surface_scale = static_cast(devicePixelRatioFromScreen()); ++ wi.surface_scale = static_cast(devicePixelRatioFromWindow()); + + return wi; + } +diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h +index b11df29b..79889a3f 100644 +--- a/src/frontend/qt_sdl/Screen.h ++++ b/src/frontend/qt_sdl/Screen.h +@@ -23,6 +23,8 @@ + #include "FrontendUtil.h" + #include "duckstation/gl/context.h" + ++#include ++ + #include + #include + #include +@@ -133,7 +135,7 @@ public: + void transferLayout(); + protected: + +- qreal devicePixelRatioFromScreen() const; ++ qreal devicePixelRatioFromWindow() const; + int scaledWindowWidth() const; + int scaledWindowHeight() const; + diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h index b11df29b..4c031e64 100644 --- a/src/frontend/qt_sdl/Screen.h +++ b/src/frontend/qt_sdl/Screen.h @@ -19,6 +19,8 @@ #ifndef SCREEN_H #define SCREEN_H +#include + #include "glad/glad.h" #include "FrontendUtil.h" #include "duckstation/gl/context.h" From fa835ecf68910baa9bc355be4c4eb1dc66e65b0f Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 27 Dec 2023 21:29:25 +0100 Subject: [PATCH 106/157] blarg --- qt-wayland-scale-hack.patch | 80 ------------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 qt-wayland-scale-hack.patch diff --git a/qt-wayland-scale-hack.patch b/qt-wayland-scale-hack.patch deleted file mode 100644 index a8740354..00000000 --- a/qt-wayland-scale-hack.patch +++ /dev/null @@ -1,80 +0,0 @@ -diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp -index d8af6624..019ce246 100644 ---- a/src/frontend/qt_sdl/Screen.cpp -+++ b/src/frontend/qt_sdl/Screen.cpp -@@ -30,6 +30,7 @@ - #include - #include - #include -+#include - #ifndef _WIN32 - #ifndef APPLE - #include -@@ -596,23 +597,20 @@ void ScreenPanelGL::drawScreenGL() - glContext->SwapBuffers(); - } - --qreal ScreenPanelGL::devicePixelRatioFromScreen() const -+qreal ScreenPanelGL::devicePixelRatioFromWindow() const - { -- const QScreen* screen_for_ratio = window()->windowHandle()->screen(); -- if (!screen_for_ratio) -- screen_for_ratio = QGuiApplication::primaryScreen(); -- -- return screen_for_ratio ? screen_for_ratio->devicePixelRatio() : static_cast(1); -+ QWindow* window = windowHandle(); -+ return window->devicePixelRatio(); - } - - int ScreenPanelGL::scaledWindowWidth() const - { -- return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromScreen())), 1); -+ return std::max(static_cast(std::ceil(static_cast(width()) * devicePixelRatioFromWindow())), 1); - } - - int ScreenPanelGL::scaledWindowHeight() const - { -- return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromScreen())), 1); -+ return std::max(static_cast(std::ceil(static_cast(height()) * devicePixelRatioFromWindow())), 1); - } - - std::optional ScreenPanelGL::getWindowInfo() -@@ -644,6 +642,7 @@ std::optional ScreenPanelGL::getWindowInfo() - - wi.display_connection = pni->nativeResourceForWindow("display", handle); - wi.window_handle = pni->nativeResourceForWindow("surface", handle); -+ qApp->processEvents(); - } - else - { -@@ -654,7 +653,7 @@ std::optional ScreenPanelGL::getWindowInfo() - - wi.surface_width = static_cast(scaledWindowWidth()); - wi.surface_height = static_cast(scaledWindowHeight()); -- wi.surface_scale = static_cast(devicePixelRatioFromScreen()); -+ wi.surface_scale = static_cast(devicePixelRatioFromWindow()); - - return wi; - } -diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h -index b11df29b..79889a3f 100644 ---- a/src/frontend/qt_sdl/Screen.h -+++ b/src/frontend/qt_sdl/Screen.h -@@ -23,6 +23,8 @@ - #include "FrontendUtil.h" - #include "duckstation/gl/context.h" - -+#include -+ - #include - #include - #include -@@ -133,7 +135,7 @@ public: - void transferLayout(); - protected: - -- qreal devicePixelRatioFromScreen() const; -+ qreal devicePixelRatioFromWindow() const; - int scaledWindowWidth() const; - int scaledWindowHeight() const; - From 54397425781072192192cfb4947102cc5bb2fe41 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 28 Dec 2023 09:25:05 +0100 Subject: [PATCH 107/157] Add basic CMake presets file I'll probably use this for CI, but regardless it's nice to have to make it easier for users to build melonDS. --- CMakePresets.json | 88 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 CMakePresets.json diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..e14eda24 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,88 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "release", + "displayName": "Release", + "description": "Default release build configuration.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build/release" + }, + { + "inherits": "release", + "name": "release-vcpkg", + "displayName": "Release (vcpkg)", + "description": "Release build with packages from vcpkg.", + "cacheVariables": { + "USE_VCPKG": { + "type": "BOOL", + "value": "ON" + } + } + }, + { + "name": "release-mac-x86_64", + "inherits": "release-vcpkg", + "displayName": "macOS release (x86_64)", + "binaryDir": "${sourceDir}/build/release-mac-x86_64", + "cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "x86_64" } + }, + { + "name": "release-mac-arm64", + "inherits": "release-vcpkg", + "displayName": "macOS release (arm64)", + "binaryDir": "${sourceDir}/build/release-mac-arm64", + "cacheVariables": { "CMAKE_OSX_ARCHITECTURES": "arm64" } + } + ], + "buildPresets": [ + { + "name": "release", + "configurePreset": "release" + }, + { + "name": "release-vcpkg", + "configurePreset": "release-vcpkg" + }, + { + "name": "release-mac-x86_64", + "configurePreset": "release-mac-x86_64" + }, + { + "name": "release-mac-arm64", + "configurePreset": "release-mac-arm64" + } + ], + "workflowPresets": [ + { + "name": "release", + "displayName": "Release", + "steps": [ + { "type": "configure", "name": "release" }, + { "type": "build", "name": "release" } + ] + }, + { + "name": "release-vcpkg", + "displayName": "Release (vcpkg)", + "steps": [ + { "type": "configure", "name": "release-vcpkg" }, + { "type": "build", "name": "release-vcpkg" } + ] + }, + { + "name": "release-mac-x86_64", + "steps": [ + { "type": "configure", "name": "release-mac-x86_64" }, + { "type": "build", "name": "release-mac-x86_64" } + ] + }, + { + "name": "release-mac-arm64", + "steps": [ + { "type": "configure", "name": "release-mac-arm64" }, + { "type": "build", "name": "release-mac-arm64" } + ] + } + ] +} \ No newline at end of file From 345b7439e41e57d159befc8fc2546f184dc10fc0 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Thu, 28 Dec 2023 14:40:37 +0100 Subject: [PATCH 108/157] integrate OSD into ScreenPanel and make it nicer --- src/frontend/qt_sdl/CMakeLists.txt | 1 - src/frontend/qt_sdl/EmuThread.cpp | 15 +- src/frontend/qt_sdl/OSD.cpp | 475 ------------------------ src/frontend/qt_sdl/OSD.h | 39 -- src/frontend/qt_sdl/Platform.cpp | 11 +- src/frontend/qt_sdl/Screen.cpp | 578 ++++++++++++++++++++++------- src/frontend/qt_sdl/Screen.h | 116 +++--- src/frontend/qt_sdl/Window.cpp | 23 +- src/frontend/qt_sdl/Window.h | 6 +- src/frontend/qt_sdl/main.cpp | 1 - 10 files changed, 548 insertions(+), 717 deletions(-) delete mode 100644 src/frontend/qt_sdl/OSD.cpp delete mode 100644 src/frontend/qt_sdl/OSD.h diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 92810210..8eeb44a4 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -32,7 +32,6 @@ set(SOURCES_QT_SDL LAN_PCap.cpp LAN_Socket.cpp LocalMP.cpp - OSD.cpp OSD_shaders.h font.h Platform.cpp diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index f86d30b7..4a75387e 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -36,7 +36,6 @@ #include "version.h" #include "FrontendUtil.h" -#include "OSD.h" #include "Args.h" #include "NDS.h" @@ -80,7 +79,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) EmuPauseStack = EmuPauseStackRunning; RunningSomething = false; - connect(this, SIGNAL(windowUpdate()), mainWindow->panelWidget, SLOT(repaint())); + connect(this, SIGNAL(windowUpdate()), mainWindow->panel, SLOT(repaint())); connect(this, SIGNAL(windowTitleChange(QString)), mainWindow, SLOT(onTitleUpdate(QString))); connect(this, SIGNAL(windowEmuStart()), mainWindow, SLOT(onEmuStart())); connect(this, SIGNAL(windowEmuStop()), mainWindow, SLOT(onEmuStop())); @@ -88,7 +87,7 @@ EmuThread::EmuThread(QObject* parent) : QThread(parent) connect(this, SIGNAL(windowEmuReset()), mainWindow->actReset, SLOT(trigger())); connect(this, SIGNAL(windowEmuFrameStep()), mainWindow->actFrameStep, SLOT(trigger())); connect(this, SIGNAL(windowLimitFPSChange()), mainWindow->actLimitFramerate, SLOT(trigger())); - connect(this, SIGNAL(screenLayoutChange()), mainWindow->panelWidget, SLOT(onScreenLayoutChanged())); + connect(this, SIGNAL(screenLayoutChange()), mainWindow->panel, SLOT(onScreenLayoutChanged())); connect(this, SIGNAL(windowFullscreenToggle()), mainWindow, SLOT(onFullscreenToggled())); connect(this, SIGNAL(swapScreensToggle()), mainWindow->actScreenSwap, SLOT(trigger())); connect(this, SIGNAL(screenEmphasisToggle()), mainWindow, SLOT(onScreenEmphasisToggled())); @@ -386,9 +385,7 @@ void EmuThread::run() int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorDown, true); if (level != -1) { - char msg[64]; - sprintf(msg, "Solar sensor level: %d", level); - OSD::AddMessage(0, msg); + mainWindow->osdAddMessage(0, "Solar sensor level: %d", level); } } if (Input::HotkeyPressed(HK_SolarSensorIncrease)) @@ -396,9 +393,7 @@ void EmuThread::run() int level = NDS->GBACartSlot.SetInput(GBACart::Input_SolarSensorUp, true); if (level != -1) { - char msg[64]; - sprintf(msg, "Solar sensor level: %d", level); - OSD::AddMessage(0, msg); + mainWindow->osdAddMessage(0, "Solar sensor level: %d", level); } } @@ -480,7 +475,7 @@ void EmuThread::run() { bool lid = !NDS->IsLidClosed(); NDS->SetLidClosed(lid); - OSD::AddMessage(0, lid ? "Lid closed" : "Lid opened"); + mainWindow->osdAddMessage(0, lid ? "Lid closed" : "Lid opened"); } // microphone input diff --git a/src/frontend/qt_sdl/OSD.cpp b/src/frontend/qt_sdl/OSD.cpp deleted file mode 100644 index 3734b76b..00000000 --- a/src/frontend/qt_sdl/OSD.cpp +++ /dev/null @@ -1,475 +0,0 @@ -/* - 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 -#include -#include -#include -#include "../types.h" - -#include "main.h" -#include "OpenGLSupport.h" -#include - -#include "OSD.h" -#include "OSD_shaders.h" -#include "font.h" - -#include "Config.h" - -using namespace melonDS; - -extern MainWindow* mainWindow; - -namespace OSD -{ - -const u32 kOSDMargin = 6; - -struct Item -{ - Uint32 Timestamp; - char Text[256]; - u32 Color; - - u32 Width, Height; - u32* Bitmap; - - bool NativeBitmapLoaded; - QImage NativeBitmap; - - bool GLTextureLoaded; - GLuint GLTexture; -}; - -std::deque ItemQueue; - -GLuint Shader[3]; -GLint uScreenSize, uOSDPos, uOSDSize; -GLfloat uScaleFactor; -GLuint OSDVertexArray; -GLuint OSDVertexBuffer; - -QMutex Rendering; - - -bool Init(bool openGL) -{ - if (openGL) - { - OpenGL::BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, Shader, "OSDShader"); - - GLuint pid = Shader[2]; - glBindAttribLocation(pid, 0, "vPosition"); - glBindFragDataLocation(pid, 0, "oColor"); - - OpenGL::LinkShaderProgram(Shader); - glUseProgram(pid); - glUniform1i(glGetUniformLocation(pid, "OSDTex"), 0); - - uScreenSize = glGetUniformLocation(pid, "uScreenSize"); - uOSDPos = glGetUniformLocation(pid, "uOSDPos"); - uOSDSize = glGetUniformLocation(pid, "uOSDSize"); - uScaleFactor = glGetUniformLocation(pid, "uScaleFactor"); - - float vertices[6*2] = - { - 0, 0, - 1, 1, - 1, 0, - 0, 0, - 0, 1, - 1, 1 - }; - - glGenBuffers(1, &OSDVertexBuffer); - glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); - - glGenVertexArrays(1, &OSDVertexArray); - glBindVertexArray(OSDVertexArray); - glEnableVertexAttribArray(0); // position - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); - } - - return true; -} - -void DeInit() -{ - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); - if (item.Bitmap) delete[] item.Bitmap; - - it = ItemQueue.erase(it); - } -} - - -int FindBreakPoint(const char* text, int i) -{ - // i = character that went out of bounds - - for (int j = i; j >= 0; j--) - { - if (text[j] == ' ') - return j; - } - - return i; -} - -void LayoutText(const char* text, u32* width, u32* height, int* breaks) -{ - u32 w = 0; - u32 h = 14; - u32 totalw = 0; - u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2); - int lastbreak = -1; - int numbrk = 0; - u16* ptr; - - memset(breaks, 0, sizeof(int)*64); - - for (int i = 0; text[i] != '\0'; ) - { - int glyphsize; - if (text[i] == ' ') - { - glyphsize = 6; - } - else - { - u32 ch = text[i]; - if (ch < 0x10 || ch > 0x7E) ch = 0x7F; - - ptr = &font[(ch-0x10) << 4]; - glyphsize = ptr[0]; - if (!glyphsize) glyphsize = 6; - else glyphsize += 2; // space around the character - } - - w += glyphsize; - if (w > maxw) - { - // wrap shit as needed - if (text[i] == ' ') - { - if (numbrk >= 64) break; - breaks[numbrk++] = i; - i++; - } - else - { - int brk = FindBreakPoint(text, i); - if (brk != lastbreak) i = brk; - - if (numbrk >= 64) break; - breaks[numbrk++] = i; - - lastbreak = brk; - } - - w = 0; - h += 14; - } - else - i++; - - if (w > totalw) totalw = w; - } - - *width = totalw; - *height = h; -} - -u32 RainbowColor(u32 inc) -{ - // inspired from Acmlmboard - - if (inc < 100) return 0xFFFF9B9B + (inc << 8); - else if (inc < 200) return 0xFFFFFF9B - ((inc-100) << 16); - else if (inc < 300) return 0xFF9BFF9B + (inc-200); - else if (inc < 400) return 0xFF9BFFFF - ((inc-300) << 8); - else if (inc < 500) return 0xFF9B9BFF + ((inc-400) << 16); - else return 0xFFFF9BFF - (inc-500); -} - -void RenderText(u32 color, const char* text, Item* item) -{ - u32 w, h; - int breaks[64]; - - bool rainbow = (color == 0); - u32 rainbowinc = ((text[0] * 17) + (SDL_GetTicks() * 13)) % 600; - - color |= 0xFF000000; - const u32 shadow = 0xE0000000; - - LayoutText(text, &w, &h, breaks); - - item->Width = w; - item->Height = h; - item->Bitmap = new u32[w*h]; - memset(item->Bitmap, 0, w*h*sizeof(u32)); - - u32 x = 0, y = 1; - u32 maxw = mainWindow->panelWidget->width() - (kOSDMargin*2); - int curline = 0; - u16* ptr; - - for (int i = 0; text[i] != '\0'; ) - { - int glyphsize; - if (text[i] == ' ') - { - x += 6; - } - else - { - u32 ch = text[i]; - if (ch < 0x10 || ch > 0x7E) ch = 0x7F; - - ptr = &font[(ch-0x10) << 4]; - int glyphsize = ptr[0]; - if (!glyphsize) x += 6; - else - { - x++; - - if (rainbow) - { - color = RainbowColor(rainbowinc); - rainbowinc = (rainbowinc + 30) % 600; - } - - // draw character - for (int cy = 0; cy < 12; cy++) - { - u16 val = ptr[4+cy]; - - for (int cx = 0; cx < glyphsize; cx++) - { - if (val & (1<Bitmap[((y+cy) * w) + x+cx] = color; - } - } - - x += glyphsize; - x++; - } - } - - i++; - if (breaks[curline] && i >= breaks[curline]) - { - i = breaks[curline++]; - if (text[i] == ' ') i++; - - x = 0; - y += 14; - } - } - - // shadow - for (y = 0; y < h; y++) - { - for (x = 0; x < w; x++) - { - u32 val; - - val = item->Bitmap[(y * w) + x]; - if ((val >> 24) == 0xFF) continue; - - if (x > 0) val = item->Bitmap[(y * w) + x-1]; - if (x < w-1) val |= item->Bitmap[(y * w) + x+1]; - if (y > 0) - { - if (x > 0) val |= item->Bitmap[((y-1) * w) + x-1]; - val |= item->Bitmap[((y-1) * w) + x]; - if (x < w-1) val |= item->Bitmap[((y-1) * w) + x+1]; - } - if (y < h-1) - { - if (x > 0) val |= item->Bitmap[((y+1) * w) + x-1]; - val |= item->Bitmap[((y+1) * w) + x]; - if (x < w-1) val |= item->Bitmap[((y+1) * w) + x+1]; - } - - if ((val >> 24) == 0xFF) - item->Bitmap[(y * w) + x] = shadow; - } - } -} - - -void AddMessage(u32 color, const char* text) -{ - if (!Config::ShowOSD) return; - - Rendering.lock(); - - Item item; - - item.Timestamp = SDL_GetTicks(); - strncpy(item.Text, text, 255); item.Text[255] = '\0'; - item.Color = color; - item.Bitmap = nullptr; - - item.NativeBitmapLoaded = false; - item.GLTextureLoaded = false; - - ItemQueue.push_back(item); - - Rendering.unlock(); -} - -void Update() -{ - if (!Config::ShowOSD) - { - Rendering.lock(); - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); - if (item.Bitmap) delete[] item.Bitmap; - - it = ItemQueue.erase(it); - } - Rendering.unlock(); - return; - } - - Rendering.lock(); - - Uint32 tick_now = SDL_GetTicks(); - Uint32 tick_min = tick_now - 2500; - - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (item.Timestamp < tick_min) - { - if (item.GLTextureLoaded) glDeleteTextures(1, &item.GLTexture); - if (item.Bitmap) delete[] item.Bitmap; - - it = ItemQueue.erase(it); - continue; - } - - if (!item.Bitmap) - { - RenderText(item.Color, item.Text, &item); - } - - it++; - } - - Rendering.unlock(); -} - -void DrawNative(QPainter& painter) -{ - if (!Config::ShowOSD) return; - - Rendering.lock(); - - u32 y = kOSDMargin; - - painter.resetTransform(); - - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (!item.NativeBitmapLoaded) - { - item.NativeBitmap = QImage((const uchar*)item.Bitmap, item.Width, item.Height, QImage::Format_ARGB32_Premultiplied); - item.NativeBitmapLoaded = true; - } - - painter.drawImage(kOSDMargin, y, item.NativeBitmap); - - y += item.Height; - it++; - } - - Rendering.unlock(); -} - -void DrawGL(float w, float h, float factor) -{ - if (!Config::ShowOSD) return; - if (!mainWindow || !mainWindow->panel) return; - - Rendering.lock(); - - u32 y = kOSDMargin; - - glUseProgram(Shader[2]); - - glUniform2f(uScreenSize, w, h); - glUniform1f(uScaleFactor, factor); - - glBindBuffer(GL_ARRAY_BUFFER, OSDVertexBuffer); - glBindVertexArray(OSDVertexArray); - - glActiveTexture(GL_TEXTURE0); - - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - - for (auto it = ItemQueue.begin(); it != ItemQueue.end(); ) - { - Item& item = *it; - - if (!item.GLTextureLoaded) - { - glGenTextures(1, &item.GLTexture); - glBindTexture(GL_TEXTURE_2D, item.GLTexture); - 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); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item.Width, item.Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, item.Bitmap); - - item.GLTextureLoaded = true; - } - - glBindTexture(GL_TEXTURE_2D, item.GLTexture); - glUniform2i(uOSDPos, kOSDMargin, y); - glUniform2i(uOSDSize, item.Width, item.Height); - glDrawArrays(GL_TRIANGLES, 0, 2*3); - - y += item.Height; - it++; - } - - glDisable(GL_BLEND); - glUseProgram(0); - - Rendering.unlock(); -} - -} diff --git a/src/frontend/qt_sdl/OSD.h b/src/frontend/qt_sdl/OSD.h deleted file mode 100644 index c907a0bb..00000000 --- a/src/frontend/qt_sdl/OSD.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - 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 OSD_H -#define OSD_H - -#include "types.h" - -namespace OSD -{ - -using namespace melonDS; -bool Init(bool openGL); -void DeInit(); - -void AddMessage(u32 color, const char* text); - -void Update(); -void DrawNative(QPainter& painter); -void DrawGL(float w, float h, float factor); - -} - -#endif // OSD_H diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 6fe87ac4..efd33400 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -39,7 +39,6 @@ #include "LAN_Socket.h" #include "LAN_PCap.h" #include "LocalMP.h" -#include "OSD.h" #include "SPI_Firmware.h" #ifdef __WIN32__ @@ -53,6 +52,10 @@ extern CameraManager* camManager[2]; void emuStop(); +// TEMP +//#include "main.h" +//extern MainWindow* mainWindow; + namespace melonDS::Platform { @@ -177,14 +180,14 @@ void SignalStop(StopReason reason) { case StopReason::GBAModeNotSupported: Log(LogLevel::Error, "!! GBA MODE NOT SUPPORTED\n"); - OSD::AddMessage(0xFFA0A0, "GBA mode not supported."); + //mainWindow->osdAddMessage(0xFFA0A0, "GBA mode not supported."); break; case StopReason::BadExceptionRegion: - OSD::AddMessage(0xFFA0A0, "Internal error."); + //mainWindow->osdAddMessage(0xFFA0A0, "Internal error."); break; case StopReason::PowerOff: case StopReason::External: - OSD::AddMessage(0xFFC040, "Shutdown"); + //mainWindow->osdAddMessage(0xFFC040, "Shutdown"); default: break; } diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index d8af6624..cfcbeed9 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -35,6 +35,7 @@ #include #endif #endif +#include #include "OpenGLSupport.h" #include "duckstation/gl/context.h" @@ -49,8 +50,8 @@ #include "Config.h" #include "main_shaders.h" - -#include "OSD.h" +#include "OSD_shaders.h" +#include "font.h" using namespace melonDS; @@ -64,23 +65,31 @@ extern int autoScreenSizing; extern int videoRenderer; extern bool videoSettingsDirty; +const u32 kOSDMargin = 6; -ScreenHandler::ScreenHandler(QWidget* widget) + +ScreenPanel::ScreenPanel(QWidget* parent) : QWidget(parent) { - widget->setMouseTracking(true); - widget->setAttribute(Qt::WA_AcceptTouchEvents); + setMouseTracking(true); + setAttribute(Qt::WA_AcceptTouchEvents); QTimer* mouseTimer = setupMouseTimer(); - widget->connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) widget->setCursor(Qt::BlankCursor);}); + connect(mouseTimer, &QTimer::timeout, [=] { if (Config::MouseHide) setCursor(Qt::BlankCursor);}); + + osdEnabled = false; + osdID = 1; } -ScreenHandler::~ScreenHandler() +ScreenPanel::~ScreenPanel() { mouseTimer->stop(); delete mouseTimer; } -void ScreenHandler::screenSetupLayout(int w, int h) +void ScreenPanel::setupScreenLayout() { + int w = width(); + int h = height(); + int sizing = Config::ScreenSizing; if (sizing == 3) sizing = autoScreenSizing; @@ -113,7 +122,7 @@ void ScreenHandler::screenSetupLayout(int w, int h) numScreens = Frontend::GetScreenTransforms(screenMatrix[0], screenKind); } -QSize ScreenHandler::screenGetMinSize(int factor = 1) +QSize ScreenPanel::screenGetMinSize(int factor = 1) { bool isHori = (Config::ScreenRotation == Frontend::screenRot_90Deg || Config::ScreenRotation == Frontend::screenRot_270Deg); @@ -158,7 +167,19 @@ QSize ScreenHandler::screenGetMinSize(int factor = 1) } } -void ScreenHandler::screenOnMousePress(QMouseEvent* event) +void ScreenPanel::onScreenLayoutChanged() +{ + setMinimumSize(screenGetMinSize()); + setupScreenLayout(); +} + +void ScreenPanel::resizeEvent(QResizeEvent* event) +{ + setupScreenLayout(); + QWidget::resizeEvent(event); +} + +void ScreenPanel::mousePressEvent(QMouseEvent* event) { event->accept(); if (event->button() != Qt::LeftButton) return; @@ -174,7 +195,7 @@ void ScreenHandler::screenOnMousePress(QMouseEvent* event) } } -void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) +void ScreenPanel::mouseReleaseEvent(QMouseEvent* event) { event->accept(); if (event->button() != Qt::LeftButton) return; @@ -187,7 +208,7 @@ void ScreenHandler::screenOnMouseRelease(QMouseEvent* event) } } -void ScreenHandler::screenOnMouseMove(QMouseEvent* event) +void ScreenPanel::mouseMoveEvent(QMouseEvent* event) { event->accept(); @@ -206,7 +227,7 @@ void ScreenHandler::screenOnMouseMove(QMouseEvent* event) } } -void ScreenHandler::screenHandleTablet(QTabletEvent* event) +void ScreenPanel::tabletEvent(QTabletEvent* event) { event->accept(); @@ -239,7 +260,7 @@ void ScreenHandler::screenHandleTablet(QTabletEvent* event) } } -void ScreenHandler::screenHandleTouch(QTouchEvent* event) +void ScreenPanel::touchEvent(QTouchEvent* event) { event->accept(); @@ -274,13 +295,26 @@ void ScreenHandler::screenHandleTouch(QTouchEvent* event) } } -void ScreenHandler::showCursor() +bool ScreenPanel::event(QEvent* event) { - mainWindow->panelWidget->setCursor(Qt::ArrowCursor); + if (event->type() == QEvent::TouchBegin + || event->type() == QEvent::TouchEnd + || event->type() == QEvent::TouchUpdate) + { + touchEvent((QTouchEvent*)event); + return true; + } + + return QWidget::event(event); +} + +void ScreenPanel::showCursor() +{ + mainWindow->panel->setCursor(Qt::ArrowCursor); mouseTimer->start(); } -QTimer* ScreenHandler::setupMouseTimer() +QTimer* ScreenPanel::setupMouseTimer() { mouseTimer = new QTimer(); mouseTimer->setSingleShot(true); @@ -290,35 +324,290 @@ QTimer* ScreenHandler::setupMouseTimer() return mouseTimer; } -ScreenPanelNative::ScreenPanelNative(QWidget* parent) : QWidget(parent), ScreenHandler(this) +int ScreenPanel::osdFindBreakPoint(const char* text, int i) +{ + // i = character that went out of bounds + + for (int j = i; j >= 0; j--) + { + if (text[j] == ' ') + return j; + } + + return i; +} + +void ScreenPanel::osdLayoutText(const char* text, int* width, int* height, int* breaks) +{ + int w = 0; + int h = 14; + int totalw = 0; + int maxw = ((QWidget*)this)->width() - (kOSDMargin*2); + int lastbreak = -1; + int numbrk = 0; + u16* ptr; + + memset(breaks, 0, sizeof(int)*64); + + for (int i = 0; text[i] != '\0'; ) + { + int glyphsize; + if (text[i] == ' ') + { + glyphsize = 6; + } + else + { + u32 ch = text[i]; + if (ch < 0x10 || ch > 0x7E) ch = 0x7F; + + ptr = &::font[(ch-0x10) << 4]; + glyphsize = ptr[0]; + if (!glyphsize) glyphsize = 6; + else glyphsize += 2; // space around the character + } + + w += glyphsize; + if (w > maxw) + { + // wrap shit as needed + if (text[i] == ' ') + { + if (numbrk >= 64) break; + breaks[numbrk++] = i; + i++; + } + else + { + int brk = osdFindBreakPoint(text, i); + if (brk != lastbreak) i = brk; + + if (numbrk >= 64) break; + breaks[numbrk++] = i; + + lastbreak = brk; + } + + w = 0; + h += 14; + } + else + i++; + + if (w > totalw) totalw = w; + } + + *width = totalw; + *height = h; +} + +unsigned int ScreenPanel::osdRainbowColor(int inc) +{ + // inspired from Acmlmboard + + if (inc < 100) return 0xFFFF9B9B + (inc << 8); + else if (inc < 200) return 0xFFFFFF9B - ((inc-100) << 16); + else if (inc < 300) return 0xFF9BFF9B + (inc-200); + else if (inc < 400) return 0xFF9BFFFF - ((inc-300) << 8); + else if (inc < 500) return 0xFF9B9BFF + ((inc-400) << 16); + else return 0xFFFF9BFF - (inc-500); +} + +void ScreenPanel::osdRenderItem(OSDItem* item) +{ + int w, h; + int breaks[64]; + + char* text = item->text; + u32 color = item->color; + + bool rainbow = (color == 0); + u32 ticks = (u32)QDateTime::currentMSecsSinceEpoch(); + u32 rainbowinc = ((text[0] * 17) + (ticks * 13)) % 600; + + color |= 0xFF000000; + const u32 shadow = 0xE0000000; + + osdLayoutText(text, &w, &h, breaks); + + item->bitmap = QImage(w, h, QImage::Format_ARGB32_Premultiplied); + u32* bitmap = (u32*)item->bitmap.bits(); + memset(bitmap, 0, w*h*sizeof(u32)); + + int x = 0, y = 1; + u32 maxw = ((QWidget*)this)->width() - (kOSDMargin*2); + int curline = 0; + u16* ptr; + + for (int i = 0; text[i] != '\0'; ) + { + int glyphsize; + if (text[i] == ' ') + { + x += 6; + } + else + { + u32 ch = text[i]; + if (ch < 0x10 || ch > 0x7E) ch = 0x7F; + + ptr = &::font[(ch-0x10) << 4]; + int glyphsize = ptr[0]; + if (!glyphsize) x += 6; + else + { + x++; + + if (rainbow) + { + color = osdRainbowColor(rainbowinc); + rainbowinc = (rainbowinc + 30) % 600; + } + + // draw character + for (int cy = 0; cy < 12; cy++) + { + u16 val = ptr[4+cy]; + + for (int cx = 0; cx < glyphsize; cx++) + { + if (val & (1<= breaks[curline]) + { + i = breaks[curline++]; + if (text[i] == ' ') i++; + + x = 0; + y += 14; + } + } + + // shadow + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + u32 val; + + val = bitmap[(y * w) + x]; + if ((val >> 24) == 0xFF) continue; + + if (x > 0) val = bitmap[(y * w) + x-1]; + if (x < w-1) val |= bitmap[(y * w) + x+1]; + if (y > 0) + { + if (x > 0) val |= bitmap[((y-1) * w) + x-1]; + val |= bitmap[((y-1) * w) + x]; + if (x < w-1) val |= bitmap[((y-1) * w) + x+1]; + } + if (y < h-1) + { + if (x > 0) val |= bitmap[((y+1) * w) + x-1]; + val |= bitmap[((y+1) * w) + x]; + if (x < w-1) val |= bitmap[((y+1) * w) + x+1]; + } + + if ((val >> 24) == 0xFF) + bitmap[(y * w) + x] = shadow; + } + } +} + +void ScreenPanel::osdDeleteItem(OSDItem* item) +{ +} + +void ScreenPanel::osdSetEnabled(bool enabled) +{ + osdMutex.lock(); + osdEnabled = enabled; + osdMutex.unlock(); +} + +void ScreenPanel::osdAddMessage(unsigned int color, const char* text) +{ + if (!osdEnabled) return; + + osdMutex.lock(); + + OSDItem item; + + item.id = osdID++; + item.timestamp = QDateTime::currentMSecsSinceEpoch(); + strncpy(item.text, text, 255); item.text[255] = '\0'; + item.color = color; + item.rendered = false; + + osdItems.push_back(item); + + osdMutex.unlock(); +} + +void ScreenPanel::osdUpdate() +{ + osdMutex.lock(); + + qint64 tick_now = QDateTime::currentMSecsSinceEpoch(); + qint64 tick_min = tick_now - 2500; + + for (auto it = osdItems.begin(); it != osdItems.end(); ) + { + OSDItem& item = *it; + + if ((!osdEnabled) || (item.timestamp < tick_min)) + { + osdDeleteItem(&item); + it = osdItems.erase(it); + continue; + } + + if (!item.rendered) + { + osdRenderItem(&item); + item.rendered = true; + } + + it++; + } + + osdMutex.unlock(); +} + + + +ScreenPanelNative::ScreenPanelNative(QWidget* parent) : ScreenPanel(parent) { screen[0] = QImage(256, 192, QImage::Format_RGB32); screen[1] = QImage(256, 192, QImage::Format_RGB32); screenTrans[0].reset(); screenTrans[1].reset(); - - OSD::Init(false); } ScreenPanelNative::~ScreenPanelNative() { - OSD::DeInit(); } void ScreenPanelNative::setupScreenLayout() { - int w = width(); - int h = height(); - - screenSetupLayout(w, h); + ScreenPanel::setupScreenLayout(); for (int i = 0; i < numScreens; i++) { float* mtx = screenMatrix[i]; screenTrans[i].setMatrix(mtx[0], mtx[1], 0.f, - mtx[2], mtx[3], 0.f, - mtx[4], mtx[5], 1.f); + mtx[2], mtx[3], 0.f, + mtx[4], mtx[5], 1.f); } } @@ -353,55 +642,32 @@ void ScreenPanelNative::paintEvent(QPaintEvent* event) } } - OSD::Update(); - OSD::DrawNative(painter); -} - -void ScreenPanelNative::resizeEvent(QResizeEvent* event) -{ - setupScreenLayout(); -} - -void ScreenPanelNative::mousePressEvent(QMouseEvent* event) -{ - screenOnMousePress(event); -} - -void ScreenPanelNative::mouseReleaseEvent(QMouseEvent* event) -{ - screenOnMouseRelease(event); -} - -void ScreenPanelNative::mouseMoveEvent(QMouseEvent* event) -{ - screenOnMouseMove(event); -} - -void ScreenPanelNative::tabletEvent(QTabletEvent* event) -{ - screenHandleTablet(event); -} - -bool ScreenPanelNative::event(QEvent* event) -{ - if (event->type() == QEvent::TouchBegin - || event->type() == QEvent::TouchEnd - || event->type() == QEvent::TouchUpdate) + osdUpdate(); + if (osdEnabled) { - screenHandleTouch((QTouchEvent*)event); - return true; + osdMutex.lock(); + + u32 y = kOSDMargin; + + painter.resetTransform(); + + for (auto it = osdItems.begin(); it != osdItems.end(); ) + { + OSDItem& item = *it; + + painter.drawImage(kOSDMargin, y, item.bitmap); + + y += item.bitmap.height(); + it++; + } + + osdMutex.unlock(); } - return QWidget::event(event); -} - -void ScreenPanelNative::onScreenLayoutChanged() -{ - setMinimumSize(screenGetMinSize()); - setupScreenLayout(); } -ScreenPanelGL::ScreenPanelGL(QWidget* parent) : QWidget(parent), ScreenHandler(this) + +ScreenPanelGL::ScreenPanelGL(QWidget* parent) : ScreenPanel(parent) { setAutoFillBackground(false); setAttribute(Qt::WA_NativeWindow, true); @@ -503,7 +769,41 @@ void ScreenPanelGL::initOpenGL() memset(zeroData, 0, sizeof(zeroData)); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 192, 256, 2, GL_RGBA, GL_UNSIGNED_BYTE, zeroData); - OSD::Init(true); + + OpenGL::BuildShaderProgram(kScreenVS_OSD, kScreenFS_OSD, osdShader, "OSDShader"); + + pid = osdShader[2]; + glBindAttribLocation(pid, 0, "vPosition"); + glBindFragDataLocation(pid, 0, "oColor"); + + OpenGL::LinkShaderProgram(osdShader); + glUseProgram(pid); + glUniform1i(glGetUniformLocation(pid, "OSDTex"), 0); + + osdScreenSizeULoc = glGetUniformLocation(pid, "uScreenSize"); + osdPosULoc = glGetUniformLocation(pid, "uOSDPos"); + osdSizeULoc = glGetUniformLocation(pid, "uOSDSize"); + osdScaleFactorULoc = glGetUniformLocation(pid, "uScaleFactor"); + + const float osdvertices[6*2] = + { + 0, 0, + 1, 1, + 1, 0, + 0, 0, + 0, 1, + 1, 1 + }; + + glGenBuffers(1, &osdVertexBuffer); + glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(osdvertices), osdvertices, GL_STATIC_DRAW); + + glGenVertexArrays(1, &osdVertexArray); + glBindVertexArray(osdVertexArray); + glEnableVertexAttribArray(0); // position + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)(0)); + glContext->SetSwapInterval(Config::ScreenVSync ? Config::ScreenVSyncInterval : 0); transferLayout(); @@ -520,13 +820,52 @@ void ScreenPanelGL::deinitOpenGL() OpenGL::DeleteShaderProgram(screenShaderProgram); - OSD::DeInit(); + + for (const auto& [key, tex] : osdTextures) + { + glDeleteTextures(1, &tex); + } + osdTextures.clear(); + + glDeleteVertexArrays(1, &osdVertexArray); + glDeleteBuffers(1, &osdVertexBuffer); + + OpenGL::DeleteShaderProgram(osdShader); + glContext->DoneCurrent(); lastScreenWidth = lastScreenHeight = -1; } +void ScreenPanelGL::osdRenderItem(OSDItem* item) +{ + ScreenPanel::osdRenderItem(item); + + GLuint tex; + glGenTextures(1, &tex); + glBindTexture(GL_TEXTURE_2D, tex); + 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); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, item->bitmap.width(), item->bitmap.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, item->bitmap.bits()); + + osdTextures[item->id] = tex; +} + +void ScreenPanelGL::osdDeleteItem(OSDItem* item) +{ + if (osdTextures.count(item->id)) + { + GLuint tex = osdTextures[item->id]; + glDeleteTextures(1, &tex); + osdTextures.erase(item->id); + } + + ScreenPanel::osdDeleteItem(item); +} + void ScreenPanelGL::drawScreenGL() { if (!glContext) return; @@ -590,8 +929,47 @@ void ScreenPanelGL::drawScreenGL() screenSettingsLock.unlock(); - OSD::Update(); - OSD::DrawGL(w, h, factor); + osdUpdate(); + if (osdEnabled) + { + osdMutex.lock(); + + u32 y = kOSDMargin; + + glUseProgram(osdShader[2]); + + glUniform2f(osdScreenSizeULoc, w, h); + glUniform1f(osdScaleFactorULoc, factor); + + glBindBuffer(GL_ARRAY_BUFFER, osdVertexBuffer); + glBindVertexArray(osdVertexArray); + + glActiveTexture(GL_TEXTURE0); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + for (auto it = osdItems.begin(); it != osdItems.end(); ) + { + OSDItem& item = *it; + + if (!osdTextures.count(item.id)) + continue; + + glBindTexture(GL_TEXTURE_2D, osdTextures[item.id]); + glUniform2i(osdPosULoc, kOSDMargin, y); + glUniform2i(osdSizeULoc, item.bitmap.width(), item.bitmap.height()); + glDrawArrays(GL_TRIANGLES, 0, 2*3); + + y += item.bitmap.height(); + it++; + } + + glDisable(GL_BLEND); + glUseProgram(0); + + osdMutex.unlock(); + } glContext->SwapBuffers(); } @@ -667,52 +1045,10 @@ QPaintEngine* ScreenPanelGL::paintEngine() const void ScreenPanelGL::setupScreenLayout() { - int w = width(); - int h = height(); - - screenSetupLayout(w, h); + ScreenPanel::setupScreenLayout(); transferLayout(); } -void ScreenPanelGL::resizeEvent(QResizeEvent* event) -{ - setupScreenLayout(); - - QWidget::resizeEvent(event); -} - -void ScreenPanelGL::mousePressEvent(QMouseEvent* event) -{ - screenOnMousePress(event); -} - -void ScreenPanelGL::mouseReleaseEvent(QMouseEvent* event) -{ - screenOnMouseRelease(event); -} - -void ScreenPanelGL::mouseMoveEvent(QMouseEvent* event) -{ - screenOnMouseMove(event); -} - -void ScreenPanelGL::tabletEvent(QTabletEvent* event) -{ - screenHandleTablet(event); -} - -bool ScreenPanelGL::event(QEvent* event) -{ - if (event->type() == QEvent::TouchBegin - || event->type() == QEvent::TouchEnd - || event->type() == QEvent::TouchUpdate) - { - screenHandleTouch((QTouchEvent*)event); - return true; - } - return QWidget::event(event); -} - void ScreenPanelGL::transferLayout() { std::optional windowInfo = getWindowInfo(); @@ -734,9 +1070,3 @@ void ScreenPanelGL::transferLayout() screenSettingsLock.unlock(); } } - -void ScreenPanelGL::onScreenLayoutChanged() -{ - setMinimumSize(screenGetMinSize()); - setupScreenLayout(); -} diff --git a/src/frontend/qt_sdl/Screen.h b/src/frontend/qt_sdl/Screen.h index 4c031e64..c2f7fda1 100644 --- a/src/frontend/qt_sdl/Screen.h +++ b/src/frontend/qt_sdl/Screen.h @@ -20,21 +20,20 @@ #define SCREEN_H #include +#include +#include + +#include +#include +#include +#include +#include +#include #include "glad/glad.h" #include "FrontendUtil.h" #include "duckstation/gl/context.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - class EmuThread; @@ -50,27 +49,54 @@ const struct { int id; float ratio; const char* label; } aspectRatios[] = constexpr int AspectRatiosNum = sizeof(aspectRatios) / sizeof(aspectRatios[0]); -class ScreenHandler +class ScreenPanel : public QWidget { - Q_GADGET + Q_OBJECT public: - ScreenHandler(QWidget* widget); - virtual ~ScreenHandler(); + explicit ScreenPanel(QWidget* parent); + virtual ~ScreenPanel(); + QTimer* setupMouseTimer(); void updateMouseTimer(); QTimer* mouseTimer; QSize screenGetMinSize(int factor); + void osdSetEnabled(bool enabled); + void osdAddMessage(unsigned int color, const char* msg); + +private slots: + void onScreenLayoutChanged(); + protected: - void screenSetupLayout(int w, int h); + struct OSDItem + { + unsigned int id; + qint64 timestamp; - void screenOnMousePress(QMouseEvent* event); - void screenOnMouseRelease(QMouseEvent* event); - void screenOnMouseMove(QMouseEvent* event); + char text[256]; + unsigned int color; - void screenHandleTablet(QTabletEvent* event); - void screenHandleTouch(QTouchEvent* event); + bool rendered; + QImage bitmap; + }; + + QMutex osdMutex; + bool osdEnabled; + unsigned int osdID; + std::deque osdItems; + + virtual void setupScreenLayout(); + + void resizeEvent(QResizeEvent* event) override; + + void mousePressEvent(QMouseEvent* event) override; + void mouseReleaseEvent(QMouseEvent* event) override; + void mouseMoveEvent(QMouseEvent* event) override; + + void tabletEvent(QTabletEvent* event) override; + void touchEvent(QTouchEvent* event); + bool event(QEvent* event) override; float screenMatrix[Frontend::MaxScreenTransforms][6]; int screenKind[Frontend::MaxScreenTransforms]; @@ -79,10 +105,19 @@ protected: bool touching = false; void showCursor(); + + int osdFindBreakPoint(const char* text, int i); + void osdLayoutText(const char* text, int* width, int* height, int* breaks); + unsigned int osdRainbowColor(int inc); + + virtual void osdRenderItem(OSDItem* item); + virtual void osdDeleteItem(OSDItem* item); + + void osdUpdate(); }; -class ScreenPanelNative : public QWidget, public ScreenHandler +class ScreenPanelNative : public ScreenPanel { Q_OBJECT @@ -93,26 +128,15 @@ public: protected: void paintEvent(QPaintEvent* event) override; - void resizeEvent(QResizeEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - - void tabletEvent(QTabletEvent* event) override; - bool event(QEvent* event) override; -private slots: - void onScreenLayoutChanged(); - private: - void setupScreenLayout(); + void setupScreenLayout() override; QImage screen[2]; QTransform screenTrans[Frontend::MaxScreenTransforms]; }; -class ScreenPanelGL : public QWidget, public ScreenHandler +class ScreenPanelGL : public ScreenPanel { Q_OBJECT @@ -141,20 +165,8 @@ protected: QPaintEngine* paintEngine() const override; - void resizeEvent(QResizeEvent* event) override; - - void mousePressEvent(QMouseEvent* event) override; - void mouseReleaseEvent(QMouseEvent* event) override; - void mouseMoveEvent(QMouseEvent* event) override; - - void tabletEvent(QTabletEvent* event) override; - bool event(QEvent* event) override; - -private slots: - void onScreenLayoutChanged(); - private: - void setupScreenLayout(); + void setupScreenLayout() override; std::unique_ptr glContext; @@ -168,6 +180,16 @@ private: bool filter; int lastScreenWidth = -1, lastScreenHeight = -1; + + GLuint osdShader[3]; + GLint osdScreenSizeULoc, osdPosULoc, osdSizeULoc; + GLfloat osdScaleFactorULoc; + GLuint osdVertexArray; + GLuint osdVertexBuffer; + std::map osdTextures; + + void osdRenderItem(OSDItem* item) override; + void osdDeleteItem(OSDItem* item) override; }; #endif // SCREEN_H diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 7b67ec52..962fb76c 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -81,8 +81,6 @@ #include "ArchiveUtil.h" #include "CameraManager.h" -#include "OSD.h" - using namespace melonDS; // TEMP @@ -689,13 +687,13 @@ void MainWindow::osdAddMessage(unsigned int color, const char* fmt, ...) if (fmt == nullptr) return; - char msg[1024]; + char msg[256]; va_list args; va_start(args, fmt); - vsnprintf(msg, 1024, fmt, args); + vsnprintf(msg, 256, fmt, args); va_end(args); - OSD::AddMessage(color, msg); + panel->osdAddMessage(color, msg); } void MainWindow::closeEvent(QCloseEvent* event) @@ -720,7 +718,6 @@ void MainWindow::createScreenPanel() panelGL->show(); panel = panelGL; - panelWidget = panelGL; panelGL->createContext(); } @@ -729,14 +726,14 @@ void MainWindow::createScreenPanel() { ScreenPanelNative* panelNative = new ScreenPanelNative(this); panel = panelNative; - panelWidget = panelNative; - panelWidget->show(); + panel->show(); } - setCentralWidget(panelWidget); + setCentralWidget(panel); actScreenFiltering->setEnabled(hasOGL); + panel->osdSetEnabled(Config::ShowOSD); - connect(this, SIGNAL(screenLayoutChange()), panelWidget, SLOT(onScreenLayoutChanged())); + connect(this, SIGNAL(screenLayoutChange()), panel, SLOT(onScreenLayoutChanged())); emit screenLayoutChange(); } @@ -1866,7 +1863,7 @@ void MainWindow::onChangeSavestateSRAMReloc(bool checked) void MainWindow::onChangeScreenSize() { int factor = ((QAction*)sender())->data().toInt(); - QSize diff = size() - panelWidget->size(); + QSize diff = size() - panel->size(); resize(panel->screenGetMinSize(factor) + diff); } @@ -1959,7 +1956,9 @@ void MainWindow::onChangeScreenFiltering(bool checked) void MainWindow::onChangeShowOSD(bool checked) { Config::ShowOSD = checked?1:0; + panel->osdSetEnabled(Config::ShowOSD); } + void MainWindow::onChangeLimitFramerate(bool checked) { Config::LimitFPS = checked?1:0; @@ -2065,7 +2064,7 @@ void MainWindow::onUpdateVideoSettings(bool glchange) delete panel; createScreenPanel(); - connect(emuThread, SIGNAL(windowUpdate()), panelWidget, SLOT(repaint())); + connect(emuThread, SIGNAL(windowUpdate()), panel, SLOT(repaint())); } videoSettingsDirty = true; diff --git a/src/frontend/qt_sdl/Window.h b/src/frontend/qt_sdl/Window.h index f0f66c26..bc207480 100644 --- a/src/frontend/qt_sdl/Window.h +++ b/src/frontend/qt_sdl/Window.h @@ -92,8 +92,7 @@ private: bool oldMax; public: - ScreenHandler* panel; - QWidget* panelWidget; + ScreenPanel* panel; };*/ class MainWindow : public QMainWindow @@ -230,8 +229,7 @@ private: bool oldMax; public: - ScreenHandler* panel; - QWidget* panelWidget; + ScreenPanel* panel; QAction* actOpenROM; QAction* actBootFirmware; diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 2f08c374..38c0ab16 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -79,7 +79,6 @@ #include "version.h" #include "FrontendUtil.h" -#include "OSD.h" #include "Args.h" #include "NDS.h" From a4b2b0c40df15713a5efa114310bf78fd369d0f4 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Thu, 28 Dec 2023 08:54:31 -0500 Subject: [PATCH 109/157] Resolve or silence some warnings (#1905) * Resolve some warnings - Their frequent appearance in the build logs is driving me nuts * Silence warnings about `offsetof` * Don't apply `-Wno-invalid-offset` to C, only to C++ --- src/ARMJIT_A64/ARMJIT_Compiler.h | 2 +- src/CMakeLists.txt | 9 ++++++ src/GPU3D_OpenGL.cpp | 2 +- src/GPU3D_Soft.h | 4 +-- src/NDS.cpp | 50 ++++++++++++++++---------------- src/SPU.cpp | 2 ++ 6 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/ARMJIT_A64/ARMJIT_Compiler.h b/src/ARMJIT_A64/ARMJIT_Compiler.h index 04f12e85..2b0048a9 100644 --- a/src/ARMJIT_A64/ARMJIT_Compiler.h +++ b/src/ARMJIT_A64/ARMJIT_Compiler.h @@ -69,7 +69,7 @@ struct Op2 bool IsSimpleReg() { return !IsImm && !Reg.ShiftAmount && Reg.ShiftType == Arm64Gen::ST_LSL; } bool ImmFits12Bit() - { return IsImm && (Imm & 0xFFF == Imm); } + { return IsImm && ((Imm & 0xFFF) == Imm); } bool IsZero() { return IsImm && !Imm; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b1ae4c47..afabc03f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -128,6 +128,15 @@ add_subdirectory(teakra EXCLUDE_FROM_ALL) target_compile_options(teakra PRIVATE "$<$:-Og>") target_link_libraries(core PRIVATE teakra) +if (NOT MSVC) + # MSVC has its own compiler flag syntax; if we ever support it, + # be sure to silence any equivalent warnings there. + + target_compile_options(core PRIVATE "$<$:-Wno-invalid-offsetof>") + # These warnings are excessive, and are only triggered in the ARMJIT code + # (which is fundamentally non-portable, so this is fine) +endif() + find_library(m MATH_LIBRARY) if (MATH_LIBRARY) diff --git a/src/GPU3D_OpenGL.cpp b/src/GPU3D_OpenGL.cpp index 27711a89..3e9ce5b0 100644 --- a/src/GPU3D_OpenGL.cpp +++ b/src/GPU3D_OpenGL.cpp @@ -31,7 +31,7 @@ namespace melonDS bool GLRenderer::BuildRenderShader(u32 flags, const char* vs, const char* fs) { char shadername[32]; - sprintf(shadername, "RenderShader%02X", flags); + snprintf(shadername, sizeof(shadername), "RenderShader%02X", flags); int headerlen = strlen(kShaderHeader); diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index 8fb42013..de65944e 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -178,7 +178,7 @@ private: { // Z-buffering: linear interpolation // still doesn't quite match hardware... - s32 base, disp, factor; + s32 base = 0, disp = 0, factor = 0; if (z0 < z1) { @@ -337,7 +337,7 @@ private: constexpr s32 XVal() const { - s32 ret; + s32 ret = 0; if (Negative) ret = x0 - (dx >> 18); else ret = x0 + (dx >> 18); diff --git a/src/NDS.cpp b/src/NDS.cpp index 7c176fae..1d7e34a9 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1497,40 +1497,40 @@ void NDS::NocashPrint(u32 ncpu, u32 addr) if (cmd[0] == 'r') { - if (!strcmp(cmd, "r0")) sprintf(subs, "%08X", cpu->R[0]); - else if (!strcmp(cmd, "r1")) sprintf(subs, "%08X", cpu->R[1]); - else if (!strcmp(cmd, "r2")) sprintf(subs, "%08X", cpu->R[2]); - else if (!strcmp(cmd, "r3")) sprintf(subs, "%08X", cpu->R[3]); - else if (!strcmp(cmd, "r4")) sprintf(subs, "%08X", cpu->R[4]); - else if (!strcmp(cmd, "r5")) sprintf(subs, "%08X", cpu->R[5]); - else if (!strcmp(cmd, "r6")) sprintf(subs, "%08X", cpu->R[6]); - else if (!strcmp(cmd, "r7")) sprintf(subs, "%08X", cpu->R[7]); - else if (!strcmp(cmd, "r8")) sprintf(subs, "%08X", cpu->R[8]); - else if (!strcmp(cmd, "r9")) sprintf(subs, "%08X", cpu->R[9]); - else if (!strcmp(cmd, "r10")) sprintf(subs, "%08X", cpu->R[10]); - else if (!strcmp(cmd, "r11")) sprintf(subs, "%08X", cpu->R[11]); - else if (!strcmp(cmd, "r12")) sprintf(subs, "%08X", cpu->R[12]); - else if (!strcmp(cmd, "r13")) sprintf(subs, "%08X", cpu->R[13]); - else if (!strcmp(cmd, "r14")) sprintf(subs, "%08X", cpu->R[14]); - else if (!strcmp(cmd, "r15")) sprintf(subs, "%08X", cpu->R[15]); + if (!strcmp(cmd, "r0")) snprintf(subs, sizeof(subs), "%08X", cpu->R[0]); + else if (!strcmp(cmd, "r1")) snprintf(subs, sizeof(subs), "%08X", cpu->R[1]); + else if (!strcmp(cmd, "r2")) snprintf(subs, sizeof(subs), "%08X", cpu->R[2]); + else if (!strcmp(cmd, "r3")) snprintf(subs, sizeof(subs), "%08X", cpu->R[3]); + else if (!strcmp(cmd, "r4")) snprintf(subs, sizeof(subs), "%08X", cpu->R[4]); + else if (!strcmp(cmd, "r5")) snprintf(subs, sizeof(subs), "%08X", cpu->R[5]); + else if (!strcmp(cmd, "r6")) snprintf(subs, sizeof(subs), "%08X", cpu->R[6]); + else if (!strcmp(cmd, "r7")) snprintf(subs, sizeof(subs), "%08X", cpu->R[7]); + else if (!strcmp(cmd, "r8")) snprintf(subs, sizeof(subs), "%08X", cpu->R[8]); + else if (!strcmp(cmd, "r9")) snprintf(subs, sizeof(subs), "%08X", cpu->R[9]); + else if (!strcmp(cmd, "r10")) snprintf(subs, sizeof(subs), "%08X", cpu->R[10]); + else if (!strcmp(cmd, "r11")) snprintf(subs, sizeof(subs), "%08X", cpu->R[11]); + else if (!strcmp(cmd, "r12")) snprintf(subs, sizeof(subs), "%08X", cpu->R[12]); + else if (!strcmp(cmd, "r13")) snprintf(subs, sizeof(subs), "%08X", cpu->R[13]); + else if (!strcmp(cmd, "r14")) snprintf(subs, sizeof(subs), "%08X", cpu->R[14]); + else if (!strcmp(cmd, "r15")) snprintf(subs, sizeof(subs), "%08X", cpu->R[15]); } else { - if (!strcmp(cmd, "sp")) sprintf(subs, "%08X", cpu->R[13]); - else if (!strcmp(cmd, "lr")) sprintf(subs, "%08X", cpu->R[14]); - else if (!strcmp(cmd, "pc")) sprintf(subs, "%08X", cpu->R[15]); - else if (!strcmp(cmd, "frame")) sprintf(subs, "%u", NumFrames); - else if (!strcmp(cmd, "scanline")) sprintf(subs, "%u", GPU.VCount); - else if (!strcmp(cmd, "totalclks")) sprintf(subs, "%" PRIu64, GetSysClockCycles(0)); - else if (!strcmp(cmd, "lastclks")) sprintf(subs, "%" PRIu64, GetSysClockCycles(1)); + if (!strcmp(cmd, "sp")) snprintf(subs, sizeof(subs), "%08X", cpu->R[13]); + else if (!strcmp(cmd, "lr")) snprintf(subs, sizeof(subs), "%08X", cpu->R[14]); + else if (!strcmp(cmd, "pc")) snprintf(subs, sizeof(subs), "%08X", cpu->R[15]); + else if (!strcmp(cmd, "frame")) snprintf(subs, sizeof(subs), "%u", NumFrames); + else if (!strcmp(cmd, "scanline")) snprintf(subs, sizeof(subs), "%u", GPU.VCount); + else if (!strcmp(cmd, "totalclks")) snprintf(subs, sizeof(subs), "%" PRIu64, GetSysClockCycles(0)); + else if (!strcmp(cmd, "lastclks")) snprintf(subs, sizeof(subs), "%" PRIu64, GetSysClockCycles(1)); else if (!strcmp(cmd, "zeroclks")) { - sprintf(subs, "%s", ""); + snprintf(subs, sizeof(subs), "%s", ""); GetSysClockCycles(1); } } - int slen = strlen(subs); + int slen = strnlen(subs, sizeof(subs)); if ((ptr+slen) > 1023) slen = 1023-ptr; strncpy(&output[ptr], subs, slen); ptr += slen; diff --git a/src/SPU.cpp b/src/SPU.cpp index f0d59464..86307097 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -621,6 +621,8 @@ s32 SPUChannel::Run() (PrevSample[0] * InterpCubic[samplepos][2]) + (val * InterpCubic[samplepos][3])) >> 14; break; + default: + break; } } From da264539119c70afcbcab1e48fc4641e4201e6cd Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 28 Dec 2023 15:03:44 +0100 Subject: [PATCH 110/157] CI stuff (#1935) Add new macOS CI using vcpkg --- .github/workflows/build-appimage.yml | 2 +- .github/workflows/build-macos-universal.yml | 76 ------------------- .github/workflows/build-macos.yml | 84 +++++++++++++++++++++ .github/workflows/build-ubuntu-aarch64.yml | 3 +- .github/workflows/build-ubuntu.yml | 3 +- .github/workflows/build-windows.yml | 2 +- 6 files changed, 90 insertions(+), 80 deletions(-) delete mode 100644 .github/workflows/build-macos-universal.yml create mode 100644 .github/workflows/build-macos.yml diff --git a/.github/workflows/build-appimage.yml b/.github/workflows/build-appimage.yml index 7e7df583..be4494e8 100644 --- a/.github/workflows/build-appimage.yml +++ b/.github/workflows/build-appimage.yml @@ -1,4 +1,4 @@ -name: CMake Build (AppImage x86-64) +name: AppImage on: push: diff --git a/.github/workflows/build-macos-universal.yml b/.github/workflows/build-macos-universal.yml deleted file mode 100644 index 4416ce7a..00000000 --- a/.github/workflows/build-macos-universal.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: CMake Build (macOS Universal) - -on: - push: - branches: - - master - pull_request: - branches: - - master - -jobs: - prepare: - runs-on: [self-hosted, macOS, ARM64] - - steps: - - name: Clean workspace - run: rm -rf ${{runner.workspace}}/build - - - uses: actions/checkout@v3 - - - build-arm64: - needs: prepare - runs-on: [self-hosted, macOS, ARM64] - env: - homebrew_prefix: /opt/homebrew - - steps: - - name: Create build directory - run: mkdir -p ${{runner.workspace}}/build/arm64 - - - name: Configure - working-directory: ${{runner.workspace}}/build/arm64 - run: arch -arm64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON - - - name: Make - working-directory: ${{runner.workspace}}/build/arm64 - run: arch -arm64 make -j$(sysctl -n hw.logicalcpu) - - build-x86_64: - needs: prepare - runs-on: [self-hosted, macOS, ARM64] - env: - homebrew_prefix: /usr/local - - steps: - - name: Create build directory - run: mkdir -p ${{runner.workspace}}/build/x86_64 - - - name: Configure - working-directory: ${{runner.workspace}}/build/x86_64 - run: arch -x86_64 ${{env.homebrew_prefix}}/bin/cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PREFIX_PATH="${{env.homebrew_prefix}}/opt/qt@6;${{env.homebrew_prefix}}/opt/libarchive" -DPKG_CONFIG_EXECUTABLE=${{env.homebrew_prefix}}/bin/pkg-config -DMACOS_BUNDLE_LIBS=ON -DUSE_QT6=ON - - - name: Make - working-directory: ${{runner.workspace}}/build/x86_64 - run: arch -x86_64 make -j$(sysctl -n hw.logicalcpu) - - universal-binary: - needs: [build-arm64, build-x86_64] - runs-on: [self-hosted, macOS, ARM64] - - steps: - - name: Merge binaries - run: $GITHUB_WORKSPACE/tools/mac-universal.py ${{runner.workspace}}/build/arm64/melonDS.app ${{runner.workspace}}/build/x86_64/melonDS.app ${{runner.workspace}}/build/universal/melonDS.app - - - name: Codesign app - run: codesign -s - --deep -f ${{runner.workspace}}/build/universal/melonDS.app - - - name: Create DMG - run: hdiutil create -fs HFS+ -volname melonDS -srcfolder ${{runner.workspace}}/build/universal/melonDS.app -ov -format UDBZ ${{runner.workspace}}/build/universal/melonDS.dmg - - - uses: actions/upload-artifact@v3 - with: - name: macOS-universal - path: ${{runner.workspace}}/build/universal/melonDS.dmg - diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml new file mode 100644 index 00000000..6d5693a1 --- /dev/null +++ b/.github/workflows/build-macos.yml @@ -0,0 +1,84 @@ +name: macOS + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build-macos: + strategy: + matrix: + arch: [x86_64, arm64] + + name: ${{ matrix.arch }} + runs-on: macos-13 + steps: + - name: Check out sources + uses: actions/checkout@v3 + - name: Install dependencies for package building + run: | + brew install autoconf automake autoconf-archive libtool && pip3 install setuptools + - name: Set up CMake + uses: lukka/get-cmake@latest + - name: Set up vcpkg + uses: lukka/run-vcpkg@v11 + with: + vcpkgGitCommitId: c8696863d371ab7f46e213d8f5ca923c4aef2a00 + - name: Build + uses: lukka/run-cmake@v10 + with: + configurePreset: release-mac-${{ matrix.arch }} + buildPreset: release-mac-${{ matrix.arch }} + - name: Compress app bundle + shell: bash + run: | + cd build/release-mac-${{ matrix.arch }} + zip -r -y ../../macOS-${{ matrix.arch }}.zip melonDS.app + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: macOS-${{ matrix.arch }} + path: macOS-${{ matrix.arch }}.zip + + universal-binary: + name: Universal binary + needs: [build-macos] + runs-on: macos-13 + steps: + - name: Download x86_64 + uses: actions/download-artifact@v4 + with: + name: macOS-x86_64 + path: x86_64 + - name: Download arm64 + uses: actions/download-artifact@v4 + with: + name: macOS-arm64 + path: arm64 + - name: Combine app bundles + shell: bash + run: | + unzip x86_64/*.zip -d x86_64 + unzip arm64/*.zip -d arm64 + lipo {x86_64,arm64}/melonDS.app/Contents/MacOS/melonDS -create -output melonDS + cp -a arm64/melonDS.app melonDS.app + cp melonDS melonDS.app/Contents/MacOS/melonDS + codesign -s - --deep melonDS.app + zip -r -y macOS-universal.zip melonDS.app + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: macOS-universal + path: macOS-universal.zip + - name: Clean up architecture-specific artifacts + uses: geekyeggo/delete-artifact@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + failOnError: false + name: | + macOS-x86_64 + macOS-arm64 diff --git a/.github/workflows/build-ubuntu-aarch64.yml b/.github/workflows/build-ubuntu-aarch64.yml index 096bc0b9..43f4d8b6 100644 --- a/.github/workflows/build-ubuntu-aarch64.yml +++ b/.github/workflows/build-ubuntu-aarch64.yml @@ -1,4 +1,4 @@ -name: CMake Build (Ubuntu aarch64) +name: Ubuntu on: push: @@ -13,6 +13,7 @@ env: jobs: build: + name: aarch64 runs-on: ubuntu-20.04 container: ubuntu:20.04 diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index b6c50e9a..438fddd0 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -1,4 +1,4 @@ -name: CMake Build (Ubuntu x86-64) +name: Ubuntu on: push: @@ -10,6 +10,7 @@ on: jobs: build: + name: x86_64 runs-on: ubuntu-20.04 diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 70b11c05..e6846da7 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -1,4 +1,4 @@ -name: CMake Build (Windows x86-64) +name: Windows on: push: From 1cd8c16bbb76254b4b000430244405f6182170e1 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 28 Dec 2023 15:54:29 +0100 Subject: [PATCH 111/157] Disable default-features for host qtbase to speed up cross build times --- vcpkg.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vcpkg.json b/vcpkg.json index 2ff0c549..a1bd0be5 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -9,6 +9,11 @@ "default-features": false, "features": ["gui", "png", "thread", "widgets", "opengl", "zstd"] }, + { + "name": "qtbase", + "host": true, + "default-features": false + }, { "name": "qtmultimedia", "default-features": false From 63141c086a63e4af47f4b17eaad600d44e8933fb Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 28 Dec 2023 19:51:46 +0100 Subject: [PATCH 112/157] guard the default vcpkg directory against multiple CMake instances using it --- cmake/ConfigureVcpkg.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index 95cbcdb9..be8f0590 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -4,6 +4,7 @@ set(_DEFAULT_VCPKG_ROOT "${CMAKE_SOURCE_DIR}/vcpkg") set(VCPKG_ROOT "${_DEFAULT_VCPKG_ROOT}" CACHE STRING "The path to the vcpkg repository") if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") + file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE) FetchContent_Declare(vcpkg GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" GIT_TAG 2023.12.12 From 8bfc6df8de216eff0be4be1dbe37b19a741bed51 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 31 Dec 2023 13:52:58 +0100 Subject: [PATCH 113/157] TitleManagerDialog: Fix wrong color format --- src/frontend/qt_sdl/TitleManagerDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/TitleManagerDialog.cpp b/src/frontend/qt_sdl/TitleManagerDialog.cpp index eda1bbce..21ca844e 100644 --- a/src/frontend/qt_sdl/TitleManagerDialog.cpp +++ b/src/frontend/qt_sdl/TitleManagerDialog.cpp @@ -114,7 +114,7 @@ void TitleManagerDialog::createTitleItem(u32 category, u32 titleid) u32 icondata[32*32]; ROMManager::ROMIcon(banner.Icon, banner.Palette, icondata); - QImage iconimg((const uchar*)icondata, 32, 32, QImage::Format_ARGB32); + QImage iconimg((const uchar*)icondata, 32, 32, QImage::Format_RGBA8888); QIcon icon(QPixmap::fromImage(iconimg.copy())); // TODO: make it possible to select other languages? From d1cbc41115e65e9441ce6cb98d94af2923f42d79 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Wed, 3 Jan 2024 07:32:17 -0500 Subject: [PATCH 114/157] Slight fixups with `FATStorage` (#1934) * Reload the SD card for `CartSD` and all subclasses * Make `ROMManager::LoadDLDISDCard` delegate to `GetDLDISDCardArgs` * Add a method overload for `CartSD::SetSDCard` * Initialize new SD card images with the correct size * Sync the old card to the host (if applicable) when move-assigning a new one * Only sync the old card to the host if it's not read-only * Remove static state in `FATStorage` - Replace `FF_ReadStorage` and `FF_WriteStorage` with lambda functions - Keep open and use the single `File` handle throughout the `FATStorage`'s life --- src/FATStorage.cpp | 65 +++++++++--------------------- src/FATStorage.h | 9 +++-- src/NDSCart.h | 16 ++++++++ src/frontend/qt_sdl/EmuThread.cpp | 7 ++-- src/frontend/qt_sdl/ROMManager.cpp | 14 +++---- 5 files changed, 51 insertions(+), 60 deletions(-) diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index 9a1a9ad4..0f1bf235 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -48,8 +48,6 @@ FATStorage::FATStorage(FATStorageArgs&& args) noexcept : SourceDir(std::move(args.SourceDir)) { Load(FilePath, FileSize, SourceDir); - - File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); } FATStorage::FATStorage(FATStorage&& other) noexcept @@ -71,7 +69,10 @@ FATStorage& FATStorage::operator=(FATStorage&& other) noexcept if (this != &other) { if (File) + { // Sync this file's contents to the host (if applicable) before closing it + if (!ReadOnly) Save(); CloseFile(File); + } FilePath = std::move(other.FilePath); IndexPath = std::move(other.IndexPath); @@ -83,6 +84,7 @@ FATStorage& FATStorage::operator=(FATStorage&& other) noexcept FileIndex = std::move(other.FileIndex); other.File = nullptr; + other.SourceDir = std::nullopt; } return *this; @@ -99,11 +101,8 @@ FATStorage::~FATStorage() bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) { if (!File) return false; - if (FF_File) return false; - FF_File = File; - FF_FileSize = FileSize; - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); FRESULT res; FATFS fs; @@ -112,7 +111,6 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) if (res != FR_OK) { ff_disk_close(); - FF_File = nullptr; return false; } @@ -124,7 +122,6 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) { f_unmount("0:"); ff_disk_close(); - FF_File = nullptr; return false; } @@ -134,18 +131,14 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) f_unmount("0:"); ff_disk_close(); - FF_File = nullptr; return nwrite==len; } u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data) { if (!File) return false; - if (FF_File) return false; - FF_File = File; - FF_FileSize = FileSize; - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); FRESULT res; FATFS fs; @@ -154,7 +147,6 @@ u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data) if (res != FR_OK) { ff_disk_close(); - FF_File = nullptr; return false; } @@ -166,7 +158,6 @@ u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data) { f_unmount("0:"); ff_disk_close(); - FF_File = nullptr; return false; } @@ -177,7 +168,6 @@ u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data) f_unmount("0:"); ff_disk_close(); - FF_File = nullptr; return nread; } @@ -197,18 +187,18 @@ u64 FATStorage::GetSectorCount() const return FileSize / 0x200; } - -FileHandle* FATStorage::FF_File; -u64 FATStorage::FF_FileSize; - -UINT FATStorage::FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num) +ff_disk_read_cb FATStorage::FF_ReadStorage() const noexcept { - return ReadSectorsInternal(FF_File, FF_FileSize, sector, num, buf); + return [this](BYTE* buf, LBA_t sector, UINT num) { + return ReadSectorsInternal(File, FileSize, sector, num, buf); + }; } -UINT FATStorage::FF_WriteStorage(const BYTE* buf, LBA_t sector, UINT num) +ff_disk_write_cb FATStorage::FF_WriteStorage() const noexcept { - return WriteSectorsInternal(FF_File, FF_FileSize, sector, num, buf); + return [this](const BYTE* buf, LBA_t sector, UINT num) { + return WriteSectorsInternal(File, FileSize, sector, num, buf); + }; } @@ -1030,8 +1020,8 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::optional // with a minimum 128MB extra, otherwise size is defaulted to 512MB bool isnew = !Platform::LocalFileExists(filename); - FF_File = Platform::OpenLocalFile(filename, static_cast(FileMode::ReadWrite | FileMode::Preserve)); - if (!FF_File) + File = Platform::OpenLocalFile(filename, static_cast(FileMode::ReadWrite | FileMode::Preserve)); + if (!File) return false; IndexPath = FilePath + ".idx"; @@ -1047,7 +1037,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::optional if (FileSize == 0) { - FileSize = FileLength(FF_File); + FileSize = FileLength(File); } } @@ -1061,8 +1051,7 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::optional } else { - FF_FileSize = FileSize; - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); res = f_mount(&fs, "0:", 1); if (res != FR_OK) @@ -1098,9 +1087,8 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::optional FileSize = 0x20000000ULL; // 512MB } - FF_FileSize = FileSize; ff_disk_close(); - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FF_FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); DirIndex.clear(); FileIndex.clear(); @@ -1137,8 +1125,6 @@ bool FATStorage::Load(const std::string& filename, u64 size, const std::optional f_unmount("0:"); ff_disk_close(); - CloseFile(FF_File); - FF_File = nullptr; return true; } @@ -1150,14 +1136,7 @@ bool FATStorage::Save() return true; // Not an error. } - FF_File = Platform::OpenLocalFile(FilePath, FileMode::ReadWriteExisting); - if (!FF_File) - { - return false; - } - - FF_FileSize = FileSize; - ff_disk_open(FF_ReadStorage, FF_WriteStorage, (LBA_t)(FileSize>>9)); + ff_disk_open(FF_ReadStorage(), FF_WriteStorage(), (LBA_t)(FileSize>>9)); FRESULT res; FATFS fs; @@ -1166,8 +1145,6 @@ bool FATStorage::Save() if (res != FR_OK) { ff_disk_close(); - CloseFile(FF_File); - FF_File = nullptr; return false; } @@ -1178,8 +1155,6 @@ bool FATStorage::Save() f_unmount("0:"); ff_disk_close(); - CloseFile(FF_File); - FF_File = nullptr; return true; } diff --git a/src/FATStorage.h b/src/FATStorage.h index 48a411b0..00628461 100644 --- a/src/FATStorage.h +++ b/src/FATStorage.h @@ -28,6 +28,7 @@ #include "Platform.h" #include "types.h" #include "fatfs/ff.h" +#include "FATIO.h" namespace melonDS { @@ -39,6 +40,8 @@ namespace melonDS struct FATStorageArgs { std::string Filename; + + /// Size of the desired SD card in bytes, or 0 for auto-detect. u64 Size; bool ReadOnly; std::optional SourceDir; @@ -74,10 +77,8 @@ private: Platform::FileHandle* File; u64 FileSize; - static Platform::FileHandle* FF_File; - static u64 FF_FileSize; - static UINT FF_ReadStorage(BYTE* buf, LBA_t sector, UINT num); - static UINT FF_WriteStorage(const BYTE* buf, LBA_t sector, UINT num); + [[nodiscard]] ff_disk_read_cb FF_ReadStorage() const noexcept; + [[nodiscard]] ff_disk_write_cb FF_WriteStorage() const noexcept; static u32 ReadSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, u8* data); static u32 WriteSectorsInternal(Platform::FileHandle* file, u64 filelen, u32 start, u32 num, const u8* data); diff --git a/src/NDSCart.h b/src/NDSCart.h index 78439a2c..2f6a3be5 100644 --- a/src/NDSCart.h +++ b/src/NDSCart.h @@ -260,6 +260,22 @@ public: // it just leaves behind an optional with a moved-from value } + void SetSDCard(std::optional&& args) noexcept + { + // Close the open SD card (if any) so that its contents are flushed to disk. + // Also, if args refers to the same image file that SD is currently using, + // this will ensure that we don't have two open read-write handles + // to the same file. + SD = std::nullopt; + + if (args) + SD = FATStorage(std::move(*args)); + + args = std::nullopt; + // moving from an optional doesn't set it to nullopt, + // it just leaves behind an optional with a moved-from value + } + protected: void ApplyDLDIPatchAt(u8* binary, u32 dldioffset, const u8* patch, u32 patchlen, bool readonly) const; void ApplyDLDIPatch(const u8* patch, u32 patchlen, bool readonly); diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 4a75387e..01431a1c 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -196,12 +196,11 @@ bool EmuThread::UpdateConsole(UpdateConsoleNDSArgs&& ndsargs, UpdateConsoleGBAAr ndsargs = {}; } - if (nextndscart && nextndscart->Type() == NDSCart::Homebrew) + if (auto* cartsd = dynamic_cast(nextndscart.get())) { - // Load DLDISDCard will return nullopt if the SD card is disabled; + // LoadDLDISDCard will return nullopt if the SD card is disabled; // SetSDCard will accept nullopt, which means no SD card - auto& homebrew = static_cast(*nextndscart); - homebrew.SetSDCard(ROMManager::LoadDLDISDCard()); + cartsd->SetSDCard(ROMManager::GetDLDISDCardArgs()); } std::unique_ptr nextgbacart; diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index a20af206..15a9ebf5 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -759,7 +759,12 @@ std::optional LoadNAND(const std::array& a return nandImage; } -constexpr u64 imgsizes[] = {0, 256, 512, 1024, 2048, 4096}; +constexpr u64 MB(u64 i) +{ + return i * 1024 * 1024; +} + +constexpr u64 imgsizes[] = {0, MB(256), MB(512), MB(1024), MB(2048), MB(4096)}; std::optional GetDSiSDCardArgs() noexcept { if (!Config::DSiSDEnable) @@ -804,12 +809,7 @@ std::optional LoadDLDISDCard() noexcept if (!Config::DLDIEnable) return std::nullopt; - return FATStorage( - Config::DLDISDPath, - imgsizes[Config::DLDISize], - Config::DLDIReadOnly, - Config::DLDIFolderSync ? std::make_optional(Config::DLDIFolderPath) : std::nullopt - ); + return FATStorage(*GetDLDISDCardArgs()); } void EnableCheats(NDS& nds, bool enable) From f68f55d002c412ead4e0ce88dd48b11d6f7900e3 Mon Sep 17 00:00:00 2001 From: Eric Warmenhoven Date: Wed, 3 Jan 2024 08:42:08 -0500 Subject: [PATCH 115/157] Reset the JIT when loading savestate (#1937) The effect of this change is simply to call JitEnableWrite(), which is necessary on apple silicon --- src/NDS.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index 1d7e34a9..f67cb84d 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -713,15 +713,11 @@ bool NDS::DoSavestate(Savestate* file) SPU.SetPowerCnt(PowerControl7 & 0x0001); Wifi.SetPowerCnt(PowerControl7 & 0x0002); - } #ifdef JIT_ENABLED - if (!file->Saving) - { - JIT.ResetBlockCache(); - JIT.Memory.Reset(); - } + JIT.Reset(); #endif + } file->Finish(); From 8143f549566029f366b774d0722c5f16011b9b56 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Sun, 7 Jan 2024 17:39:43 -0500 Subject: [PATCH 116/157] Protect savestates while the threaded software renderer is running (#1864) * First crack at ensuring the render thread doesn't touch GPU state while it's being serialized * Get rid of the semaphore wait * Add some extra fields into GPU3D's serialization * Oops, TempVertexBuffer is already serialized * Move vertex serialization into its own method * Lock the GPU3D state when rendering on the render thread or serializing it * Revert "Lock the GPU3D state when rendering on the render thread or serializing it" This reverts commit 2f49a551c13934b9dc815bbda67a45098f0482a7. * Add comments that describe the synchronization within GPU3D_Soft - I need to understand it before I can solve my actual problem - Now I do * Revert "Revert "Lock the GPU3D state when rendering on the render thread or serializing it"" This reverts commit 1977566a6d8671d72bd94ba4ebf832c3bf08933a. * Let's try locking the GPU3D state throughout NDS::RunFrame - Just to see what happens * Slim down the lock's scope * Narrow the lock's scope some more * Remove the lock entirely * Try protecting the GPU3D state with just a mutex - I'll clean this up once I know it works * Remove a duplicate method definition * Add a missing `noexcept` specifier * Remove an unused function * Cut some non-hardware state from `GPU3D`'s savestate * Assume that the next frame after loading a savestate won't be identical * Actually, it _is_ worth it * Don't serialize the clip matrix - It's recalculated anyway * Serialize `RenderPolygonRAM` as an array of indexes * Clean up some comments - I liked the dialogue style, but oh well * Try restarting the render thread instead of using the lock - Let's see what happens * Put the lock back * Fix some polygon and vertex indexes being saved incorrectly - Taking the difference between two pointers results in the number of elements, not the number of bytes * Remove `SoftRenderer::StateBusy` since it turns out we don't need it - The real synchronization was the friends we made along the way --- src/GPU3D.cpp | 107 +++++++++++++++++++++++++++------------------ src/GPU3D.h | 6 ++- src/GPU3D_Soft.cpp | 57 ++++++++++++++++++++---- src/GPU3D_Soft.h | 9 ++++ src/Savestate.h | 2 +- 5 files changed, 128 insertions(+), 53 deletions(-) diff --git a/src/GPU3D.cpp b/src/GPU3D.cpp index 47877021..47abae2f 100644 --- a/src/GPU3D.cpp +++ b/src/GPU3D.cpp @@ -146,6 +146,19 @@ GPU3D::GPU3D(melonDS::NDS& nds, std::unique_ptr&& renderer) noexcept { } +void Vertex::DoSavestate(Savestate* file) noexcept +{ + file->VarArray(Position, sizeof(Position)); + file->VarArray(Color, sizeof(Color)); + file->VarArray(TexCoords, sizeof(TexCoords)); + + file->Bool32(&Clipped); + + file->VarArray(FinalPosition, sizeof(FinalPosition)); + file->VarArray(FinalColor, sizeof(FinalColor)); + file->VarArray(HiresPosition, sizeof(HiresPosition)); +} + void GPU3D::SetCurrentRenderer(std::unique_ptr&& renderer) noexcept { CurrentRenderer = std::move(renderer); @@ -297,6 +310,12 @@ void GPU3D::DoSavestate(Savestate* file) noexcept { file->Section("GP3D"); + SoftRenderer* softRenderer = dynamic_cast(CurrentRenderer.get()); + if (softRenderer && softRenderer->IsThreaded()) + { + softRenderer->SetupRenderThread(NDS.GPU); + } + CmdFIFO.DoSavestate(file); CmdPIPE.DoSavestate(file); @@ -372,33 +391,21 @@ void GPU3D::DoSavestate(Savestate* file) noexcept file->Var32(&VertexNumInPoly); file->Var32(&NumConsecutivePolygons); - for (int i = 0; i < 4; i++) + for (Vertex& vtx : TempVertexBuffer) { - Vertex* vtx = &TempVertexBuffer[i]; - - file->VarArray(vtx->Position, sizeof(s32)*4); - file->VarArray(vtx->Color, sizeof(s32)*3); - file->VarArray(vtx->TexCoords, sizeof(s16)*2); - - file->Bool32(&vtx->Clipped); - - file->VarArray(vtx->FinalPosition, sizeof(s32)*2); - file->VarArray(vtx->FinalColor, sizeof(s32)*3); + vtx.DoSavestate(file); } if (file->Saving) { - u32 id; - if (LastStripPolygon) id = (u32)((LastStripPolygon - (&PolygonRAM[0])) / sizeof(Polygon)); - else id = -1; - file->Var32(&id); + u32 index = LastStripPolygon ? (u32)(LastStripPolygon - &PolygonRAM[0]) : UINT32_MAX; + file->Var32(&index); } else { - u32 id; - file->Var32(&id); - if (id == 0xFFFFFFFF) LastStripPolygon = NULL; - else LastStripPolygon = &PolygonRAM[id]; + u32 index = UINT32_MAX; + file->Var32(&index); + LastStripPolygon = (index == UINT32_MAX) ? nullptr : &PolygonRAM[index]; } file->Var32(&CurRAMBank); @@ -409,18 +416,9 @@ void GPU3D::DoSavestate(Savestate* file) noexcept file->Var32(&FlushRequest); file->Var32(&FlushAttributes); - for (int i = 0; i < 6144*2; i++) + for (Vertex& vtx : VertexRAM) { - Vertex* vtx = &VertexRAM[i]; - - file->VarArray(vtx->Position, sizeof(s32)*4); - file->VarArray(vtx->Color, sizeof(s32)*3); - file->VarArray(vtx->TexCoords, sizeof(s16)*2); - - file->Bool32(&vtx->Clipped); - - file->VarArray(vtx->FinalPosition, sizeof(s32)*2); - file->VarArray(vtx->FinalColor, sizeof(s32)*3); + vtx.DoSavestate(file); } for(int i = 0; i < 2048*2; i++) @@ -434,20 +432,17 @@ void GPU3D::DoSavestate(Savestate* file) noexcept for (int j = 0; j < 10; j++) { Vertex* ptr = poly->Vertices[j]; - u32 id; - if (ptr) id = (u32)((ptr - (&VertexRAM[0])) / sizeof(Vertex)); - else id = -1; - file->Var32(&id); + u32 index = ptr ? (u32)(ptr - &VertexRAM[0]) : UINT32_MAX; + file->Var32(&index); } } else { for (int j = 0; j < 10; j++) { - u32 id = -1; - file->Var32(&id); - if (id == 0xFFFFFFFF) poly->Vertices[j] = NULL; - else poly->Vertices[j] = &VertexRAM[id]; + u32 index = UINT32_MAX; + file->Var32(&index); + poly->Vertices[j] = index == UINT32_MAX ? nullptr : &VertexRAM[index]; } } @@ -495,7 +490,6 @@ void GPU3D::DoSavestate(Savestate* file) noexcept } } - // probably not worth storing the vblank-latched Renderxxxxxx variables CmdStallQueue.DoSavestate(file); file->Var32((u32*)&VertexPipeline); @@ -511,10 +505,27 @@ void GPU3D::DoSavestate(Savestate* file) noexcept CurVertexRAM = &VertexRAM[CurRAMBank ? 6144 : 0]; CurPolygonRAM = &PolygonRAM[CurRAMBank ? 2048 : 0]; + } - // better safe than sorry, I guess - // might cause a blank frame but atleast it won't shit itself - RenderNumPolygons = 0; + file->Var32(&RenderNumPolygons); + if (file->Saving) + { + for (const Polygon* p : RenderPolygonRAM) + { + u32 index = p ? (p - &PolygonRAM[0]) : UINT32_MAX; + + file->Var32(&index); + } + } + else + { + for (int i = 0; i < RenderPolygonRAM.size(); ++i) + { + u32 index = UINT32_MAX; + file->Var32(&index); + + RenderPolygonRAM[i] = index == UINT32_MAX ? nullptr : &PolygonRAM[index]; + } } file->VarArray(CurVertex, sizeof(s16)*3); @@ -534,6 +545,18 @@ void GPU3D::DoSavestate(Savestate* file) noexcept file->VarArray(ShininessTable, 128*sizeof(u8)); file->Bool32(&AbortFrame); + file->Bool32(&GeometryEnabled); + file->Bool32(&RenderingEnabled); + file->Var32(&PolygonMode); + file->Var32(&PolygonAttr); + file->Var32(&CurPolygonAttr); + file->Var32(&TexParam); + file->Var32(&TexPalette); + RenderFrameIdentical = false; + if (softRenderer && softRenderer->IsThreaded()) + { + softRenderer->EnableRenderThread(); + } } diff --git a/src/GPU3D.h b/src/GPU3D.h index 7c42eeb5..4a5fe6e0 100644 --- a/src/GPU3D.h +++ b/src/GPU3D.h @@ -47,6 +47,7 @@ struct Vertex // TODO maybe: hi-res color? (that survives clipping) s32 HiresPosition[2]; + void DoSavestate(Savestate* file) noexcept; }; struct Polygon @@ -78,6 +79,7 @@ struct Polygon u32 SortKey; + void DoSavestate(Savestate* file) noexcept; }; class Renderer3D; @@ -269,7 +271,7 @@ public: u32 RenderClearAttr1 = 0; u32 RenderClearAttr2 = 0; - bool RenderFrameIdentical = false; + bool RenderFrameIdentical = false; // not part of the hardware state, don't serialize bool AbortFrame = false; @@ -323,7 +325,7 @@ public: u32 FlushRequest = 0; u32 FlushAttributes = 0; - u32 ScrolledLine[256]; + u32 ScrolledLine[256]; // not part of the hardware state, don't serialize }; class Renderer3D diff --git a/src/GPU3D_Soft.cpp b/src/GPU3D_Soft.cpp index c96464a6..74027d5b 100644 --- a/src/GPU3D_Soft.cpp +++ b/src/GPU3D_Soft.cpp @@ -34,8 +34,11 @@ void SoftRenderer::StopRenderThread() { if (RenderThreadRunning.load(std::memory_order_relaxed)) { + // Tell the render thread to stop drawing new frames, and finish up the current one. RenderThreadRunning = false; + Platform::Semaphore_Post(Sema_RenderStart); + Platform::Thread_Wait(RenderThread); Platform::Thread_Free(RenderThread); RenderThread = nullptr; @@ -47,24 +50,36 @@ void SoftRenderer::SetupRenderThread(GPU& gpu) if (Threaded) { if (!RenderThreadRunning.load(std::memory_order_relaxed)) - { - RenderThreadRunning = true; + { // If the render thread isn't already running... + RenderThreadRunning = true; // "Time for work, render thread!" RenderThread = Platform::Thread_Create([this, &gpu]() { RenderThreadFunc(gpu); }); } - // otherwise more than one frame can be queued up at once + // "Be on standby, but don't start rendering until I tell you to!" Platform::Semaphore_Reset(Sema_RenderStart); + // "Oh, sorry, were you already in the middle of a frame from the last iteration?" if (RenderThreadRendering) + // "Tell me when you're done, I'll wait here." Platform::Semaphore_Wait(Sema_RenderDone); - Platform::Semaphore_Reset(Sema_RenderDone); - Platform::Semaphore_Reset(Sema_RenderStart); - Platform::Semaphore_Reset(Sema_ScanlineCount); + // "All good? Okay, let me give you your training." + // "(Maybe you're still the same thread, but I have to tell you this stuff anyway.)" - Platform::Semaphore_Post(Sema_RenderStart); + // "This is the signal you'll send when you're done with a frame." + // "I'll listen for it when I need to show something to the frontend." + Platform::Semaphore_Reset(Sema_RenderDone); + + // "This is the signal I'll send when I want you to start rendering." + // "Don't do anything until you get the message." + Platform::Semaphore_Reset(Sema_RenderStart); + + // "This is the signal you'll send every time you finish drawing a line." + // "I might need some of your scanlines before you finish the whole buffer," + // "so let me know as soon as you're done with each one." + Platform::Semaphore_Reset(Sema_ScanlineCount); } else { @@ -72,6 +87,13 @@ void SoftRenderer::SetupRenderThread(GPU& gpu) } } +void SoftRenderer::EnableRenderThread() +{ + if (Threaded && Sema_RenderStart) + { + Platform::Semaphore_Post(Sema_RenderStart); + } +} SoftRenderer::SoftRenderer(bool threaded) noexcept : Renderer3D(false), Threaded(threaded) @@ -103,6 +125,7 @@ void SoftRenderer::Reset(GPU& gpu) PrevIsShadowMask = false; SetupRenderThread(gpu); + EnableRenderThread(); } void SoftRenderer::SetThreaded(bool threaded, GPU& gpu) noexcept @@ -111,6 +134,7 @@ void SoftRenderer::SetThreaded(bool threaded, GPU& gpu) noexcept { Threaded = threaded; SetupRenderThread(gpu); + EnableRenderThread(); } } @@ -1692,12 +1716,14 @@ void SoftRenderer::RenderPolygons(const GPU& gpu, bool threaded, Polygon** polyg ScanlineFinalPass(gpu.GPU3D, y-1); if (threaded) + // Notify the main thread that we're done with a scanline. Platform::Semaphore_Post(Sema_ScanlineCount); } ScanlineFinalPass(gpu.GPU3D, 191); if (threaded) + // If this renderer is threaded, notify the main thread that we're done with the frame. Platform::Semaphore_Post(Sema_ScanlineCount); } @@ -1719,6 +1745,7 @@ void SoftRenderer::RenderFrame(GPU& gpu) if (RenderThreadRunning.load(std::memory_order_relaxed)) { + // "Render thread, you're up! Get moving." Platform::Semaphore_Post(Sema_RenderStart); } else if (!FrameIdentical) @@ -1731,18 +1758,26 @@ void SoftRenderer::RenderFrame(GPU& gpu) void SoftRenderer::RestartFrame(GPU& gpu) { SetupRenderThread(gpu); + EnableRenderThread(); } void SoftRenderer::RenderThreadFunc(GPU& gpu) { for (;;) { + // Wait for a notice from the main thread to start rendering (or to stop entirely). Platform::Semaphore_Wait(Sema_RenderStart); if (!RenderThreadRunning) return; + // Protect the GPU state from the main thread. + // Some melonDS frontends (though not ours) + // will repeatedly save or load states; + // if they do so while the render thread is busy here, + // the ensuing race conditions may cause a crash + // (since some of the GPU state includes pointers). RenderThreadRendering = true; if (FrameIdentical) - { + { // If no rendering is needed, just say we're done. Platform::Semaphore_Post(Sema_ScanlineCount, 192); } else @@ -1751,7 +1786,10 @@ void SoftRenderer::RenderThreadFunc(GPU& gpu) RenderPolygons(gpu, true, &gpu.GPU3D.RenderPolygonRAM[0], gpu.GPU3D.RenderNumPolygons); } + // Tell the main thread that we're done rendering + // and that it's safe to access the GPU state again. Platform::Semaphore_Post(Sema_RenderDone); + RenderThreadRendering = false; } } @@ -1761,6 +1799,9 @@ u32* SoftRenderer::GetLine(int line) if (RenderThreadRunning.load(std::memory_order_relaxed)) { if (line < 192) + // We need a scanline, so let's wait for the render thread to finish it. + // (both threads process scanlines from top-to-bottom, + // so we don't need to wait for a specific row) Platform::Semaphore_Wait(Sema_ScanlineCount); } diff --git a/src/GPU3D_Soft.h b/src/GPU3D_Soft.h index de65944e..9cfdf9ad 100644 --- a/src/GPU3D_Soft.h +++ b/src/GPU3D_Soft.h @@ -42,8 +42,10 @@ public: u32* GetLine(int line) override; void SetupRenderThread(GPU& gpu); + void EnableRenderThread(); void StopRenderThread(); private: + friend void GPU3D::DoSavestate(Savestate* file) noexcept; // Notes on the interpolator: // // This is a theory on how the DS hardware interpolates values. It matches hardware output @@ -506,8 +508,15 @@ private: Platform::Thread* RenderThread; std::atomic_bool RenderThreadRunning; std::atomic_bool RenderThreadRendering; + + // Used by the main thread to tell the render thread to start rendering a frame Platform::Semaphore* Sema_RenderStart; + + // Used by the render thread to tell the main thread that it's done rendering a frame Platform::Semaphore* Sema_RenderDone; + + // Used to allow the main thread to read some scanlines + // before (the 3D portion of) the entire frame is rasterized. Platform::Semaphore* Sema_ScanlineCount; }; } diff --git a/src/Savestate.h b/src/Savestate.h index 236aa643..2e1400a0 100644 --- a/src/Savestate.h +++ b/src/Savestate.h @@ -24,7 +24,7 @@ #include #include "types.h" -#define SAVESTATE_MAJOR 11 +#define SAVESTATE_MAJOR 12 #define SAVESTATE_MINOR 1 namespace melonDS From 740305cc25d7bebd40076d6588097cb4de2f4ef8 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Mon, 8 Jan 2024 09:20:48 -0500 Subject: [PATCH 117/157] Don't reset the VRAM cache if saving a state (#1944) - This fixes a flickering bug in melonDS DS --- src/GPU.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GPU.cpp b/src/GPU.cpp index c226ccbe..f23e641e 100644 --- a/src/GPU.cpp +++ b/src/GPU.cpp @@ -275,7 +275,8 @@ void GPU::DoSavestate(Savestate* file) noexcept GPU2D_B.DoSavestate(file); GPU3D.DoSavestate(file); - ResetVRAMCache(); + if (!file->Saving) + ResetVRAMCache(); } void GPU::AssignFramebuffers() noexcept From f4c8202b1a4a094680e9e84329bef88c2fc65885 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 17 Jan 2024 18:50:08 +0100 Subject: [PATCH 118/157] add missing 8/16-bit reads to ROMCnt and ROM SPICnt --- src/NDS.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/NDS.cpp b/src/NDS.cpp index f67cb84d..de21eb63 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -2728,11 +2728,37 @@ u8 NDS::ARM9IORead8(u32 addr) case 0x04000132: return KeyCnt[0] & 0xFF; case 0x04000133: return KeyCnt[0] >> 8; + case 0x040001A0: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetSPICnt() & 0xFF; + return 0; + case 0x040001A1: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetSPICnt() >> 8; + return 0; + case 0x040001A2: if (!(ExMemCnt[0] & (1<<11))) return NDSCartSlot.ReadSPIData(); return 0; + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetROMCnt() & 0xFF; + return 0; + case 0x040001A5: + if (!(ExMemCnt[0] & (1<<11))) + return (NDSCartSlot.GetROMCnt() >> 8) & 0xFF; + return 0; + case 0x040001A6: + if (!(ExMemCnt[0] & (1<<11))) + return (NDSCartSlot.GetROMCnt() >> 16) & 0xFF; + return 0; + case 0x040001A7: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetROMCnt() >> 24; + return 0; + case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) return NDSCartSlot.GetROMCommand(0); @@ -2884,6 +2910,15 @@ u16 NDS::ARM9IORead16(u32 addr) return NDSCartSlot.ReadSPIData(); return 0; + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetROMCnt() & 0xFFFF; + return 0; + case 0x040001A6: + if (!(ExMemCnt[0] & (1<<11))) + return NDSCartSlot.GetROMCnt() >> 16; + return 0; + case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) return NDSCartSlot.GetROMCommand(0) | @@ -3592,11 +3627,37 @@ u8 NDS::ARM7IORead8(u32 addr) case 0x04000138: return RTC.Read() & 0xFF; + case 0x040001A0: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetSPICnt() & 0xFF; + return 0; + case 0x040001A1: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetSPICnt() >> 8; + return 0; + case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.ReadSPIData(); return 0; + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetROMCnt() & 0xFF; + return 0; + case 0x040001A5: + if (ExMemCnt[0] & (1<<11)) + return (NDSCartSlot.GetROMCnt() >> 8) & 0xFF; + return 0; + case 0x040001A6: + if (ExMemCnt[0] & (1<<11)) + return (NDSCartSlot.GetROMCnt() >> 16) & 0xFF; + return 0; + case 0x040001A7: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetROMCnt() >> 24; + return 0; + case 0x040001A8: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.GetROMCommand(0); @@ -3697,6 +3758,15 @@ u16 NDS::ARM7IORead16(u32 addr) case 0x040001A0: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.GetSPICnt(); return 0; case 0x040001A2: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.ReadSPIData(); return 0; + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetROMCnt() & 0xFFFF; + return 0; + case 0x040001A6: + if (ExMemCnt[0] & (1<<11)) + return NDSCartSlot.GetROMCnt() >> 16; + return 0; + case 0x040001A8: if (ExMemCnt[0] & (1<<11)) return NDSCartSlot.GetROMCommand(0) | From 7897bd387bfd37615a049eba28d02dc23cfa5194 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Wed, 17 Jan 2024 18:54:48 +0100 Subject: [PATCH 119/157] also add writes while we're at it, we know Gericom's gonna abuse them someday :P --- src/NDS.cpp | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/NDS.cpp b/src/NDS.cpp index de21eb63..1f9597ce 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -3182,6 +3182,23 @@ void NDS::ARM9IOWrite8(u32 addr, u8 val) NDSCartSlot.WriteSPIData(val); return; + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFFFF00) | val); + return; + case 0x040001A5: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFF00FF) | (val << 8)); + return; + case 0x040001A6: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFF00FFFF) | (val << 16)); + return; + case 0x040001A7: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0x00FFFFFF) | (val << 24)); + return; + case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(0, val); return; case 0x040001A9: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(1, val); return; case 0x040001AA: if (!(ExMemCnt[0] & (1<<11))) NDSCartSlot.SetROMCommand(2, val); return; @@ -3311,6 +3328,15 @@ void NDS::ARM9IOWrite16(u32 addr, u16 val) NDSCartSlot.WriteSPIData(val & 0xFF); return; + case 0x040001A4: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFF0000) | val); + return; + case 0x040001A6: + if (!(ExMemCnt[0] & (1<<11))) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0x0000FFFF) | (val << 16)); + return; + case 0x040001A8: if (!(ExMemCnt[0] & (1<<11))) { @@ -3954,6 +3980,23 @@ void NDS::ARM7IOWrite8(u32 addr, u8 val) NDSCartSlot.WriteSPIData(val); return; + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFFFF00) | val); + return; + case 0x040001A5: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFF00FF) | (val << 8)); + return; + case 0x040001A6: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFF00FFFF) | (val << 16)); + return; + case 0x040001A7: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0x00FFFFFF) | (val << 24)); + return; + case 0x040001A8: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(0, val); return; case 0x040001A9: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(1, val); return; case 0x040001AA: if (ExMemCnt[0] & (1<<11)) NDSCartSlot.SetROMCommand(2, val); return; @@ -4059,6 +4102,15 @@ void NDS::ARM7IOWrite16(u32 addr, u16 val) NDSCartSlot.WriteSPIData(val & 0xFF); return; + case 0x040001A4: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFFFFFF00) | val); + return; + case 0x040001A6: + if (ExMemCnt[0] & (1<<11)) + NDSCartSlot.WriteROMCnt((NDSCartSlot.GetROMCnt() & 0xFF00FFFF) | (val << 16)); + return; + case 0x040001A8: if (ExMemCnt[0] & (1<<11)) { From 77274735d62b2fe34ee2ad0fc2eb0c37197f3f19 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 24 Jan 2024 09:52:22 +0100 Subject: [PATCH 120/157] the readme for delete-artifact doesn't have this anymore so maybe it'll stop failing if I remove it --- .github/workflows/build-macos.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 6d5693a1..5f4b2815 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -77,7 +77,6 @@ jobs: - name: Clean up architecture-specific artifacts uses: geekyeggo/delete-artifact@v4 with: - token: ${{ secrets.GITHUB_TOKEN }} failOnError: false name: | macOS-x86_64 From 4b576d066e4b0513eacaba5c62018d1a850bbce1 Mon Sep 17 00:00:00 2001 From: Steveice10 <1269164+Steveice10@users.noreply.github.com> Date: Wed, 24 Jan 2024 01:27:25 -0800 Subject: [PATCH 121/157] Add support for using a portable directory without special build flags. (#1956) --- src/frontend/qt_sdl/CMakeLists.txt | 12 ++--- src/frontend/qt_sdl/Platform.cpp | 78 +++++++++++++++--------------- src/frontend/qt_sdl/main.cpp | 4 +- 3 files changed, 45 insertions(+), 49 deletions(-) diff --git a/src/frontend/qt_sdl/CMakeLists.txt b/src/frontend/qt_sdl/CMakeLists.txt index 8eeb44a4..1dec174b 100644 --- a/src/frontend/qt_sdl/CMakeLists.txt +++ b/src/frontend/qt_sdl/CMakeLists.txt @@ -166,11 +166,13 @@ target_link_libraries(melonDS PRIVATE ${QT_LINK_LIBS} ${CMAKE_DL_LIBS}) target_include_directories(melonDS PRIVATE "${Slirp_INCLUDE_DIRS}") target_link_libraries(melonDS PRIVATE "${Slirp_LINK_LIBRARIES}") -if (UNIX) - option(PORTABLE "Make a portable build that looks for its configuration in the current directory" OFF) -elseif (WIN32) +if (WIN32) option(PORTABLE "Make a portable build that looks for its configuration in the current directory" ON) + if (PORTABLE) + target_compile_definitions(melonDS PRIVATE WIN32_PORTABLE) + endif() + configure_file("${CMAKE_SOURCE_DIR}/res/melon.rc.in" "${CMAKE_BINARY_DIR}/res/melon.rc") target_sources(melonDS PUBLIC "${CMAKE_BINARY_DIR}/res/melon.rc") target_include_directories(melonDS PRIVATE "${CMAKE_BINARY_DIR}/res") @@ -189,10 +191,6 @@ elseif (WIN32) set_target_properties(melonDS PROPERTIES LINK_FLAGS_DEBUG "-mconsole") endif() -if (PORTABLE) - target_compile_definitions(melonDS PRIVATE PORTABLE) -endif() - if (APPLE) target_sources(melonDS PRIVATE sem_timedwait.cpp) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index efd33400..222e512a 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -60,6 +61,42 @@ void emuStop(); namespace melonDS::Platform { +void PathInit(int argc, char** argv) +{ + // First, check for the portable directory next to the executable. + QString appdirpath = QCoreApplication::applicationDirPath(); + QString portablepath = appdirpath + QDir::separator() + "portable"; + +#if defined(__APPLE__) + // On Apple platforms we may need to navigate outside an app bundle. + // The executable directory would be "melonDS.app/Contents/MacOS", so we need to go a total of three steps up. + QDir bundledir(appdirpath); + if (bundledir.cd("..") && bundledir.cd("..") && bundledir.dirName().endsWith(".app") && bundledir.cd("..")) + { + portablepath = bundledir.absolutePath() + QDir::separator() + "portable"; + } +#endif + + QDir portabledir(portablepath); + if (portabledir.exists()) + { + EmuDirectory = portabledir.absolutePath().toStdString(); + } + else + { + // If no overrides are specified, use the default path. +#if defined(__WIN32__) && defined(WIN32_PORTABLE) + EmuDirectory = appdirpath.toStdString(); +#else + QString confdir; + QDir config(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)); + config.mkdir("melonDS"); + confdir = config.absolutePath() + QDir::separator() + "melonDS"; + EmuDirectory = confdir.toStdString(); +#endif + } +} + QSharedMemory* IPCBuffer = nullptr; int IPCInstanceID; @@ -133,38 +170,7 @@ void IPCDeInit() void Init(int argc, char** argv) { -#if defined(__WIN32__) || defined(PORTABLE) - if (argc > 0 && strlen(argv[0]) > 0) - { - int len = strlen(argv[0]); - while (len > 0) - { - if (argv[0][len] == '/') break; - if (argv[0][len] == '\\') break; - len--; - } - if (len > 0) - { - std::string emudir = argv[0]; - EmuDirectory = emudir.substr(0, len); - } - else - { - EmuDirectory = "."; - } - } - else - { - EmuDirectory = "."; - } -#else - QString confdir; - QDir config(QStandardPaths::writableLocation(QStandardPaths::ConfigLocation)); - config.mkdir("melonDS"); - confdir = config.absolutePath() + "/melonDS/"; - EmuDirectory = confdir.toStdString(); -#endif - + PathInit(argc, argv); IPCInit(); } @@ -284,15 +290,7 @@ FileHandle* OpenLocalFile(const std::string& path, FileMode mode) } else { -#ifdef PORTABLE fullpath = QString::fromStdString(EmuDirectory) + QDir::separator() + qpath; -#else - // Check user configuration directory - QDir config(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)); - config.mkdir("melonDS"); - fullpath = config.absolutePath() + "/melonDS/"; - fullpath.append(qpath); -#endif } return OpenFile(fullpath.toStdString(), mode); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 38c0ab16..3a5c1a11 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -299,10 +299,10 @@ int main(int argc, char** argv) if (argc != 0 && (!strcasecmp(argv[0], "derpDS") || !strcasecmp(argv[0], "./derpDS"))) printf("did you just call me a derp???\n"); - Platform::Init(argc, argv); - MelonApplication melon(argc, argv); + Platform::Init(argc, argv); + CLI::CommandLineOptions* options = CLI::ManageArgs(melon); // http://stackoverflow.com/questions/14543333/joystick-wont-work-using-sdl From 890035c688b481d22564b79444855df9e9a5dada Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 25 Jan 2024 10:21:19 +0100 Subject: [PATCH 122/157] readme: fix macOS build badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index de494305..268c2b3a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ - +

DS emulator, sorta From 7dd4152d6711db76fbe2a2acffcc208a524bb25d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 26 Jan 2024 13:06:32 +0100 Subject: [PATCH 123/157] Add MaxFPS setting --- src/frontend/qt_sdl/Config.cpp | 2 + src/frontend/qt_sdl/Config.h | 1 + src/frontend/qt_sdl/EmuThread.cpp | 2 +- .../qt_sdl/InterfaceSettingsDialog.cpp | 2 + .../qt_sdl/InterfaceSettingsDialog.ui | 120 +++++++++++++----- 5 files changed, 92 insertions(+), 35 deletions(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index b6fca7d6..02f43de7 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -61,6 +61,7 @@ int GL_ScaleFactor; bool GL_BetterPolygons; bool LimitFPS; +int MaxFPS; bool AudioSync; bool ShowOSD; @@ -251,6 +252,7 @@ ConfigEntry ConfigFile[] = {"GL_BetterPolygons", 1, &GL_BetterPolygons, false, false}, {"LimitFPS", 1, &LimitFPS, true, false}, + {"MaxFPS", 0, &MaxFPS, 1000, false}, {"AudioSync", 1, &AudioSync, false}, {"ShowOSD", 1, &ShowOSD, true, false}, diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 5e3db823..11644dc2 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -105,6 +105,7 @@ extern int GL_ScaleFactor; extern bool GL_BetterPolygons; extern bool LimitFPS; +extern int MaxFPS; extern bool AudioSync; extern bool ShowOSD; diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 01431a1c..0728a085 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -576,7 +576,7 @@ void EmuThread::run() { bool limitfps = Config::LimitFPS && !fastforward; - double practicalFramelimit = limitfps ? frametimeStep : 1.0 / 1000.0; + double practicalFramelimit = limitfps ? frametimeStep : 1.0 / Config::MaxFPS; double curtime = SDL_GetPerformanceCounter() * perfCountsSec; diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index 2f7417f6..75497bc3 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -34,6 +34,7 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare ui->spinMouseHideSeconds->setEnabled(Config::MouseHide != 0); ui->spinMouseHideSeconds->setValue(Config::MouseHideSeconds); ui->cbPauseLostFocus->setChecked(Config::PauseLostFocus != 0); + ui->spinMaxFPS->setValue(Config::MaxFPS); } InterfaceSettingsDialog::~InterfaceSettingsDialog() @@ -60,6 +61,7 @@ void InterfaceSettingsDialog::done(int r) Config::MouseHide = ui->cbMouseHide->isChecked() ? 1:0; Config::MouseHideSeconds = ui->spinMouseHideSeconds->value(); Config::PauseLostFocus = ui->cbPauseLostFocus->isChecked() ? 1:0; + Config::MaxFPS = ui->spinMaxFPS->value(); Config::Save(); diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui index 8ee9feda..01ba4a46 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui @@ -6,8 +6,8 @@ 0 0 - 262 - 113 + 337 + 233 @@ -19,32 +19,96 @@ Interface settings - melonDS - - - - - Hide after + + + + + Main window + + + + + Hide mouse after inactivity + + + + + + + 18 + + + + + After + + + spinMouseHideSeconds + + + + + + + + + + seconds + + + spinMouseHideSeconds + + + + + + + + + Pause emulation when window is not in focus + + + + - - - - Pause emulation when window is not in focus + + + + Framerate + + + + + Fast-forward limit + + + spinMaxFPS + + + + + + + FPS + + + 60 + + + 1000 + + + 1000 + + + + - - - - Hide mouse after inactivity - - - - - - - + Qt::Horizontal @@ -54,20 +118,8 @@ - - - - seconds of inactivity - - - - - cbMouseHide - spinMouseHideSeconds - cbPauseLostFocus - From d48e5f2da0439c7109c7ed5c003fa00d58dadbe9 Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Thu, 1 Feb 2024 08:36:35 -0500 Subject: [PATCH 124/157] Fix DSiWare detection (#1969) - According to GBATek, all DSiWare games have a high title ID of 0x00030004 - Some homebrew apps set the Unitcode bits to DSi mode to enable support of DSi features --- src/NDS_Header.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/NDS_Header.h b/src/NDS_Header.h index de75f7c7..77a5baca 100644 --- a/src/NDS_Header.h +++ b/src/NDS_Header.h @@ -39,6 +39,8 @@ enum RegionMask : u32 RegionFree = 0xFFFFFFFF, }; +constexpr u32 DSiWareTitleIDHigh = 0x00030004; + // Consult GBATEK for info on what these are struct NDSHeader { @@ -198,8 +200,9 @@ struct NDSHeader u8 HeaderSignature[128]; // RSA-SHA1 across 0x000..0xDFF - /// @return \c true if this header represents a DSi title - /// (either a physical cartridge or a DSiWare title). + /// @return \c true if this header represents a title + /// that is DSi-exclusive (including DSiWare) + /// or DSi-enhanced (including cartridges). [[nodiscard]] bool IsDSi() const { return (UnitCode & 0x02) != 0; } [[nodiscard]] u32 GameCodeAsU32() const { return (u32)GameCode[3] << 24 | @@ -213,7 +216,7 @@ struct NDSHeader } /// @return \c true if this header represents a DSiWare title. - [[nodiscard]] bool IsDSiWare() const { return IsDSi() && DSiRegionStart == 0; } + [[nodiscard]] bool IsDSiWare() const { return IsDSi() && DSiTitleIDHigh == DSiWareTitleIDHigh; } }; static_assert(sizeof(NDSHeader) == 4096, "NDSHeader is not 4096 bytes!"); From a7575ec7b3112eff56bbcde6113ab07d90a24265 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 7 Feb 2024 20:12:23 +0100 Subject: [PATCH 125/157] Allow the user to choose the UI theme Mainly useful for those who want dark mode on Windows. --- src/frontend/qt_sdl/Config.cpp | 2 ++ src/frontend/qt_sdl/Config.h | 1 + .../qt_sdl/InterfaceSettingsDialog.cpp | 23 ++++++++++++++++++- .../qt_sdl/InterfaceSettingsDialog.ui | 23 ++++++++++++++++--- src/frontend/qt_sdl/main.cpp | 12 ++++++++++ src/frontend/qt_sdl/main.h | 2 ++ 6 files changed, 59 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 02f43de7..215ad0b7 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -142,6 +142,7 @@ bool MouseHide; int MouseHideSeconds; bool PauseLostFocus; +std::string UITheme; int64_t RTCOffset; @@ -344,6 +345,7 @@ ConfigEntry ConfigFile[] = {"MouseHide", 1, &MouseHide, false, false}, {"MouseHideSeconds", 0, &MouseHideSeconds, 5, false}, {"PauseLostFocus", 1, &PauseLostFocus, false, false}, + {"UITheme", 2, &UITheme, (std::string)"", false}, {"RTCOffset", 3, &RTCOffset, (int64_t)0, true}, diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 11644dc2..18930656 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -185,6 +185,7 @@ extern bool EnableCheats; extern bool MouseHide; extern int MouseHideSeconds; extern bool PauseLostFocus; +extern std::string UITheme; extern int64_t RTCOffset; diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp index 75497bc3..851e7abf 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.cpp @@ -16,15 +16,16 @@ with melonDS. If not, see http://www.gnu.org/licenses/. */ +#include #include "InterfaceSettingsDialog.h" #include "ui_InterfaceSettingsDialog.h" #include "types.h" #include "Platform.h" #include "Config.h" +#include "main.h" InterfaceSettingsDialog* InterfaceSettingsDialog::currentDlg = nullptr; - InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::InterfaceSettingsDialog) { ui->setupUi(this); @@ -35,6 +36,18 @@ InterfaceSettingsDialog::InterfaceSettingsDialog(QWidget* parent) : QDialog(pare ui->spinMouseHideSeconds->setValue(Config::MouseHideSeconds); ui->cbPauseLostFocus->setChecked(Config::PauseLostFocus != 0); ui->spinMaxFPS->setValue(Config::MaxFPS); + + const QList themeKeys = QStyleFactory::keys(); + const QString currentTheme = qApp->style()->objectName(); + + ui->cbxUITheme->addItem("System default", ""); + + for (int i = 0; i < themeKeys.length(); i++) + { + ui->cbxUITheme->addItem(themeKeys[i], themeKeys[i]); + if (!Config::UITheme.empty() && themeKeys[i].compare(currentTheme, Qt::CaseInsensitive) == 0) + ui->cbxUITheme->setCurrentIndex(i + 1); + } } InterfaceSettingsDialog::~InterfaceSettingsDialog() @@ -63,8 +76,16 @@ void InterfaceSettingsDialog::done(int r) Config::PauseLostFocus = ui->cbPauseLostFocus->isChecked() ? 1:0; Config::MaxFPS = ui->spinMaxFPS->value(); + QString themeName = ui->cbxUITheme->currentData().toString(); + Config::UITheme = themeName.toStdString(); + Config::Save(); + if (!Config::UITheme.empty()) + qApp->setStyle(themeName); + else + qApp->setStyle(*systemThemeName); + emit updateMouseTimer(); } diff --git a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui index 01ba4a46..21d8434e 100644 --- a/src/frontend/qt_sdl/InterfaceSettingsDialog.ui +++ b/src/frontend/qt_sdl/InterfaceSettingsDialog.ui @@ -7,7 +7,7 @@ 0 0 337 - 233 + 275 @@ -20,12 +20,29 @@ Interface settings - melonDS - + - Main window + User interface + + + + + + Theme + + + cbxUITheme + + + + + + + + diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 3a5c1a11..7e34e6af 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -114,6 +114,7 @@ QStringList NdsRomExtensions { ".nds", ".srl", ".dsi", ".ids" }; QString GbaRomMimeType = "application/x-gba-rom"; QStringList GbaRomExtensions { ".gba", ".agb" }; +QString* systemThemeName; // This list of supported archive formats is based on libarchive(3) version 3.6.2 (2022-12-09). QStringList ArchiveMimeTypes @@ -292,6 +293,11 @@ int main(int argc, char** argv) qputenv("QT_SCALE_FACTOR", "1"); +#if QT_VERSION_MAJOR == 6 && defined(__WIN32__) + // Allow using the system dark theme palette on Windows + qputenv("QT_QPA_PLATFORM", "windows:darkmode=2"); +#endif + printf("melonDS " MELONDS_VERSION "\n"); printf(MELONDS_URL "\n"); @@ -360,6 +366,12 @@ int main(int argc, char** argv) camManager[0]->setXFlip(Config::Camera[0].XFlip); camManager[1]->setXFlip(Config::Camera[1].XFlip); + systemThemeName = new QString(QApplication::style()->objectName()); + + if (!Config::UITheme.empty()) + { + QApplication::setStyle(QString::fromStdString(Config::UITheme)); + } Input::JoystickID = Config::JoystickID; Input::OpenJoystick(); diff --git a/src/frontend/qt_sdl/main.h b/src/frontend/qt_sdl/main.h index 51157c6c..5751f229 100644 --- a/src/frontend/qt_sdl/main.h +++ b/src/frontend/qt_sdl/main.h @@ -44,4 +44,6 @@ public: bool event(QEvent* event) override; }; +extern QString* systemThemeName; + #endif // MAIN_H From 17a1bfa6734bfd3603a3d9361dc46be3748422ee Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 7 Feb 2024 22:27:04 +0100 Subject: [PATCH 126/157] macOS CI updates (#1973) * Use macOS 14 M1-based runners for macOS CI * Hopefully make the universal build erroring not fail the build (does sometimes with delete-artifact) * Update vcpkg version --- .github/workflows/build-macos.yml | 5 +++-- cmake/ConfigureVcpkg.cmake | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 5f4b2815..349f99d2 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -15,7 +15,7 @@ jobs: arch: [x86_64, arm64] name: ${{ matrix.arch }} - runs-on: macos-13 + runs-on: macos-14 steps: - name: Check out sources uses: actions/checkout@v3 @@ -27,7 +27,7 @@ jobs: - name: Set up vcpkg uses: lukka/run-vcpkg@v11 with: - vcpkgGitCommitId: c8696863d371ab7f46e213d8f5ca923c4aef2a00 + vcpkgGitCommitId: 53bef8994c541b6561884a8395ea35715ece75db - name: Build uses: lukka/run-cmake@v10 with: @@ -48,6 +48,7 @@ jobs: name: Universal binary needs: [build-macos] runs-on: macos-13 + continue-on-error: true steps: - name: Download x86_64 uses: actions/download-artifact@v4 diff --git a/cmake/ConfigureVcpkg.cmake b/cmake/ConfigureVcpkg.cmake index be8f0590..c9f3e92f 100644 --- a/cmake/ConfigureVcpkg.cmake +++ b/cmake/ConfigureVcpkg.cmake @@ -7,7 +7,7 @@ if (VCPKG_ROOT STREQUAL "${_DEFAULT_VCPKG_ROOT}") file(LOCK "${_DEFAULT_VCPKG_ROOT}" DIRECTORY GUARD FILE) FetchContent_Declare(vcpkg GIT_REPOSITORY "https://github.com/Microsoft/vcpkg.git" - GIT_TAG 2023.12.12 + GIT_TAG 2024.01.12 SOURCE_DIR "${CMAKE_SOURCE_DIR}/vcpkg") FetchContent_MakeAvailable(vcpkg) endif() From 71e1ba8c40468385f2e66142cdbc943c4efb8f55 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 7 Feb 2024 22:29:13 +0100 Subject: [PATCH 127/157] Linux CI updates (#1965) * Switch to using Qt 6 * Use Ubuntu 22.04 for newer dependency versions * Combine AppImage and regular x86_64 builds so it doesn't have to build twice * Misc cleanup --- .github/workflows/build-appimage.yml | 55 --------------- .github/workflows/build-ubuntu-aarch64.yml | 51 -------------- .github/workflows/build-ubuntu.yml | 79 +++++++++++++++++----- 3 files changed, 63 insertions(+), 122 deletions(-) delete mode 100644 .github/workflows/build-appimage.yml delete mode 100644 .github/workflows/build-ubuntu-aarch64.yml diff --git a/.github/workflows/build-appimage.yml b/.github/workflows/build-appimage.yml deleted file mode 100644 index be4494e8..00000000 --- a/.github/workflows/build-appimage.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: AppImage - -on: - push: - branches: - - master - pull_request: - branches: - - master - -jobs: - build: - - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v1 - - name: Install dependencies - run: | - sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list - sudo apt update - sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev libqt5multimedia5-plugins qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades - - name: Create build environment - run: mkdir ${{runner.workspace}}/build - - name: Configure - working-directory: ${{runner.workspace}}/build - run: cmake $GITHUB_WORKSPACE - - name: Make - working-directory: ${{runner.workspace}}/build - run: | - make -j$(nproc --all) - - name: Prepare AppDir for AppImage - working-directory: ${{runner.workspace}}/build - run: | - make install DESTDIR=AppDir - mv ./AppDir/usr/local/bin ./AppDir/usr/bin - mv ./AppDir/usr/local/share ./AppDir/usr/share - rm -rf ./AppDir/usr/local - - name: Prepare necessary Tools for building the AppImage - working-directory: ${{runner.workspace}}/build - run: | - wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage - wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage - chmod a+x linuxdeploy-x86_64.AppImage - chmod a+x linuxdeploy-plugin-qt-x86_64.AppImage - - name: Build the AppImage - working-directory: ${{runner.workspace}}/build - run: | - ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage - mkdir dist - cp ./melonDS*.AppImage ./dist - - uses: actions/upload-artifact@v1 - with: - name: melonDS-appimage-x86_64 - path: ${{runner.workspace}}/build/dist diff --git a/.github/workflows/build-ubuntu-aarch64.yml b/.github/workflows/build-ubuntu-aarch64.yml deleted file mode 100644 index 43f4d8b6..00000000 --- a/.github/workflows/build-ubuntu-aarch64.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Ubuntu - -on: - push: - branches: - - master - pull_request: - branches: - - master - -env: - BUILD_TYPE: Release - -jobs: - build: - name: aarch64 - runs-on: ubuntu-20.04 - container: ubuntu:20.04 - - steps: - - name: Prepare system - shell: bash - run: | - apt update - apt -y full-upgrade - apt -y install git - - name: Check out source - uses: actions/checkout@v1 - - name: Install dependencies - shell: bash - run: | - dpkg --add-architecture arm64 - sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new" - rm /etc/apt/sources.list - mv /etc/apt/sources.list{.new,} - apt update - DEBIAN_FRONTEND=noninteractive apt install -y {gcc-10,g++-10,pkg-config}-aarch64-linux-gnu {libsdl2,qtbase5,qtbase5-private,qtmultimedia5,libslirp,libarchive,libzstd}-dev:arm64 zstd:arm64 cmake extra-cmake-modules dpkg-dev - - name: Configure - shell: bash - run: | - CC=aarch64-linux-gnu-gcc-10 CXX=aarch64-linux-gnu-g++-10 cmake -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -B build - - name: Make - shell: bash - run: | - cmake --build build -j$(nproc --all) - mkdir dist - cp build/melonDS dist - - uses: actions/upload-artifact@v1 - with: - name: melonDS-ubuntu-aarch64 - path: dist diff --git a/.github/workflows/build-ubuntu.yml b/.github/workflows/build-ubuntu.yml index 438fddd0..e96f98aa 100644 --- a/.github/workflows/build-ubuntu.yml +++ b/.github/workflows/build-ubuntu.yml @@ -9,30 +9,77 @@ on: - master jobs: - build: + build-x86_64: name: x86_64 - - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 + name: Check out sources - name: Install dependencies run: | sudo rm -f /etc/apt/sources.list.d/dotnetdev.list /etc/apt/sources.list.d/microsoft-prod.list sudo apt update - sudo apt install cmake extra-cmake-modules libcurl4-gnutls-dev libpcap0.8-dev libsdl2-dev qt5-default qtbase5-private-dev qtmultimedia5-dev libslirp0 libslirp-dev libarchive-dev zstd libzstd-dev --allow-downgrades - - name: Create build environment - run: mkdir ${{runner.workspace}}/build + sudo apt install --allow-downgrades cmake ninja-build extra-cmake-modules libpcap0.8-dev libsdl2-dev \ + qt6-{base,base-private,multimedia}-dev libslirp0 libslirp-dev libarchive-dev libzstd-dev libfuse2 - name: Configure - working-directory: ${{runner.workspace}}/build - run: cmake $GITHUB_WORKSPACE - - name: Make - working-directory: ${{runner.workspace}}/build + run: cmake -B build -G Ninja -DUSE_QT6=ON -DCMAKE_INSTALL_PREFIX=/usr + - name: Build run: | - make -j$(nproc --all) - mkdir dist - cp melonDS dist - - uses: actions/upload-artifact@v1 + cmake --build build + DESTDIR=AppDir cmake --install build + - uses: actions/upload-artifact@v4 with: name: melonDS-ubuntu-x86_64 - path: ${{runner.workspace}}/build/dist + path: AppDir/usr/bin/melonDS + - name: Fetch AppImage tools + run: | + wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + wget https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage + chmod a+x linuxdeploy-*.AppImage + - name: Build the AppImage + env: + QMAKE: /usr/lib/qt6/bin/qmake + run: | + ./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage + - uses: actions/upload-artifact@v4 + with: + name: melonDS-appimage-x86_64 + path: melonDS*.AppImage + + build-aarch64: + name: aarch64 + runs-on: ubuntu-latest + container: ubuntu:22.04 + + steps: + - name: Prepare system + shell: bash + run: | + dpkg --add-architecture arm64 + sh -c "sed \"s|^deb \([a-z\.:/]*\) \([a-z\-]*\) \(.*\)$|deb [arch=amd64] \1 \2 \3\ndeb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports \2 \3|\" /etc/apt/sources.list > /etc/apt/sources.list.new" + rm /etc/apt/sources.list + mv /etc/apt/sources.list{.new,} + apt update + apt -y full-upgrade + apt -y install git {gcc-12,g++-12}-aarch64-linux-gnu cmake ninja-build extra-cmake-modules \ + {libsdl2,qt6-{base,base-private,multimedia},libslirp,libarchive,libzstd}-dev:arm64 \ + pkg-config dpkg-dev + - name: Check out source + uses: actions/checkout@v4 + - name: Configure + shell: bash + run: | + cmake -B build -G Ninja \ + -DPKG_CONFIG_EXECUTABLE=/usr/bin/aarch64-linux-gnu-pkg-config \ + -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc-12 \ + -DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++-12 \ + -DUSE_QT6=ON + - name: Build + shell: bash + run: | + cmake --build build + - uses: actions/upload-artifact@v4 + with: + name: melonDS-ubuntu-aarch64 + path: build/melonDS From 5ffa6429804a5668edf198a26a1595c170149798 Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Wed, 7 Feb 2024 17:04:36 -0500 Subject: [PATCH 128/157] Check for write permissions for some key files (#1972) * check if an nds save file can be opened for writing also add the ability to open a file in append mode * fix multi-instance saves also move the check for file writability into a separate function (probably uneeded?) * implement check for gba roms * move rom load error messages into the functions also finish gba slot (oops) * improve error string * check write perms before saving path settings * fix memory leak * check for writability of firmware/nand/sds * add secondary checks for nand/firmware * add check for config file being writable * Return the file write error as a QString to avoid the invalid char* causing a garbled error message. Qt wants it as QString either way. --- src/Platform.h | 12 ++++ src/frontend/qt_sdl/Config.cpp | 23 ++++-- src/frontend/qt_sdl/Config.h | 2 +- src/frontend/qt_sdl/EmuSettingsDialog.cpp | 32 +++++++++ src/frontend/qt_sdl/PathSettingsDialog.cpp | 20 ++++++ src/frontend/qt_sdl/Platform.cpp | 28 +++++++- src/frontend/qt_sdl/ROMManager.cpp | 82 ++++++++++++++++++++-- src/frontend/qt_sdl/ROMManager.h | 5 +- src/frontend/qt_sdl/Window.cpp | 43 +++--------- src/frontend/qt_sdl/main.cpp | 2 +- 10 files changed, 199 insertions(+), 50 deletions(-) diff --git a/src/Platform.h b/src/Platform.h index 21b3d465..425c712c 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -136,6 +136,11 @@ enum FileMode : unsigned { */ Text = 0b01'00'00, + /** + * Opens a file in append mode. + */ + Append = 0b10'00'00, + /** * Opens a file for reading and writing. * Equivalent to Read | Write. @@ -201,6 +206,13 @@ FileHandle* OpenLocalFile(const std::string& path, FileMode mode); bool FileExists(const std::string& name); bool LocalFileExists(const std::string& name); +// Returns true if we have permission to write to the file. +// Warning: Also creates the file if not present! +bool CheckFileWritable(const std::string& filepath); + +// Same as above (CheckFileWritable()) but for local files. +bool CheckLocalFileWritable(const std::string& filepath); + /** Close a file opened with \c OpenFile. * @returns \c true if the file was closed successfully, false otherwise. * @post \c file is no longer valid and should not be used. diff --git a/src/frontend/qt_sdl/Config.cpp b/src/frontend/qt_sdl/Config.cpp index 215ad0b7..2fdfc3ba 100644 --- a/src/frontend/qt_sdl/Config.cpp +++ b/src/frontend/qt_sdl/Config.cpp @@ -378,7 +378,7 @@ ConfigEntry ConfigFile[] = }; -void LoadFile(int inst) +bool LoadFile(int inst, int actualinst) { Platform::FileHandle* f; if (inst > 0) @@ -386,11 +386,17 @@ void LoadFile(int inst) char name[100] = {0}; snprintf(name, 99, kUniqueConfigFile, inst+1); f = Platform::OpenLocalFile(name, Platform::FileMode::ReadText); + + if (!Platform::CheckLocalFileWritable(name)) return false; } else + { f = Platform::OpenLocalFile(kConfigFile, Platform::FileMode::ReadText); - if (!f) return; + if (actualinst == 0 && !Platform::CheckLocalFileWritable(kConfigFile)) return false; + } + + if (!f) return true; char linebuf[1024]; char entryname[32]; @@ -425,9 +431,10 @@ void LoadFile(int inst) } CloseFile(f); + return true; } -void Load() +bool Load() { for (ConfigEntry* entry = &ConfigFile[0]; entry->Value; entry++) @@ -440,12 +447,14 @@ void Load() case 3: *(int64_t*)entry->Value = std::get(entry->Default); break; } } - - LoadFile(0); - + int inst = Platform::InstanceID(); + + bool ret = LoadFile(0, inst); if (inst > 0) - LoadFile(inst); + ret = LoadFile(inst, inst); + + return ret; } void Save() diff --git a/src/frontend/qt_sdl/Config.h b/src/frontend/qt_sdl/Config.h index 18930656..722384a3 100644 --- a/src/frontend/qt_sdl/Config.h +++ b/src/frontend/qt_sdl/Config.h @@ -204,7 +204,7 @@ extern bool GdbARM7BreakOnStartup; extern bool GdbARM9BreakOnStartup; -void Load(); +bool Load(); void Save(); } diff --git a/src/frontend/qt_sdl/EmuSettingsDialog.cpp b/src/frontend/qt_sdl/EmuSettingsDialog.cpp index 0a834a65..ca9c6716 100644 --- a/src/frontend/qt_sdl/EmuSettingsDialog.cpp +++ b/src/frontend/qt_sdl/EmuSettingsDialog.cpp @@ -380,6 +380,12 @@ void EmuSettingsDialog::on_btnFirmwareBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to firmware file.\nPlease check file/folder write permissions."); + return; + } + updateLastBIOSFolder(file); ui->txtFirmwarePath->setText(file); @@ -436,6 +442,12 @@ void EmuSettingsDialog::on_btnDLDISDBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to DLDI SD image.\nPlease check file/folder write permissions."); + return; + } + updateLastBIOSFolder(file); ui->txtDLDISDPath->setText(file); @@ -468,6 +480,13 @@ void EmuSettingsDialog::on_btnDSiFirmwareBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to DSi firmware file.\nPlease check file/folder write permissions."); + return; + } + + updateLastBIOSFolder(file); ui->txtDSiFirmwarePath->setText(file); @@ -482,6 +501,13 @@ void EmuSettingsDialog::on_btnDSiNANDBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to DSi NAND image.\nPlease check file/folder write permissions."); + return; + } + + updateLastBIOSFolder(file); ui->txtDSiNANDPath->setText(file); @@ -510,6 +536,12 @@ void EmuSettingsDialog::on_btnDSiSDBrowse_clicked() if (file.isEmpty()) return; + if (!Platform::CheckFileWritable(file.toStdString())) + { + QMessageBox::critical(this, "melonDS", "Unable to write to DSi SD image.\nPlease check file/folder write permissions."); + return; + } + updateLastBIOSFolder(file); ui->txtDSiSDPath->setText(file); diff --git a/src/frontend/qt_sdl/PathSettingsDialog.cpp b/src/frontend/qt_sdl/PathSettingsDialog.cpp index 1d698537..71342087 100644 --- a/src/frontend/qt_sdl/PathSettingsDialog.cpp +++ b/src/frontend/qt_sdl/PathSettingsDialog.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include "types.h" #include "Config.h" @@ -37,6 +38,7 @@ extern bool RunningSomething; bool PathSettingsDialog::needsReset = false; +constexpr char errordialog[] = "melonDS cannot write to that directory."; PathSettingsDialog::PathSettingsDialog(QWidget* parent) : QDialog(parent), ui(new Ui::PathSettingsDialog) { @@ -101,6 +103,12 @@ void PathSettingsDialog::on_btnSaveFileBrowse_clicked() QString::fromStdString(EmuDirectory)); if (dir.isEmpty()) return; + + if (!QTemporaryFile(dir).open()) + { + QMessageBox::critical(this, "melonDS", errordialog); + return; + } ui->txtSaveFilePath->setText(dir); } @@ -112,6 +120,12 @@ void PathSettingsDialog::on_btnSavestateBrowse_clicked() QString::fromStdString(EmuDirectory)); if (dir.isEmpty()) return; + + if (!QTemporaryFile(dir).open()) + { + QMessageBox::critical(this, "melonDS", errordialog); + return; + } ui->txtSavestatePath->setText(dir); } @@ -123,6 +137,12 @@ void PathSettingsDialog::on_btnCheatFileBrowse_clicked() QString::fromStdString(EmuDirectory)); if (dir.isEmpty()) return; + + if (!QTemporaryFile(dir).open()) + { + QMessageBox::critical(this, "melonDS", errordialog); + return; + } ui->txtCheatFilePath->setText(dir); } diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 222e512a..9bb19d1a 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -217,6 +217,10 @@ std::string InstanceFileSuffix() constexpr char AccessMode(FileMode mode, bool file_exists) { + + if (mode & FileMode::Append) + return 'a'; + if (!(mode & FileMode::Write)) // If we're only opening the file for reading... return 'r'; @@ -255,7 +259,7 @@ static std::string GetModeString(FileMode mode, bool file_exists) FileHandle* OpenFile(const std::string& path, FileMode mode) { - if ((mode & FileMode::ReadWrite) == FileMode::None) + if ((mode & (FileMode::ReadWrite | FileMode::Append)) == FileMode::None) { // If we aren't reading or writing, then we can't open the file Log(LogLevel::Error, "Attempted to open \"%s\" in neither read nor write mode (FileMode 0x%x)\n", path.c_str(), mode); return nullptr; @@ -327,6 +331,28 @@ bool LocalFileExists(const std::string& name) return true; } +bool CheckFileWritable(const std::string& filepath) +{ + FileHandle* file = Platform::OpenFile(filepath.c_str(), FileMode::Append); + if (file) + { + Platform::CloseFile(file); + return true; + } + else return false; +} + +bool CheckLocalFileWritable(const std::string& name) +{ + FileHandle* file = Platform::OpenLocalFile(name.c_str(), FileMode::Append); + if (file) + { + Platform::CloseFile(file); + return true; + } + else return false; +} + bool FileSeek(FileHandle* file, s64 offset, FileSeekOrigin origin) { int stdorigin; diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 15a9ebf5..e3ceebbe 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #ifdef ARCHIVE_SUPPORT_ENABLED @@ -210,6 +211,9 @@ QString VerifyDSFirmware() f = Platform::OpenLocalFile(Config::FirmwarePath, FileMode::Read); if (!f) return "DS firmware was not found or could not be accessed. Check your emu settings."; + if (!Platform::CheckFileWritable(Config::FirmwarePath)) + return "DS firmware is unable to be written to.\nPlease check file/folder write permissions."; + len = FileLength(f); if (len == 0x20000) { @@ -237,6 +241,9 @@ QString VerifyDSiFirmware() f = Platform::OpenLocalFile(Config::DSiFirmwarePath, FileMode::Read); if (!f) return "DSi firmware was not found or could not be accessed. Check your emu settings."; + if (!Platform::CheckFileWritable(Config::FirmwarePath)) + return "DSi firmware is unable to be written to.\nPlease check file/folder write permissions."; + len = FileLength(f); if (len != 0x20000) { @@ -259,6 +266,9 @@ QString VerifyDSiNAND() f = Platform::OpenLocalFile(Config::DSiNANDPath, FileMode::ReadWriteExisting); if (!f) return "DSi NAND was not found or could not be accessed. Check your emu settings."; + if (!Platform::CheckFileWritable(Config::FirmwarePath)) + return "DSi NAND is unable to be written to.\nPlease check file/folder write permissions."; + // TODO: some basic checks // check that it has the nocash footer, and all @@ -1276,7 +1286,18 @@ bool LoadROMData(const QStringList& filepath, std::unique_ptr& filedata, u return false; } -bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) +QString GetSavErrorString(std::string& filepath, bool gba) +{ + std::string console = gba ? "GBA" : "DS"; + std::string err1 = "Unable to write to "; + std::string err2 = " save.\nPlease check file/folder write permissions.\n\nAttempted to Access:\n"; + + err1 += console + err2 + filepath; + + return QString::fromStdString(err1); +} + +bool LoadROM(QMainWindow* mainWindow, EmuThread* emuthread, QStringList filepath, bool reset) { unique_ptr filedata = nullptr; u32 filelen; @@ -1284,7 +1305,10 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) std::string romname; if (!LoadROMData(filepath, filedata, filelen, basepath, romname)) + { + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); return false; + } NDSSave = nullptr; @@ -1300,7 +1324,22 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) savname += Platform::InstanceFileSuffix(); FileHandle* sav = Platform::OpenFile(savname, FileMode::Read); - if (!sav) sav = Platform::OpenFile(origsav, FileMode::Read); + if (!sav) + { + if (!Platform::CheckFileWritable(origsav)) + { + QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(origsav, false)); + return false; + } + + sav = Platform::OpenFile(origsav, FileMode::Read); + } + else if (!Platform::CheckFileWritable(savname)) + { + QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(savname, false)); + return false; + } + if (sav) { savelen = (u32)Platform::FileLength(sav); @@ -1322,13 +1361,19 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) auto cart = NDSCart::ParseROM(std::move(filedata), filelen, std::move(cartargs)); if (!cart) + { // If we couldn't parse the ROM... + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); return false; + } if (reset) { if (!emuthread->UpdateConsole(std::move(cart), Keep {})) + { + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the DS ROM."); return false; + } InitFirmwareSaveManager(emuthread); emuthread->NDS->Reset(); @@ -1351,7 +1396,7 @@ bool LoadROM(EmuThread* emuthread, QStringList filepath, bool reset) NDSSave = std::make_unique(savname); LoadCheats(*emuthread->NDS); - return true; + return true; // success } void EjectCart(NDS& nds) @@ -1388,9 +1433,13 @@ QString CartLabel() } -bool LoadGBAROM(NDS& nds, QStringList filepath) +bool LoadGBAROM(QMainWindow* mainWindow, NDS& nds, QStringList filepath) { - if (nds.ConsoleType == 1) return false; // DSi doesn't have a GBA slot + if (nds.ConsoleType == 1) + { + QMessageBox::critical(mainWindow, "melonDS", "The DSi doesn't have a GBA slot."); + return false; + } unique_ptr filedata = nullptr; u32 filelen; @@ -1398,7 +1447,10 @@ bool LoadGBAROM(NDS& nds, QStringList filepath) std::string romname; if (!LoadROMData(filepath, filedata, filelen, basepath, romname)) + { + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM."); return false; + } GBASave = nullptr; @@ -1414,7 +1466,22 @@ bool LoadGBAROM(NDS& nds, QStringList filepath) savname += Platform::InstanceFileSuffix(); FileHandle* sav = Platform::OpenFile(savname, FileMode::Read); - if (!sav) sav = Platform::OpenFile(origsav, FileMode::Read); + if (!sav) + { + if (!Platform::CheckFileWritable(origsav)) + { + QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(origsav, true)); + return false; + } + + sav = Platform::OpenFile(origsav, FileMode::Read); + } + else if (!Platform::CheckFileWritable(savname)) + { + QMessageBox::critical(mainWindow, "melonDS", GetSavErrorString(savname, true)); + return false; + } + if (sav) { savelen = (u32)FileLength(sav); @@ -1430,7 +1497,10 @@ bool LoadGBAROM(NDS& nds, QStringList filepath) auto cart = GBACart::ParseROM(std::move(filedata), filelen, std::move(savedata), savelen); if (!cart) + { + QMessageBox::critical(mainWindow, "melonDS", "Failed to load the GBA ROM."); return false; + } nds.SetGBACart(std::move(cart)); GBACartType = 0; diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 0b640c84..38ed65cd 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -23,6 +23,7 @@ #include "SaveManager.h" #include "AREngine.h" #include "DSi_NAND.h" +#include #include "MemConstants.h" #include @@ -72,12 +73,12 @@ std::optional LoadFirmware(int type) noexcept; std::optional LoadNAND(const std::array& arm7ibios) noexcept; /// Inserts a ROM into the emulated console. -bool LoadROM(EmuThread*, QStringList filepath, bool reset); +bool LoadROM(QMainWindow* mainWindow, EmuThread*, QStringList filepath, bool reset); void EjectCart(NDS& nds); bool CartInserted(); QString CartLabel(); -bool LoadGBAROM(NDS& nds, QStringList filepath); +bool LoadGBAROM(QMainWindow* mainWindow, NDS& nds, QStringList filepath); void LoadGBAAddon(NDS& nds, int type); void EjectGBACart(NDS& nds); bool GBACartInserted(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 962fb76c..0f0b71fd 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -865,10 +865,8 @@ void MainWindow::dropEvent(QDropEvent* event) if (isNdsRom) { - if (!ROMManager::LoadROM(emuThread, file, true)) + if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the DS ROM."); emuThread->emuUnpause(); return; } @@ -886,10 +884,8 @@ void MainWindow::dropEvent(QDropEvent* event) } else if (isGbaRom) { - if (!ROMManager::LoadGBAROM(*emuThread->NDS, file)) + if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, file)) { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); emuThread->emuUnpause(); return; } @@ -952,12 +948,7 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) bool gbaloaded = false; if (!gbafile.isEmpty()) { - if (!ROMManager::LoadGBAROM(*emuThread->NDS, gbafile)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the GBA ROM."); - return false; - } + if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, gbafile)) return false; gbaloaded = true; } @@ -965,12 +956,8 @@ bool MainWindow::preloadROMs(QStringList file, QStringList gbafile, bool boot) bool ndsloaded = false; if (!file.isEmpty()) { - if (!ROMManager::LoadROM(emuThread, file, true)) - { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); - return false; - } + if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) return false; + recentFileList.removeAll(file.join("|")); recentFileList.prepend(file.join("|")); updateRecentFilesMenu(); @@ -1173,11 +1160,9 @@ void MainWindow::onOpenFile() emuThread->emuUnpause(); return; } - - if (!ROMManager::LoadROM(emuThread, file, true)) + + if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); emuThread->emuUnpause(); return; } @@ -1272,11 +1257,9 @@ void MainWindow::onClickRecentFile() emuThread->emuUnpause(); return; } - - if (!ROMManager::LoadROM(emuThread, file, true)) + + if (!ROMManager::LoadROM(mainWindow, emuThread, file, true)) { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); emuThread->emuUnpause(); return; } @@ -1326,10 +1309,8 @@ void MainWindow::onInsertCart() return; } - if (!ROMManager::LoadROM(emuThread, file, false)) + if (!ROMManager::LoadROM(mainWindow, emuThread, file, false)) { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); emuThread->emuUnpause(); return; } @@ -1361,10 +1342,8 @@ void MainWindow::onInsertGBACart() return; } - if (!ROMManager::LoadGBAROM(*emuThread->NDS, file)) + if (!ROMManager::LoadGBAROM(mainWindow, *emuThread->NDS, file)) { - // TODO: better error reporting? - QMessageBox::critical(this, "melonDS", "Failed to load the ROM."); emuThread->emuUnpause(); return; } diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 7e34e6af..6e87d5bb 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -337,7 +337,7 @@ int main(int argc, char** argv) SDL_InitSubSystem(SDL_INIT_VIDEO); SDL_EnableScreenSaver(); SDL_DisableScreenSaver(); - Config::Load(); + if (!Config::Load()) QMessageBox::critical(NULL, "melonDS", "Unable to write to config.\nPlease check the write permissions of the folder you placed melonDS in."); #define SANITIZE(var, min, max) { var = std::clamp(var, min, max); } SANITIZE(Config::ConsoleType, 0, 1); From 646ed3cb321633a430ae9cbc6428177d132378f5 Mon Sep 17 00:00:00 2001 From: Luca D'Amico Date: Wed, 7 Feb 2024 23:15:30 +0100 Subject: [PATCH 129/157] Add Haiku (BeOS-like OS) support (#1858) --- src/CMakeLists.txt | 4 +++- src/sha1/sha1.c | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index afabc03f..3dfd3b0d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -154,11 +154,13 @@ endif() if (WIN32) target_link_libraries(core PRIVATE ole32 comctl32 wsock32 ws2_32) -elseif(NOT APPLE) +elseif(NOT APPLE AND NOT HAIKU) check_library_exists(rt shm_open "" NEED_LIBRT) if (NEED_LIBRT) target_link_libraries(core PRIVATE rt) endif() +elseif(HAIKU) + target_link_libraries(core PRIVATE network) endif() if (ENABLE_JIT_PROFILING) diff --git a/src/sha1/sha1.c b/src/sha1/sha1.c index c0052b70..c34ace30 100644 --- a/src/sha1/sha1.c +++ b/src/sha1/sha1.c @@ -27,6 +27,9 @@ A million repetitions of "a" #if defined(__sun) #include "solarisfixes.h" #endif +#if defined(__HAIKU__) +#include +#endif #include "sha1.h" #ifndef BYTE_ORDER From 3415e23105cf92dd34e566fbb6215cc2abaaef18 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Tue, 13 Feb 2024 20:15:03 +0100 Subject: [PATCH 130/157] delete-artifact keeps failing PR CI even when you tell it not to fail on error so I guess we're just not using it. --- .github/workflows/build-macos.yml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 349f99d2..1d4b9171 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -43,6 +43,7 @@ jobs: with: name: macOS-${{ matrix.arch }} path: macOS-${{ matrix.arch }}.zip + retention-days: 1 universal-binary: name: Universal binary @@ -75,10 +76,10 @@ jobs: with: name: macOS-universal path: macOS-universal.zip - - name: Clean up architecture-specific artifacts - uses: geekyeggo/delete-artifact@v4 - with: - failOnError: false - name: | - macOS-x86_64 - macOS-arm64 +# - name: Clean up architecture-specific artifacts +# uses: geekyeggo/delete-artifact@v4 +# with: +# failOnError: false +# name: | +# macOS-x86_64 +# macOS-arm64 From a8429af13150dcdb81fbb9aeb1b66b7c5ece582d Mon Sep 17 00:00:00 2001 From: Jaklyy <102590697+Jaklyy@users.noreply.github.com> Date: Tue, 13 Feb 2024 14:17:29 -0500 Subject: [PATCH 131/157] dont make a save file on launching a game (#1974) avoids the issue of saves being created for roms that dont use save files. --- src/frontend/qt_sdl/Platform.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 9bb19d1a..0cd4f615 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include "Platform.h" @@ -333,13 +334,29 @@ bool LocalFileExists(const std::string& name) bool CheckFileWritable(const std::string& filepath) { - FileHandle* file = Platform::OpenFile(filepath.c_str(), FileMode::Append); + FileHandle* file = Platform::OpenFile(filepath.c_str(), FileMode::Read); + if (file) { + // if the file exists, check if it can be opened for writing. Platform::CloseFile(file); - return true; + file = Platform::OpenFile(filepath.c_str(), FileMode::Append); + if (file) + { + Platform::CloseFile(file); + return true; + } + else return false; + } + else + { + // if the file does not exist, create a temporary file to check, to avoid creating an empty file. + if (QTemporaryFile(filepath.c_str()).open()) + { + return true; + } + else return false; } - else return false; } bool CheckLocalFileWritable(const std::string& name) From 9430502b16c0b4f1f82cd31d79beff15edd00762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=BBP=2E=28P=20izzy=29?= Date: Mon, 19 Feb 2024 20:33:39 -0600 Subject: [PATCH 132/157] fix malloc on OpenBSD targets (#1979) --- src/frontend/duckstation/gl/context.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/frontend/duckstation/gl/context.cpp b/src/frontend/duckstation/gl/context.cpp index a0a4183b..308b3c40 100644 --- a/src/frontend/duckstation/gl/context.cpp +++ b/src/frontend/duckstation/gl/context.cpp @@ -3,11 +3,7 @@ #include "loader.h" #include #include -#ifdef __APPLE__ #include -#else -#include -#endif Log_SetChannel(GL::Context); #if defined(_WIN32) From 21e2a876ec7392eb3d8d2c4d0f0aee5cd9a298f0 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 24 Feb 2024 01:47:04 +0100 Subject: [PATCH 133/157] build teakra's test_generator.cpp only if building its unit tests is enabled speeds up builds a bit --- src/teakra/src/CMakeLists.txt | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/teakra/src/CMakeLists.txt b/src/teakra/src/CMakeLists.txt index b96c500b..30683374 100644 --- a/src/teakra/src/CMakeLists.txt +++ b/src/teakra/src/CMakeLists.txt @@ -32,11 +32,16 @@ add_library(teakra register.h shared_memory.h teakra.cpp - test.h - test_generator.cpp - test_generator.h ) +if (TEAKRA_BUILD_UNIT_TESTS) + target_sources(teakra PUBLIC + test.h + test_generator.cpp + test_generator.h + ) +endif() + create_target_directory_groups(teakra) target_link_libraries(teakra PRIVATE Threads::Threads) From 67ca4997e22cd88e349ecffd2cd388431dcc8de3 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 25 Feb 2024 14:25:50 +0100 Subject: [PATCH 134/157] Release all keyboard keys on focus loss (fixes #1987) --- src/frontend/qt_sdl/Input.cpp | 5 +++++ src/frontend/qt_sdl/Input.h | 1 + src/frontend/qt_sdl/Window.cpp | 1 + 3 files changed, 7 insertions(+) diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index c429cd36..27c42e9a 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -128,6 +128,11 @@ void KeyRelease(QKeyEvent* event) KeyHotkeyMask &= ~(1<emuIsRunning()) emuThread->emuPause(); } From e227902cecfb629fd113e9fd7c69cea6b98dd790 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 3 Mar 2024 16:58:59 +0100 Subject: [PATCH 135/157] Util_Audio: use basic linear interpolation Should remove the artifacts caused by the previous nearest resampling. May be worth replacing with something better in the future, but this is an improvement for now. --- src/frontend/Util_Audio.cpp | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/frontend/Util_Audio.cpp b/src/frontend/Util_Audio.cpp index 02b3026e..25e04db3 100644 --- a/src/frontend/Util_Audio.cpp +++ b/src/frontend/Util_Audio.cpp @@ -63,21 +63,28 @@ int AudioOut_GetNumSamples(int outlen) void AudioOut_Resample(s16* inbuf, int inlen, s16* outbuf, int outlen, int volume) { - float res_incr = inlen / (float)outlen; - float res_timer = 0; - int res_pos = 0; + double factor = (double) inlen / (double) outlen; + double inpos = -(factor / 2); + double vol = (double) volume / 256.f; - for (int i = 0; i < outlen; i++) + for (int i = 0; i < outlen * 2; i += 2) { - outbuf[i*2 ] = (inbuf[res_pos*2 ] * volume) >> 8; - outbuf[i*2+1] = (inbuf[res_pos*2+1] * volume) >> 8; + double intpart_d; + double frac = modf(inpos, &intpart_d); + int intpart = (int) intpart_d; - res_timer += res_incr; - while (res_timer >= 1.0) - { - res_timer -= 1.0; - res_pos++; - } + double l1 = inbuf[ intpart * 2]; + double l2 = inbuf[(intpart * 2) + 2]; + double r1 = inbuf[(intpart * 2) + 1]; + double r2 = inbuf[(intpart * 2) + 3]; + + double ldiff = l2 - l1; + double rdiff = r2 - r1; + + outbuf[i] = (s16) round((l1 + ldiff * frac) * vol); + outbuf[i+1] = (s16) round((r1 + rdiff * frac) * vol); + + inpos += factor; } } From faf3c0f2e08c0aa199d817feddafb0f4ff67e64a Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 8 Mar 2024 16:36:00 +0100 Subject: [PATCH 136/157] Add Gaussian (SNES) audio interpolation Probably not a good choice for most DS games unless you really want a very soft sound, but it could be fun if you wanted to run lolSnes in melonDS :p --- src/SPU.cpp | 50 ++++++++++++++++++++- src/SPU.h | 1 + src/frontend/qt_sdl/AudioSettingsDialog.cpp | 3 +- src/frontend/qt_sdl/main.cpp | 2 +- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/SPU.cpp b/src/SPU.cpp index 86307097..4e6fb13f 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -140,6 +140,41 @@ constexpr array2d InterpCubic = []() constexpr { return interp; }(); +const std::array InterpSNESGauss = { + 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, 0x000, + 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x001, 0x002, 0x002, 0x002, 0x002, 0x002, + 0x002, 0x002, 0x003, 0x003, 0x003, 0x003, 0x003, 0x004, 0x004, 0x004, 0x004, 0x004, 0x005, 0x005, 0x005, 0x005, + 0x006, 0x006, 0x006, 0x006, 0x007, 0x007, 0x007, 0x008, 0x008, 0x008, 0x009, 0x009, 0x009, 0x00A, 0x00A, 0x00A, + 0x00B, 0x00B, 0x00B, 0x00C, 0x00C, 0x00D, 0x00D, 0x00E, 0x00E, 0x00F, 0x00F, 0x00F, 0x010, 0x010, 0x011, 0x011, + 0x012, 0x013, 0x013, 0x014, 0x014, 0x015, 0x015, 0x016, 0x017, 0x017, 0x018, 0x018, 0x019, 0x01A, 0x01B, 0x01B, + 0x01C, 0x01D, 0x01D, 0x01E, 0x01F, 0x020, 0x020, 0x021, 0x022, 0x023, 0x024, 0x024, 0x025, 0x026, 0x027, 0x028, + 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F, 0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, + 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x040, 0x041, 0x042, 0x043, 0x045, 0x046, 0x047, 0x049, 0x04A, 0x04C, 0x04D, + 0x04E, 0x050, 0x051, 0x053, 0x054, 0x056, 0x057, 0x059, 0x05A, 0x05C, 0x05E, 0x05F, 0x061, 0x063, 0x064, 0x066, + 0x068, 0x06A, 0x06B, 0x06D, 0x06F, 0x071, 0x073, 0x075, 0x076, 0x078, 0x07A, 0x07C, 0x07E, 0x080, 0x082, 0x084, + 0x086, 0x089, 0x08B, 0x08D, 0x08F, 0x091, 0x093, 0x096, 0x098, 0x09A, 0x09C, 0x09F, 0x0A1, 0x0A3, 0x0A6, 0x0A8, + 0x0AB, 0x0AD, 0x0AF, 0x0B2, 0x0B4, 0x0B7, 0x0BA, 0x0BC, 0x0BF, 0x0C1, 0x0C4, 0x0C7, 0x0C9, 0x0CC, 0x0CF, 0x0D2, + 0x0D4, 0x0D7, 0x0DA, 0x0DD, 0x0E0, 0x0E3, 0x0E6, 0x0E9, 0x0EC, 0x0EF, 0x0F2, 0x0F5, 0x0F8, 0x0FB, 0x0FE, 0x101, + 0x104, 0x107, 0x10B, 0x10E, 0x111, 0x114, 0x118, 0x11B, 0x11E, 0x122, 0x125, 0x129, 0x12C, 0x130, 0x133, 0x137, + 0x13A, 0x13E, 0x141, 0x145, 0x148, 0x14C, 0x150, 0x153, 0x157, 0x15B, 0x15F, 0x162, 0x166, 0x16A, 0x16E, 0x172, + 0x176, 0x17A, 0x17D, 0x181, 0x185, 0x189, 0x18D, 0x191, 0x195, 0x19A, 0x19E, 0x1A2, 0x1A6, 0x1AA, 0x1AE, 0x1B2, + 0x1B7, 0x1BB, 0x1BF, 0x1C3, 0x1C8, 0x1CC, 0x1D0, 0x1D5, 0x1D9, 0x1DD, 0x1E2, 0x1E6, 0x1EB, 0x1EF, 0x1F3, 0x1F8, + 0x1FC, 0x201, 0x205, 0x20A, 0x20F, 0x213, 0x218, 0x21C, 0x221, 0x226, 0x22A, 0x22F, 0x233, 0x238, 0x23D, 0x241, + 0x246, 0x24B, 0x250, 0x254, 0x259, 0x25E, 0x263, 0x267, 0x26C, 0x271, 0x276, 0x27B, 0x280, 0x284, 0x289, 0x28E, + 0x293, 0x298, 0x29D, 0x2A2, 0x2A6, 0x2AB, 0x2B0, 0x2B5, 0x2BA, 0x2BF, 0x2C4, 0x2C9, 0x2CE, 0x2D3, 0x2D8, 0x2DC, + 0x2E1, 0x2E6, 0x2EB, 0x2F0, 0x2F5, 0x2FA, 0x2FF, 0x304, 0x309, 0x30E, 0x313, 0x318, 0x31D, 0x322, 0x326, 0x32B, + 0x330, 0x335, 0x33A, 0x33F, 0x344, 0x349, 0x34E, 0x353, 0x357, 0x35C, 0x361, 0x366, 0x36B, 0x370, 0x374, 0x379, + 0x37E, 0x383, 0x388, 0x38C, 0x391, 0x396, 0x39B, 0x39F, 0x3A4, 0x3A9, 0x3AD, 0x3B2, 0x3B7, 0x3BB, 0x3C0, 0x3C5, + 0x3C9, 0x3CE, 0x3D2, 0x3D7, 0x3DC, 0x3E0, 0x3E5, 0x3E9, 0x3ED, 0x3F2, 0x3F6, 0x3FB, 0x3FF, 0x403, 0x408, 0x40C, + 0x410, 0x415, 0x419, 0x41D, 0x421, 0x425, 0x42A, 0x42E, 0x432, 0x436, 0x43A, 0x43E, 0x442, 0x446, 0x44A, 0x44E, + 0x452, 0x455, 0x459, 0x45D, 0x461, 0x465, 0x468, 0x46C, 0x470, 0x473, 0x477, 0x47A, 0x47E, 0x481, 0x485, 0x488, + 0x48C, 0x48F, 0x492, 0x496, 0x499, 0x49C, 0x49F, 0x4A2, 0x4A6, 0x4A9, 0x4AC, 0x4AF, 0x4B2, 0x4B5, 0x4B7, 0x4BA, + 0x4BD, 0x4C0, 0x4C3, 0x4C5, 0x4C8, 0x4CB, 0x4CD, 0x4D0, 0x4D2, 0x4D5, 0x4D7, 0x4D9, 0x4DC, 0x4DE, 0x4E0, 0x4E3, + 0x4E5, 0x4E7, 0x4E9, 0x4EB, 0x4ED, 0x4EF, 0x4F1, 0x4F3, 0x4F5, 0x4F6, 0x4F8, 0x4FA, 0x4FB, 0x4FD, 0x4FF, 0x500, + 0x502, 0x503, 0x504, 0x506, 0x507, 0x508, 0x50A, 0x50B, 0x50C, 0x50D, 0x50E, 0x50F, 0x510, 0x511, 0x511, 0x512, + 0x513, 0x514, 0x514, 0x515, 0x516, 0x516, 0x517, 0x517, 0x517, 0x518, 0x518, 0x518, 0x518, 0x518, 0x519, 0x519 +}; + SPU::SPU(melonDS::NDS& nds, AudioBitDepth bitdepth, AudioInterpolation interpolation) : NDS(nds), Channels { @@ -621,6 +656,19 @@ s32 SPUChannel::Run() (PrevSample[0] * InterpCubic[samplepos][2]) + (val * InterpCubic[samplepos][3])) >> 14; break; + + case AudioInterpolation::SNESGaussian: { + // Avoid clipping (from fullsnes) + #define CLAMP(s) (std::clamp((s) >> 1, -0x3FFA, 0x3FF8)) + s32 out = (InterpSNESGauss[0x0FF - samplepos] * CLAMP(PrevSample[2]) >> 10); + out = out + ((InterpSNESGauss[0x1FF - samplepos] * CLAMP(PrevSample[1])) >> 10); + out = out + ((InterpSNESGauss[0x100 + samplepos] * CLAMP(PrevSample[0])) >> 10); + out = out + ((InterpSNESGauss[0x000 + samplepos] * CLAMP(val)) >> 10); + val = std::clamp(out, -0x7FFF, 0x7FFF); + #undef CLAMP + break; + } + default: break; } @@ -1257,4 +1305,4 @@ void SPU::Write32(u32 addr, u32 val) } } -} \ No newline at end of file +} diff --git a/src/SPU.h b/src/SPU.h index b2b05ac7..6e3d1aae 100644 --- a/src/SPU.h +++ b/src/SPU.h @@ -40,6 +40,7 @@ enum class AudioInterpolation Linear, Cosine, Cubic, + SNESGaussian }; class SPUChannel diff --git a/src/frontend/qt_sdl/AudioSettingsDialog.cpp b/src/frontend/qt_sdl/AudioSettingsDialog.cpp index 5e8812e9..8e08ef2b 100644 --- a/src/frontend/qt_sdl/AudioSettingsDialog.cpp +++ b/src/frontend/qt_sdl/AudioSettingsDialog.cpp @@ -51,6 +51,7 @@ AudioSettingsDialog::AudioSettingsDialog(QWidget* parent, bool emuActive, EmuThr ui->cbInterpolation->addItem("Linear"); ui->cbInterpolation->addItem("Cosine"); ui->cbInterpolation->addItem("Cubic"); + ui->cbInterpolation->addItem("Gaussian (SNES)"); ui->cbInterpolation->setCurrentIndex(Config::AudioInterp); ui->cbBitDepth->addItem("Automatic"); @@ -173,7 +174,7 @@ void AudioSettingsDialog::on_cbBitDepth_currentIndexChanged(int idx) void AudioSettingsDialog::on_cbInterpolation_currentIndexChanged(int idx) { // prevent a spurious change - if (ui->cbInterpolation->count() < 4) return; + if (ui->cbInterpolation->count() < 5) return; Config::AudioInterp = ui->cbInterpolation->currentIndex(); diff --git a/src/frontend/qt_sdl/main.cpp b/src/frontend/qt_sdl/main.cpp index 6e87d5bb..01ba52c7 100644 --- a/src/frontend/qt_sdl/main.cpp +++ b/src/frontend/qt_sdl/main.cpp @@ -348,7 +348,7 @@ int main(int argc, char** argv) #endif SANITIZE(Config::ScreenVSyncInterval, 1, 20); SANITIZE(Config::GL_ScaleFactor, 1, 16); - SANITIZE(Config::AudioInterp, 0, 3); + SANITIZE(Config::AudioInterp, 0, 4); SANITIZE(Config::AudioVolume, 0, 256); SANITIZE(Config::MicInputType, 0, (int)micInputType_MAX); SANITIZE(Config::ScreenRotation, 0, (int)Frontend::screenRot_MAX); From b117bb8f58938edf3da1e348ca5ff32fa68b279e Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Fri, 8 Mar 2024 16:59:31 +0100 Subject: [PATCH 137/157] that should be 0x8000 --- src/SPU.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/SPU.cpp b/src/SPU.cpp index 4e6fb13f..69c0b9de 100644 --- a/src/SPU.cpp +++ b/src/SPU.cpp @@ -659,13 +659,13 @@ s32 SPUChannel::Run() case AudioInterpolation::SNESGaussian: { // Avoid clipping (from fullsnes) - #define CLAMP(s) (std::clamp((s) >> 1, -0x3FFA, 0x3FF8)) +#define CLAMP(s) (std::clamp((s) >> 1, -0x3FFA, 0x3FF8)) s32 out = (InterpSNESGauss[0x0FF - samplepos] * CLAMP(PrevSample[2]) >> 10); out = out + ((InterpSNESGauss[0x1FF - samplepos] * CLAMP(PrevSample[1])) >> 10); out = out + ((InterpSNESGauss[0x100 + samplepos] * CLAMP(PrevSample[0])) >> 10); out = out + ((InterpSNESGauss[0x000 + samplepos] * CLAMP(val)) >> 10); - val = std::clamp(out, -0x7FFF, 0x7FFF); - #undef CLAMP + val = std::clamp(out, -0x8000, 0x7FFF); +#undef CLAMP break; } From 18d1df606f475c49bf7bfc6cdbbbdb9208e0e7df Mon Sep 17 00:00:00 2001 From: RSDuck Date: Tue, 12 Mar 2024 08:35:56 +0100 Subject: [PATCH 138/157] fix #1959 Use QT again for opening file so that we don't depend on locale --- src/frontend/qt_sdl/Platform.cpp | 34 ++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 0cd4f615..7ffabfd7 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -216,9 +216,30 @@ std::string InstanceFileSuffix() return suffix; } +static QIODevice::OpenMode GetQMode(FileMode mode) +{ + QIODevice::OpenMode qmode = 0; + if (mode & FileMode::Read) + qmode |= QIODevice::OpenModeFlag::ReadOnly; + if (mode & FileMode::Write) + qmode |= QIODevice::OpenModeFlag::WriteOnly; + if (mode & FileMode::Append) + qmode |= QIODevice::OpenModeFlag::Append; + + if ((mode & FileMode::Write) && !(mode & FileMode::Preserve)) + qmode |= QIODevice::OpenModeFlag::Truncate; + + if (mode & FileMode::NoCreate) + qmode |= QIODevice::OpenModeFlag::ExistingOnly; + + if (mode & FileMode::Text) + qmode |= QIODevice::OpenModeFlag::Text; + + return qmode; +} + constexpr char AccessMode(FileMode mode, bool file_exists) { - if (mode & FileMode::Append) return 'a'; @@ -266,10 +287,15 @@ FileHandle* OpenFile(const std::string& path, FileMode mode) return nullptr; } - bool file_exists = QFile::exists(QString::fromStdString(path)); - std::string modeString = GetModeString(mode, file_exists); + QString qpath{QString::fromStdString(path)}; + + std::string modeString = GetModeString(mode, QFile::exists(qpath)); + QIODevice::OpenMode qmode = GetQMode(mode); + QFile qfile{qpath}; + qfile.open(qmode); + FILE* file = fdopen(dup(qfile.handle()), modeString.c_str()); + qfile.close(); - FILE* file = fopen(path.c_str(), modeString.c_str()); if (file) { Log(LogLevel::Debug, "Opened \"%s\" with FileMode 0x%x (effective mode \"%s\")\n", path.c_str(), mode, modeString.c_str()); From 5fdd285c9a7f47601ff674df9d52abfd15355978 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Tue, 12 Mar 2024 08:41:42 +0100 Subject: [PATCH 139/157] fix aarch64 build --- src/frontend/qt_sdl/Platform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Platform.cpp b/src/frontend/qt_sdl/Platform.cpp index 7ffabfd7..b3230a41 100644 --- a/src/frontend/qt_sdl/Platform.cpp +++ b/src/frontend/qt_sdl/Platform.cpp @@ -218,7 +218,7 @@ std::string InstanceFileSuffix() static QIODevice::OpenMode GetQMode(FileMode mode) { - QIODevice::OpenMode qmode = 0; + QIODevice::OpenMode qmode = QIODevice::OpenModeFlag::NotOpen; if (mode & FileMode::Read) qmode |= QIODevice::OpenModeFlag::ReadOnly; if (mode & FileMode::Write) From ea1755bed0378a2f3783e78c5e63c266583218ce Mon Sep 17 00:00:00 2001 From: RSDuck Date: Tue, 12 Mar 2024 09:23:20 +0100 Subject: [PATCH 140/157] call Start again NDS object after Reset fixes issue where game doesn't properly start after changing settings --- src/frontend/qt_sdl/ROMManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index e3ceebbe..de1d8644 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -916,6 +916,8 @@ void Reset(EmuThread* thread) thread->NDS->SetupDirectBoot(BaseROMName); } } + + thread->NDS->Start(); } From 31a7f53282040c7df93204d647de470390b96c4b Mon Sep 17 00:00:00 2001 From: Jesse Talavera Date: Wed, 13 Mar 2024 09:55:20 -0400 Subject: [PATCH 141/157] Fix a crash when using DSi mode in debug builds on macOS (#1976) Store the BIOS images in `NDSArgs`/`DSiArgs` through pointers, not directly - This will make it easier to keep such objects on the stack --- src/Args.h | 12 ++++++--- src/DSi.cpp | 4 +-- src/NDS.cpp | 8 +++--- src/frontend/qt_sdl/EmuThread.cpp | 8 +++--- src/frontend/qt_sdl/ROMManager.cpp | 40 +++++++++++++++--------------- src/frontend/qt_sdl/ROMManager.h | 12 +++++---- 6 files changed, 45 insertions(+), 39 deletions(-) diff --git a/src/Args.h b/src/Args.h index d836b643..0b20bbf0 100644 --- a/src/Args.h +++ b/src/Args.h @@ -69,6 +69,10 @@ struct JITArgs bool FastMemory = true; }; +using ARM9BIOSImage = std::array; +using ARM7BIOSImage = std::array; +using DSiBIOSImage = std::array; + struct GDBArgs { u16 PortARM7 = 0; @@ -95,11 +99,11 @@ struct NDSArgs /// NDS ARM9 BIOS to install. /// Defaults to FreeBIOS, which is not compatible with DSi mode. - std::array ARM9BIOS = bios_arm9_bin; + std::unique_ptr ARM9BIOS = std::make_unique(bios_arm9_bin); /// NDS ARM7 BIOS to install. /// Defaults to FreeBIOS, which is not compatible with DSi mode. - std::array ARM7BIOS = bios_arm7_bin; + std::unique_ptr ARM7BIOS = std::make_unique(bios_arm7_bin); /// Firmware image to install. /// Defaults to generated NDS firmware. @@ -131,8 +135,8 @@ struct NDSArgs /// Contains no virtual methods, so there's no vtable. struct DSiArgs final : public NDSArgs { - std::array ARM9iBIOS = BrokenBIOS; - std::array ARM7iBIOS = BrokenBIOS; + std::unique_ptr ARM9iBIOS = std::make_unique(BrokenBIOS); + std::unique_ptr ARM7iBIOS = std::make_unique(BrokenBIOS); /// NAND image to install. /// Required, there is no default value. diff --git a/src/DSi.cpp b/src/DSi.cpp index c929c6d2..306c5d1c 100644 --- a/src/DSi.cpp +++ b/src/DSi.cpp @@ -82,8 +82,8 @@ DSi::DSi(DSiArgs&& args) noexcept : DSi_NDMA(1, 2, *this), DSi_NDMA(1, 3, *this), }, - ARM7iBIOS(args.ARM7iBIOS), - ARM9iBIOS(args.ARM9iBIOS), + ARM7iBIOS(*args.ARM7iBIOS), + ARM9iBIOS(*args.ARM9iBIOS), DSP(*this), SDMMC(*this, std::move(args.NANDImage), std::move(args.DSiSDCard)), SDIO(*this), diff --git a/src/NDS.cpp b/src/NDS.cpp index 1f9597ce..284f6eb0 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -80,8 +80,8 @@ NDS::NDS() noexcept : NDSArgs { nullptr, nullptr, - bios_arm9_bin, - bios_arm7_bin, + std::make_unique(bios_arm9_bin), + std::make_unique(bios_arm7_bin), Firmware(0), } ) @@ -90,8 +90,8 @@ NDS::NDS() noexcept : NDS::NDS(NDSArgs&& args, int type) noexcept : ConsoleType(type), - ARM7BIOS(args.ARM7BIOS), - ARM9BIOS(args.ARM9BIOS), + ARM7BIOS(*args.ARM7BIOS), + ARM9BIOS(*args.ARM9BIOS), ARM7BIOSNative(CRC32(ARM7BIOS.data(), ARM7BIOS.size()) == ARM7BIOSCRC32), ARM9BIOSNative(CRC32(ARM9BIOS.data(), ARM9BIOS.size()) == ARM9BIOSCRC32), JIT(*this, args.JIT), diff --git a/src/frontend/qt_sdl/EmuThread.cpp b/src/frontend/qt_sdl/EmuThread.cpp index 0728a085..d16aead4 100644 --- a/src/frontend/qt_sdl/EmuThread.cpp +++ b/src/frontend/qt_sdl/EmuThread.cpp @@ -131,8 +131,8 @@ std::unique_ptr EmuThread::CreateConsole( NDSArgs ndsargs { std::move(ndscart), std::move(gbacart), - *arm9bios, - *arm7bios, + std::move(arm9bios), + std::move(arm7bios), std::move(*firmware), #ifdef JIT_ENABLED Config::JIT_Enable ? std::make_optional(jitargs) : std::nullopt, @@ -165,8 +165,8 @@ std::unique_ptr EmuThread::CreateConsole( auto sdcard = ROMManager::LoadDSiSDCard(); DSiArgs args { std::move(ndsargs), - *arm9ibios, - *arm7ibios, + std::move(arm9ibios), + std::move(arm7ibios), std::move(*nand), std::move(sdcard), Config::DSiFullBIOSBoot, diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index de1d8644..65508467 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -512,59 +512,59 @@ void LoadCheats(NDS& nds) nds.AREngine.SetCodeFile(CheatsOn ? CheatFile : nullptr); } -std::optional> LoadARM9BIOS() noexcept +std::unique_ptr LoadARM9BIOS() noexcept { if (!Config::ExternalBIOSEnable) { - return Config::ConsoleType == 0 ? std::make_optional(bios_arm9_bin) : std::nullopt; + return Config::ConsoleType == 0 ? std::make_unique(bios_arm9_bin) : nullptr; } if (FileHandle* f = OpenLocalFile(Config::BIOS9Path, Read)) { - std::array bios {}; + std::unique_ptr bios = std::make_unique(); FileRewind(f); - FileRead(bios.data(), sizeof(bios), 1, f); + FileRead(bios->data(), bios->size(), 1, f); CloseFile(f); Log(Info, "ARM9 BIOS loaded from %s\n", Config::BIOS9Path.c_str()); return bios; } Log(Warn, "ARM9 BIOS not found\n"); - return std::nullopt; + return nullptr; } -std::optional> LoadARM7BIOS() noexcept +std::unique_ptr LoadARM7BIOS() noexcept { if (!Config::ExternalBIOSEnable) { - return Config::ConsoleType == 0 ? std::make_optional(bios_arm7_bin) : std::nullopt; + return Config::ConsoleType == 0 ? std::make_unique(bios_arm7_bin) : nullptr; } if (FileHandle* f = OpenLocalFile(Config::BIOS7Path, Read)) { - std::array bios {}; - FileRead(bios.data(), sizeof(bios), 1, f); + std::unique_ptr bios = std::make_unique(); + FileRead(bios->data(), bios->size(), 1, f); CloseFile(f); Log(Info, "ARM7 BIOS loaded from %s\n", Config::BIOS7Path.c_str()); return bios; } Log(Warn, "ARM7 BIOS not found\n"); - return std::nullopt; + return nullptr; } -std::optional> LoadDSiARM9BIOS() noexcept +std::unique_ptr LoadDSiARM9BIOS() noexcept { if (FileHandle* f = OpenLocalFile(Config::DSiBIOS9Path, Read)) { - std::array bios {}; - FileRead(bios.data(), sizeof(bios), 1, f); + std::unique_ptr bios = std::make_unique(); + FileRead(bios->data(), bios->size(), 1, f); CloseFile(f); if (!Config::DSiFullBIOSBoot) { // herp - *(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector + *(u32*)bios->data() = 0xEAFFFFFE; // overwrites the reset vector // TODO!!!! // hax the upper 32K out of the goddamn DSi @@ -575,21 +575,21 @@ std::optional> LoadDSiARM9BIOS() noexcept } Log(Warn, "ARM9i BIOS not found\n"); - return std::nullopt; + return nullptr; } -std::optional> LoadDSiARM7BIOS() noexcept +std::unique_ptr LoadDSiARM7BIOS() noexcept { if (FileHandle* f = OpenLocalFile(Config::DSiBIOS7Path, Read)) { - std::array bios {}; - FileRead(bios.data(), sizeof(bios), 1, f); + std::unique_ptr bios = std::make_unique(); + FileRead(bios->data(), bios->size(), 1, f); CloseFile(f); if (!Config::DSiFullBIOSBoot) { // herp - *(u32*)&bios[0] = 0xEAFFFFFE; // overwrites the reset vector + *(u32*)bios->data() = 0xEAFFFFFE; // overwrites the reset vector // TODO!!!! // hax the upper 32K out of the goddamn DSi @@ -600,7 +600,7 @@ std::optional> LoadDSiARM7BIOS() noexcept } Log(Warn, "ARM7i BIOS not found\n"); - return std::nullopt; + return nullptr; } Firmware GenerateFirmware(int type) noexcept diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 38ed65cd..6d0b81d3 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -26,6 +26,8 @@ #include #include "MemConstants.h" + +#include #include #include #include @@ -56,11 +58,11 @@ void ClearBackupState(); /// Returns the configured ARM9 BIOS loaded from disk, /// the FreeBIOS if external BIOS is disabled and we're in NDS mode, -/// or nullopt if loading failed. -std::optional> LoadARM9BIOS() noexcept; -std::optional> LoadARM7BIOS() noexcept; -std::optional> LoadDSiARM9BIOS() noexcept; -std::optional> LoadDSiARM7BIOS() noexcept; +/// or nullptr if loading failed. +std::unique_ptr LoadARM9BIOS() noexcept; +std::unique_ptr LoadARM7BIOS() noexcept; +std::unique_ptr LoadDSiARM9BIOS() noexcept; +std::unique_ptr LoadDSiARM7BIOS() noexcept; std::optional GetDSiSDCardArgs() noexcept; std::optional LoadDSiSDCard() noexcept; std::optional GetDLDISDCardArgs() noexcept; From 6e26559cd2a225437072a67b4a55f7fd5831057d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Wed, 3 Apr 2024 14:49:27 +0200 Subject: [PATCH 142/157] ci: fix macOS build GitHub Actions' macOS runners have Python from homebrew installed and it's used by default instead of the Python that ships with macOS. Apparently Homebrew decided you shouldn't be able to install stuff with `pip3` anymore so our build broke since `setuptools` is no longer included by default and `glib` from vcpkg needs it to build. Additionally,, the whole liblzma mess ended up breaking our builds too because libarchive (and its dependency libxml2) depends on it and the download is no longer available. The build option changes here should be reverted once this is sorted out because this is probably partially breaking archive support. PS: Fuck you Jia Tan. --- .github/workflows/build-macos.yml | 2 +- vcpkg.json | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index 1d4b9171..4178157d 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -21,7 +21,7 @@ jobs: uses: actions/checkout@v3 - name: Install dependencies for package building run: | - brew install autoconf automake autoconf-archive libtool && pip3 install setuptools + brew install autoconf automake autoconf-archive libtool python-setuptools - name: Set up CMake uses: lukka/get-cmake@latest - name: Set up vcpkg diff --git a/vcpkg.json b/vcpkg.json index a1bd0be5..3e23ff1b 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,7 +1,11 @@ { "dependencies": [ "sdl2", - "libarchive", + { + "name": "libarchive", + "default-features": false, + "features": ["bzip2", "crypto", "lz4", "zstd"] + }, "libslirp", "zstd", { From 968bd26d854f71faf19d9de3ba454e62f1f13f58 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 9 Apr 2024 11:38:38 +0200 Subject: [PATCH 143/157] fix generation of instance-unique MAC address when using an external firmware --- src/frontend/qt_sdl/ROMManager.cpp | 114 +++++++++++++++-------------- src/frontend/qt_sdl/ROMManager.h | 2 +- 2 files changed, 60 insertions(+), 56 deletions(-) diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index 65508467..bff9e4ca 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -642,7 +642,7 @@ Firmware GenerateFirmware(int type) noexcept } } - CustomizeFirmware(firmware); + CustomizeFirmware(firmware, true); // If we don't have Wi-fi settings to load, // then the defaults will have already been populated by the constructor. @@ -681,10 +681,7 @@ std::optional LoadFirmware(int type) noexcept return std::nullopt; } - if (Config::FirmwareOverrideSettings) - { - CustomizeFirmware(firmware); - } + CustomizeFirmware(firmware, Config::FirmwareOverrideSettings); return firmware; } @@ -1120,54 +1117,59 @@ bool ParseMacAddress(void* data) return false; } -void CustomizeFirmware(Firmware& firmware) noexcept +void CustomizeFirmware(Firmware& firmware, bool overridesettings) noexcept { - auto& currentData = firmware.GetEffectiveUserData(); - - // setting up username - std::string orig_username = Config::FirmwareUsername; - if (!orig_username.empty()) - { // If the frontend defines a username, take it. If not, leave the existing one. - std::u16string username = std::wstring_convert, 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(Config::FirmwareLanguage); - if (language != Firmware::Language::Reserved) - { // If the frontend specifies a language (rather than using the existing value)... - currentData.Settings &= ~Firmware::Language::Reserved; // ..clear the existing language... - currentData.Settings |= language; // ...and set the new one. - } - - // setting up color - u8 favoritecolor = Config::FirmwareFavouriteColour; - if (favoritecolor != 0xFF) + if (overridesettings) { - currentData.FavoriteColor = favoritecolor; - } + auto ¤tData = firmware.GetEffectiveUserData(); - u8 birthmonth = Config::FirmwareBirthdayMonth; - if (birthmonth != 0) - { // If the frontend specifies a birth month (rather than using the existing value)... - currentData.BirthdayMonth = birthmonth; - } + // setting up username + std::string orig_username = Config::FirmwareUsername; + if (!orig_username.empty()) + { // If the frontend defines a username, take it. If not, leave the existing one. + std::u16string username = std::wstring_convert, 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)); + } - u8 birthday = Config::FirmwareBirthdayDay; - if (birthday != 0) - { // If the frontend specifies a birthday (rather than using the existing value)... - currentData.BirthdayDay = birthday; - } + auto language = static_cast(Config::FirmwareLanguage); + if (language != Firmware::Language::Reserved) + { // If the frontend specifies a language (rather than using the existing value)... + currentData.Settings &= ~Firmware::Language::Reserved; // ..clear the existing language... + currentData.Settings |= language; // ...and set the new one. + } - // setup message - std::string orig_message = Config::FirmwareMessage; - if (!orig_message.empty()) - { - std::u16string message = std::wstring_convert, 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)); + // setting up color + u8 favoritecolor = Config::FirmwareFavouriteColour; + if (favoritecolor != 0xFF) + { + currentData.FavoriteColor = favoritecolor; + } + + u8 birthmonth = Config::FirmwareBirthdayMonth; + if (birthmonth != 0) + { // If the frontend specifies a birth month (rather than using the existing value)... + currentData.BirthdayMonth = birthmonth; + } + + u8 birthday = Config::FirmwareBirthdayDay; + if (birthday != 0) + { // If the frontend specifies a birthday (rather than using the existing value)... + currentData.BirthdayDay = birthday; + } + + // setup message + std::string orig_message = Config::FirmwareMessage; + if (!orig_message.empty()) + { + std::u16string message = std::wstring_convert, 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; @@ -1176,14 +1178,16 @@ void CustomizeFirmware(Firmware& firmware) noexcept memcpy(&mac, header.MacAddr.data(), sizeof(MacAddress)); - - MacAddress configuredMac; - rep = ParseMacAddress(&configuredMac); - rep &= (configuredMac != MacAddress()); - - if (rep) + if (overridesettings) { - mac = configuredMac; + MacAddress configuredMac; + rep = ParseMacAddress(&configuredMac); + rep &= (configuredMac != MacAddress()); + + if (rep) + { + mac = configuredMac; + } } int inst = Platform::InstanceID(); diff --git a/src/frontend/qt_sdl/ROMManager.h b/src/frontend/qt_sdl/ROMManager.h index 6d0b81d3..ae854617 100644 --- a/src/frontend/qt_sdl/ROMManager.h +++ b/src/frontend/qt_sdl/ROMManager.h @@ -67,7 +67,7 @@ std::optional GetDSiSDCardArgs() noexcept; std::optional LoadDSiSDCard() noexcept; std::optional GetDLDISDCardArgs() noexcept; std::optional LoadDLDISDCard() noexcept; -void CustomizeFirmware(Firmware& firmware) noexcept; +void CustomizeFirmware(Firmware& firmware, bool overridesettings) noexcept; Firmware GenerateFirmware(int type) noexcept; /// Loads and customizes a firmware image based on the values in Config std::optional LoadFirmware(int type) noexcept; From 0b87dd5fa6aa70277d07a2af301db843be9e9a01 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Tue, 9 Apr 2024 12:54:31 +0200 Subject: [PATCH 144/157] fix touchscreen bug on Wayland --- src/frontend/qt_sdl/Screen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/qt_sdl/Screen.cpp b/src/frontend/qt_sdl/Screen.cpp index cfcbeed9..73236504 100644 --- a/src/frontend/qt_sdl/Screen.cpp +++ b/src/frontend/qt_sdl/Screen.cpp @@ -214,7 +214,7 @@ void ScreenPanel::mouseMoveEvent(QMouseEvent* event) showCursor(); - if (!(event->buttons() & Qt::LeftButton)) return; + //if (!(event->buttons() & Qt::LeftButton)) return; if (!touching) return; int x = event->pos().x(); From 111dc7a563dc0050939f794445574a342836bdde Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 12 Apr 2024 17:28:51 +0200 Subject: [PATCH 145/157] wifi improvements: * implement channels * rework power-down support, fixing bugs * fix bug when W_BeaconInterval is zero * fix potential missing IRQs when writing to W_IE --- src/NDS.cpp | 2 +- src/Wifi.cpp | 449 ++++++++++++++++++++++++--------- src/Wifi.h | 14 +- src/WifiAP.cpp | 10 +- src/WifiAP.h | 1 + src/frontend/qt_sdl/Window.cpp | 2 +- 6 files changed, 344 insertions(+), 134 deletions(-) diff --git a/src/NDS.cpp b/src/NDS.cpp index 284f6eb0..68227ea5 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -1845,7 +1845,7 @@ void NDS::debug(u32 param) //for (int i = 0; i < 9; i++) // printf("VRAM %c: %02X\n", 'A'+i, GPU->VRAMCNT[i]); - Platform::FileHandle* shit = Platform::OpenFile("debug/DSfirmware.bin", FileMode::Write); + Platform::FileHandle* shit = Platform::OpenFile("debug/pokeplat.bin", FileMode::Write); Platform::FileWrite(ARM9.ITCM, 0x8000, 1, shit); for (u32 i = 0x02000000; i < 0x02400000; i+=4) { diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 4da253ef..12926c6f 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -159,11 +159,45 @@ void Wifi::Reset() #undef BBREG_FIXED const Firmware& fw = NDS.SPI.GetFirmware(); + const auto& fwheader = fw.GetHeader(); - RFVersion = fw.GetHeader().RFChipType; + RFVersion = fwheader.RFChipType; memset(RFRegs, 0, 4*0x40); - Firmware::FirmwareConsoleType console = fw.GetHeader().ConsoleType; + // load channel index/data from the firmware + // the current channel will be determined by RF settings + // so we compare the two 'most important' RF registers to these values to figure out which channel is selected + + if (RFVersion == 3) + { + RFChannelIndex[0] = fwheader.Type3Config.RFIndex1; + RFChannelIndex[1] = fwheader.Type3Config.RFIndex2; + + for (int i = 0; i < 14; i++) + { + RFChannelData[i][0] = fwheader.Type3Config.RFData1[i]; + RFChannelData[i][1] = fwheader.Type3Config.RFData2[i]; + } + } + else + { + RFChannelIndex[0] = fwheader.Type2Config.InitialRF56Values[2] >> 2; + RFChannelIndex[1] = fwheader.Type2Config.InitialRF56Values[5] >> 2; + + for (int i = 0; i < 14; i++) + { + RFChannelData[i][0] = fwheader.Type2Config.InitialRF56Values[i*6 + 0] | + (fwheader.Type2Config.InitialRF56Values[i*6 + 1] << 8) | + ((fwheader.Type2Config.InitialRF56Values[i*6 + 2] & 0x03) << 16); + RFChannelData[i][1] = fwheader.Type2Config.InitialRF56Values[i*6 + 3] | + (fwheader.Type2Config.InitialRF56Values[i*6 + 4] << 8) | + ((fwheader.Type2Config.InitialRF56Values[i*6 + 5] & 0x03) << 16); + } + } + + CurChannel = 0; + + Firmware::FirmwareConsoleType console = fwheader.ConsoleType; if (console == Firmware::FirmwareConsoleType::DS) IOPORT(0x000) = 0x1440; else if (console == Firmware::FirmwareConsoleType::DSLite) @@ -182,6 +216,8 @@ void Wifi::Reset() // TODO: find out what the initial values are IOPORT(W_PowerUS) = 0x0001; + //IOPORT(W_BeaconInterval) = 100; + USTimestamp = 0; USCounter = 0; @@ -209,7 +245,6 @@ void Wifi::Reset() CmdCounter = 0; USUntilPowerOn = 0; - ForcePowerOn = false; IsMP = false; IsMPClient = false; @@ -244,6 +279,8 @@ void Wifi::DoSavestate(Savestate* file) file->Var8(&RFVersion); file->VarArray(RFRegs, 4*0x40); + file->Var32((u32*)&CurChannel); + file->Var64(&USCounter); file->Var64(&USCompare); file->Bool32(&BlockBeaconIRQ14); @@ -285,7 +322,6 @@ void Wifi::DoSavestate(Savestate* file) file->Var16(&MPLastSeqno); file->Var32((u32*)&USUntilPowerOn); - file->Bool32(&ForcePowerOn); file->Bool32(&IsMP); file->Bool32(&IsMPClient); @@ -351,33 +387,38 @@ void Wifi::SetPowerCnt(u32 val) } -void Wifi::SetIRQ(u32 irq) +void Wifi::CheckIRQ(u16 oldflags) { - u32 oldflags = IOPORT(W_IF) & IOPORT(W_IE); - - IOPORT(W_IF) |= (1<CurPhase = 13; slot->CurPhaseTime = CmdCounter - 100; } + + // starting a CMD transfer wakes up the transceiver automatically + UpdatePowerStatus(1); } void Wifi::StartTX_Beacon() @@ -678,6 +819,9 @@ void Wifi::SendMPDefaultReply() // TODO reply[0x8] = 0x14; + if (CurChannel == 0) return; + reply[0x9] = CurChannel; + *(u16*)&reply[0xC + 0x00] = 0x0158; *(u16*)&reply[0xC + 0x02] = 0x00F0;//0; // TODO?? *(u16*)&reply[0xC + 0x04] = IOPORT(W_BSSID0); @@ -767,6 +911,9 @@ void Wifi::SendMPAck(u16 cmdcount, u16 clientfail) if (TXSlots[1].Rate == 2) ack[0x8] = 0x14; else ack[0x8] = 0xA; + if (CurChannel == 0) return; + ack[0x9] = CurChannel; + *(u16*)&ack[0xC + 0x00] = 0x0218; *(u16*)&ack[0xC + 0x02] = 0; *(u16*)&ack[0xC + 0x04] = 0x0903; @@ -1110,8 +1257,11 @@ void Wifi::FinishRX() if (!ComStatus) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) + { + IOPORT(W_TRXPower) = 0; SetStatus(9); + } else SetStatus(1); } @@ -1380,7 +1530,7 @@ void Wifi::FinishRX() void Wifi::MPClientReplyRX(int client) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) return; if (!(IOPORT(W_RXCnt) & 0x8000)) @@ -1421,7 +1571,7 @@ void Wifi::MPClientReplyRX(int client) bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) return false; if (!(IOPORT(W_RXCnt) & 0x8000)) @@ -1433,7 +1583,7 @@ bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames int rxlen; int framelen; u16 framectl; - u8 txrate; + u8 txrate, chan; u64 timestamp; for (;;) @@ -1468,6 +1618,13 @@ bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames continue; } + chan = RXBuffer[9]; + if (chan != CurChannel || CurChannel == 0) + { + Log(LogLevel::Debug, "received frame but bad channel %d (expected %d)\n", chan, CurChannel); + continue; + } + framectl = *(u16*)&RXBuffer[12+0]; txrate = RXBuffer[8]; @@ -1528,7 +1685,6 @@ bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames // we also need to determine how far we can run after having received this frame RXTimestamp = timestamp; - //if (RXTimestamp < USTimestamp) printf("CRAP!! %04X %016llX %016llX\n", framectl, RXTimestamp, USTimestamp); if (RXTimestamp < USTimestamp) RXTimestamp = USTimestamp; NextSync = RXTimestamp + (framelen * (txrate==0x14 ? 4:8)); @@ -1570,11 +1726,13 @@ void Wifi::MSTimer() } } - IOPORT(W_BeaconCount1)--; - if (IOPORT(W_BeaconCount1) == 0) + if (IOPORT(W_BeaconCount1) != 0) { - SetIRQ14(1); + IOPORT(W_BeaconCount1)--; + if (IOPORT(W_BeaconCount1) == 0) SetIRQ14(1); } + if (IOPORT(W_BeaconCount1) == 0) + IOPORT(W_BeaconCount1) = IOPORT(W_BeaconInterval); if (IOPORT(W_BeaconCount2) != 0) { @@ -1605,19 +1763,19 @@ void Wifi::USTimer(u32 param) if (!(USTimestamp & 0x3FF & kTimeCheckMask)) WifiAP->MSTimer(); - bool switchOffPowerSaving = false; if (USUntilPowerOn < 0) { USUntilPowerOn += kTimerInterval; - switchOffPowerSaving = (USUntilPowerOn >= 0) && (IOPORT(W_PowerUnk) & 0x0001 || ForcePowerOn); - } - if ((USUntilPowerOn >= 0) && (IOPORT(W_PowerState) & 0x0002 || switchOffPowerSaving)) - { - IOPORT(W_PowerState) = 0; - IOPORT(W_RFPins) = 1; - IOPORT(W_RFPins) = 0x0084; - SetIRQ(11); + if (USUntilPowerOn >= 0) + { + USUntilPowerOn = 0; + + IOPORT(W_PowerState) = 0; + SetStatus(1); + + UpdatePowerStatus(0); + } } if (IOPORT(W_USCountCnt)) @@ -1660,7 +1818,7 @@ void Wifi::USTimer(u32 param) u16 txbusy = IOPORT(W_TXBusy); if (txbusy) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) { ComStatus = 0; TXCurSlot = -1; @@ -1695,9 +1853,10 @@ void Wifi::USTimer(u32 param) bool finished = ProcessTX(&TXSlots[TXCurSlot], TXCurSlot); if (finished) { - if (IOPORT(W_PowerState) & 0x0300) + if (IOPORT(W_PowerState) & (1<<9)) { IOPORT(W_TXBusy) = 0; + IOPORT(W_TRXPower) = 0; SetStatus(9); } @@ -1754,8 +1913,9 @@ void Wifi::USTimer(u32 param) RXCounter = 0; } // TODO: proper error management - if ((!ComStatus) && (IOPORT(W_PowerState) & 0x0300)) + if ((!ComStatus) && (IOPORT(W_PowerState) & (1<<9))) { + IOPORT(W_TRXPower) = 0; SetStatus(9); } } @@ -1766,6 +1926,28 @@ void Wifi::USTimer(u32 param) } +void Wifi::ChangeChannel() +{ + u32 val1 = RFRegs[RFChannelIndex[0]]; + u32 val2 = RFRegs[RFChannelIndex[1]]; + + CurChannel = 0; + + for (int i = 0; i < 14; i++) + { + if (val1 == RFChannelData[i][0] && val2 == RFChannelData[i][1]) + { + CurChannel = i+1; + break; + } + } + + if (CurChannel > 0) + Log(LogLevel::Debug, "wifi: switching to channel %d\n", CurChannel); + else + Log(LogLevel::Debug, "wifi: invalid channel values %05X:%05X\n", val1, val2); +} + void Wifi::RFTransfer_Type2() { u32 id = (IOPORT(W_RFData2) >> 2) & 0x1F; @@ -1780,6 +1962,9 @@ void Wifi::RFTransfer_Type2() { u32 data = IOPORT(W_RFData1) | ((IOPORT(W_RFData2) & 0x0003) << 16); RFRegs[id] = data; + + if (id == RFChannelIndex[0] || id == RFChannelIndex[1]) + ChangeChannel(); } } @@ -1796,6 +1981,9 @@ void Wifi::RFTransfer_Type3() { u32 data = IOPORT(W_RFData1) & 0xFF; RFRegs[id] = data; + + if (id == RFChannelIndex[0] || id == RFChannelIndex[1]) + ChangeChannel(); } } @@ -1819,6 +2007,7 @@ u16 Wifi::Read(u32 addr) switch (addr) { case W_Random: // random generator. not accurate + // TODO: rotate the sequence based on the ARM7 cycle counter (if this is important) Random = (Random & 0x1) ^ (((Random & 0x3FF) << 1) | (Random >> 10)); return Random; @@ -1899,7 +2088,6 @@ u16 Wifi::Read(u32 addr) } } - //printf("WIFI: read %08X\n", addr); return IOPORT(addr&0xFFF); } @@ -1923,28 +2111,20 @@ void Wifi::Write(u32 addr, u16 val) case W_ModeReset: { u16 oldval = IOPORT(W_ModeReset); + IOPORT(W_ModeReset) = val & 0x0001; if (!(oldval & 0x0001) && (val & 0x0001)) { - if (!(USUntilPowerOn < 0 && ForcePowerOn)) - { - //printf("mode reset power on %08x\n", NDS::ARM7->R[15]); - IOPORT(0x034) = 0x0002; - IOPORT(0x27C) = 0x0005; - // TODO: 02A2?? + IOPORT(0x27C) = 0x0005; + // TODO: 02A2?? - if (IOPORT(W_PowerUnk) & 0x0002) - { - USUntilPowerOn = -2048; - IOPORT(W_PowerState) |= 0x100; - } - } + UpdatePowerStatus(0); } else if ((oldval & 0x0001) && !(val & 0x0001)) { - //printf("mode reset shutdown %08x\n", NDS::ARM7->R[15]); IOPORT(0x27C) = 0x000A; - PowerDown(); + + UpdatePowerStatus(0); } if (val & 0x2000) @@ -1986,23 +2166,43 @@ void Wifi::Write(u32 addr, u16 val) IOPORT(0x230) = 0x0047; } } - break; + return; case W_ModeWEP: val &= 0x007F; - //printf("writing mode web %x\n", val); - if ((val & 0x7) == 1) - IOPORT(W_PowerUnk) |= 0x0002; - if ((val & 0x7) == 2) - IOPORT(W_PowerUnk) = 0x0003; - break; + IOPORT(W_ModeWEP) = val; + if (IOPORT(W_PowerTX) & (1<<1)) + { + if ((val & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((val & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; + + if ((val & 0x7) != 3) + IOPORT(W_PowerState) &= 0x0300; + + UpdatePowerStatus(0); + } + return; + + case W_IE: + { + u16 oldflags = IOPORT(W_IF) & IOPORT(W_IE); + IOPORT(W_IE) = val; + CheckIRQ(oldflags); + } + return; case W_IF: IOPORT(W_IF) &= ~val; return; case W_IFSet: - IOPORT(W_IF) |= (val & 0xFBFF); - Log(LogLevel::Debug, "wifi: force-setting IF %04X\n", val); + { + u16 oldflags = IOPORT(W_IF) & IOPORT(W_IE); + IOPORT(W_IF) |= (val & 0xFBFF); + CheckIRQ(oldflags); + Log(LogLevel::Debug, "wifi: force-setting IF %04X\n", val); + } return; case W_AIDLow: @@ -2012,67 +2212,63 @@ void Wifi::Write(u32 addr, u16 val) IOPORT(W_AIDFull) = val & 0x07FF; return; - case W_PowerState: - //printf("writing power state %x %08x\n", val, NDS::ARM7->R[15]); - IOPORT(W_PowerState) |= val & 0x0002; - - if (IOPORT(W_ModeReset) & 0x0001 && IOPORT(W_PowerState) & 0x0002) - { - /*if (IOPORT(W_PowerState) & 0x100) - { - AlwaysPowerOn = true; - USUntilPowerOn = -1; - } - else */ - if (IOPORT(W_PowerForce) == 1) - { - //printf("power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = false; - } - } - return; - case W_PowerForce: - //if ((val&0x8001)==0x8000) printf("WIFI: forcing power %04X\n", val); - - val &= 0x8001; - //printf("writing power force %x %08x\n", val, NDS::ARM7->R[15]); - if (val == 0x8001) - { - //printf("force power off\n"); - IOPORT(0x034) = 0x0002; - IOPORT(W_PowerState) = 0x0200; - IOPORT(W_TXReqRead) = 0; - PowerDown(); - } - if (val == 1 && IOPORT(W_PowerState) & 0x0002) - { - //printf("power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = false; - } - if (val == 0x8000) - { - //printf("force power on\n"); - IOPORT(W_PowerState) |= 0x100; - USUntilPowerOn = -2048; - ForcePowerOn = true; - } - break; case W_PowerUS: IOPORT(W_PowerUS) = val & 0x0003; UpdatePowerOn(); return; - case W_PowerUnk: - val &= 0x0003; - //printf("writing power unk %x\n", val); - if ((IOPORT(W_ModeWEP) & 0x7) == 1) - val |= 2; - else if ((IOPORT(W_ModeWEP) & 0x7) == 2) - val = 3; - break; + + case W_PowerTX: + IOPORT(W_PowerTX) = val & 0x0003; + if (val & (1<<1)) + { + if ((IOPORT(W_ModeWEP) & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((IOPORT(W_ModeWEP) & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; + + UpdatePowerStatus(0); + } + return; + + case W_PowerState: + if ((IOPORT(W_ModeWEP) & 0x7) != 3) + return; + + val = (IOPORT(W_PowerState) & 0x0300) | (val & 0x0003); + if ((val & 0x0300) == 0x0200) + val &= ~(1<<0); + else + val &= ~(1<<1); + + if (!(val & (1<<9))) + val &= ~(1<<8); + + IOPORT(W_PowerState) = val; + UpdatePowerStatus(0); + return; + + case W_PowerForce: + val &= 0x8001; + IOPORT(W_PowerForce) = val; + UpdatePowerStatus(0); + return; + + case W_PowerDownCtrl: + IOPORT(W_PowerDownCtrl) = val & 0x0003; + + if (IOPORT(W_PowerTX) & (1<<1)) + { + if ((IOPORT(W_ModeWEP) & 0x7) == 1) + IOPORT(W_PowerDownCtrl) |= (1<<1); + else if ((IOPORT(W_ModeWEP) & 0x7) == 2) + IOPORT(W_PowerDownCtrl) = 3; + } + + if (val != 0 && val != 3) + Log(LogLevel::Warn, "wifi: unusual W_PowerDownCtrl value %04X\n", val); + + UpdatePowerStatus(0); + return; case W_USCountCnt: val &= 0x0001; break; case W_USCompareCnt: @@ -2231,6 +2427,7 @@ void Wifi::Write(u32 addr, u16 val) // read-only ports case 0x000: + case 0x034: case 0x044: case 0x054: case 0x098: diff --git a/src/Wifi.h b/src/Wifi.h index 5553a6f5..2e0465a6 100644 --- a/src/Wifi.h +++ b/src/Wifi.h @@ -52,11 +52,12 @@ public: W_RXCnt = 0x030, W_WEPCnt = 0x032, + W_TRXPower = 0x034, W_PowerUS = 0x036, W_PowerTX = 0x038, W_PowerState = 0x03C, W_PowerForce = 0x040, - W_PowerUnk = 0x48, + W_PowerDownCtrl = 0x48, W_Random = 0x044, @@ -206,6 +207,10 @@ private: u8 RFVersion; u32 RFRegs[0x40]; + u32 RFChannelIndex[2]; + u32 RFChannelData[14][2]; + int CurChannel; + struct TXSlot { bool Valid; @@ -240,7 +245,6 @@ private: bool LANInited; int USUntilPowerOn; - bool ForcePowerOn; // MULTIPLAYER SYNC APPARATUS bool IsMP; @@ -253,13 +257,15 @@ private: void ScheduleTimer(bool first); void UpdatePowerOn(); + void CheckIRQ(u16 oldflags); void SetIRQ(u32 irq); void SetIRQ13(); void SetIRQ14(int source); void SetIRQ15(); void SetStatus(u32 status); - void PowerDown(); + + void UpdatePowerStatus(int power); int PreambleLen(int rate) const; u32 NumClients(u16 bitmask) const; @@ -284,6 +290,8 @@ private: void MSTimer(); + void ChangeChannel(); + void RFTransfer_Type2(); void RFTransfer_Type3(); }; diff --git a/src/WifiAP.cpp b/src/WifiAP.cpp index 4c645203..855dc244 100644 --- a/src/WifiAP.cpp +++ b/src/WifiAP.cpp @@ -35,6 +35,7 @@ using Platform::LogLevel; const char* WifiAP::APName = "melonAP"; const u8 WifiAP::APMac[6] = {0x00, 0xF0, 0x77, 0x77, 0x77, 0x77}; +const u8 WifiAP::APChannel = 6; #define PWRITE_8(p, v) *p++ = v; #define PWRITE_16(p, v) *(u16*)p = v; p += 2; @@ -55,7 +56,7 @@ const u8 WifiAP::APMac[6] = {0x00, 0xF0, 0x77, 0x77, 0x77, 0x77}; PWRITE_16(p, 0); \ PWRITE_16(p, 0); \ PWRITE_8(p, rate); \ - PWRITE_8(p, 0); \ + PWRITE_8(p, APChannel); \ PWRITE_16(p, len); //#define PALIGN_4(p, base) p += ((4 - ((ptrdiff_t)(p-base) & 0x3)) & 0x3); @@ -174,7 +175,7 @@ int WifiAP::HandleManagementFrame(const u8* data, int len) PWRITE_16(p, 128); // beacon interval PWRITE_16(p, 0x0021); // capability PWRITE_8(p, 0x01); PWRITE_8(p, 0x02); PWRITE_8(p, 0x82); PWRITE_8(p, 0x84); // rates - PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, 0x06); // current channel + PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, APChannel); // current channel PWRITE_8(p, 0x00); PWRITE_8(p, strlen(APName)); memcpy(p, APName, strlen(APName)); p += strlen(APName); @@ -260,6 +261,9 @@ int WifiAP::HandleManagementFrame(const u8* data, int len) int WifiAP::SendPacket(const u8* data, int len) { + if (data[9] != APChannel) + return 0; + data += 12; u16 framectl = *(u16*)&data[0]; @@ -327,7 +331,7 @@ int WifiAP::RecvPacket(u8* data) PWRITE_16(p, 128); // beacon interval PWRITE_16(p, 0x0021); // capability PWRITE_8(p, 0x01); PWRITE_8(p, 0x02); PWRITE_8(p, 0x82); PWRITE_8(p, 0x84); // rates - PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, 0x06); // current channel + PWRITE_8(p, 0x03); PWRITE_8(p, 0x01); PWRITE_8(p, APChannel); // current channel PWRITE_8(p, 0x05); PWRITE_8(p, 0x04); PWRITE_8(p, 0); PWRITE_8(p, 0); PWRITE_8(p, 0); PWRITE_8(p, 0); // TIM PWRITE_8(p, 0x00); PWRITE_8(p, strlen(APName)); memcpy(p, APName, strlen(APName)); p += strlen(APName); diff --git a/src/WifiAP.h b/src/WifiAP.h index 8f3ed111..a9e80c3b 100644 --- a/src/WifiAP.h +++ b/src/WifiAP.h @@ -34,6 +34,7 @@ public: static const char* APName; static const u8 APMac[6]; + static const u8 APChannel; void MSTimer(); diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index fde29b69..23ba784e 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -805,7 +805,7 @@ void MainWindow::keyPressEvent(QKeyEvent* event) if (event->isAutoRepeat()) return; // TODO!! REMOVE ME IN RELEASE BUILDS!! - //if (event->key() == Qt::Key_F11) NDS::debug(0); + //if (event->key() == Qt::Key_F11) emuThread->NDS->debug(0); Input::KeyPress(event); } From d99c571f947ac69890fc60c4525122d1394c7338 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 12 Apr 2024 19:43:02 +0200 Subject: [PATCH 146/157] FATStorage: make sure to always properly unmount the volume (fixes evil bug) --- src/FATStorage.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/FATStorage.cpp b/src/FATStorage.cpp index 0f1bf235..f735d4b8 100644 --- a/src/FATStorage.cpp +++ b/src/FATStorage.cpp @@ -110,6 +110,7 @@ bool FATStorage::InjectFile(const std::string& path, u8* data, u32 len) res = f_mount(&fs, "0:", 1); if (res != FR_OK) { + f_unmount("0:"); ff_disk_close(); return false; } @@ -146,6 +147,7 @@ u32 FATStorage::ReadFile(const std::string& path, u32 start, u32 len, u8* data) res = f_mount(&fs, "0:", 1); if (res != FR_OK) { + f_unmount("0:"); ff_disk_close(); return false; } @@ -1144,6 +1146,7 @@ bool FATStorage::Save() res = f_mount(&fs, "0:", 1); if (res != FR_OK) { + f_unmount("0:"); ff_disk_close(); return false; } From 8feeee61037b23fd0cb22d2f035f6ebfa0c76e32 Mon Sep 17 00:00:00 2001 From: Arisotura Date: Fri, 12 Apr 2024 20:02:16 +0200 Subject: [PATCH 147/157] Input: only check joystick input if a joystick actually exists --- src/frontend/qt_sdl/Input.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/frontend/qt_sdl/Input.cpp b/src/frontend/qt_sdl/Input.cpp index 27c42e9a..7ebd7e2a 100644 --- a/src/frontend/qt_sdl/Input.cpp +++ b/src/frontend/qt_sdl/Input.cpp @@ -209,16 +209,22 @@ void Process() } JoyInputMask = 0xFFF; - for (int i = 0; i < 12; i++) - if (JoystickButtonDown(Config::JoyMapping[i])) - JoyInputMask &= ~(1< Date: Sat, 13 Apr 2024 12:17:16 +0200 Subject: [PATCH 148/157] wifi: try ignoring MP frames if not engaging in MP comm --- src/Wifi.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Wifi.cpp b/src/Wifi.cpp index 12926c6f..d8f440b4 100644 --- a/src/Wifi.cpp +++ b/src/Wifi.cpp @@ -1625,6 +1625,17 @@ bool Wifi::CheckRX(int type) // 0=regular 1=MP replies 2=MP host frames continue; } + // hack: ignore MP frames if not engaged in a MP comm + if (type == 0 && (!IsMP)) + { + if (MACEqual(&RXBuffer[12 + 16], MPReplyMAC) || + MACEqual(&RXBuffer[12 + 4], MPCmdMAC) || + MACEqual(&RXBuffer[12 + 4], MPReplyMAC)) + { + continue; + } + } + framectl = *(u16*)&RXBuffer[12+0]; txrate = RXBuffer[8]; From 5a852cb00d56342f7e013d0ca6e14b10a9c4b84c Mon Sep 17 00:00:00 2001 From: Rayyan Ansari Date: Tue, 16 Apr 2024 23:06:44 +0100 Subject: [PATCH 149/157] ROMManager: optimise ROMIcon function Precompute all 16 5-bit RGB palette colours into 8-bit RGBA to avoid repeated and superfluous calculation within the nested loop at the point of index lookup. A speedup was observed, from ~7ms, to a consistent 1ms (i.e. now practically instantaneous) through timing with std::chrono::high_resolution_clock. Also improve comprehensibility, by using meaningful names, where appropriate, for loop counter variables. --- src/frontend/qt_sdl/ROMManager.cpp | 31 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index bff9e4ca..a2b1ab75 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -1572,23 +1572,28 @@ QString GBACartLabel() void ROMIcon(const u8 (&data)[512], const u16 (&palette)[16], u32 (&iconRef)[32*32]) { - int index = 0; - for (int i = 0; i < 4; i++) + u32 paletteRGBA[16]; + for (int i = 0; i < 16; i++) { - for (int j = 0; j < 4; j++) + u8 r = ((palette[i] >> 0) & 0x1F) * 255 / 31; + u8 g = ((palette[i] >> 5) & 0x1F) * 255 / 31; + u8 b = ((palette[i] >> 10) & 0x1F) * 255 / 31; + u8 a = i ? 255 : 0; + paletteRGBA[i] = r | (g << 8) | (b << 16) | (a << 24); + } + + int count = 0; + for (int ytile = 0; ytile < 4; ytile++) + { + for (int xtile = 0; xtile < 4; xtile++) { - for (int k = 0; k < 8; k++) + for (int ypixel = 0; ypixel < 8; ypixel++) { - for (int l = 0; l < 8; l++) + for (int xpixel = 0; xpixel < 8; xpixel++) { - u8 pal_index = index % 2 ? data[index/2] >> 4 : data[index/2] & 0x0F; - u8 r = ((palette[pal_index] >> 0) & 0x1F) * 255 / 31; - u8 g = ((palette[pal_index] >> 5) & 0x1F) * 255 / 31; - u8 b = ((palette[pal_index] >> 10) & 0x1F) * 255 / 31; - u8 a = pal_index ? 255: 0; - u32* row = &iconRef[256 * i + 32 * k + 8 * j]; - row[l] = r | (g << 8) | (b << 16) | (a << 24); - index++; + u8 pal_index = count % 2 ? data[count/2] >> 4 : data[count/2] & 0x0F; + iconRef[ytile*256 + ypixel*32 + xtile*8 + xpixel] = paletteRGBA[pal_index]; + count++; } } } From 84474105e29c858bdfe22fcbe443ede7753e9609 Mon Sep 17 00:00:00 2001 From: RealAstolfo Date: Thu, 18 Apr 2024 05:40:38 -0600 Subject: [PATCH 150/157] ssize_t is not defined in stddef.h (#1999) i had to add sys/types.h and patch my gentoo ebuilds for a successful compilation. --- src/debug/GdbStub.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/debug/GdbStub.h b/src/debug/GdbStub.h index ace07bf6..99b88158 100644 --- a/src/debug/GdbStub.h +++ b/src/debug/GdbStub.h @@ -3,6 +3,7 @@ #define GDBSTUB_H_ #include +#include #include #include From ba8d547dfa81539f4e8474152d92e866ad89241b Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Thu, 18 Apr 2024 12:25:41 +0200 Subject: [PATCH 151/157] Windows: Work around CMake not finding libarchive's include directory because MSYS2 CMake doesn't like UNIX paths. --- cmake/FixInterfaceIncludes.cmake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmake/FixInterfaceIncludes.cmake b/cmake/FixInterfaceIncludes.cmake index 513c1117..5c285d7a 100644 --- a/cmake/FixInterfaceIncludes.cmake +++ b/cmake/FixInterfaceIncludes.cmake @@ -19,6 +19,13 @@ function(fix_interface_includes) if (PARENT_DIR MATCHES "include$") list(APPEND NEW_DIRS "${PARENT_DIR}") endif() + + # HACK + # The libarchive pkg-config file in MSYS2 seems to include a UNIX-style path for its + # include directory and CMake doesn't like that. + if (WIN32 AND MINGW AND target STREQUAL PkgConfig::LibArchive) + list(FILTER DIRS EXCLUDE REGEX "^/[^.]+64/.*") + endif() endforeach() list(APPEND DIRS ${NEW_DIRS}) From 6112aa120ad0f83be4235b79a05738a689f940a4 Mon Sep 17 00:00:00 2001 From: Jakly <102590697+Jaklyy@users.noreply.github.com> Date: Thu, 2 May 2024 11:44:59 -0400 Subject: [PATCH 152/157] Pu region sizing/bounds fix (#2024) * fix the pu region's end point overflowing According to gericom it cannot overflow at all * set a minimum and a better maximum for the pu region size * fix pu logging * PU regions with a size of 31 always take up the entire address space also tweak some logging a little more * start is actually force aligned by size, oops * small tweaks * hopefully more clear code * math is for nerds --- src/CP15.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/CP15.cpp b/src/CP15.cpp index 58137fdd..5e5b35ea 100644 --- a/src/CP15.cpp +++ b/src/CP15.cpp @@ -186,10 +186,14 @@ void ARMv5::UpdatePURegion(u32 n) return; } - u32 start = rgn >> 12; - u32 sz = 2 << ((rgn >> 1) & 0x1F); - u32 end = start + (sz >> 12); - // TODO: check alignment of start + // notes: + // * min size of a pu region is 4KiB (12 bits) + // * size is calculated as size + 1, but the 12 lsb of address space are ignored, therefore we need it as size + 1 - 12, or size - 11 + // * pu regions are aligned based on their size + u32 size = std::max((int)((rgn>>1) & 0x1F) - 11, 0); // obtain the size, subtract 11 and clamp to a min of 0. + u32 start = ((rgn >> 12) >> size) << size; // determine the start offset, and use shifts to force alignment with a multiple of the size. + u32 end = start + (1<> 4) & 0xF, val, val & 1 ? "enabled" : "disabled", val & 0xFFFFF000, - (val & 0xFFFFF000) + (2 << ((val & 0x3E) >> 1)) + (val & 0x3E) >> 1 ); Log(LogLevel::Debug, "%s", log_output); // Some implementations of Log imply a newline, so we build up the line before printing it From 35cea5e1d736bbdcb4fae6108f3550167ca29a2d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 4 May 2024 18:16:24 +0200 Subject: [PATCH 153/157] Fix zstd ROM loading issues * fix use-after-free of inContent * don't try to free the DStream twice --- src/frontend/qt_sdl/ROMManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/qt_sdl/ROMManager.cpp b/src/frontend/qt_sdl/ROMManager.cpp index a2b1ab75..9607c848 100644 --- a/src/frontend/qt_sdl/ROMManager.cpp +++ b/src/frontend/qt_sdl/ROMManager.cpp @@ -948,8 +948,8 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, unique_ptr& outCo if (realSize != ZSTD_CONTENTSIZE_UNKNOWN) { - outContent = make_unique(realSize); - u64 decompressed = ZSTD_decompress(outContent.get(), realSize, inContent, inSize); + auto newOutContent = make_unique(realSize); + u64 decompressed = ZSTD_decompress(newOutContent.get(), realSize, inContent, inSize); if (ZSTD_isError(decompressed)) { @@ -957,6 +957,7 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, unique_ptr& outCo return 0; } + outContent = std::move(newOutContent); return realSize; } else @@ -1011,7 +1012,6 @@ u32 DecompressROM(const u8* inContent, const u32 inSize, unique_ptr& outCo } } while (inBuf.pos < inBuf.size); - ZSTD_freeDStream(dStream); outContent = make_unique(outBuf.pos); memcpy(outContent.get(), outBuf.dst, outBuf.pos); From 474bf6e7847f19b9b018592dc3db372927b4928d Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 5 May 2024 08:10:21 +0200 Subject: [PATCH 154/157] Set default optimization flags less intrusively --- CMakeLists.txt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97dfc5bd..cd830a05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ endif() set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) +set(CMAKE_USER_MAKE_RULES_OVERRIDE "${CMAKE_SOURCE_DIR}/cmake/DefaultBuildFlags.cmake") option(USE_VCPKG "Use vcpkg for dependency packages" OFF) if (USE_VCPKG) @@ -78,14 +79,6 @@ if (ENABLE_LTO) set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) endif() -if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Og") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Og") -endif() - -string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}") -string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") - if (NOT APPLE) set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -s") endif() From ee2c6cc7c2bec8982a532bff1299f9cee4f942de Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 5 May 2024 08:14:00 +0200 Subject: [PATCH 155/157] actually add the cmake script too --- cmake/DefaultBuildFlags.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 cmake/DefaultBuildFlags.cmake diff --git a/cmake/DefaultBuildFlags.cmake b/cmake/DefaultBuildFlags.cmake new file mode 100644 index 00000000..683767b3 --- /dev/null +++ b/cmake/DefaultBuildFlags.cmake @@ -0,0 +1,9 @@ +if (CMAKE_C_COMPILER_ID STREQUAL GNU) + set(CMAKE_C_FLAGS_DEBUG_INIT "-g -Og") +endif() +if (CMAKE_CXX_COMPILER_ID STREQUAL GNU) + set(CMAKE_CXX_FLAGS_DEBUG_INIT "-g -Og") +endif() + +string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT}") +string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_CXX_FLAGS_RELEASE_INIT}") From 10798c3464ca0e199087960fda918ed99acc2e21 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sun, 5 May 2024 08:40:37 +0200 Subject: [PATCH 156/157] fix README build badges finally --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 268c2b3a..2cef7983 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,9 @@
- - - - + + +

DS emulator, sorta From c85a2103bbd41e7119e7d41daa5180dee823b591 Mon Sep 17 00:00:00 2001 From: Nadia Holmquist Pedersen Date: Sat, 11 May 2024 22:40:45 +0200 Subject: [PATCH 157/157] Allow adding a suffix to the displayed melonDS version --- CMakeLists.txt | 2 -- src/CMakeLists.txt | 7 ++++++- src/NDS.cpp | 3 ++- src/frontend/qt_sdl/Window.cpp | 1 + src/{version.h => version.h.in} | 6 +++++- 5 files changed, 14 insertions(+), 5 deletions(-) rename src/{version.h => version.h.in} (75%) diff --git a/CMakeLists.txt b/CMakeLists.txt index cd830a05..57400e36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,8 +34,6 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -add_compile_definitions(MELONDS_VERSION="${melonDS_VERSION}") - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3dfd3b0d..a34cc968 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,7 +52,6 @@ add_library(core STATIC types.h Utils.cpp Utils.h - version.h Wifi.cpp WifiAP.cpp @@ -123,6 +122,12 @@ if (ENABLE_JIT) endif() endif() +set(MELONDS_VERSION_SUFFIX "$ENV{MELONDS_VERSION_SUFFIX}" CACHE STRING "Suffix to add to displayed melonDS version") + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.h.in" "${CMAKE_CURRENT_BINARY_DIR}/version.h") +target_sources(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/version.h") +target_include_directories(core PUBLIC "${CMAKE_CURRENT_BINARY_DIR}") + add_subdirectory(teakra EXCLUDE_FROM_ALL) # Workaround for building teakra with -O0 on Windows either failing or hanging forever target_compile_options(teakra PRIVATE "$<$:-Og>") diff --git a/src/NDS.cpp b/src/NDS.cpp index 68227ea5..94a24029 100644 --- a/src/NDS.cpp +++ b/src/NDS.cpp @@ -35,6 +35,7 @@ #include "Platform.h" #include "FreeBIOS.h" #include "Args.h" +#include "version.h" #include "DSi.h" #include "DSi_SPI_TSC.h" @@ -2839,7 +2840,7 @@ u8 NDS::ARM9IORead8(u32 addr) if(addr >= 0x04FFFA00 && addr < 0x04FFFA10) { // FIX: GBATek says this should be padded with spaces - static char const emuID[16] = "melonDS " MELONDS_VERSION; + static char const emuID[16] = "melonDS " MELONDS_VERSION_BASE; auto idx = addr - 0x04FFFA00; return (u8)(emuID[idx]); } diff --git a/src/frontend/qt_sdl/Window.cpp b/src/frontend/qt_sdl/Window.cpp index 23ba784e..a99546bd 100644 --- a/src/frontend/qt_sdl/Window.cpp +++ b/src/frontend/qt_sdl/Window.cpp @@ -72,6 +72,7 @@ #include "Platform.h" #include "Config.h" +#include "version.h" #include "Savestate.h" #include "LocalMP.h" diff --git a/src/version.h b/src/version.h.in similarity index 75% rename from src/version.h rename to src/version.h.in index 131c610d..a3db45b3 100644 --- a/src/version.h +++ b/src/version.h.in @@ -19,7 +19,11 @@ #ifndef VERSION_H #define VERSION_H -#define MELONDS_URL "https://melonds.kuribo64.net/" +#define MELONDS_URL "${melonDS_HOMEPAGE_URL}" + +#define MELONDS_VERSION_BASE "${melonDS_VERSION}" +#define MELONDS_VERSION_SUFFIX "${MELONDS_VERSION_SUFFIX}" +#define MELONDS_VERSION MELONDS_VERSION_BASE MELONDS_VERSION_SUFFIX #endif // VERSION_H