diff --git a/source/common/common.h b/source/common/common.h index 86bdada..3dd9801 100644 --- a/source/common/common.h +++ b/source/common/common.h @@ -38,7 +38,7 @@ (((v) % (a)) ? ((v) + (a) - ((v) % (a))) : (v)) // GodMode9 version -#define VERSION "0.9.6" +#define VERSION "0.9.7" // input / output paths #define INPUT_PATHS "0:", "0:/files9", "1:/rw/files9" diff --git a/source/fs/filetype.c b/source/fs/filetype.c index 292530d..8be9df9 100644 --- a/source/fs/filetype.c +++ b/source/fs/filetype.c @@ -59,7 +59,7 @@ u32 IdentifyFileType(const char* path) { fname && (strncasecmp(fname, NCCHINFO_NAME, 32) == 0)) { return BIN_NCCHNFO; // ncchinfo.bin file #if PAYLOAD_MAX_SIZE <= TEMP_BUFFER_SIZE - } else if ((fsize <= PAYLOAD_MAX_SIZE) && (strncasecmp(ext, "bin", 4) == 0)) { + } else if ((fsize <= PAYLOAD_MAX_SIZE) && ext && (strncasecmp(ext, "bin", 4) == 0)) { return BIN_LAUNCH; // assume it's an ARM9 payload #endif } diff --git a/source/fs/filetype.h b/source/fs/filetype.h index 2df2f0d..a22f7f2 100644 --- a/source/fs/filetype.h +++ b/source/fs/filetype.h @@ -14,15 +14,17 @@ #define SYS_FIRM (1<<9) #define BIN_NCCHNFO (1<<10) #define BIN_LAUNCH (1<<11) +#define TYPE_BASE 0x00FFFFFF // 24 bit reserved for base types #define FLAG_CXI (1<<31) #define FTYPE_MOUNTABLE(tp) (tp&(IMG_FAT|IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_EXEFS|GAME_ROMFS|SYS_FIRM)) #define FYTPE_VERIFICABLE(tp) (tp&(IMG_NAND|GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_TMD|GAME_BOSS|SYS_FIRM)) #define FYTPE_DECRYPTABLE(tp) (tp&(GAME_CIA|GAME_NCSD|GAME_NCCH|GAME_BOSS|SYS_FIRM)) +#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_L(tp) (FTYPE_BUILDABLE(tp) && (tp&(GAME_TMD))) -#define FTYPE_HSINJECTABLE(tp) ((tp&(GAME_NCCH|FLAG_CXI)) == (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_XORPAD(tp) (tp&(BIN_NCCHNFO)) #define FTYPE_PAYLOAD(tp) (tp&(BIN_LAUNCH)) diff --git a/source/game/gameutil.c b/source/game/gameutil.c index a9c4972..1e4fe82 100644 --- a/source/game/gameutil.c +++ b/source/game/gameutil.c @@ -8,6 +8,10 @@ #include "sha.h" #include "vff.h" +// use NCCH crypto defines for everything +#define CRYPTO_DECRYPT NCCH_NOCRYPTO +#define CRYPTO_ENCRYPT NCCH_STDCRYPTO + u32 GetOutputPath(char* dest, const char* path, const char* ext) { // special handling for input from title directories (somewhat hacky) if ((strspn(path, "AB147") > 0) && (strncmp(path + 1, ":/title/", 8) == 0)) { @@ -670,7 +674,7 @@ u32 CheckEncryptedGameFile(const char* path) { else return 1; } -u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 crypto, // crypto only relevant for NCCH +u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 crypto, u32 offset, u32 size, TmdContentChunk* chunk, const u8* titlekey) { // this line only for CIA contents // this will do a simple copy for unencrypted files bool inplace = (strncmp(orig, dest, 256) == 0); @@ -680,6 +684,14 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 FIL* dfp = (inplace) ? &ofile : &dfile; FSIZE_t fsize; + // FIRM encryption is not possible (yet) + if ((mode & SYS_FIRM) && (crypto != CRYPTO_DECRYPT)) + return 1; + + // check for BOSS crypto + bool crypt_boss = ((mode & GAME_BOSS) && (CheckEncryptedBossFile(orig) == 0)); + crypt_boss = ((mode & GAME_BOSS) && (crypt_boss == (crypto == CRYPTO_DECRYPT))); + // open file(s) if (inplace) { if (fvx_open(ofp, orig, FA_READ | FA_WRITE | FA_OPEN_EXISTING) != FR_OK) @@ -701,14 +713,14 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 u32 ret = 0; if (!ShowProgress(offset, fsize, dest)) ret = 1; - if (mode & (GAME_NCCH|GAME_NCSD|GAME_BOSS|SYS_FIRM)) { // for NCCH / NCSD / FIRM files + if (mode & (GAME_NCCH|GAME_NCSD|GAME_BOSS|SYS_FIRM)) { // for NCCH / NCSD / BOSS / FIRM files for (u32 i = 0; (i < size) && (ret == 0); i += MAIN_BUFFER_SIZE) { u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); UINT bytes_read, bytes_written; if (fvx_read(ofp, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1; if (((mode & GAME_NCCH) && (CryptNcchSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) || - ((mode & GAME_NCSD) && (DecryptNcsdSequential(MAIN_BUFFER, i, read_bytes) != 0)) || - ((mode & GAME_BOSS) && (CryptBossSequential(MAIN_BUFFER, i, read_bytes) != 0)) || + ((mode & GAME_NCSD) && (CryptNcsdSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) || + ((mode & GAME_BOSS) && crypt_boss && (CryptBossSequential(MAIN_BUFFER, i, read_bytes) != 0)) || ((mode & SYS_FIRM) && (DecryptFirmSequential(MAIN_BUFFER, i, read_bytes) != 0))) ret = 1; if (inplace) fvx_lseek(ofp, fvx_tell(ofp) - read_bytes); @@ -726,8 +738,8 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 GetTmdCtr(ctr, chunk); // NCCH crypto? if (fvx_read(ofp, MAIN_BUFFER, sizeof(NcchHeader), &bytes_read) != FR_OK) ret = 1; if (cia_crypto) DecryptCiaContentSequential(MAIN_BUFFER, sizeof(NcchHeader), ctr, titlekey); - ncch_crypto = ((ValidateNcchHeader(ncch) == 0) && NCCH_ENCRYPTED(ncch)); - if (ncch_crypto && (SetupNcchCrypto(ncch, NCCH_NOCRYPTO) != 0)) + ncch_crypto = ((ValidateNcchHeader(ncch) == 0) && (NCCH_ENCRYPTED(ncch) || !(crypto & NCCH_NOCRYPTO))); + if (ncch_crypto && (SetupNcchCrypto(ncch, crypto) != 0)) ret = 1; GetTmdCtr(ctr, chunk); @@ -737,7 +749,7 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 u32 read_bytes = min(MAIN_BUFFER_SIZE, (size - i)); if (fvx_read(ofp, MAIN_BUFFER, read_bytes, &bytes_read) != FR_OK) ret = 1; if (cia_crypto && (DecryptCiaContentSequential(MAIN_BUFFER, read_bytes, ctr, titlekey) != 0)) ret = 1; - if (ncch_crypto && (DecryptNcchSequential(MAIN_BUFFER, i, read_bytes) != 0)) ret = 1; + if (ncch_crypto && (CryptNcchSequential(MAIN_BUFFER, i, read_bytes, crypto) != 0)) ret = 1; if (inplace) fvx_lseek(ofp, fvx_tell(ofp) - read_bytes); if (fvx_write(dfp, MAIN_BUFFER, read_bytes, &bytes_written) != FR_OK) ret = 1; sha_update(MAIN_BUFFER, read_bytes); @@ -754,7 +766,7 @@ u32 CryptNcchNcsdBossFirmFile(const char* orig, const char* dest, u32 mode, u16 return ret; } -u32 DecryptCiaFile(const char* orig, const char* dest) { +u32 CryptCiaFile(const char* orig, const char* dest, u16 crypto) { bool inplace = (strncmp(orig, dest, 256) == 0); CiaStub* cia = (CiaStub*) TEMP_BUFFER; CiaInfo info; @@ -779,7 +791,7 @@ u32 DecryptCiaFile(const char* orig, const char* dest) { for (u32 i = 0; (i < content_count) && (i < TMD_MAX_CONTENTS); i++) { TmdContentChunk* chunk = &(cia->content_list[i]); u64 size = getbe64(chunk->size); - if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_CIA, NCCH_NOCRYPTO, next_offset, size, chunk, titlekey) != 0) + if (CryptNcchNcsdBossFirmFile(orig, dest, GAME_CIA, crypto, next_offset, size, chunk, titlekey) != 0) return 1; next_offset += size; } @@ -798,7 +810,7 @@ u32 DecryptFirmFile(const char* orig, const char* dest) { UINT btr; // actual decryption - if (CryptNcchNcsdBossFirmFile(orig, dest, SYS_FIRM, 0, 0, 0, NULL, NULL) != 0) + if (CryptNcchNcsdBossFirmFile(orig, dest, SYS_FIRM, CRYPTO_DECRYPT, 0, 0, NULL, NULL) != 0) return 1; // open destination file, get FIRM header @@ -850,8 +862,9 @@ u32 DecryptFirmFile(const char* orig, const char* dest) { return 0; } -u32 DecryptGameFile(const char* path, bool inplace) { +u32 CryptGameFile(const char* path, bool inplace, bool encrypt) { u32 filetype = IdentifyFileType(path); + u16 crypto = encrypt ? CRYPTO_ENCRYPT : CRYPTO_DECRYPT; char dest[256]; char* destptr = (char*) path; u32 ret = 0; @@ -871,11 +884,11 @@ u32 DecryptGameFile(const char* path, bool inplace) { } if (filetype & GAME_CIA) - ret = DecryptCiaFile(path, destptr); + ret = CryptCiaFile(path, destptr, crypto); else if (filetype & SYS_FIRM) ret = DecryptFirmFile(path, destptr); else if (filetype & (GAME_NCCH|GAME_NCSD|GAME_BOSS)) - ret = CryptNcchNcsdBossFirmFile(path, destptr, filetype, NCCH_NOCRYPTO, 0, 0, NULL, NULL); + ret = CryptNcchNcsdBossFirmFile(path, destptr, filetype, crypto, 0, 0, NULL, NULL); else ret = 1; if (!inplace && (ret != 0)) @@ -1302,7 +1315,7 @@ u32 InjectHealthAndSafety(const char* path, const char* destdrv) { // copy / decrypt the source CXI u32 ret = 0; - if (CryptNcchNcsdBossFirmFile(path, path_cxi, GAME_NCCH, NCCH_NOCRYPTO, 0, 0, NULL, NULL) != 0) + if (CryptNcchNcsdBossFirmFile(path, path_cxi, GAME_NCCH, CRYPTO_DECRYPT, 0, 0, NULL, NULL) != 0) ret = 1; // fix up the injected H&S NCCH header (copy H&S signature, title ID) diff --git a/source/game/gameutil.h b/source/game/gameutil.h index f3a8b80..cc6e8f1 100644 --- a/source/game/gameutil.h +++ b/source/game/gameutil.h @@ -4,7 +4,7 @@ u32 VerifyGameFile(const char* path); u32 CheckEncryptedGameFile(const char* path); -u32 DecryptGameFile(const char* path, bool inplace); +u32 CryptGameFile(const char* path, bool inplace, bool encrypt); u32 BuildCiaFromGameFile(const char* path, bool force_legit); u32 BuildNcchInfoXorpads(const char* destdir, const char* path); u32 InjectHealthAndSafety(const char* path, const char* destdrv); diff --git a/source/game/ncch.h b/source/game/ncch.h index 791519a..8357df1 100644 --- a/source/game/ncch.h +++ b/source/game/ncch.h @@ -11,7 +11,8 @@ #define NCCH_ENCRYPTED(ncch) (!((ncch)->flags[7] & 0x04)) #define NCCH_IS_CXI(ncch) ((ncch)->flags[5] & 0x02) -#define NCCH_NOCRYPTO 0x0004 +#define NCCH_NOCRYPTO 0x0004 +#define NCCH_STDCRYPTO 0x0000 #define NCCH_GET_CRYPTO(ncch) (!NCCH_ENCRYPTED(ncch) ? NCCH_NOCRYPTO : (((ncch)->flags[3] << 8) | ((ncch)->flags[7]&(0x01|0x20)))) // wrapper defines diff --git a/source/game/ncsd.c b/source/game/ncsd.c index eec28d1..b553fda 100644 --- a/source/game/ncsd.c +++ b/source/game/ncsd.c @@ -35,7 +35,7 @@ u64 GetNcsdTrimmedSize(NcsdHeader* header) { } // on the fly decryptor for NCSD -u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data) { +u32 CryptNcsdSequential(u8* data, u32 offset_data, u32 size_data, u16 crypto) { // warning: this will only work for sequential processing static NcsdHeader ncsd; @@ -64,7 +64,7 @@ u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data) { if (size_i > size_data - (data_i - data)) size_i = size_data - (data_i - data); // decrypt ncch segment - if (DecryptNcchSequential(data_i, offset_i, size_i) != 0) + if (CryptNcchSequential(data_i, offset_i, size_i, crypto) != 0) return 1; } diff --git a/source/game/ncsd.h b/source/game/ncsd.h index cf9d012..a7c8d79 100644 --- a/source/game/ncsd.h +++ b/source/game/ncsd.h @@ -10,6 +10,10 @@ #define NCSD_DINFO_SIZE 0x300 #define NCSD_CNT0_OFFSET 0x4000 +// wrapper defines +#define DecryptNcsdSequential(data, offset, size) CryptNcsdSequential(data, offset, size, NCCH_NOCRYPTO) +#define EncryptNcsdSequential(data, offset, size, crypto) CryptNcsdSequential(data, offset, size, crypto) + typedef struct { u32 offset; u32 size; @@ -34,4 +38,4 @@ typedef struct { u32 ValidateNcsdHeader(NcsdHeader* header); u64 GetNcsdTrimmedSize(NcsdHeader* header); -u32 DecryptNcsdSequential(u8* data, u32 offset_data, u32 size_data); +u32 CryptNcsdSequential(u8* data, u32 offset_data, u32 size_data, u16 crypto); diff --git a/source/godmode.c b/source/godmode.c index 5330a5b..d7e7a83 100644 --- a/source/godmode.c +++ b/source/godmode.c @@ -571,7 +571,7 @@ u32 Sha256Calculator(const char* path) { u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* current_dir, DirStruct* clipboard) { DirEntry* curr_entry = &(current_dir->entry[*cursor]); - const char* optionstr[8]; + const char* optionstr[16]; // check for file lock if (!FileUnlock(curr_entry->path)) return 1; @@ -585,14 +585,15 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur bool mountable = (FTYPE_MOUNTABLE(filetype) && !(drvtype & DRV_IMAGE)); bool verificable = (FYTPE_VERIFICABLE(filetype)); bool decryptable = (FYTPE_DECRYPTABLE(filetype)); - bool decryptable_inplace = (decryptable && (drvtype & (DRV_SDCARD|DRV_RAMDRIVE))); + bool encryptable = (FYTPE_ENCRYPTABLE(filetype)); + bool cryptable_inplace = ((encryptable||decryptable) && (drvtype & DRV_FAT)); bool buildable = (FTYPE_BUILDABLE(filetype)); bool buildable_legit = (FTYPE_BUILDABLE_L(filetype)); bool hsinjectable = (FTYPE_HSINJECTABLE(filetype)); bool restorable = (FTYPE_RESTORABLE(filetype) && CheckA9lh() && !(drvtype & DRV_SYSNAND)); bool xorpadable = (FTYPE_XORPAD(filetype)); bool launchable = ((FTYPE_PAYLOAD(filetype)) && (drvtype & DRV_FAT)); - bool special_opt = mountable || verificable || decryptable || decryptable_inplace || + bool special_opt = mountable || verificable || decryptable || encryptable || buildable || buildable_legit || hsinjectable || restorable || xorpadable || launchable; char pathstr[32 + 1]; @@ -697,7 +698,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur int mount = (mountable) ? ++n_opt : -1; int restore = (restorable) ? ++n_opt : -1; int decrypt = (decryptable) ? ++n_opt : -1; - int decrypt_inplace = (decryptable_inplace) ? ++n_opt : -1; + int encrypt = (encryptable) ? ++n_opt : -1; int build = (buildable) ? ++n_opt : -1; int build_legit = (buildable_legit) ? ++n_opt : -1; int verify = (verificable) ? ++n_opt : -1; @@ -707,8 +708,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur int launch = (launchable) ? ++n_opt : -1; if (mount > 0) optionstr[mount-1] = "Mount image to drive"; if (restore > 0) optionstr[restore-1] = "Restore SysNAND (safe)"; - if (decrypt > 0) optionstr[decrypt-1] = "Decrypt file (SD output)"; - if (decrypt_inplace > 0) optionstr[decrypt_inplace-1] = "Decrypt file (inplace)"; + 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 (build > 0) optionstr[build-1] = (build_legit < 0) ? "Build CIA from file" : "Build CIA (standard)"; if (build_legit > 0) optionstr[build_legit-1] = "Build CIA (legit)"; if (verify > 0) optionstr[verify-1] = "Verify file"; @@ -741,17 +742,24 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur } } return 0; - } else if ((user_select == decrypt) || (user_select == decrypt_inplace)) { // -> decrypt game file - bool inplace = (user_select == decrypt_inplace); - if ((n_marked > 1) && ShowPrompt(true, "Try to decrypt all %lu selected files?", n_marked)) { + } else if (user_select == decrypt) { // -> decrypt game file + if (cryptable_inplace) { + optionstr[0] = "Decrypt to " OUTPUT_PATH; + optionstr[1] = "Decrypt inplace"; + user_select = (int) ShowSelectPrompt(2, optionstr, pathstr); + } else user_select = 1; + bool inplace = (user_select == 2); + if (!user_select) { // do nothing when no choice is made + } else if ((n_marked > 1) && ShowPrompt(true, "Try to decrypt all %lu selected files?", n_marked)) { u32 n_success = 0; u32 n_unencrypted = 0; u32 n_other = 0; + ShowString("Trying to decrypt %lu files...", n_marked); for (u32 i = 0; i < current_dir->n_entries; i++) { const char* path = current_dir->entry[i].path; if (!current_dir->entry[i].marked) continue; - if (IdentifyFileType(path) != filetype) { + if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { n_other++; continue; } @@ -760,7 +768,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur continue; } current_dir->entry[i].marked = false; - if (DecryptGameFile(path, inplace) == 0) n_success++; + if (CryptGameFile(path, inplace, false) == 0) n_success++; else { // on failure: set cursor on failed title, break; *cursor = i; break; @@ -768,19 +776,57 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur } if (n_other || n_unencrypted) { ShowPrompt(false, "%lu/%lu files decrypted ok\n%lu/%lu not encrypted\n%lu/%lu not of same type", - n_success, n_marked, n_unencrypted, n_marked, n_other, n_marked); + n_success, n_marked, n_unencrypted, n_marked, n_other, n_marked); } else ShowPrompt(false, "%lu/%lu files decrypted ok", n_success, n_marked); if (!inplace && n_success) ShowPrompt(false, "%lu files written to %s", n_success, OUTPUT_PATH); } else { if (CheckEncryptedGameFile(curr_entry->path) != 0) { ShowPrompt(false, "%s\nFile is not encrypted", pathstr); } else { - u32 ret = DecryptGameFile(curr_entry->path, inplace); + u32 ret = CryptGameFile(curr_entry->path, inplace, false); if (inplace || (ret != 0)) ShowPrompt(false, "%s\nDecryption %s", pathstr, (ret == 0) ? "success" : "failed"); else ShowPrompt(false, "%s\nDecrypted to %s", pathstr, OUTPUT_PATH); } } return 0; + } else if (user_select == encrypt) { // -> encrypt game file + if (cryptable_inplace) { + optionstr[0] = "Encrypt to " OUTPUT_PATH; + optionstr[1] = "Encrypt inplace"; + user_select = (int) ShowSelectPrompt(2, optionstr, pathstr); + } else user_select = 1; + bool inplace = (user_select == 2); + if (!user_select) { // do nothing when no choice is made + } else if ((n_marked > 1) && ShowPrompt(true, "Try to encrypt all %lu selected files?", n_marked)) { + u32 n_success = 0; + u32 n_other = 0; + ShowString("Trying to encrypt %lu files...", n_marked); + for (u32 i = 0; i < current_dir->n_entries; i++) { + const char* path = current_dir->entry[i].path; + if (!current_dir->entry[i].marked) + continue; + if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { + n_other++; + continue; + } + current_dir->entry[i].marked = false; + if (CryptGameFile(path, inplace, true) == 0) n_success++; + else { // on failure: set cursor on failed title, break; + *cursor = i; + break; + } + } + if (n_other) { + ShowPrompt(false, "%lu/%lu files encrypted ok\n%lu/%lu not of same type", + n_success, n_marked, n_other, n_marked); + } else ShowPrompt(false, "%lu/%lu files encrypted ok", n_success, n_marked); + if (!inplace && n_success) ShowPrompt(false, "%lu files written to %s", n_success, OUTPUT_PATH); + } else { + u32 ret = CryptGameFile(curr_entry->path, inplace, true); + if (inplace || (ret != 0)) ShowPrompt(false, "%s\nEncryption %s", pathstr, (ret == 0) ? "success" : "failed"); + else ShowPrompt(false, "%s\nEncrypted to %s", pathstr, OUTPUT_PATH); + } + return 0; } else if ((user_select == build) || (user_select == build_legit)) { // -> build CIA bool force_legit = (user_select == build_legit); if ((n_marked > 1) && ShowPrompt(true, "Try to process all %lu selected files?", n_marked)) { @@ -790,7 +836,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur const char* path = current_dir->entry[i].path; if (!current_dir->entry[i].marked) continue; - if (IdentifyFileType(path) != filetype) { + if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { n_other++; continue; } @@ -822,7 +868,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, DirStruct* cur const char* path = current_dir->entry[i].path; if (!current_dir->entry[i].marked) continue; - if (IdentifyFileType(path) != filetype) { + if (!(IdentifyFileType(path) & filetype & TYPE_BASE)) { n_other++; continue; }