diff --git a/arm9/source/file_browse.cpp b/arm9/source/file_browse.cpp index d3398f0..9a2171b 100644 --- a/arm9/source/file_browse.cpp +++ b/arm9/source/file_browse.cpp @@ -39,6 +39,7 @@ #include "driveOperations.h" #include "dumpOperations.h" #include "hexEditor.h" +#include "ndsInfo.h" #include "nitrofs.h" #include "inifile.h" #include "nds_loader_arm9.h" @@ -225,6 +226,8 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { { assignedOp[++maxCursors] = FileOperation::mountNitroFS; printf(" Mount NitroFS\n"); + assignedOp[++maxCursors] = FileOperation::ndsInfo; + printf(" Show NDS file info\n"); } else if(extension(entry->name, {"sav", "sav1", "sav2", "sav3", "sav4", "sav5", "sav6", "sav7", "sav8", "sav9"})) { @@ -376,6 +379,9 @@ FileOperation fileBrowse_A(DirEntry* entry, char path[PATH_MAX]) { currentDrive = 5; } break; + } case FileOperation::ndsInfo: { + ndsInfo(entry->name.c_str()); + break; } case FileOperation::showInfo: { changeFileAttribs(entry); break; diff --git a/arm9/source/file_browse.h b/arm9/source/file_browse.h index 8de3f7b..a7deb72 100644 --- a/arm9/source/file_browse.h +++ b/arm9/source/file_browse.h @@ -45,6 +45,7 @@ enum class FileOperation { copyFatOut, calculateSHA1, hexEdit, + ndsInfo, }; bool extension(const std::string &filename, const std::vector &extensions); diff --git a/arm9/source/main.cpp b/arm9/source/main.cpp index accb374..bbe0384 100644 --- a/arm9/source/main.cpp +++ b/arm9/source/main.cpp @@ -152,6 +152,7 @@ int main(int argc, char **argv) { // Subscreen as a console videoSetModeSub(MODE_0_2D); vramSetBankH(VRAM_H_SUB_BG); + vramSetBankI(VRAM_I_SUB_SPRITE); consoleInit(&bottomConsoleBG, 1, BgType_Text4bpp, BgSize_T_256x256, 7, 0, false, true); consoleInit(&bottomConsole, 0, BgType_Text4bpp, BgSize_T_256x256, 15, 0, false, true); diff --git a/arm9/source/ndsInfo.cpp b/arm9/source/ndsInfo.cpp new file mode 100644 index 0000000..35357dd --- /dev/null +++ b/arm9/source/ndsInfo.cpp @@ -0,0 +1,134 @@ +#include "ndsInfo.h" + +#include "date.h" +#include "tonccpy.h" + +#include +#include + +extern PrintConsole bottomConsole, bottomConsoleBG, topConsole; + +constexpr const char *langNames[8] { + "Japanese", + "English", + "French", + "German", + "Italian", + "Spanish", + "Chinese", + "Korean" +}; + +extern void reinitConsoles(void); + +void ndsInfo(const char *path) { + FILE *file = fopen(path, "rb"); + if(!file) + return; + + char headerTitle[0xD] = {0}; + fread(headerTitle, 1, 0xC, file); + + char tid[5] = {0}; + fread(tid, 1, 4, file); + + u32 ofs; + fseek(file, 0x68, SEEK_SET); + fread(&ofs, sizeof(u32), 1, file); + fseek(file, ofs, SEEK_SET); + + u16 version; + fread(&version, sizeof(u16), 1, file); + + u8 *iconBitmap = new u8[8 * 0x200]; + u16 *iconPalette = new u16[8 * 0x10]; + u16 *iconAnimation = new u16[0x40](); // Initialize to 0 for DS icons + + if(version == 0x0103) { // DSi + fseek(file, 0x1240 - 2, SEEK_CUR); + fread(iconBitmap, 1, 8 * 0x200, file); + fread(iconPalette, 2, 8 * 0x10, file); + fread(iconAnimation, 2, 0x40, file); + + fseek(file, ofs + 0x240, SEEK_SET); + } else { // DS + fseek(file, 0x20 - 2, SEEK_CUR); + fread(iconBitmap, 1, 0x200, file); + fread(iconPalette, 2, 0x10, file); + } + + int languages = 5 + (version & 0x3); + char16_t *titles = new char16_t[languages * 0x80]; + fread(titles, 2, languages * 0x80, file); + + fclose(file); + + oamInit(&oamSub, SpriteMapping_Bmp_1D_128, false); + + u16 *iconGfx = oamAllocateGfx(&oamSub, SpriteSize_32x32, SpriteColorFormat_16Color); + oamSet(&oamSub, 0, 256 - 36, 4, 0, 0, SpriteSize_32x32, SpriteColorFormat_16Color, iconGfx, -1, false, false, false, false, false); + + tonccpy(iconGfx, iconBitmap, 0x200); + tonccpy(SPRITE_PALETTE_SUB, iconPalette, 0x20); + + oamUpdate(&oamSub); + + u16 pressed = 0, held = 0; + int animationFrame = 0, frameDelay = 0, lang = 1; + while(1) { + consoleClear(); + + iprintf("Header Title: %s\n", headerTitle); + iprintf("Title ID: %s\n", tid); + iprintf("Title: (%s)\n ", langNames[lang]); + for(int j = 0; j < 0x80 && titles[lang * 0x80 + j]; j++) { + if(titles[lang * 0x80 + j] == '\n') + iprintf("\n "); + else + iprintf("%c", titles[lang * 0x80 + j]); + } + iprintf("\n"); + + consoleSelect(&topConsole); + do { + swiWaitForVBlank(); + scanKeys(); + pressed = keysDown(); + held = keysDownRepeat(); + iprintf("\x1B[30m\x1B[0;26H %s", RetTime().c_str()); // Print time + if(iconAnimation[animationFrame] && animationFrame < 0x40) { + if(frameDelay < (iconAnimation[animationFrame] & 0xFF) - 1) { + frameDelay++; + } else { + frameDelay = 0; + if(!iconAnimation[++animationFrame]) + animationFrame = 0; + + tonccpy(iconGfx, iconBitmap + ((iconAnimation[animationFrame] >> 8) & 7) * 0x200, 0x200); + tonccpy(SPRITE_PALETTE_SUB, iconPalette + ((iconAnimation[animationFrame] >> 0xB) & 7) * 0x10, 0x20); + oamSetFlip(&oamSub, 0, iconAnimation[animationFrame] & BIT(14), iconAnimation[animationFrame] & BIT(15)); + oamUpdate(&oamSub); + } + } + } while(!held); + consoleSelect(&bottomConsole); + + if(held & KEY_UP) { + if(lang > 0) + lang--; + } else if(held & KEY_DOWN) { + if(lang < languages - 1) + lang++; + } else if(pressed & KEY_B) { + break; + } + } + + delete[] iconBitmap; + delete[] iconPalette; + delete[] iconAnimation; + delete[] titles; + + oamFreeGfx(&oamSub, iconGfx); + oamDisable(&oamSub); +} diff --git a/arm9/source/ndsInfo.h b/arm9/source/ndsInfo.h new file mode 100644 index 0000000..da41f83 --- /dev/null +++ b/arm9/source/ndsInfo.h @@ -0,0 +1,6 @@ +#ifndef NDS_INFO_H +#define NDS_INFO_H + +void ndsInfo(const char *path); + +#endif