diff --git a/arm9/source/common/ui.c b/arm9/source/common/ui.c index d64b0b7..d31eee2 100644 --- a/arm9/source/common/ui.c +++ b/arm9/source/common/ui.c @@ -169,6 +169,10 @@ void DrawBitmap(u8* screen, int x, int y, int w, int h, u8* bitmap) if (x < 0) x = (SCREEN_WIDTH(screen) - w) >> 1; if (y < 0) y = (SCREEN_HEIGHT - h) >> 1; + // bug out on too big bitmaps / too large dimensions + if ((x < 0) || (y < 0) || (w > SCREEN_WIDTH(screen)) || (h > SCREEN_HEIGHT)) + return; + u8* bitmapPos = bitmap; for (int yy = 0; yy < h; yy++) { int xDisplacement = (x * BYTES_PER_PIXEL * SCREEN_HEIGHT); diff --git a/arm9/source/filesys/filetype.c b/arm9/source/filesys/filetype.c index 3dc81c5..102e63a 100644 --- a/arm9/source/filesys/filetype.c +++ b/arm9/source/filesys/filetype.c @@ -6,6 +6,7 @@ #include "keydb.h" #include "ctrtransfer.h" #include "scripting.h" +#include "pcx.h" #include "ui.h" // only for font file detection u64 IdentifyFileType(const char* path) { @@ -13,6 +14,7 @@ u64 IdentifyFileType(const char* path) { const u8 tickdb_magic[] = { TICKDB_MAGIC }; const u8 smdh_magic[] = { SMDH_MAGIC }; const u8 threedsx_magic[] = { THREEDSX_EXT_MAGIC }; + const u8 pcx_magic[] = { PCX_MAGIC }; if (!path) return 0; // safety u8 header[0x200] __attribute__((aligned(32))); // minimum required size @@ -110,6 +112,9 @@ u64 IdentifyFileType(const char* path) { (GetNcchInfoVersion((NcchInfoHeader*) data)) && fname && (strncasecmp(fname, NCCHINFO_NAME, 32) == 0)) { return BIN_NCCHNFO; // ncchinfo.bin file + } else if ((fsize > sizeof(pcx_magic)) && (memcmp(data, pcx_magic, sizeof(pcx_magic)) == 0) && + (strncasecmp(ext, "pcx", 4) == 0)) { + return GFX_PCX; } else if (ext && ((strncasecmp(ext, "cdn", 4) == 0) || (strncasecmp(ext, "nus", 4) == 0))) { char path_cetk[256]; char* ext_cetk = path_cetk + (ext - path); diff --git a/arm9/source/filesys/filetype.h b/arm9/source/filesys/filetype.h index d173f5a..07db005 100644 --- a/arm9/source/filesys/filetype.h +++ b/arm9/source/filesys/filetype.h @@ -27,9 +27,10 @@ #define BIN_LEGKEY (1ULL<<22) #define TXT_SCRIPT (1ULL<<23) #define TXT_GENERIC (1ULL<<24) -#define FONT_PBM (1ULL<<25) -#define NOIMG_NAND (1ULL<<26) -#define HDR_NAND (1ULL<<27) +#define GFX_PCX (1ULL<<25) +#define FONT_PBM (1ULL<<26) +#define NOIMG_NAND (1ULL<<27) +#define HDR_NAND (1ULL<<28) #define TYPE_BASE 0xFFFFFFFFULL // 32 bit reserved for base types // #define FLAG_FIRM (1ULL<<58) // <--- for CXIs containing FIRMs @@ -60,6 +61,7 @@ #define FTYPE_KEYINSTALL(tp) (tp&(BIN_KEYDB)) #define FTYPE_SCRIPT(tp) (tp&(TXT_SCRIPT)) #define FTYPE_FONT(tp) (tp&(FONT_PBM)) +#define FTYPE_GFX(tp) (tp&(GFX_PCX)) #define FTYPE_BOOTABLE(tp) (tp&(SYS_FIRM)) #define FTYPE_INSTALLABLE(tp) (tp&(SYS_FIRM)) #define FTPYE_AGBSAVE(tp) (tp&(SYS_AGBSAVE)) diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index 0dc11cc..78ae499 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -390,6 +390,36 @@ u32 SdFormatMenu(void) { return 0; } +u32 FileGraphicsViewer(const char* path) { + u64 filetype = IdentifyFileType(path); + u8* bitmap = TEMP_BUFFER; + u32 buffer_size = TEMP_BUFFER_SIZE / 2; + u32 w = 0; + u32 h = 0; + + if (filetype & GFX_PCX) { + u8* pcx = TEMP_BUFFER + TEMP_BUFFER_SIZE / 2; + u32 pcx_size = FileGetData(path, pcx, TEMP_BUFFER_SIZE / 2, 0); + if ((pcx_size > 0) && (pcx_size < TEMP_BUFFER_SIZE / 2) && + (PCX_Decompress(bitmap, buffer_size, pcx, pcx_size))) { + PCXHdr* hdr = (PCXHdr*) (void*) pcx; + w = PCX_Width(hdr); + h = PCX_Height(hdr); + } + } + + if (w && h && (w < SCREEN_WIDTH(ALT_SCREEN)) && (h < SCREEN_HEIGHT)) { + ClearScreenF(true, true, COLOR_STD_BG); + DrawBitmap(ALT_SCREEN, -1, -1, w, h, bitmap); + ShowString("Press to continue"); + InputWait(0); + ClearScreenF(true, true, COLOR_STD_BG); + return 0; + } + + return 1; +} + u32 FileHexViewer(const char* path) { const u32 max_data = (SCREEN_HEIGHT / FONT_HEIGHT_EXT) * 16 * ((FONT_WIDTH_EXT > 4) ? 1 : 2); static u32 mode = 0; @@ -962,6 +992,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan bool keyinstallable = (FTYPE_KEYINSTALL(filetype)) && !((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); bool scriptable = (FTYPE_SCRIPT(filetype)); bool fontable = (FTYPE_FONT(filetype)); + bool viewable = (FTYPE_GFX(filetype)); bool bootable = (FTYPE_BOOTABLE(filetype)); bool installable = (FTYPE_INSTALLABLE(filetype)); bool agbexportable = (FTPYE_AGBSAVE(filetype) && (drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); @@ -976,7 +1007,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan extrcodeable = (FTYPE_HASCODE(filetype_cxi)); } - bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable || keyinstallable || bootable || scriptable || fontable || installable || agbexportable || agbimportable; + bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || keyinitable || keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable || agbimportable; char pathstr[32+1]; TruncateString(pathstr, file_path, 32, 8); @@ -1026,7 +1057,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan (filetype & BIN_LEGKEY) ? "Build " KEYDB_NAME : (filetype & BIN_NCCHNFO)? "NCCHinfo options..." : (filetype & TXT_SCRIPT) ? "Execute GM9 script" : - (filetype & FONT_PBM) ? "Set as active font" : + (filetype & FONT_PBM) ? "Set as active font" : + (filetype & GFX_PCX) ? "View PCX bitmap file" : (filetype & HDR_NAND) ? "Rebuild NCSD header" : (filetype & NOIMG_NAND) ? "Rebuild NCSD header" : "???"; optionstr[hexviewer-1] = "Show in Hexeditor"; @@ -1170,6 +1202,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan int boot = (bootable) ? ++n_opt : -1; int script = (scriptable) ? ++n_opt : -1; int font = (fontable) ? ++n_opt : -1; + int view = (viewable) ? ++n_opt : -1; int agbexport = (agbexportable) ? ++n_opt : -1; int agbimport = (agbimportable) ? ++n_opt : -1; if (mount > 0) optionstr[mount-1] = (filetype & GAME_TMD) ? "Mount CXI/NDS to drive" : "Mount image to drive"; @@ -1197,6 +1230,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan if (install > 0) optionstr[install-1] = "Install FIRM"; if (boot > 0) optionstr[boot-1] = "Boot FIRM"; if (script > 0) optionstr[script-1] = "Execute GM9 script"; + if (view > 0) optionstr[font-1] = "View PCX bitmap file"; if (font > 0) optionstr[font-1] = "Set as active font"; if (agbexport > 0) optionstr[agbexport-1] = "Dump GBA VC save"; if (agbimport > 0) optionstr[agbimport-1] = "Inject GBA VC save"; @@ -1611,6 +1645,11 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan ClearScreenF(true, true, COLOR_STD_BG); return 0; } + else if (user_select == view) { // view gfx + if (FileGraphicsViewer(file_path) != 0) + ShowPrompt(false, "%s\nError: Cannot view file"); + return 0; + } else if (user_select == agbexport) { // export GBA VC save if (DumpGbaVcSavegame(file_path) == 0) ShowPrompt(false, "Savegame dumped to " OUTPUT_PATH); diff --git a/arm9/source/system/pcx.h b/arm9/source/system/pcx.h index ffb7266..5fd802f 100644 --- a/arm9/source/system/pcx.h +++ b/arm9/source/system/pcx.h @@ -3,6 +3,8 @@ #include #include "common.h" +#define PCX_MAGIC 0x0A, 0x05, 0x01, 0x08 + typedef struct { u8 manufacturer; u8 version;