diff --git a/arm9/source/filesys/filetype.h b/arm9/source/filesys/filetype.h index a6ecad1..64e57a7 100644 --- a/arm9/source/filesys/filetype.h +++ b/arm9/source/filesys/filetype.h @@ -53,6 +53,7 @@ #define FTYPE_KEYBUILD(tp) (tp&(BIN_KEYDB|BIN_LEGKEY)) #define FTYPE_TITLEINFO(tp) (tp&(GAME_SMDH|GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_TMD|GAME_NDS|GAME_GBA|GAME_TAD|GAME_3DSX)) #define FTYPE_RENAMABLE(tp) (tp&(GAME_NCCH|GAME_NCSD|GAME_CIA|GAME_NDS|GAME_GBA)) +#define FTYPE_TRIMABLE(tp) (tp&(IMG_NAND|GAME_NCCH|GAME_NCSD|GAME_NDS|SYS_FIRM)) #define FTYPE_TRANSFERABLE(tp) ((u64) (tp&(IMG_FAT|FLAG_CTR)) == (u64) (IMG_FAT|FLAG_CTR)) #define FTYPE_NCSDFIXABLE(tp) (tp&(HDR_NAND|NOIMG_NAND)) #define FTYPE_HASCODE(tp) (((u64) (tp&(GAME_NCCH|FLAG_CXI)) == (u64) (GAME_NCCH|FLAG_CXI))|(tp&GAME_NCSD)) diff --git a/arm9/source/filesys/vff.c b/arm9/source/filesys/vff.c index dc97116..70a9c91 100644 --- a/arm9/source/filesys/vff.c +++ b/arm9/source/filesys/vff.c @@ -1,4 +1,3 @@ -#include "sddata.h" #include "virtual.h" #include "ffconf.h" #include "vff.h" diff --git a/arm9/source/filesys/vff.h b/arm9/source/filesys/vff.h index 0fa1e6b..17ebe8d 100644 --- a/arm9/source/filesys/vff.h +++ b/arm9/source/filesys/vff.h @@ -1,6 +1,7 @@ #pragma once #include "common.h" +#include "sddata.h" #include "ff.h" #define AM_VRT 0x40 // Virtual (FILINFO FAT attribute) diff --git a/arm9/source/godmode.c b/arm9/source/godmode.c index f0fa11e..63486ab 100644 --- a/arm9/source/godmode.c +++ b/arm9/source/godmode.c @@ -1029,6 +1029,8 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan bool key_buildable = (FTYPE_KEYBUILD(filetype)) && !in_output_path && !((drvtype & DRV_VIRTUAL) && (drvtype & DRV_SYSNAND)); bool titleinfo = (FTYPE_TITLEINFO(filetype)); bool renamable = (FTYPE_RENAMABLE(filetype)); + bool trimable = (FTYPE_TRIMABLE(filetype)) && !(drvtype & DRV_VIRTUAL) && !(drvtype & DRV_ALIAS) && + !(drvtype & DRV_CTRNAND) && !(drvtype & DRV_TWLNAND) && !(drvtype & DRV_IMAGE); bool transferable = (FTYPE_TRANSFERABLE(filetype) && IS_A9LH && (drvtype & DRV_FAT)); bool hsinjectable = (FTYPE_HASCODE(filetype)); bool extrcodeable = (FTYPE_HASCODE(filetype)); @@ -1056,7 +1058,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 || extrdiffable || keyinitable || keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable || agbimportable; + bool special_opt = mountable || verificable || decryptable || encryptable || cia_buildable || cia_buildable_legit || cxi_dumpable || tik_buildable || key_buildable || titleinfo || renamable || trimable || transferable || hsinjectable || restorable || xorpadable || ebackupable || ncsdfixable || extrcodeable || extrdiffable || keyinitable || keyinstallable || bootable || scriptable || fontable || viewable || installable || agbexportable || agbimportable; char pathstr[32+1]; TruncateString(pathstr, file_path, 32, 8); @@ -1244,6 +1246,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan int hsinject = (hsinjectable) ? ++n_opt : -1; int extrcode = (extrcodeable) ? ++n_opt : -1; int extrdiff = (extrdiffable) ? ++n_opt : -1; + int trim = (trimable) ? ++n_opt : -1; int rename = (renamable) ? ++n_opt : -1; int xorpad = (xorpadable) ? ++n_opt : -1; int xorpad_inplace = (xorpadable) ? ++n_opt : -1; @@ -1272,6 +1275,7 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan if (verify > 0) optionstr[verify-1] = "Verify file"; if (ctrtransfer > 0) optionstr[ctrtransfer-1] = "Transfer image to CTRNAND"; if (hsinject > 0) optionstr[hsinject-1] = "Inject to H&S"; + if (trim > 0) optionstr[trim-1] = "Trim file"; if (rename > 0) optionstr[rename-1] = "Rename file"; if (xorpad > 0) optionstr[xorpad-1] = "Build XORpads (SD output)"; if (xorpad_inplace > 0) optionstr[xorpad_inplace-1] = "Build XORpads (inplace)"; @@ -1556,6 +1560,30 @@ u32 FileHandlerMenu(char* current_path, u32* cursor, u32* scroll, PaneData** pan (BuildKeyDb(file_path, true) == 0) ? "success" : "failed"); return 0; } + else if (user_select == trim) { // -> Game file trimmer + u64 trimsize = GetGameFileTrimmedSize(file_path); + u64 currentsize = FileGetSize(file_path); + char tsizestr[32]; + char csizestr[32]; + char dsizestr[32]; + FormatBytes(tsizestr, trimsize); + FormatBytes(csizestr, currentsize); + FormatBytes(dsizestr, currentsize - trimsize); + + if (!trimsize || trimsize > currentsize) { + ShowPrompt(false, "%s\nFile can't be trimmed.", pathstr); + } else if (trimsize == currentsize) { + ShowPrompt(false, "%s\nFile is already trimmed.", pathstr); + } else if (ShowPrompt(true, "%s\nCurrent size: %s\nTrimmed size: %s\nDifference: %s\n \nTrim this file?", + pathstr, csizestr, tsizestr, dsizestr)) { + if (TrimGameFile(file_path) != 0) ShowPrompt(false, "%s\nTrimming failed.", pathstr); + else { + ShowPrompt(false, "%s\nTrimmed by %s.", pathstr, dsizestr); + GetDirContents(current_dir, current_path); + } + } + return 0; + } else if (user_select == rename) { // -> Game file renamer if ((n_marked > 1) && ShowPrompt(true, "Try to rename all %lu selected files?", n_marked)) { u32 n_success = 0; diff --git a/arm9/source/utils/gameutil.c b/arm9/source/utils/gameutil.c index efbde7f..557b8c8 100644 --- a/arm9/source/utils/gameutil.c +++ b/arm9/source/utils/gameutil.c @@ -1,6 +1,7 @@ #include "gameutil.h" #include "disadiff.h" #include "game.h" +#include "nand.h" // so that we can trim NAND images #include "hid.h" #include "ui.h" #include "fs.h" @@ -1654,6 +1655,56 @@ u32 ExtractDataFromDisaDiff(const char* path) { return ret; } +u64 GetGameFileTrimmedSize(const char* path) { + u64 filetype = IdentifyFileType(path); + u64 trimsize = 0; + + if (filetype & GAME_NDS) { + TwlHeader hdr; + if (fvx_qread(path, &hdr, 0, sizeof(TwlHeader), NULL) != FR_OK) + return 0; + if (hdr.unit_code != 0x00) // DSi or NDS+DSi + trimsize = hdr.ntr_twl_rom_size; + else trimsize = hdr.ntr_rom_size; // regular NDS + } else { + u8 hdr[0x200]; + if (fvx_qread(path, &hdr, 0, 0x200, NULL) != FR_OK) + return 0; + if (filetype & IMG_NAND) + trimsize = GetNandNcsdMinSizeSectors((NandNcsdHeader*) (void*) hdr) * 0x200; + else if (filetype & SYS_FIRM) + trimsize = GetFirmSize((FirmHeader*) (void*) hdr); + else if (filetype & GAME_NCSD) + trimsize = GetNcsdTrimmedSize((NcsdHeader*) (void*) hdr); + else if (filetype & GAME_NCCH) + trimsize = ((NcchHeader*) (void*) hdr)->size * NCCH_MEDIA_UNIT; + } + + // safety check for file size + if (trimsize > fvx_qsize(path)) + trimsize = 0; + + return trimsize; +} + +u32 TrimGameFile(const char* path) { + u64 trimsize = GetGameFileTrimmedSize(path); + if (!trimsize) return 1; + + // actual truncate routine - FAT only + FIL fp; + if (fx_open(&fp, path, FA_WRITE | FA_OPEN_EXISTING) != FR_OK) + return 1; + if ((f_lseek(&fp, (u32) trimsize) != FR_OK) || (f_truncate(&fp) != FR_OK)) { + fx_close(&fp); + return 1; + } + fx_close(&fp); + + // all done + return 0; +} + u32 LoadSmdhFromGameFile(const char* path, Smdh* smdh) { u64 filetype = IdentifyFileType(path); diff --git a/arm9/source/utils/gameutil.h b/arm9/source/utils/gameutil.h index 25e0f37..5d28d67 100644 --- a/arm9/source/utils/gameutil.h +++ b/arm9/source/utils/gameutil.h @@ -9,6 +9,8 @@ u32 BuildCiaFromGameFile(const char* path, bool force_legit); u32 DumpCxiSrlFromTmdFile(const char* path); u32 ExtractCodeFromCxiFile(const char* path, const char* path_out, char* extstr); u32 ExtractDataFromDisaDiff(const char* path); +u64 GetGameFileTrimmedSize(const char* path); +u32 TrimGameFile(const char* path); u32 ShowGameFileTitleInfo(const char* path); u32 GetTmdContentPath(char* path_content, const char* path_tmd); u32 BuildNcchInfoXorpads(const char* destdir, const char* path);