Add title info display for SMDH/NCCH/NCSD/CIA/TMD

This commit is contained in:
d0k3 2017-03-29 02:23:06 +02:00
parent 433d79d050
commit b9501318c5
11 changed files with 296 additions and 8 deletions

View File

@ -46,6 +46,21 @@ void DrawRectangle(u8* screen, int x, int y, int width, int height, int color)
} }
} }
void DrawBitmap(u8* screen, int x, int y, int w, int h, u8* bitmap)
{
u8* bitmapPos = bitmap;
for (int yy = 0; yy < h; yy++) {
int xDisplacement = (x * BYTES_PER_PIXEL * SCREEN_HEIGHT);
int yDisplacement = ((SCREEN_HEIGHT - (y + yy) - 1) * BYTES_PER_PIXEL);
u8* screenPos = screen + xDisplacement + yDisplacement;
for (int xx = w - 1; xx >= 0; xx--) {
memcpy(screenPos, bitmapPos, BYTES_PER_PIXEL);
bitmapPos += BYTES_PER_PIXEL;
screenPos += BYTES_PER_PIXEL * SCREEN_HEIGHT;
}
}
}
void DrawCharacter(u8* screen, int character, int x, int y, int color, int bgcolor) void DrawCharacter(u8* screen, int character, int x, int y, int color, int bgcolor)
{ {
for (int yy = 0; yy < FONT_HEIGHT; yy++) { for (int yy = 0; yy < FONT_HEIGHT; yy++) {
@ -110,6 +125,27 @@ u32 GetDrawStringWidth(const char* str) {
return width; return width;
} }
void WordWrapString(char* str, int llen) {
char* last_brk = str - 1;
char* last_spc = str - 1;
if (!llen) llen = (SCREEN_WIDTH_TOP / FONT_WIDTH);
for (char* str_ptr = str;; str_ptr++) {
if (!*str_ptr || (*str_ptr == ' ')) { // on space or string_end
if (str_ptr - last_brk > llen) { // if maximum line lenght is exceeded
if (last_spc > last_brk) { // put a line_brk at the last space
*last_spc = '\n';
last_brk = last_spc;
last_spc = str_ptr;
} else if (*str_ptr) { // if we have no applicable space
*str_ptr = '\n';
last_brk = str_ptr;
}
} else if (*str_ptr) last_spc = str_ptr;
} else if (*str_ptr == '\n') last_brk = str_ptr;
if (!*str_ptr) break;
}
}
void ResizeString(char* dest, const char* orig, int nsize, int tpos, bool align_right) { void ResizeString(char* dest, const char* orig, int nsize, int tpos, bool align_right) {
int osize = strnlen(orig, 256); int osize = strnlen(orig, 256);
if (nsize < osize) { if (nsize < osize) {
@ -180,6 +216,33 @@ void ShowString(const char *format, ...)
} else ClearScreenF(true, false, COLOR_STD_BG); } else ClearScreenF(true, false, COLOR_STD_BG);
} }
void ShowIconString(u8* icon, int w, int h, const char *format, ...)
{
static const u32 icon_offset = 10;
u32 str_width, str_height, tot_height;
u32 x_str, y_str, x_bmp, y_bmp;
ClearScreenF(true, false, COLOR_STD_BG);
if (!format || !*format) return; // only if there is something in there
char str[STRBUF_SIZE] = { 0 };
va_list va;
va_start(va, format);
vsnprintf(str, STRBUF_SIZE, format, va);
va_end(va);
str_width = GetDrawStringWidth(str);
str_height = GetDrawStringHeight(str);
tot_height = h + icon_offset + str_height;
x_str = (str_width >= SCREEN_WIDTH_TOP) ? 0 : (SCREEN_WIDTH_TOP - str_width) / 2;
y_str = (str_height >= SCREEN_HEIGHT) ? 0 : h + icon_offset + (SCREEN_HEIGHT - tot_height) / 2;
x_bmp = (w >= SCREEN_WIDTH_TOP) ? 0 : (SCREEN_WIDTH_TOP - w) / 2;
y_bmp = (tot_height >= SCREEN_HEIGHT) ? 0 : (SCREEN_HEIGHT - tot_height) / 2;
DrawBitmap(TOP_SCREEN, x_bmp, y_bmp, w, h, icon);
DrawStringF(TOP_SCREEN, x_str, y_str, COLOR_STD_FONT, COLOR_STD_BG, str);
}
bool ShowPrompt(bool ask, const char *format, ...) bool ShowPrompt(bool ask, const char *format, ...)
{ {
u32 str_width, str_height; u32 str_width, str_height;

View File

@ -58,6 +58,7 @@
void ClearScreen(unsigned char *screen, int color); void ClearScreen(unsigned char *screen, int color);
void ClearScreenF(bool clear_top, bool clear_bottom, int color); void ClearScreenF(bool clear_top, bool clear_bottom, int color);
void DrawRectangle(u8* screen, int x, int y, int width, int height, int color); void DrawRectangle(u8* screen, int x, int y, int width, int height, int color);
void DrawBitmap(u8* screen, int x, int y, int w, int h, u8* bitmap);
void DrawCharacter(unsigned char *screen, int character, int x, int y, int color, int bgcolor); void DrawCharacter(unsigned char *screen, int character, int x, int y, int color, int bgcolor);
void DrawString(unsigned char *screen, const char *str, int x, int y, int color, int bgcolor); void DrawString(unsigned char *screen, const char *str, int x, int y, int color, int bgcolor);
@ -66,12 +67,14 @@ void DrawStringF(unsigned char *screen, int x, int y, int color, int bgcolor, co
u32 GetDrawStringHeight(const char* str); u32 GetDrawStringHeight(const char* str);
u32 GetDrawStringWidth(const char* str); u32 GetDrawStringWidth(const char* str);
void WordWrapString(char* str, int llen);
void ResizeString(char* dest, const char* orig, int nsize, int tpos, bool align_right); void ResizeString(char* dest, const char* orig, int nsize, int tpos, bool align_right);
void TruncateString(char* dest, const char* orig, int nsize, int tpos); void TruncateString(char* dest, const char* orig, int nsize, int tpos);
void FormatNumber(char* str, u64 number); void FormatNumber(char* str, u64 number);
void FormatBytes(char* str, u64 bytes); void FormatBytes(char* str, u64 bytes);
void ShowString(const char *format, ...); void ShowString(const char *format, ...);
void ShowIconString(u8* icon, int w, int h, const char *format, ...);
bool ShowPrompt(bool ask, const char *format, ...); bool ShowPrompt(bool ask, const char *format, ...);
bool ShowUnlockSequence(u32 seqlvl, const char *format, ...); bool ShowUnlockSequence(u32 seqlvl, const char *format, ...);
u32 ShowSelectPrompt(u32 n, const char** options, const char *format, ...); u32 ShowSelectPrompt(u32 n, const char** options, const char *format, ...);

View File

@ -8,6 +8,7 @@
u32 IdentifyFileType(const char* path) { u32 IdentifyFileType(const char* path) {
const u8 romfs_magic[] = { ROMFS_MAGIC }; const u8 romfs_magic[] = { ROMFS_MAGIC };
const u8 tickdb_magic[] = { TICKDB_MAGIC }; const u8 tickdb_magic[] = { TICKDB_MAGIC };
const u8 smdh_magic[] = { SMDH_MAGIC };
u8 header[0x200] __attribute__((aligned(32))); // minimum required size u8 header[0x200] __attribute__((aligned(32))); // minimum required size
void* data = (void*) header; void* data = (void*) header;
size_t fsize = FileGetSize(path); size_t fsize = FileGetSize(path);
@ -60,6 +61,8 @@ u32 IdentifyFileType(const char* path) {
return SYS_FIRM; // FIRM file return SYS_FIRM; // FIRM file
} else if (memcmp(header + 0x100, tickdb_magic, sizeof(tickdb_magic)) == 0) { } else if (memcmp(header + 0x100, tickdb_magic, sizeof(tickdb_magic)) == 0) {
return SYS_TICKDB; // ticket.db return SYS_TICKDB; // ticket.db
} else if (memcmp(header, smdh_magic, sizeof(smdh_magic)) == 0) {
return GAME_SMDH; // SMDH file
} }
} }
if ((fsize > sizeof(BossHeader)) && if ((fsize > sizeof(BossHeader)) &&

View File

@ -13,11 +13,12 @@
#define GAME_BOSS (1UL<<8) #define GAME_BOSS (1UL<<8)
#define GAME_NUSCDN (1UL<<9) #define GAME_NUSCDN (1UL<<9)
#define GAME_TICKET (1UL<<10) #define GAME_TICKET (1UL<<10)
#define SYS_FIRM (1UL<<11) #define GAME_SMDH (1UL<<11)
#define SYS_TICKDB (1UL<<12) #define SYS_FIRM (1UL<<12)
#define BIN_NCCHNFO (1UL<<13) #define SYS_TICKDB (1UL<<13)
#define BIN_LAUNCH (1UL<<14) #define BIN_NCCHNFO (1UL<<14)
#define BIN_SUPPORT (1UL<<15) #define BIN_LAUNCH (1UL<<15)
#define BIN_SUPPORT (1UL<<16)
#define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types #define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types
#define FLAG_CTR (1UL<<29) #define FLAG_CTR (1UL<<29)
@ -30,6 +31,7 @@
#define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS)) #define FYTPE_ENCRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS))
#define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD)) #define FTYPE_BUILDABLE(tp) (tp&(GAME_NCSD|GAME_NCCH|GAME_TMD))
#define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD))) #define FTYPE_BUILDABLE_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD)))
#define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD))
#define FTYPE_TRANSFERABLE(tp) ((u32) (tp&(IMG_FAT|FLAG_CTR)) == (u32) (IMG_FAT|FLAG_CTR)) #define FTYPE_TRANSFERABLE(tp) ((u32) (tp&(IMG_FAT|FLAG_CTR)) == (u32) (IMG_FAT|FLAG_CTR))
#define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI)) #define FTYPE_HSINJECTABLE(tp) ((u32) (tp&(GAME_NCCH|FLAG_CXI)) == (u32) (GAME_NCCH|FLAG_CXI))
#define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND)) #define FTYPE_RESTORABLE(tp) (tp&(IMG_NAND))

View File

@ -17,7 +17,7 @@ u32 ValidateCiaHeader(CiaHeader* header) {
} }
u32 GetCiaInfo(CiaInfo* info, CiaHeader* header) { u32 GetCiaInfo(CiaInfo* info, CiaHeader* header) {
memcpy(info, header, 0x20); // take over first 0x20 byte if ((u8*) info != (u8*) header) memcpy(info, header, 0x20); // take over first 0x20 byte
info->offset_cert = align(header->size_header, 64); info->offset_cert = align(header->size_header, 64);
info->offset_ticket = info->offset_cert + align(header->size_cert, 64); info->offset_ticket = info->offset_cert + align(header->size_cert, 64);

View File

@ -7,4 +7,5 @@
#include "romfs.h" #include "romfs.h"
#include "firm.h" #include "firm.h"
#include "boss.h" #include "boss.h"
#include "smdh.h"
#include "ncchinfo.h" #include "ncchinfo.h"

View File

@ -1,5 +1,6 @@
#include "gameutil.h" #include "gameutil.h"
#include "game.h" #include "game.h"
#include "hid.h"
#include "ui.h" #include "ui.h"
#include "fsperm.h" #include "fsperm.h"
#include "filetype.h" #include "filetype.h"
@ -191,6 +192,7 @@ u32 LoadExeFsFile(void* data, const char* path, u32 offset, const char* name, u3
break; break;
} }
} }
if (exefile) { if (exefile) {
u32 size_exefile = exefile->size; u32 size_exefile = exefile->size;
u32 offset_exefile = (ncch.offset_exefs * NCCH_MEDIA_UNIT) + sizeof(ExeFsHeader) + exefile->offset; u32 offset_exefile = (ncch.offset_exefs * NCCH_MEDIA_UNIT) + sizeof(ExeFsHeader) + exefile->offset;
@ -1374,6 +1376,105 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit) {
return ret; return ret;
} }
u32 ShowSmdhTitleInfo(Smdh* smdh) {
const u32 lwrap = 24;
u8* icon = (u8*) (TEMP_BUFFER + sizeof(Smdh));
char* desc_l = (char*) icon + SMDH_SIZE_ICON_BIG;
char* desc_s = (char*) desc_l + SMDH_SIZE_DESC_LONG;
char* pub = (char*) desc_s + SMDH_SIZE_DESC_SHORT;
if ((GetSmdhIconBig(icon, smdh) != 0) ||
(GetSmdhDescLong(desc_l, smdh) != 0) ||
(GetSmdhDescShort(desc_s, smdh) != 0) ||
(GetSmdhPublisher(pub, smdh) != 0))
return 1;
WordWrapString(desc_l, lwrap);
WordWrapString(desc_s, lwrap);
WordWrapString(pub, lwrap);
ShowIconString(icon, SMDH_DIM_ICON_BIG, SMDH_DIM_ICON_BIG, "%s\n%s\n%s", desc_l, desc_s, pub);
InputWait();
ClearScreenF(true, false, COLOR_STD_BG);
return 0;
}
u32 ShowSmdhFileTitleInfo(const char* path) {
Smdh* smdh = (Smdh*) (void*) TEMP_BUFFER;
UINT btr;
if ((fvx_qread(path, smdh, 0, sizeof(Smdh), &btr) != FR_OK) || (btr != sizeof(Smdh)))
return 1;
return ShowSmdhTitleInfo(smdh);
}
u32 ShowNcchFileTitleInfo(const char* path) {
Smdh* smdh = (Smdh*) (void*) TEMP_BUFFER;
if (LoadExeFsFile(smdh, path, 0, "icon", sizeof(Smdh)) != 0)
return 1;
return ShowSmdhTitleInfo(smdh);
}
u32 ShowNcsdFileTitleInfo(const char* path) {
Smdh* smdh = (Smdh*) (void*) TEMP_BUFFER;
if (LoadExeFsFile(smdh, path, NCSD_CNT0_OFFSET, "icon", sizeof(Smdh)) != 0)
return 1;
return ShowSmdhTitleInfo(smdh);
}
u32 ShowCiaFileTitleInfo(const char* path) {
Smdh* smdh = (Smdh*) (void*) TEMP_BUFFER;
CiaInfo info;
UINT btr;
if ((fvx_qread(path, &info, 0, 0x20, &btr) != FR_OK) || (btr != 0x20) ||
(GetCiaInfo(&info, (CiaHeader*) &info) != 0))
return 1;
if ((info.offset_meta) && ((fvx_qread(path, smdh, info.offset_meta + 0x400, sizeof(Smdh), &btr) != FR_OK) ||
(btr != sizeof(Smdh)))) return 1;
else if (LoadExeFsFile(smdh, path, info.offset_content, "icon", sizeof(Smdh)) != 0) return 1;
return ShowSmdhTitleInfo(smdh);
}
u32 ShowTmdFileTitleInfo(const char* path) {
const u8 dlc_tid_high[] = { DLC_TID_HIGH };
TitleMetaData* tmd = (TitleMetaData*) TEMP_BUFFER;
TmdContentChunk* chunk = (TmdContentChunk*) (tmd + 1);
// content path string
char path_content[256];
char* name_content;
strncpy(path_content, path, 256);
name_content = strrchr(path_content, '/');
if (!name_content) return 1; // will not happen
name_content++;
// load TMD file
if ((LoadTmdFile(tmd, path) != 0) || !getbe16(tmd->content_count))
return 1;
snprintf(name_content, 256 - (name_content - path_content),
(memcmp(tmd->title_id, dlc_tid_high, sizeof(dlc_tid_high)) == 0) ? "00000000/%08lx.app" : "%08lx.app", getbe32(chunk->id));
return ShowGameFileTitleInfo(path_content);
}
u32 ShowGameFileTitleInfo(const char* path) {
u32 filetype = IdentifyFileType(path);
u32 ret = 1;
// build CIA from game file
if (filetype & GAME_SMDH) {
ret = ShowSmdhFileTitleInfo(path);
} else if (filetype & GAME_NCCH) {
ret = ShowNcchFileTitleInfo(path);
} else if (filetype & GAME_NCSD) {
ret = ShowNcsdFileTitleInfo(path);
} else if (filetype & GAME_CIA) {
ret = ShowCiaFileTitleInfo(path);
} else if (filetype & GAME_TMD) {
ret = ShowTmdFileTitleInfo(path);
}
return ret;
}
u32 BuildNcchInfoXorpads(const char* destdir, const char* path) { u32 BuildNcchInfoXorpads(const char* destdir, const char* path) {
FIL fp_info; FIL fp_info;
FIL fp_xorpad; FIL fp_xorpad;

View File

@ -8,6 +8,7 @@ u32 VerifyGameFile(const char* path);
u32 CheckEncryptedGameFile(const char* path); u32 CheckEncryptedGameFile(const char* path);
u32 CryptGameFile(const char* path, bool inplace, bool encrypt); u32 CryptGameFile(const char* path, bool inplace, bool encrypt);
u32 BuildCiaFromGameFile(const char* path, bool force_legit); u32 BuildCiaFromGameFile(const char* path, bool force_legit);
u32 ShowGameFileTitleInfo(const char* path);
u32 BuildNcchInfoXorpads(const char* destdir, const char* path); u32 BuildNcchInfoXorpads(const char* destdir, const char* path);
u32 CheckHealthAndSafetyInject(const char* hsdrv); u32 CheckHealthAndSafetyInject(const char* hsdrv);
u32 InjectHealthAndSafety(const char* path, const char* destdrv); u32 InjectHealthAndSafety(const char* path, const char* destdrv);

61
source/game/smdh.c Normal file
View File

@ -0,0 +1,61 @@
#include "smdh.h"
#define SMDH_STRING(str, src, len) for (u32 i = 0; i < len; i++) str[i] = src[i]
// shamelessly stolen from bch2obj.py / 3ds_hb_menu :)
#define SMDH_LUT 0, 1, 8, 9, 2, 3, 10, 11, 16, 17, 24, 25, 18, 19, 26, 27, \
4, 5, 12, 13, 6, 7, 14, 15, 20, 21, 28, 29, 22, 23, 30, 31, \
32, 33, 40, 41, 34, 35, 42, 43, 48, 49, 56, 57, 50, 51, 58, 59, \
36, 37, 44, 45, 38, 39, 46, 47, 52, 53, 60, 61, 54, 55, 62, 63
u32 ConvertSmdhIcon(u8* icon, const u16* smdh_icon, u32 w, u32 h) {
const u32 lut[8*8] = { SMDH_LUT };
u16* pix565 = (u16*) smdh_icon;
for (u32 y = 0; y < h; y += 8) {
for (u32 x = 0; x < w; x += 8) {
for (u32 i = 0; i < 8*8; i++) {
u32 ix = x + (lut[i] & 0x7);
u32 iy = y + (lut[i] >> 3);
u8* pix888 = icon + ((iy * w) + ix) * 3;
*(pix888++) = ((*pix565 >> 0) & 0x1F) << 3; // B
*(pix888++) = ((*pix565 >> 5) & 0x3F) << 2; // G
*(pix888++) = ((*pix565 >> 11) & 0x1F) << 3; // R
pix565++;
}
}
}
return 0;
}
// short desc is max 64(+1) chars long
u32 GetSmdhDescShort(char* desc, const Smdh* smdh) {
const SmdhAppTitle* title = &(smdh->apptitles[1]); // english title
memset(desc, 0, SMDH_SIZE_DESC_SHORT + 1);
SMDH_STRING(desc, title->short_desc, SMDH_SIZE_DESC_SHORT);
return 0;
}
// long desc is max 128(+1) chars long
u32 GetSmdhDescLong(char* desc, const Smdh* smdh) {
const SmdhAppTitle* title = &(smdh->apptitles[1]); // english title
memset(desc, 0, SMDH_SIZE_DESC_LONG + 1);
SMDH_STRING(desc, title->long_desc, SMDH_SIZE_DESC_LONG);
return 0;
}
// publisher is max 64(+1) chars long
u32 GetSmdhPublisher(char* pub, const Smdh* smdh) {
const SmdhAppTitle* title = &(smdh->apptitles[1]); // english title
memset(pub, 0, SMDH_SIZE_PUBLISHER + 1);
SMDH_STRING(pub, title->publisher, SMDH_SIZE_PUBLISHER);
return 0;
}
// small icons are 24x24 => 0x6C0 byte in RGB888
u32 GetSmdhIconSmall(u8* icon, const Smdh* smdh) {
return ConvertSmdhIcon(icon, smdh->icon_small, SMDH_DIM_ICON_SMALL, SMDH_DIM_ICON_SMALL);
}
// big icons are 48x48 => 0x1B00 byte in RGB888
u32 GetSmdhIconBig(u8* icon, const Smdh* smdh) {
return ConvertSmdhIcon(icon, smdh->icon_big, SMDH_DIM_ICON_BIG, SMDH_DIM_ICON_BIG);
}

45
source/game/smdh.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include "common.h"
#define SMDH_MAGIC 'S', 'M', 'D', 'H'
#define SMDH_SIZE_DESC_SHORT 64
#define SMDH_SIZE_DESC_LONG 128
#define SMDH_SIZE_PUBLISHER 64
#define SMDH_DIM_ICON_SMALL 24
#define SMDH_DIM_ICON_BIG 48
#define SMDH_SIZE_ICON_SMALL (SMDH_DIM_ICON_SMALL * SMDH_DIM_ICON_SMALL * 3) // w * h * bpp (rgb888)
#define SMDH_SIZE_ICON_BIG (SMDH_DIM_ICON_BIG * SMDH_DIM_ICON_BIG * 3) // w * h * bpp (rgb888)
// see: https://www.3dbrew.org/wiki/SMDH#Application_Titles
typedef struct {
u16 short_desc[0x40];
u16 long_desc[0x80];
u16 publisher[0x40];
} __attribute__((packed)) SmdhAppTitle;
// see: https://www.3dbrew.org/wiki/SMDH
typedef struct {
char magic[4];
u16 version;
u16 reserved0;
SmdhAppTitle apptitles[0x10]; // 1 -> english title
u8 game_ratings[0x10];
u32 region_lockout;
u32 matchmaker_id;
u64 matchmaker_id_bit;
u32 flags;
u16 version_eula;
u16 reserved1;
u32 anim_def_frame;
u32 cec_id;
u64 reserved2;
u16 icon_small[0x240]; // 24x24x16bpp / 8x8 tiles / rgb565
u16 icon_big[0x900]; // 48x48x16bpp / 8x8 tiles / rgb565
} __attribute__((packed)) Smdh;
u32 GetSmdhDescShort(char* desc, const Smdh* smdh);
u32 GetSmdhDescLong(char* desc, const Smdh* smdh);
u32 GetSmdhPublisher(char* pub, const Smdh* smdh);
u32 GetSmdhIconSmall(u8* icon, const Smdh* smdh);
u32 GetSmdhIconBig(u8* icon, const Smdh* smdh);

View File

@ -653,14 +653,15 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
bool cryptable_inplace = ((encryptable||decryptable) && !in_output_path && (drvtype & DRV_FAT)); bool cryptable_inplace = ((encryptable||decryptable) && !in_output_path && (drvtype & DRV_FAT));
bool buildable = (FTYPE_BUILDABLE(filetype)); bool buildable = (FTYPE_BUILDABLE(filetype));
bool buildable_legit = (FTYPE_BUILDABLE_L(filetype)); bool buildable_legit = (FTYPE_BUILDABLE_L(filetype));
bool titleinfo = (FTYPE_TITLEINFO(filetype));
bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_A9LH && (drvtype & DRV_FAT)); bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_A9LH && (drvtype & DRV_FAT));
bool hsinjectable = (FTYPE_HSINJECTABLE(filetype)); bool hsinjectable = (FTYPE_HSINJECTABLE(filetype));
bool restorable = (FTYPE_RESTORABLE(filetype) && IS_A9LH && !(drvtype & DRV_SYSNAND)); bool restorable = (FTYPE_RESTORABLE(filetype) && IS_A9LH && !(drvtype & DRV_SYSNAND));
bool ebackupable = (FTYPE_EBACKUP(filetype)); bool ebackupable = (FTYPE_EBACKUP(filetype));
bool xorpadable = (FTYPE_XORPAD(filetype)); bool xorpadable = (FTYPE_XORPAD(filetype));
bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT)); bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT));
bool special_opt = mountable || verificable || decryptable || encryptable || bool special_opt = mountable || verificable || decryptable || encryptable || buildable || buildable_legit ||
buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable || ebackupable; titleinfo || hsinjectable || restorable || xorpadable || launchable || ebackupable;
char pathstr[32+1]; char pathstr[32+1];
TruncateString(pathstr, curr_entry->path, 32, 8); TruncateString(pathstr, curr_entry->path, 32, 8);
@ -695,6 +696,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
(filetype & GAME_TMD ) ? "TMD file options..." : (filetype & GAME_TMD ) ? "TMD file options..." :
(filetype & GAME_BOSS ) ? "BOSS file options..." : (filetype & GAME_BOSS ) ? "BOSS file options..." :
(filetype & GAME_NUSCDN)? "Decrypt NUS/CDN file" : (filetype & GAME_NUSCDN)? "Decrypt NUS/CDN file" :
(filetype & GAME_SMDH) ? "Show SMDH title info" :
(filetype & SYS_FIRM ) ? "FIRM image options..." : (filetype & SYS_FIRM ) ? "FIRM image options..." :
(filetype & SYS_TICKDB) ? "Mount as ticket.db" : (filetype & SYS_TICKDB) ? "Mount as ticket.db" :
(filetype & BIN_NCCHNFO)? "NCCHinfo options..." : (filetype & BIN_NCCHNFO)? "NCCHinfo options..." :
@ -786,6 +788,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
// stuff for special menu starts here // stuff for special menu starts here
n_opt = 0; n_opt = 0;
int show_info = (titleinfo) ? ++n_opt : -1;
int mount = (mountable) ? ++n_opt : -1; int mount = (mountable) ? ++n_opt : -1;
int restore = (restorable) ? ++n_opt : -1; int restore = (restorable) ? ++n_opt : -1;
int ebackup = (ebackupable) ? ++n_opt : -1; int ebackup = (ebackupable) ? ++n_opt : -1;
@ -802,6 +805,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
if (mount > 0) optionstr[mount-1] = "Mount image to drive"; if (mount > 0) optionstr[mount-1] = "Mount image to drive";
if (restore > 0) optionstr[restore-1] = "Restore SysNAND (safe)"; if (restore > 0) optionstr[restore-1] = "Restore SysNAND (safe)";
if (ebackup > 0) optionstr[ebackup-1] = "Update embedded backup"; if (ebackup > 0) optionstr[ebackup-1] = "Update embedded backup";
if (show_info > 0) optionstr[show_info-1] = "Show title info";
if (decrypt > 0) optionstr[decrypt-1] = (cryptable_inplace) ? "Decrypt file (...)" : "Decrypt file (" OUTPUT_PATH ")"; if (decrypt > 0) optionstr[decrypt-1] = (cryptable_inplace) ? "Decrypt file (...)" : "Decrypt file (" OUTPUT_PATH ")";
if (encrypt > 0) optionstr[encrypt-1] = (cryptable_inplace) ? "Encrypt file (...)" : "Encrypt file (" OUTPUT_PATH ")"; if (encrypt > 0) optionstr[encrypt-1] = (cryptable_inplace) ? "Encrypt file (...)" : "Encrypt file (" OUTPUT_PATH ")";
if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)"; if (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)";
@ -993,6 +997,10 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur
(VerifyGameFile(curr_entry->path) == 0) ? "success" : "failed"); (VerifyGameFile(curr_entry->path) == 0) ? "success" : "failed");
} }
return 0; return 0;
} else if (user_select == show_info) { // -> Show title info
if (ShowGameFileTitleInfo(curr_entry->path) != 0)
ShowPrompt(false, "Title info: not found");
return 0;
} else if (user_select == hsinject) { // -> Inject to Health & Safety } else if (user_select == hsinject) { // -> Inject to Health & Safety
char* destdrv[2] = { NULL }; char* destdrv[2] = { NULL };
n_opt = 0; n_opt = 0;