mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
5277 lines
268 KiB
C
5277 lines
268 KiB
C
#include <stdarg.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/stat.h>
|
|
#include <math.h>
|
|
|
|
#include <switch.h>
|
|
|
|
#include <ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
|
|
#include <turbojpeg.h>
|
|
|
|
#include "dumper.h"
|
|
#include "fs_ext.h"
|
|
#include "ui.h"
|
|
#include "util.h"
|
|
#include "keys.h"
|
|
|
|
/* Extern variables */
|
|
|
|
extern dumpOptions dumpCfg;
|
|
|
|
extern bool keysFileAvailable;
|
|
|
|
extern gamecard_ctx_t gameCardInfo;
|
|
|
|
extern u32 titleAppCount, titlePatchCount, titleAddOnCount;
|
|
extern u32 sdCardTitleAppCount, sdCardTitlePatchCount, sdCardTitleAddOnCount;
|
|
extern u32 emmcTitleAppCount, emmcTitlePatchCount, emmcTitleAddOnCount;
|
|
|
|
extern base_app_ctx_t *baseAppEntries;
|
|
extern patch_addon_ctx_t *patchEntries, *addOnEntries;
|
|
|
|
extern char **filenameBuffer;
|
|
extern int filenameCount;
|
|
|
|
extern char curRomFsPath[NAME_BUF_LEN];
|
|
extern romfs_browser_entry *romFsBrowserEntries;
|
|
|
|
extern browser_entry_size_info *hfs0ExeFsEntriesSizes;
|
|
|
|
extern orphan_patch_addon_entry *orphanEntries;
|
|
|
|
extern char strbuf[NAME_BUF_LEN];
|
|
|
|
extern char cfwDirStr[32];
|
|
|
|
/* Statically allocated variables */
|
|
|
|
static PlFontData sharedFonts[PlSharedFontType_Total];
|
|
static FT_Library library;
|
|
static FT_Face sharedFontsFaces[PlSharedFontType_Total];
|
|
static Framebuffer fb;
|
|
|
|
static u32 *framebuf = NULL;
|
|
static u32 framebuf_width = 0;
|
|
|
|
static const u8 bgColors[3] = { BG_COLOR_RGB };
|
|
static const u8 hlBgColors[3] = { HIGHLIGHT_BG_COLOR_RGB };
|
|
|
|
int cursor = 0;
|
|
int scroll = 0;
|
|
int breaks = 0;
|
|
int font_height = 0;
|
|
|
|
int titleListCursor = 0, titleListScroll = 0;
|
|
int orphanListCursor = 0, orphanListScroll = 0;
|
|
int browserCursor = 0, browserScroll = 0;
|
|
|
|
curMenuType menuType;
|
|
static bool orphanMode = false;
|
|
|
|
static char titleSelectorStr[NAME_BUF_LEN] = {'\0'};
|
|
static char exeFsAndRomFsSelectorStr[NAME_BUF_LEN] = {'\0'};
|
|
static char dumpedContentInfoStr[NAME_BUF_LEN] = {'\0'};
|
|
|
|
static u32 selectedAppInfoIndex = 0;
|
|
static u32 selectedAppIndex;
|
|
static u32 selectedPatchIndex;
|
|
static u32 selectedAddOnIndex;
|
|
static u32 selectedPartitionIndex;
|
|
static u32 selectedFileIndex;
|
|
|
|
static nspDumpType selectedNspDumpType;
|
|
|
|
static bool exeFsUpdateFlag = false;
|
|
static selectedRomFsType curRomFsType = ROMFS_TYPE_APP;
|
|
|
|
static selectedTicketType curTikType = TICKET_TYPE_APP;
|
|
|
|
static bool updatePerformed = false;
|
|
|
|
bool highlight = false;
|
|
|
|
static char statusMessage[2048] = {'\0'};
|
|
static int statusMessageFadeout = 0;
|
|
|
|
extern u64 freeSpace;
|
|
extern char freeSpaceStr[32];
|
|
|
|
static UIState uiState;
|
|
|
|
static bool fb_init = false, romfs_init = false, ft_lib_init = false, ft_faces_init[PlSharedFontType_Total];
|
|
|
|
static const char *dirNormalIconPath = "romfs:/browser/dir_normal.jpg";
|
|
static u8 *dirNormalIconBuf = NULL;
|
|
|
|
static const char *dirHighlightIconPath = "romfs:/browser/dir_highlight.jpg";
|
|
static u8 *dirHighlightIconBuf = NULL;
|
|
|
|
static const char *fileNormalIconPath = "romfs:/browser/file_normal.jpg";
|
|
u8 *fileNormalIconBuf = NULL;
|
|
|
|
static const char *fileHighlightIconPath = "romfs:/browser/file_highlight.jpg";
|
|
u8 *fileHighlightIconBuf = NULL;
|
|
|
|
static const char *enabledNormalIconPath = "romfs:/browser/enabled_normal.jpg";
|
|
u8 *enabledNormalIconBuf = NULL;
|
|
|
|
static const char *enabledHighlightIconPath = "romfs:/browser/enabled_highlight.jpg";
|
|
u8 *enabledHighlightIconBuf = NULL;
|
|
|
|
static const char *disabledNormalIconPath = "romfs:/browser/disabled_normal.jpg";
|
|
u8 *disabledNormalIconBuf = NULL;
|
|
|
|
static const char *disabledHighlightIconPath = "romfs:/browser/disabled_highlight.jpg";
|
|
u8 *disabledHighlightIconBuf = NULL;
|
|
|
|
static const char *appHeadline = "NXDumpTool v" APP_VERSION ". Built on " __DATE__ " - " __TIME__ ".\nMade by DarkMatterCore.\n\n";
|
|
static const char *appControlsCommon = "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_A " ] Select | [ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_PLUS " ] Exit";
|
|
static const char *appControlsGameCardMultiApp = "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_A " ] Select | [ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_L " / " NINTENDO_FONT_R " / " NINTENDO_FONT_ZL " / " NINTENDO_FONT_ZR " ] Show info from another base application | [ " NINTENDO_FONT_PLUS " ] Exit";
|
|
static const char *appControlsNoContent = "[ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_PLUS " ] Exit";
|
|
static const char *appControlsSdCardEmmcFull = "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_A " ] Select | [ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_X " ] Batch mode | [ " NINTENDO_FONT_Y " ] Dump installed content with missing base application | [ " NINTENDO_FONT_PLUS " ] Exit";
|
|
static const char *appControlsSdCardEmmcNoOrphan = "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_A " ] Select | [ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_X " ] Batch mode | [ " NINTENDO_FONT_PLUS " ] Exit";
|
|
static const char *appControlsSdCardEmmcNoApp = "[ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_X " ] Batch mode | [ " NINTENDO_FONT_Y " ] Dump installed content with missing base application | [ " NINTENDO_FONT_PLUS " ] Exit";
|
|
static const char *appControlsRomFs = "[ " NINTENDO_FONT_DPAD " / " NINTENDO_FONT_LSTICK " / " NINTENDO_FONT_RSTICK " ] Move | [ " NINTENDO_FONT_A " ] Select | [ " NINTENDO_FONT_B " ] Back | [ " NINTENDO_FONT_Y " ] Dump current directory | [ " NINTENDO_FONT_PLUS " ] Exit";
|
|
|
|
static const char *mainMenuItems[] = { "Dump gamecard content", "Dump installed SD card / eMMC content", "Update options" };
|
|
static const char *gameCardMenuItems[] = { "NX Card Image (XCI) dump", "Nintendo Submission Package (NSP) dump", "HFS0 options", "ExeFS options", "RomFS options", "Dump gamecard certificate" };
|
|
static const char *xciDumpMenuItems[] = { "Start XCI dump process", "Split output dump (FAT32 support): ", "Create directory with archive bit set: ", "Keep certificate: ", "Trim output dump: ", "CRC32 checksum calculation + dump verification: ", "Dump verification method: ", "Output naming scheme: " };
|
|
static const char *nspDumpGameCardMenuItems[] = { "Dump base application NSP", "Dump bundled update NSP", "Dump bundled DLC NSP" };
|
|
static const char *nspDumpSdCardEmmcMenuItems[] = { "Dump base application NSP", "Dump installed update NSP", "Dump installed DLC NSP" };
|
|
static const char *nspAppDumpMenuItems[] = { "Start NSP dump process", "Split output dump (FAT32 support): ", "Verify dump using No-Intro database: ", "Remove console specific data: ", "Generate ticket-less dump: ", "Change NPDM RSA key/sig in Program NCA: ", "Base application to dump: ", "Output naming scheme: " };
|
|
static const char *nspPatchDumpMenuItems[] = { "Start NSP dump process", "Split output dump (FAT32 support): ", "Verify dump using No-Intro database: ", "Remove console specific data: ", "Generate ticket-less dump: ", "Change NPDM RSA key/sig in Program NCA: ", "Dump delta fragments: ", "Update to dump: ", "Output naming scheme: " };
|
|
static const char *nspAddOnDumpMenuItems[] = { "Start NSP dump process", "Split output dump (FAT32 support): ", "Verify dump using No-Intro database: ", "Remove console specific data: ", "Generate ticket-less dump: ", "DLC to dump: ", "Output naming scheme: " };
|
|
static const char *hfs0MenuItems[] = { "Raw HFS0 partition dump", "HFS0 partition data dump", "Browse HFS0 partitions" };
|
|
static const char *hfs0PartitionDumpType1MenuItems[] = { "Dump HFS0 partition 0 (Update)", "Dump HFS0 partition 1 (Normal)", "Dump HFS0 partition 2 (Secure)" };
|
|
static const char *hfs0PartitionDumpType2MenuItems[] = { "Dump HFS0 partition 0 (Update)", "Dump HFS0 partition 1 (Logo)", "Dump HFS0 partition 2 (Normal)", "Dump HFS0 partition 3 (Secure)" };
|
|
static const char *hfs0BrowserType1MenuItems[] = { "Browse HFS0 partition 0 (Update)", "Browse HFS0 partition 1 (Normal)", "Browse HFS0 partition 2 (Secure)" };
|
|
static const char *hfs0BrowserType2MenuItems[] = { "Browse HFS0 partition 0 (Update)", "Browse HFS0 partition 1 (Logo)", "Browse HFS0 partition 2 (Normal)", "Browse HFS0 partition 3 (Secure)" };
|
|
static const char *exeFsMenuItems[] = { "ExeFS section data dump", "Browse ExeFS section", "Split files bigger than 4 GiB (FAT32 support): ", "Save data to CFW directory (LayeredFS): ", "Use update: " };
|
|
static const char *exeFsSectionDumpMenuItems[] = { "Start ExeFS data dump process", "Base application to dump: ", "Use update: " };
|
|
static const char *exeFsSectionBrowserMenuItems[] = { "Browse ExeFS section", "Base application to browse: ", "Use update: " };
|
|
static const char *romFsMenuItems[] = { "RomFS section data dump", "Browse RomFS section", "Split files bigger than 4 GiB (FAT32 support): ", "Save data to CFW directory (LayeredFS): ", "Use update/DLC: " };
|
|
static const char *romFsSectionDumpMenuItems[] = { "Start RomFS data dump process", "Base application to dump: ", "Use update/DLC: " };
|
|
static const char *romFsSectionBrowserMenuItems[] = { "Browse RomFS section", "Base application to browse: ", "Use update/DLC: " };
|
|
static const char *sdCardEmmcMenuItems[] = { "Nintendo Submission Package (NSP) dump", "ExeFS options", "RomFS options", "Ticket options" };
|
|
static const char *batchModeMenuItems[] = { "Start batch dump process", "Dump base applications: ", "Dump updates: ", "Dump DLCs: ", "Split output dumps (FAT32 support): ", "Remove console specific data: ", "Generate ticket-less dumps: ", "Change NPDM RSA key/sig in Program NCA: ", "Dump delta fragments from updates: ", "Skip already dumped titles: ", "Remember dumped titles: ", "Halt dump process on errors: ", "Output naming scheme: ", "Source storage: " };
|
|
static const char *ticketMenuItems[] = { "Start ticket dump", "Remove console specific data: ", "Use ticket from title: " };
|
|
static const char *updateMenuItems[] = { "Update NSWDB.COM XML database", "Update application" };
|
|
|
|
static const char *xciChecksumLookupMethods[] = { "NSWDB.COM XML database (offline)", "No-Intro database lookup (online)" };
|
|
|
|
static const char *xciNamingSchemes[] = { "TitleName v[TitleVersion] ([TitleID])", "TitleName [[TitleID]][v[TitleVersion]]" };
|
|
static const char *nspNamingSchemes[] = { "TitleName v[TitleVersion] ([TitleID]) ([TitleType])", "TitleName [[TitleID]][v[TitleVersion]][[TitleType]]" };
|
|
|
|
void uiFill(int x, int y, int width, int height, u8 r, u8 g, u8 b)
|
|
{
|
|
/* Perform validity checks */
|
|
if (width <= 0 || height <= 0 || (x + width) < 0 || (y + height) < 0 || x >= FB_WIDTH || y >= FB_HEIGHT) return;
|
|
|
|
if (x < 0)
|
|
{
|
|
width += x;
|
|
x = 0;
|
|
}
|
|
|
|
if (y < 0)
|
|
{
|
|
height += y;
|
|
y = 0;
|
|
}
|
|
|
|
if ((x + width) >= FB_WIDTH) width = (FB_WIDTH - x);
|
|
|
|
if ((y + height) >= FB_HEIGHT) height = (FB_HEIGHT - y);
|
|
|
|
if (framebuf == NULL)
|
|
{
|
|
/* Begin new frame */
|
|
u32 stride;
|
|
framebuf = (u32*)framebufferBegin(&fb, &stride);
|
|
framebuf_width = (stride / sizeof(u32));
|
|
}
|
|
|
|
int lx, ly;
|
|
u32 framex, framey;
|
|
|
|
for (ly = 0; ly < height; ly++)
|
|
{
|
|
for (lx = 0; lx < width; lx++)
|
|
{
|
|
framex = (u32)(x + lx);
|
|
framey = (u32)(y + ly);
|
|
|
|
framebuf[(framey * framebuf_width) + framex] = RGBA8_MAXALPHA(r, g, b);
|
|
}
|
|
}
|
|
}
|
|
|
|
void uiDrawIcon(const u8 *icon, int width, int height, int x, int y)
|
|
{
|
|
/* Perform validity checks */
|
|
if (!icon || !width || !height || (x + width) < 0 || (y + height) < 0 || x >= FB_WIDTH || y >= FB_HEIGHT) return;
|
|
|
|
if (x < 0)
|
|
{
|
|
width += x;
|
|
x = 0;
|
|
}
|
|
|
|
if (y < 0)
|
|
{
|
|
height += y;
|
|
y = 0;
|
|
}
|
|
|
|
if ((x + width) >= FB_WIDTH) width = (FB_WIDTH - x);
|
|
|
|
if ((y + height) >= FB_HEIGHT) height = (FB_HEIGHT - y);
|
|
|
|
if (framebuf == NULL)
|
|
{
|
|
/* Begin new frame */
|
|
u32 stride;
|
|
framebuf = (u32*)framebufferBegin(&fb, &stride);
|
|
framebuf_width = (stride / sizeof(u32));
|
|
}
|
|
|
|
int lx, ly;
|
|
u32 framex, framey, pos;
|
|
|
|
for (ly = 0; ly < height; ly++)
|
|
{
|
|
for (lx = 0; lx < width; lx++)
|
|
{
|
|
framex = (u32)(x + lx);
|
|
framey = (u32)(y + ly);
|
|
|
|
pos = (u32)(((ly * width) + lx) * 3);
|
|
|
|
framebuf[(framey * framebuf_width) + framex] = RGBA8_MAXALPHA(icon[pos], icon[pos + 1], icon[pos + 2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool uiLoadJpgFromMem(u8 *rawJpg, size_t rawJpgSize, int expectedWidth, int expectedHeight, int desiredWidth, int desiredHeight, u8 **outBuf)
|
|
{
|
|
if (!rawJpg || !rawJpgSize || !expectedWidth || !expectedHeight || !desiredWidth || !desiredHeight || !outBuf)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: invalid parameters to process JPG image buffer!", __func__);
|
|
return false;
|
|
}
|
|
|
|
int ret, w, h, samp;
|
|
tjhandle _jpegDecompressor = NULL;
|
|
bool success = false;
|
|
|
|
bool foundScalingFactor = false;
|
|
int i, numScalingFactors = 0, pitch;
|
|
tjscalingfactor *scalingFactors = NULL;
|
|
|
|
u8 *jpgScaledBuf = NULL;
|
|
|
|
_jpegDecompressor = tjInitDecompress();
|
|
if (!_jpegDecompressor)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: tjInitDecompress failed!", __func__);
|
|
return success;
|
|
}
|
|
|
|
ret = tjDecompressHeader2(_jpegDecompressor, rawJpg, rawJpgSize, &w, &h, &samp);
|
|
if (ret == -1)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: tjDecompressHeader2 failed! (%d)", __func__, ret);
|
|
goto out;
|
|
}
|
|
|
|
if (w != expectedWidth || h != expectedHeight)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: invalid image width/height!", __func__);
|
|
goto out;
|
|
}
|
|
|
|
scalingFactors = tjGetScalingFactors(&numScalingFactors);
|
|
if (!scalingFactors)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: unable to retrieve scaling factors!", __func__);
|
|
goto out;
|
|
}
|
|
|
|
for(i = 0; i < numScalingFactors; i++)
|
|
{
|
|
if (TJSCALED(expectedWidth, scalingFactors[i]) == desiredWidth && TJSCALED(expectedHeight, scalingFactors[i]) == desiredHeight)
|
|
{
|
|
foundScalingFactor = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundScalingFactor)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: unable to find a valid scaling factor!", __func__);
|
|
goto out;
|
|
}
|
|
|
|
pitch = TJPAD(desiredWidth * tjPixelSize[TJPF_RGB]);
|
|
|
|
jpgScaledBuf = malloc(pitch * desiredHeight);
|
|
if (!jpgScaledBuf)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: unable to allocate memory for the scaled RGB image output!", __func__);
|
|
goto out;
|
|
}
|
|
|
|
ret = tjDecompress2(_jpegDecompressor, rawJpg, rawJpgSize, jpgScaledBuf, desiredWidth, 0, desiredHeight, TJPF_RGB, TJFLAG_ACCURATEDCT);
|
|
if (ret == -1)
|
|
{
|
|
free(jpgScaledBuf);
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: tjDecompress2 failed! (%d)", __func__, ret);
|
|
goto out;
|
|
}
|
|
|
|
*outBuf = jpgScaledBuf;
|
|
success = true;
|
|
|
|
out:
|
|
tjDestroy(_jpegDecompressor);
|
|
|
|
return success;
|
|
}
|
|
|
|
bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeight, int desiredWidth, int desiredHeight, u8 **outBuf)
|
|
{
|
|
if (!filename || !desiredWidth || !desiredHeight || !outBuf)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: invalid parameters to process JPG image file!", __func__);
|
|
return false;
|
|
}
|
|
|
|
u8 *buf = NULL;
|
|
FILE *fp = NULL;
|
|
size_t filesize = 0, read = 0;
|
|
|
|
fp = fopen(filename, "rb");
|
|
if (!fp)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: failed to open file \"%s\"!", __func__, filename);
|
|
return false;
|
|
}
|
|
|
|
fseek(fp, 0, SEEK_END);
|
|
filesize = ftell(fp);
|
|
rewind(fp);
|
|
|
|
if (!filesize)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: file \"%s\" is empty!", __func__, filename);
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
|
|
buf = malloc(filesize);
|
|
if (!buf)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: error allocating memory for image \"%s\"!", __func__, filename);
|
|
fclose(fp);
|
|
return false;
|
|
}
|
|
|
|
read = fread(buf, 1, filesize, fp);
|
|
fclose(fp);
|
|
|
|
if (read != filesize)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s: error reading image \"%s\"!", __func__, filename);
|
|
free(buf);
|
|
return false;
|
|
}
|
|
|
|
bool ret = uiLoadJpgFromMem(buf, filesize, expectedWidth, expectedHeight, desiredWidth, desiredHeight, outBuf);
|
|
|
|
free(buf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void uiDrawChar(FT_Bitmap *bitmap, int x, int y, u8 r, u8 g, u8 b)
|
|
{
|
|
if (framebuf == NULL) return;
|
|
|
|
u32 framex, framey, framebuf_offset;
|
|
u32 tmpx, tmpy;
|
|
u8 *imageptr = bitmap->buffer;
|
|
|
|
u8 src_val;
|
|
float opacity;
|
|
|
|
u8 fontR, fontG, fontB;
|
|
|
|
if (bitmap->pixel_mode != FT_PIXEL_MODE_GRAY) return;
|
|
|
|
for(tmpy = 0; tmpy < bitmap->rows; tmpy++)
|
|
{
|
|
for (tmpx = 0; tmpx < bitmap->width; tmpx++)
|
|
{
|
|
framex = (x + tmpx);
|
|
framey = (y + tmpy);
|
|
|
|
if (framex >= FB_WIDTH || framey >= FB_HEIGHT) continue;
|
|
|
|
framebuf_offset = ((framey * framebuf_width) + framex);
|
|
|
|
src_val = imageptr[tmpx];
|
|
if (!src_val)
|
|
{
|
|
/* Render background color */
|
|
framebuf[framebuf_offset] = (highlight ? RGBA8_MAXALPHA(hlBgColors[0], hlBgColors[1], hlBgColors[2]) : RGBA8_MAXALPHA(bgColors[0], bgColors[1], bgColors[2]));
|
|
} else {
|
|
/* Calculate alpha (opacity) */
|
|
opacity = (src_val / 255.0);
|
|
|
|
fontR = (r * opacity + (1 - opacity) * (highlight ? hlBgColors[0] : bgColors[0]));
|
|
fontG = (g * opacity + (1 - opacity) * (highlight ? hlBgColors[1] : bgColors[1]));
|
|
fontB = (b * opacity + (1 - opacity) * (highlight ? hlBgColors[2] : bgColors[2]));
|
|
|
|
framebuf[framebuf_offset] = RGBA8_MAXALPHA(fontR, fontG, fontB);
|
|
}
|
|
}
|
|
|
|
imageptr += bitmap->pitch;
|
|
}
|
|
}
|
|
|
|
void uiDrawString(int x, int y, u8 r, u8 g, u8 b, const char *fmt, ...)
|
|
{
|
|
if (!fmt || !*fmt) return;
|
|
|
|
char string[NAME_BUF_LEN] = {'\0'};
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(string, MAX_CHARACTERS(string), fmt, args);
|
|
va_end(args);
|
|
|
|
u32 tmpx = (x < 8 ? 8 : x);
|
|
u32 tmpy = (font_height + (y < 8 ? 8 : y));
|
|
|
|
FT_Error ret = 0;
|
|
FT_UInt glyph_index = 0;
|
|
|
|
u32 i, j;
|
|
u32 str_size = strlen(string);
|
|
u32 tmpchar;
|
|
ssize_t unitcount = 0;
|
|
|
|
if (framebuf == NULL)
|
|
{
|
|
/* Begin new frame */
|
|
u32 stride;
|
|
framebuf = (u32*)framebufferBegin(&fb, &stride);
|
|
framebuf_width = (stride / sizeof(u32));
|
|
}
|
|
|
|
for(i = 0; i < str_size;)
|
|
{
|
|
unitcount = decode_utf8(&tmpchar, (const u8*)&string[i]);
|
|
if (unitcount <= 0) break;
|
|
i += unitcount;
|
|
|
|
if (tmpchar == '\n')
|
|
{
|
|
tmpx = 8;
|
|
tmpy += LINE_HEIGHT;
|
|
breaks++;
|
|
continue;
|
|
} else
|
|
if (tmpchar == '\t')
|
|
{
|
|
tmpx += (font_height * TAB_WIDTH);
|
|
continue;
|
|
} else
|
|
if (tmpchar == '\r')
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for(j = 0; j < PlSharedFontType_Total; j++)
|
|
{
|
|
glyph_index = FT_Get_Char_Index(sharedFontsFaces[j], tmpchar);
|
|
if (glyph_index) break;
|
|
}
|
|
|
|
if (!glyph_index && j == PlSharedFontType_Total) j = 0;
|
|
|
|
ret = FT_Load_Glyph(sharedFontsFaces[j], glyph_index, FT_LOAD_DEFAULT);
|
|
if (ret == 0) ret = FT_Render_Glyph(sharedFontsFaces[j]->glyph, FT_RENDER_MODE_NORMAL);
|
|
|
|
if (ret) break;
|
|
|
|
if ((tmpx + (sharedFontsFaces[j]->glyph->advance.x >> 6)) > (FB_WIDTH - 8))
|
|
{
|
|
tmpx = 8;
|
|
tmpy += LINE_HEIGHT;
|
|
breaks++;
|
|
}
|
|
|
|
uiDrawChar(&(sharedFontsFaces[j]->glyph->bitmap), tmpx + sharedFontsFaces[j]->glyph->bitmap_left, tmpy - sharedFontsFaces[j]->glyph->bitmap_top, r, g, b);
|
|
|
|
tmpx += (sharedFontsFaces[j]->glyph->advance.x >> 6);
|
|
tmpy += (sharedFontsFaces[j]->glyph->advance.y >> 6);
|
|
}
|
|
}
|
|
|
|
u32 uiGetStrWidth(const char *fmt, ...)
|
|
{
|
|
if (!fmt || !*fmt) return 0;
|
|
|
|
char string[NAME_BUF_LEN] = {'\0'};
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(string, MAX_CHARACTERS(string), fmt, args);
|
|
va_end(args);
|
|
|
|
FT_Error ret = 0;
|
|
FT_UInt glyph_index = 0;
|
|
|
|
u32 i, j;
|
|
u32 str_size = strlen(string);
|
|
u32 tmpchar;
|
|
ssize_t unitcount = 0;
|
|
u32 width = 0;
|
|
|
|
for(i = 0; i < str_size;)
|
|
{
|
|
unitcount = decode_utf8(&tmpchar, (const u8*)&string[i]);
|
|
if (unitcount <= 0) break;
|
|
i += unitcount;
|
|
|
|
if (tmpchar == '\n' || tmpchar == '\r')
|
|
{
|
|
break;
|
|
} else
|
|
if (tmpchar == '\t')
|
|
{
|
|
width += (font_height * TAB_WIDTH);
|
|
continue;
|
|
}
|
|
|
|
for(j = 0; j < PlSharedFontType_Total; j++)
|
|
{
|
|
glyph_index = FT_Get_Char_Index(sharedFontsFaces[j], tmpchar);
|
|
if (glyph_index) break;
|
|
}
|
|
|
|
if (!glyph_index && j == PlSharedFontType_Total) j = 0;
|
|
|
|
ret = FT_Load_Glyph(sharedFontsFaces[j], glyph_index, FT_LOAD_DEFAULT);
|
|
if (ret == 0) ret = FT_Render_Glyph(sharedFontsFaces[j]->glyph, FT_RENDER_MODE_NORMAL);
|
|
|
|
if (ret) break;
|
|
|
|
width += (sharedFontsFaces[j]->glyph->advance.x >> 6);
|
|
}
|
|
|
|
return width;
|
|
}
|
|
|
|
void uiRefreshDisplay()
|
|
{
|
|
if (framebuf != NULL)
|
|
{
|
|
framebufferEnd(&fb);
|
|
framebuf = NULL;
|
|
framebuf_width = 0;
|
|
}
|
|
}
|
|
|
|
void uiStatusMsg(const char *fmt, ...)
|
|
{
|
|
statusMessageFadeout = 2500;
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(statusMessage, MAX_CHARACTERS(statusMessage), fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
void uiUpdateStatusMsg()
|
|
{
|
|
if (!strlen(statusMessage) || !statusMessageFadeout) return;
|
|
|
|
uiFill(0, FB_HEIGHT - (font_height * 2), FB_WIDTH, font_height * 2, BG_COLOR_RGB);
|
|
|
|
if ((statusMessageFadeout - 4) > bgColors[0])
|
|
{
|
|
int fadeout = (statusMessageFadeout > 255 ? 255 : statusMessageFadeout);
|
|
uiDrawString(STRING_X_POS, FB_HEIGHT - (font_height * 2), fadeout, fadeout, fadeout, statusMessage);
|
|
statusMessageFadeout -= 4;
|
|
} else {
|
|
statusMessageFadeout = 0;
|
|
}
|
|
}
|
|
|
|
void uiClearStatusMsg()
|
|
{
|
|
statusMessageFadeout = 0;
|
|
statusMessage[0] = '\0';
|
|
}
|
|
|
|
void uiPleaseWait(u8 wait)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Please wait...");
|
|
uiRefreshDisplay();
|
|
if (wait) delay(wait);
|
|
}
|
|
|
|
void uiClearScreen()
|
|
{
|
|
uiFill(0, 0, FB_WIDTH, FB_HEIGHT, BG_COLOR_RGB);
|
|
}
|
|
|
|
void uiPrintHeadline()
|
|
{
|
|
breaks = 0;
|
|
uiClearScreen();
|
|
uiDrawString(STRING_DEFAULT_POS, FONT_COLOR_RGB, appHeadline);
|
|
}
|
|
|
|
void uiPrintOption(int x, int y, int endPosition, bool leftArrow, bool rightArrow, int r, int g, int b, const char *fmt, ...)
|
|
{
|
|
if (x < 8 || x > OPTIONS_X_END_POS || y < 8 || y > (FB_HEIGHT - 8) || endPosition < OPTIONS_X_END_POS || endPosition > (FB_WIDTH - 8) || !fmt || !*fmt) return;
|
|
|
|
int xpos = x;
|
|
char option[NAME_BUF_LEN] = {'\0'};
|
|
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(option, MAX_CHARACTERS(option), fmt, args);
|
|
va_end(args);
|
|
|
|
u32 optionStrWidth = uiGetStrWidth(option);
|
|
|
|
if (leftArrow) uiDrawString(xpos, y, FONT_COLOR_RGB, "<");
|
|
|
|
xpos += uiGetStrWidth("<");
|
|
|
|
xpos += (((endPosition - xpos) / 2) - (optionStrWidth / 2));
|
|
|
|
uiDrawString(xpos, y, r, g, b, option);
|
|
|
|
if (rightArrow)
|
|
{
|
|
xpos = endPosition;
|
|
|
|
uiDrawString(xpos, y, FONT_COLOR_RGB, ">");
|
|
}
|
|
}
|
|
|
|
void uiTruncateOptionStr(char *str, int x, int y, int endPosition)
|
|
{
|
|
if (!str || !strlen(str) || x < 8 || x > OPTIONS_X_END_POS || y < 8 || y > (FB_HEIGHT - 8) || endPosition < OPTIONS_X_END_POS || endPosition > (FB_WIDTH - 8)) return;
|
|
|
|
int xpos = x;
|
|
char *option = str;
|
|
|
|
u32 optionStrWidth = uiGetStrWidth(option);
|
|
u32 limit = (u32)(endPosition - xpos - (font_height * 2));
|
|
|
|
// Check if we're dealing with a long title selector string
|
|
if (optionStrWidth >= limit)
|
|
{
|
|
while(optionStrWidth >= limit)
|
|
{
|
|
option++;
|
|
optionStrWidth = uiGetStrWidth(option);
|
|
}
|
|
|
|
option[0] = option[1] = option[2] = '.';
|
|
|
|
memmove(str, option, strlen(option));
|
|
|
|
str[strlen(option)] = '\0';
|
|
}
|
|
}
|
|
|
|
bool uiInit()
|
|
{
|
|
Result result = 0;
|
|
FT_Error ret = 0;
|
|
|
|
u32 i;
|
|
bool success = false;
|
|
char tmp[256] = {'\0'};
|
|
|
|
/* Set initial UI state */
|
|
uiState = stateMainMenu;
|
|
menuType = MENUTYPE_MAIN;
|
|
cursor = 0;
|
|
scroll = 0;
|
|
|
|
/* Clear FreeType init flags */
|
|
memset(ft_faces_init, 0, PlSharedFontType_Total);
|
|
|
|
/* Retrieve shared fonts */
|
|
for(i = 0; i < PlSharedFontType_Total; i++)
|
|
{
|
|
result = plGetSharedFontByType(&sharedFonts[i], i);
|
|
if (R_FAILED(result)) break;
|
|
}
|
|
|
|
if (R_FAILED(result))
|
|
{
|
|
consoleErrorScreen("%s: plGetSharedFontByType() failed to retrieve shared font #%u! (0x%08X)", __func__, i, result);
|
|
goto out;
|
|
}
|
|
|
|
/* Initialize FreeType */
|
|
ret = FT_Init_FreeType(&library);
|
|
if (ret)
|
|
{
|
|
consoleErrorScreen("%s: FT_Init_FreeType() failed! (%d)", __func__, ret);
|
|
goto out;
|
|
}
|
|
|
|
ft_lib_init = true;
|
|
|
|
/* Create memory faces for the shared fonts */
|
|
for(i = 0; i < PlSharedFontType_Total; i++)
|
|
{
|
|
ret = FT_New_Memory_Face(library, sharedFonts[i].address, sharedFonts[i].size, 0, &sharedFontsFaces[i]);
|
|
if (ret) break;
|
|
ft_faces_init[i] = true;
|
|
}
|
|
|
|
if (ret)
|
|
{
|
|
consoleErrorScreen("%s: FT_New_Memory_Face() failed to create memory face for shared font #%u! (%d)", __func__, i, ret);
|
|
goto out;
|
|
}
|
|
|
|
/* Set character size for all shared fonts */
|
|
for(i = 0; i < PlSharedFontType_Total; i++)
|
|
{
|
|
ret = FT_Set_Char_Size(sharedFontsFaces[i], 0, CHAR_PT_SIZE * 64, SCREEN_DPI_CNT, SCREEN_DPI_CNT);
|
|
if (ret) break;
|
|
}
|
|
|
|
if (ret)
|
|
{
|
|
consoleErrorScreen("%s: FT_Set_Char_Size() failed to set character size for shared font #%u! (%d)", __func__, i, ret);
|
|
goto out;
|
|
}
|
|
|
|
/* Store font height */
|
|
font_height = (sharedFontsFaces[0]->size->metrics.height / 64);
|
|
|
|
/* Mount Application's RomFS */
|
|
result = romfsInit();
|
|
if (R_FAILED(result))
|
|
{
|
|
consoleErrorScreen("%s: romfsInit() failed! (0x%08X)", __func__, result);
|
|
goto out;
|
|
}
|
|
|
|
romfs_init = true;
|
|
|
|
if (!uiLoadJpgFromFile(dirNormalIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &dirNormalIconBuf))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to load directory icon (normal)!", __func__);
|
|
strcat(strbuf, tmp);
|
|
consoleErrorScreen(strbuf);
|
|
goto out;
|
|
}
|
|
|
|
if (!uiLoadJpgFromFile(dirHighlightIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &dirHighlightIconBuf))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to load directory icon (highlighted)!", __func__);
|
|
strcat(strbuf, tmp);
|
|
consoleErrorScreen(strbuf);
|
|
goto out;
|
|
}
|
|
|
|
if (!uiLoadJpgFromFile(fileNormalIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &fileNormalIconBuf))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to load file icon (normal)!", __func__);
|
|
strcat(strbuf, tmp);
|
|
consoleErrorScreen(strbuf);
|
|
goto out;
|
|
}
|
|
|
|
if (!uiLoadJpgFromFile(fileHighlightIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &fileHighlightIconBuf))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to load file icon (highlighted)!", __func__);
|
|
strcat(strbuf, tmp);
|
|
consoleErrorScreen(strbuf);
|
|
goto out;
|
|
}
|
|
|
|
if (!uiLoadJpgFromFile(enabledNormalIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &enabledNormalIconBuf))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to load enabled icon (normal)!", __func__);
|
|
strcat(strbuf, tmp);
|
|
consoleErrorScreen(strbuf);
|
|
goto out;
|
|
}
|
|
|
|
if (!uiLoadJpgFromFile(enabledHighlightIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &enabledHighlightIconBuf))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to load enabled icon (highlighted)!", __func__);
|
|
strcat(strbuf, tmp);
|
|
consoleErrorScreen(strbuf);
|
|
goto out;
|
|
}
|
|
|
|
if (!uiLoadJpgFromFile(disabledNormalIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &disabledNormalIconBuf))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to load disabled icon (normal)!", __func__);
|
|
strcat(strbuf, tmp);
|
|
consoleErrorScreen(strbuf);
|
|
goto out;
|
|
}
|
|
|
|
if (!uiLoadJpgFromFile(disabledHighlightIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &disabledHighlightIconBuf))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "\n%s: failed to load disabled icon (highlighted)!", __func__);
|
|
strcat(strbuf, tmp);
|
|
consoleErrorScreen(strbuf);
|
|
goto out;
|
|
}
|
|
|
|
/* Unmount Application's RomFS */
|
|
romfsExit();
|
|
romfs_init = false;
|
|
|
|
/* Create framebuffer */
|
|
framebufferCreate(&fb, nwindowGetDefault(), FB_WIDTH, FB_HEIGHT, PIXEL_FORMAT_RGBA_8888, 2);
|
|
framebufferMakeLinear(&fb);
|
|
fb_init = true;
|
|
|
|
/* Clear screen */
|
|
uiClearScreen();
|
|
|
|
/* Set output status */
|
|
success = true;
|
|
|
|
out:
|
|
return success;
|
|
}
|
|
|
|
void uiDeinit()
|
|
{
|
|
/* Free framebuffer object */
|
|
if (fb_init) framebufferClose(&fb);
|
|
|
|
/* Free enabled/disabled icons (batch mode summary list) */
|
|
if (disabledHighlightIconBuf) free(disabledHighlightIconBuf);
|
|
if (disabledNormalIconBuf) free(disabledNormalIconBuf);
|
|
if (enabledHighlightIconBuf) free(enabledHighlightIconBuf);
|
|
if (enabledNormalIconBuf) free(enabledNormalIconBuf);
|
|
|
|
/* Free directory/file icons */
|
|
if (fileHighlightIconBuf) free(fileHighlightIconBuf);
|
|
if (fileNormalIconBuf) free(fileNormalIconBuf);
|
|
if (dirHighlightIconBuf) free(dirHighlightIconBuf);
|
|
if (dirNormalIconBuf) free(dirNormalIconBuf);
|
|
|
|
/* Unmount Application's RomFS */
|
|
if (romfs_init) romfsExit();
|
|
|
|
/* Free FreeType resources */
|
|
for(u32 i = 0; i < PlSharedFontType_Total; i++)
|
|
{
|
|
if (ft_faces_init[i]) FT_Done_Face(sharedFontsFaces[i]);
|
|
}
|
|
|
|
if (ft_lib_init) FT_Done_FreeType(library);
|
|
}
|
|
|
|
void uiSetState(UIState state)
|
|
{
|
|
if (uiState == stateSdCardEmmcMenu)
|
|
{
|
|
if (state != stateMainMenu)
|
|
{
|
|
// Store current cursor/scroll values
|
|
titleListCursor = cursor;
|
|
titleListScroll = scroll;
|
|
} else {
|
|
// Reset title list cursor/scroll values
|
|
titleListCursor = 0;
|
|
titleListScroll = 0;
|
|
}
|
|
} else
|
|
if (uiState == stateSdCardEmmcOrphanPatchAddOnMenu)
|
|
{
|
|
if (state != stateSdCardEmmcMenu)
|
|
{
|
|
// Store current cursor/scroll values
|
|
orphanListCursor = cursor;
|
|
orphanListScroll = scroll;
|
|
} else {
|
|
// Reset orphan list cursor/scroll values
|
|
orphanListCursor = 0;
|
|
orphanListScroll = 0;
|
|
}
|
|
} else
|
|
if (uiState == stateHfs0Browser || uiState == stateExeFsSectionBrowser || uiState == stateRomFsSectionBrowser)
|
|
{
|
|
if ((uiState == stateHfs0Browser && state != stateHfs0BrowserMenu) || (uiState == stateExeFsSectionBrowser && state != stateExeFsSectionBrowserMenu && state != stateExeFsMenu) || (uiState == stateRomFsSectionBrowser && state != stateRomFsSectionBrowserMenu && state != stateRomFsMenu && state != stateRomFsSectionBrowserChangeDir))
|
|
{
|
|
// Store current cursor/scroll values
|
|
browserCursor = cursor;
|
|
browserScroll = scroll;
|
|
} else {
|
|
// Reset browser cursor/scroll values
|
|
browserCursor = 0;
|
|
browserScroll = 0;
|
|
}
|
|
}
|
|
|
|
uiState = state;
|
|
|
|
if (state == stateSdCardEmmcMenu)
|
|
{
|
|
// Override cursor/scroll values
|
|
cursor = titleListCursor;
|
|
scroll = titleListScroll;
|
|
} else
|
|
if (state == stateSdCardEmmcOrphanPatchAddOnMenu)
|
|
{
|
|
// Override cursor/scroll values
|
|
cursor = orphanListCursor;
|
|
scroll = orphanListScroll;
|
|
} else
|
|
if (uiState == stateHfs0Browser || uiState == stateExeFsSectionBrowser || uiState == stateRomFsSectionBrowser)
|
|
{
|
|
// Override cursor/scroll values
|
|
cursor = browserCursor;
|
|
scroll = browserScroll;
|
|
} else {
|
|
cursor = 0;
|
|
scroll = 0;
|
|
}
|
|
|
|
titleSelectorStr[0] = '\0';
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
|
|
UIState uiGetState()
|
|
{
|
|
return uiState;
|
|
}
|
|
|
|
UIResult uiProcess()
|
|
{
|
|
UIResult res = resultNone;
|
|
|
|
int i, j;
|
|
|
|
const char **menu = NULL;
|
|
int menuItemsCount = 0;
|
|
|
|
u64 keysDown = 0, keysHeld = 0;
|
|
|
|
int scrollAmount = 0;
|
|
bool scrollWithKeysDown = false;
|
|
|
|
u32 patch, addon, xpos, ypos, startYPos;
|
|
|
|
int maxElements = (uiState == stateSdCardEmmcMenu ? SDCARD_MAX_ELEMENTS : (uiState == stateSdCardEmmcOrphanPatchAddOnMenu ? ORPHAN_MAX_ELEMENTS : (uiState == stateHfs0Browser ? HFS0_MAX_ELEMENTS : ((uiState == stateExeFsSectionBrowser || uiState == stateRomFsSectionBrowser) ? ROMFS_MAX_ELEMENTS : (uiState == stateSdCardEmmcBatchModeMenu ? BATCH_MAX_ELEMENTS : COMMON_MAX_ELEMENTS)))));
|
|
|
|
const char *upwardsArrow = UPWARDS_ARROW;
|
|
const char *downwardsArrow = DOWNWARDS_ARROW;
|
|
|
|
bool forcedXciDump = false;
|
|
|
|
uiPrintHeadline();
|
|
loadTitleInfo();
|
|
|
|
if (uiState == stateMainMenu || uiState == stateGameCardMenu || uiState == stateXciDumpMenu || uiState == stateNspDumpMenu || uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu || uiState == stateHfs0Menu || uiState == stateRawHfs0PartitionDumpMenu || uiState == stateHfs0PartitionDataDumpMenu || uiState == stateHfs0BrowserMenu || uiState == stateHfs0Browser || uiState == stateExeFsMenu || uiState == stateExeFsSectionDataDumpMenu || uiState == stateExeFsSectionBrowserMenu || uiState == stateExeFsSectionBrowser || uiState == stateRomFsMenu || uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu || uiState == stateRomFsSectionBrowser || uiState == stateSdCardEmmcMenu || uiState == stateSdCardEmmcTitleMenu || uiState == stateSdCardEmmcOrphanPatchAddOnMenu || uiState == stateSdCardEmmcBatchModeMenu || uiState == stateTicketMenu || uiState == stateUpdateMenu)
|
|
{
|
|
switch(menuType)
|
|
{
|
|
case MENUTYPE_MAIN:
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsCommon);
|
|
break;
|
|
case MENUTYPE_GAMECARD:
|
|
if (uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) > 1)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsRomFs);
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, (!gameCardInfo.isInserted ? appControlsNoContent : (titleAppCount > 1 ? appControlsGameCardMultiApp : appControlsCommon)));
|
|
}
|
|
break;
|
|
case MENUTYPE_SDCARD_EMMC:
|
|
if (uiState == stateSdCardEmmcBatchModeMenu)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsCommon);
|
|
} else {
|
|
if (!orphanMode)
|
|
{
|
|
if (titleAppCount)
|
|
{
|
|
if (uiState == stateSdCardEmmcMenu && (calculateOrphanPatchOrAddOnCount(false) || calculateOrphanPatchOrAddOnCount(true)))
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsSdCardEmmcFull);
|
|
breaks += 2;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Hint: installed updates/DLCs for gamecard titles can be found in the orphan title list (press the " NINTENDO_FONT_Y " button).");
|
|
} else
|
|
if (uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) > 1)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsRomFs);
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsSdCardEmmcNoOrphan);
|
|
}
|
|
} else {
|
|
if (titlePatchCount || titleAddOnCount)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsSdCardEmmcNoApp);
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsNoContent);
|
|
}
|
|
}
|
|
} else {
|
|
if (uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) > 1)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsRomFs);
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, appControlsCommon);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
breaks += 2;
|
|
}
|
|
|
|
if (uiState != stateSdCardEmmcBatchDump)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Free SD card space: %s (%lu bytes).", freeSpaceStr, freeSpace);
|
|
breaks += 2;
|
|
}
|
|
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (!gameCardInfo.isInserted || !gameCardInfo.rootHfs0Header || (gameCardInfo.hfs0PartitionCnt != GAMECARD_TYPE1_PARTITION_CNT && gameCardInfo.hfs0PartitionCnt != GAMECARD_TYPE2_PARTITION_CNT) || !titleAppCount || !baseAppEntries)
|
|
{
|
|
if (gameCardInfo.isInserted)
|
|
{
|
|
if (gameCardInfo.rootHfs0Header)
|
|
{
|
|
if (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT || gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE2_PARTITION_CNT)
|
|
{
|
|
forcedXciDump = true;
|
|
|
|
if (titleAppCount)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to retrieve title entries from the inserted gamecard!");
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: gamecard application count is zero!");
|
|
}
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unknown root HFS0 header partition count! (%u)", gameCardInfo.hfs0PartitionCnt);
|
|
}
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Error: unable to get root HFS0 header data!");
|
|
}
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "Gamecard is not inserted!");
|
|
}
|
|
|
|
breaks += 2;
|
|
|
|
if (gameCardInfo.isInserted)
|
|
{
|
|
if (forcedXciDump)
|
|
{
|
|
if (strlen(gameCardInfo.updateVersionStr))
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Bundled FW Update: %s", gameCardInfo.updateVersionStr);
|
|
breaks++;
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "In order to be able to parse any kind of metadata from this gamecard and/or dump its contents to NSPs,\nmake sure your console is at least on this FW version.");
|
|
breaks += 2;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Press " NINTENDO_FONT_Y " to dump the gamecard to \"gamecard.xci\".");
|
|
} else {
|
|
if (!gameCardInfo.rootHfs0Header) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Are you using \"nogc\" spoofing in your CFW? If so, please consider this option disables all gamecard I/O.");
|
|
}
|
|
}
|
|
|
|
uiUpdateStatusMsg();
|
|
uiRefreshDisplay();
|
|
|
|
res = resultShowGameCardMenu;
|
|
|
|
scanPads();
|
|
keysDown = getButtonsDown();
|
|
|
|
// Exit
|
|
if (keysDown & HidNpadButton_Plus) res = resultExit;
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B)
|
|
{
|
|
res = resultShowMainMenu;
|
|
menuType = MENUTYPE_MAIN;
|
|
}
|
|
|
|
// Forced XCI dump
|
|
if ((keysDown & HidNpadButton_Y) && forcedXciDump)
|
|
{
|
|
uiPrintHeadline();
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[0]);
|
|
breaks++;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
// Set default options
|
|
xciOptions xciDumpCfg;
|
|
memset(&xciDumpCfg, 0, sizeof(xciOptions));
|
|
|
|
xciDumpCfg.isFat32 = true;
|
|
xciDumpCfg.keepCert = true;
|
|
|
|
dumpNXCardImage(&xciDumpCfg);
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
}
|
|
|
|
return res;
|
|
}
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
if (!titleAppCount && !orphanMode)
|
|
{
|
|
if (titlePatchCount || titleAddOnCount)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "No base applications available in the SD card / eMMC storage!");
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Use the " NINTENDO_FONT_Y " button to dump installed content with missing base applications!");
|
|
} else {
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_ERROR_RGB, "No titles available in the SD card / eMMC storage!");
|
|
}
|
|
|
|
uiUpdateStatusMsg();
|
|
uiRefreshDisplay();
|
|
|
|
res = resultShowSdCardEmmcMenu;
|
|
|
|
scanPads();
|
|
keysDown = getButtonsDown();
|
|
|
|
// Exit
|
|
if (keysDown & HidNpadButton_Plus) res = resultExit;
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B)
|
|
{
|
|
res = resultShowMainMenu;
|
|
menuType = MENUTYPE_MAIN;
|
|
}
|
|
|
|
// Dump installed content with missing base application
|
|
if ((titlePatchCount || titleAddOnCount) && (keysDown & HidNpadButton_Y))
|
|
{
|
|
res = resultShowSdCardEmmcOrphanPatchAddOnMenu;
|
|
orphanMode = true;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
}
|
|
|
|
if (uiState == stateMainMenu || uiState == stateGameCardMenu || uiState == stateXciDumpMenu || uiState == stateNspDumpMenu || uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu || uiState == stateHfs0Menu || uiState == stateRawHfs0PartitionDumpMenu || uiState == stateHfs0PartitionDataDumpMenu || uiState == stateHfs0BrowserMenu || uiState == stateHfs0Browser || uiState == stateExeFsMenu || uiState == stateExeFsSectionDataDumpMenu || uiState == stateExeFsSectionBrowserMenu || uiState == stateExeFsSectionBrowser || uiState == stateRomFsMenu || uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu || uiState == stateRomFsSectionBrowser || uiState == stateSdCardEmmcMenu || uiState == stateSdCardEmmcTitleMenu || uiState == stateSdCardEmmcOrphanPatchAddOnMenu || uiState == stateSdCardEmmcBatchModeMenu || uiState == stateTicketMenu || uiState == stateUpdateMenu)
|
|
{
|
|
if ((menuType == MENUTYPE_GAMECARD && uiState != stateHfs0Browser && uiState != stateExeFsSectionBrowser && uiState != stateRomFsSectionBrowser) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && uiState != stateSdCardEmmcMenu && uiState != stateSdCardEmmcBatchModeMenu && uiState != stateExeFsSectionBrowser && uiState != stateRomFsSectionBrowser))
|
|
{
|
|
/* Print application info */
|
|
xpos = STRING_X_POS;
|
|
ypos = STRING_Y_POS(breaks);
|
|
startYPos = ypos;
|
|
|
|
/* Draw icon */
|
|
if (baseAppEntries[selectedAppInfoIndex].icon != NULL)
|
|
{
|
|
uiDrawIcon(baseAppEntries[selectedAppInfoIndex].icon, NACP_ICON_DOWNSCALED, NACP_ICON_DOWNSCALED, xpos, ypos);
|
|
xpos += (NACP_ICON_DOWNSCALED + 8);
|
|
ypos += 8;
|
|
}
|
|
|
|
if (strlen(baseAppEntries[selectedAppInfoIndex].name))
|
|
{
|
|
uiDrawString(xpos, ypos, FONT_COLOR_SUCCESS_RGB, "Name: %s", baseAppEntries[selectedAppInfoIndex].name);
|
|
ypos += LINE_HEIGHT;
|
|
}
|
|
|
|
if (strlen(baseAppEntries[selectedAppInfoIndex].author))
|
|
{
|
|
uiDrawString(xpos, ypos, FONT_COLOR_SUCCESS_RGB, "Publisher: %s", baseAppEntries[selectedAppInfoIndex].author);
|
|
ypos += LINE_HEIGHT;
|
|
}
|
|
|
|
uiDrawString(xpos, ypos, FONT_COLOR_SUCCESS_RGB, "Title ID: %016lX", baseAppEntries[selectedAppInfoIndex].titleId);
|
|
|
|
if (titlePatchCount > 0)
|
|
{
|
|
u32 patchCnt = 0;
|
|
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s update(s): v", (menuType == MENUTYPE_GAMECARD ? "Bundled" : "Installed"));
|
|
|
|
for(patch = 0; patch < titlePatchCount; patch++)
|
|
{
|
|
if (checkIfPatchOrAddOnBelongsToBaseApplication(patch, selectedAppInfoIndex, false) && ((menuType == MENUTYPE_GAMECARD && baseAppEntries[selectedAppInfoIndex].storageId == patchEntries[patch].storageId) || menuType == MENUTYPE_SDCARD_EMMC))
|
|
{
|
|
if (patchCnt > 0) strcat(strbuf, ", v");
|
|
|
|
strcat(strbuf, patchEntries[patch].versionStr);
|
|
|
|
patchCnt++;
|
|
}
|
|
}
|
|
|
|
if (patchCnt > 0) uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), ypos, FONT_COLOR_SUCCESS_RGB, strbuf);
|
|
}
|
|
|
|
ypos += LINE_HEIGHT;
|
|
|
|
uiDrawString(xpos, ypos, FONT_COLOR_SUCCESS_RGB, "Version: %s", baseAppEntries[selectedAppInfoIndex].versionStr);
|
|
|
|
if (titleAddOnCount > 0)
|
|
{
|
|
u32 addOnCnt = 0;
|
|
|
|
for(addon = 0; addon < titleAddOnCount; addon++)
|
|
{
|
|
if (checkIfPatchOrAddOnBelongsToBaseApplication(addon, selectedAppInfoIndex, true) && ((menuType == MENUTYPE_GAMECARD && baseAppEntries[selectedAppInfoIndex].storageId == addOnEntries[addon].storageId) || menuType == MENUTYPE_SDCARD_EMMC)) addOnCnt++;
|
|
}
|
|
|
|
if (addOnCnt > 0) uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), ypos, FONT_COLOR_SUCCESS_RGB, "%s DLC(s): %u", (menuType == MENUTYPE_GAMECARD ? "Bundled" : "Installed"), addOnCnt);
|
|
}
|
|
|
|
ypos += LINE_HEIGHT;
|
|
|
|
ypos += 8;
|
|
if (xpos > 8 && (ypos - NACP_ICON_DOWNSCALED) < startYPos) ypos += (NACP_ICON_DOWNSCALED - (ypos - startYPos));
|
|
ypos += LINE_HEIGHT;
|
|
|
|
breaks += (int)round((double)(ypos - startYPos) / (double)LINE_HEIGHT);
|
|
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Capacity: %s | Used space: %s", gameCardInfo.sizeStr, gameCardInfo.trimmedSizeStr);
|
|
|
|
if (titleAppCount > 1)
|
|
{
|
|
if (baseAppEntries[selectedAppInfoIndex].contentSize)
|
|
{
|
|
uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Base application count: %u | Base application displayed: %u | Content size: %s", titleAppCount, selectedAppInfoIndex + 1, baseAppEntries[selectedAppInfoIndex].contentSizeStr);
|
|
} else {
|
|
uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Base application count: %u | Base application displayed: %u", titleAppCount, selectedAppInfoIndex + 1);
|
|
}
|
|
} else {
|
|
if (baseAppEntries[selectedAppInfoIndex].contentSize) uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Content size: %s", baseAppEntries[selectedAppInfoIndex].contentSizeStr);
|
|
}
|
|
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Partition count: %u (%s)", gameCardInfo.hfs0PartitionCnt, GAMECARD_TYPE(gameCardInfo.hfs0PartitionCnt));
|
|
|
|
if (strlen(gameCardInfo.updateVersionStr)) uiDrawString((FB_WIDTH / 2) - (FB_WIDTH / 8), STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Bundled FW update: %s", gameCardInfo.updateVersionStr);
|
|
|
|
breaks++;
|
|
|
|
if (titleAppCount > 1 && (titlePatchCount > 0 || titleAddOnCount > 0))
|
|
{
|
|
if (titlePatchCount > 0) uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Total bundled update(s): %u", titlePatchCount);
|
|
|
|
if (titleAddOnCount > 0) uiDrawString((titlePatchCount > 0 ? ((FB_WIDTH / 2) - (FB_WIDTH / 8)) : 8), STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Total bundled DLC(s): %u", titleAddOnCount);
|
|
|
|
breaks++;
|
|
}
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
if (baseAppEntries[selectedAppInfoIndex].contentSize)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Content size: %s", baseAppEntries[selectedAppInfoIndex].contentSizeStr);
|
|
breaks++;
|
|
}
|
|
}
|
|
|
|
if (!strlen(dumpedContentInfoStr))
|
|
{
|
|
// Look for dumped content in the SD card
|
|
char *dumpName = NULL;
|
|
char dumpPath[NAME_BUF_LEN] = {'\0'}, tmpStr[64] = {'\0'};
|
|
bool dumpedXci = false, dumpedXciCertificate = false, dumpedBase = false, dumpedBaseConsoleData = false;
|
|
|
|
u32 patchCnt = 0, addOnCnt = 0;
|
|
u32 patchCntConsoleData = 0, addOnCntConsoleData = 0;
|
|
|
|
snprintf(dumpedContentInfoStr, MAX_CHARACTERS(dumpedContentInfoStr), "Content already dumped: ");
|
|
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
for(i = 0; i < 2; i++)
|
|
{
|
|
dumpName = generateGameCardDumpName(i == 1);
|
|
if (!dumpName) continue;
|
|
|
|
// First check if a full XCI dump is available
|
|
snprintf(dumpPath, MAX_CHARACTERS(dumpPath), "%s%s.xci", XCI_DUMP_PATH, dumpName);
|
|
if (!(dumpedXci = checkIfFileExists(dumpPath)))
|
|
{
|
|
// Check if a split XCI dump is available
|
|
snprintf(dumpPath, MAX_CHARACTERS(dumpPath), "%s%s.xc0", XCI_DUMP_PATH, dumpName);
|
|
dumpedXci = checkIfFileExists(dumpPath);
|
|
}
|
|
|
|
free(dumpName);
|
|
dumpName = NULL;
|
|
|
|
if (dumpedXci)
|
|
{
|
|
dumpedXciCertificate = checkIfDumpedXciContainsCertificate(dumpPath);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now search for dumped NSPs
|
|
|
|
// Look for a dumped base application
|
|
for(i = 0; i < 2; i++)
|
|
{
|
|
dumpName = generateNSPDumpName(DUMP_APP_NSP, selectedAppInfoIndex, (i == 1));
|
|
if (!dumpName) continue;
|
|
|
|
snprintf(dumpPath, MAX_CHARACTERS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName);
|
|
|
|
free(dumpName);
|
|
dumpName = NULL;
|
|
|
|
if (checkIfFileExists(dumpPath))
|
|
{
|
|
dumpedBase = true;
|
|
dumpedBaseConsoleData = checkIfDumpedNspContainsConsoleData(dumpPath);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Look for dumped updates
|
|
for(patch = 0; patch < titlePatchCount; patch++)
|
|
{
|
|
if (!checkIfPatchOrAddOnBelongsToBaseApplication(patch, selectedAppInfoIndex, false)) continue;
|
|
|
|
for(i = 0; i < 2; i++)
|
|
{
|
|
dumpName = generateNSPDumpName(DUMP_PATCH_NSP, patch, (i == 1));
|
|
if (!dumpName) continue;
|
|
|
|
snprintf(dumpPath, MAX_CHARACTERS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName);
|
|
|
|
free(dumpName);
|
|
dumpName = NULL;
|
|
|
|
if (checkIfFileExists(dumpPath))
|
|
{
|
|
patchCnt++;
|
|
if (checkIfDumpedNspContainsConsoleData(dumpPath)) patchCntConsoleData++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Look for dumped DLCs
|
|
for(addon = 0; addon < titleAddOnCount; addon++)
|
|
{
|
|
if (!checkIfPatchOrAddOnBelongsToBaseApplication(addon, selectedAppInfoIndex, true)) continue;
|
|
|
|
for(i = 0; i < 2; i++)
|
|
{
|
|
dumpName = generateNSPDumpName(DUMP_ADDON_NSP, addon, (i == 1));
|
|
if (!dumpName) continue;
|
|
|
|
snprintf(dumpPath, MAX_CHARACTERS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName);
|
|
|
|
free(dumpName);
|
|
dumpName = NULL;
|
|
|
|
if (checkIfFileExists(dumpPath))
|
|
{
|
|
addOnCnt++;
|
|
if (checkIfDumpedNspContainsConsoleData(dumpPath)) addOnCntConsoleData++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!dumpedXci && !dumpedBase && !patchCnt && !addOnCnt)
|
|
{
|
|
strcat(dumpedContentInfoStr, "NONE");
|
|
} else {
|
|
if (dumpedXci)
|
|
{
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "XCI (%s cert)", (dumpedXciCertificate ? "with" : "without"));
|
|
strcat(dumpedContentInfoStr, tmpStr);
|
|
}
|
|
|
|
if (dumpedBase)
|
|
{
|
|
if (dumpedXci) strcat(dumpedContentInfoStr, ", ");
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "BASE (%s console data)", (dumpedBaseConsoleData ? "with" : "without"));
|
|
strcat(dumpedContentInfoStr, tmpStr);
|
|
}
|
|
|
|
if (patchCnt)
|
|
{
|
|
if (dumpedXci || dumpedBase) strcat(dumpedContentInfoStr, ", ");
|
|
|
|
if (patchCntConsoleData)
|
|
{
|
|
if (patchCntConsoleData == patchCnt)
|
|
{
|
|
if (patchCnt > 1)
|
|
{
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "%u UPD (all with console data)", patchCnt);
|
|
} else {
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "UPD (with console data)");
|
|
}
|
|
} else {
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "%u UPD (%u with console data)", patchCnt, patchCntConsoleData);
|
|
}
|
|
} else {
|
|
if (patchCnt > 1)
|
|
{
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "%u UPD (all without console data)", patchCnt);
|
|
} else {
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "UPD (without console data)");
|
|
}
|
|
}
|
|
|
|
strcat(dumpedContentInfoStr, tmpStr);
|
|
}
|
|
|
|
if (addOnCnt)
|
|
{
|
|
if (dumpedXci || dumpedBase || patchCnt) strcat(dumpedContentInfoStr, ", ");
|
|
|
|
if (addOnCntConsoleData)
|
|
{
|
|
if (addOnCntConsoleData == addOnCnt)
|
|
{
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "%u DLC (%s console data)", addOnCnt, (addOnCnt > 1 ? "all with" : "with"));
|
|
} else {
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "%u DLC (%u with console data)", addOnCnt, addOnCntConsoleData);
|
|
}
|
|
} else {
|
|
snprintf(tmpStr, MAX_CHARACTERS(tmpStr), "%u DLC (%s console data)", addOnCnt, (addOnCnt > 1 ? "all without" : "without"));
|
|
}
|
|
|
|
strcat(dumpedContentInfoStr, tmpStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, dumpedContentInfoStr);
|
|
breaks += 2;
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC && orphanMode && (uiState == stateSdCardEmmcTitleMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu || uiState == stateExeFsMenu || uiState == stateRomFsMenu || uiState == stateTicketMenu))
|
|
{
|
|
if (strlen(orphanEntries[orphanListCursor].name))
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Parent base application: %s", orphanEntries[orphanListCursor].name);
|
|
breaks++;
|
|
}
|
|
|
|
patch_addon_ctx_t *ptr = (orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_PATCH ? &(patchEntries[selectedPatchIndex]) : &(addOnEntries[selectedAddOnIndex]));
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Title ID: %016lX", ptr->titleId);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Version: %s", ptr->versionStr);
|
|
breaks++;
|
|
|
|
if (ptr->contentSize)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, "Content size: %s", ptr->contentSizeStr);
|
|
breaks++;
|
|
}
|
|
|
|
if (!strlen(dumpedContentInfoStr))
|
|
{
|
|
// Look for dumped content in the SD card
|
|
char *dumpName = NULL;
|
|
char dumpPath[NAME_BUF_LEN] = {'\0'};
|
|
bool dumpedOrphan = false;
|
|
nspDumpType orphanDumpType = (orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_PATCH ? DUMP_PATCH_NSP : DUMP_ADDON_NSP);
|
|
u32 orphanIndex = (orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_PATCH ? selectedPatchIndex : selectedAddOnIndex);
|
|
|
|
snprintf(dumpedContentInfoStr, MAX_CHARACTERS(dumpedContentInfoStr), "Title already dumped: ");
|
|
|
|
for(i = 0; i < 2; i++)
|
|
{
|
|
dumpName = generateNSPDumpName(orphanDumpType, orphanIndex, (i == 1));
|
|
if (!dumpName) continue;
|
|
|
|
snprintf(dumpPath, MAX_CHARACTERS(dumpPath), "%s%s.nsp", NSP_DUMP_PATH, dumpName);
|
|
|
|
free(dumpName);
|
|
dumpName = NULL;
|
|
|
|
dumpedOrphan = checkIfFileExists(dumpPath);
|
|
if (dumpedOrphan)
|
|
{
|
|
strcat(dumpedContentInfoStr, "Yes");
|
|
|
|
if (checkIfDumpedNspContainsConsoleData(dumpPath))
|
|
{
|
|
strcat(dumpedContentInfoStr, " (with console data)");
|
|
} else {
|
|
strcat(dumpedContentInfoStr, " (without console data)");
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!dumpedOrphan) strcat(dumpedContentInfoStr, "No");
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_SUCCESS_RGB, dumpedContentInfoStr);
|
|
breaks += 2;
|
|
} else {
|
|
dumpedContentInfoStr[0] = '\0';
|
|
}
|
|
|
|
switch(uiState)
|
|
{
|
|
case stateMainMenu:
|
|
menu = mainMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(mainMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Main menu");
|
|
|
|
break;
|
|
case stateGameCardMenu:
|
|
menu = gameCardMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(gameCardMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, mainMenuItems[0]);
|
|
|
|
break;
|
|
case stateXciDumpMenu:
|
|
menu = xciDumpMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(xciDumpMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[0]);
|
|
|
|
break;
|
|
case stateNspDumpMenu:
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
menu = nspDumpGameCardMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(nspDumpGameCardMenuItems);
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
menu = nspDumpSdCardEmmcMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(nspDumpSdCardEmmcMenuItems);
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[1]);
|
|
|
|
break;
|
|
case stateNspAppDumpMenu:
|
|
menu = nspAppDumpMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(nspAppDumpMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, nspDumpGameCardMenuItems[0]);
|
|
|
|
break;
|
|
case stateNspPatchDumpMenu:
|
|
menu = nspPatchDumpMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(nspPatchDumpMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[1] : nspDumpSdCardEmmcMenuItems[1]));
|
|
|
|
break;
|
|
case stateNspAddOnDumpMenu:
|
|
menu = nspAddOnDumpMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(nspAddOnDumpMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[2] : nspDumpSdCardEmmcMenuItems[2]));
|
|
|
|
break;
|
|
case stateHfs0Menu:
|
|
menu = hfs0MenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(hfs0MenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[2]);
|
|
|
|
break;
|
|
case stateRawHfs0PartitionDumpMenu:
|
|
case stateHfs0PartitionDataDumpMenu:
|
|
menu = (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems : hfs0PartitionDumpType2MenuItems);
|
|
menuItemsCount = (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT ? MAX_ELEMENTS(hfs0PartitionDumpType1MenuItems) : MAX_ELEMENTS(hfs0PartitionDumpType2MenuItems));
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (uiState == stateRawHfs0PartitionDumpMenu ? hfs0MenuItems[0] : hfs0MenuItems[1]));
|
|
|
|
break;
|
|
case stateHfs0BrowserMenu:
|
|
menu = (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems : hfs0BrowserType2MenuItems);
|
|
menuItemsCount = (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT ? MAX_ELEMENTS(hfs0BrowserType1MenuItems) : MAX_ELEMENTS(hfs0BrowserType2MenuItems));
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, hfs0MenuItems[2]);
|
|
|
|
break;
|
|
case stateHfs0Browser:
|
|
menu = (const char**)filenameBuffer;
|
|
menuItemsCount = filenameCount;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems[selectedPartitionIndex] : hfs0BrowserType2MenuItems[selectedPartitionIndex]));
|
|
breaks += 2;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Entry count: %d | Current entry: %d", menuItemsCount, cursor + 1);
|
|
|
|
break;
|
|
case stateExeFsMenu:
|
|
menu = exeFsMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(exeFsMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[3]);
|
|
|
|
break;
|
|
case stateExeFsSectionDataDumpMenu:
|
|
menu = exeFsSectionDumpMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(exeFsSectionDumpMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[0]);
|
|
|
|
break;
|
|
case stateExeFsSectionBrowserMenu:
|
|
menu = exeFsSectionBrowserMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(exeFsSectionBrowserMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[1]);
|
|
|
|
break;
|
|
case stateExeFsSectionBrowser:
|
|
menu = (const char**)filenameBuffer;
|
|
menuItemsCount = filenameCount;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[1]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", exeFsMenuItems[2], (dumpCfg.exeFsDumpCfg.isFat32 ? "Yes" : "No"), exeFsMenuItems[3], (dumpCfg.exeFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
if (!exeFsUpdateFlag)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s v%s", exeFsSectionBrowserMenuItems[1], baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
} else {
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update to browse: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Entry count: %d | Current entry: %d", menuItemsCount, cursor + 1);
|
|
|
|
break;
|
|
case stateRomFsMenu:
|
|
menu = romFsMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(romFsMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[4]);
|
|
|
|
break;
|
|
case stateRomFsSectionDataDumpMenu:
|
|
menu = romFsSectionDumpMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(romFsSectionDumpMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[0]);
|
|
|
|
break;
|
|
case stateRomFsSectionBrowserMenu:
|
|
menu = romFsSectionBrowserMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(romFsSectionBrowserMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[1]);
|
|
|
|
break;
|
|
case stateRomFsSectionBrowser:
|
|
menu = (const char**)filenameBuffer;
|
|
menuItemsCount = filenameCount;
|
|
|
|
// Skip the parent directory entry ("..") in the RomFS browser if we're currently at the root directory
|
|
if (menu && menuItemsCount && strlen(curRomFsPath) <= 1)
|
|
{
|
|
menu++;
|
|
menuItemsCount--;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[1]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", romFsMenuItems[2], (dumpCfg.romFsDumpCfg.isFat32 ? "Yes" : "No"), romFsMenuItems[3], (dumpCfg.romFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
switch(curRomFsType)
|
|
{
|
|
case ROMFS_TYPE_APP:
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s v%s", romFsSectionBrowserMenuItems[1], baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
break;
|
|
case ROMFS_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update to browse: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
break;
|
|
case ROMFS_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, true, "DLC to browse: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Path: romfs:%s", curRomFsPath);
|
|
breaks += 2;
|
|
|
|
if (strlen(curRomFsPath) <= 1 || (strlen(curRomFsPath) > 1 && cursor > 0))
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "Entry count: %d | Current entry: %d", filenameCount - 1, (strlen(curRomFsPath) <= 1 ? (cursor + 1) : cursor));
|
|
} else {
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "Entry count: %d", filenameCount - 1);
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, strbuf);
|
|
|
|
break;
|
|
case stateSdCardEmmcMenu:
|
|
generateSdCardEmmcTitleList();
|
|
|
|
menu = (const char**)filenameBuffer;
|
|
menuItemsCount = filenameCount;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, mainMenuItems[1]);
|
|
|
|
if (menuItemsCount)
|
|
{
|
|
breaks += 2;
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Title count: %d | Current title: %d", menuItemsCount, cursor + 1);
|
|
}
|
|
|
|
breaks++;
|
|
|
|
break;
|
|
case stateSdCardEmmcTitleMenu:
|
|
menu = sdCardEmmcMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(sdCardEmmcMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (!orphanMode ? mainMenuItems[1] : (orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_PATCH ? "Dump orphan update content" : "Dump orphan DLC content")));
|
|
|
|
break;
|
|
case stateSdCardEmmcOrphanPatchAddOnMenu:
|
|
// Generate orphan content list
|
|
// If orphanEntries == NULL or if orphanEntriesCnt == 0, both variables will be regenerated
|
|
// Otherwise, this will only fill filenameBuffer
|
|
generateOrphanPatchOrAddOnList();
|
|
|
|
menu = (const char**)filenameBuffer;
|
|
menuItemsCount = filenameCount;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Dump installed content with missing base application");
|
|
|
|
if (menuItemsCount)
|
|
{
|
|
breaks += 2;
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "Title count: %d | Current title: %d", menuItemsCount, cursor + 1);
|
|
}
|
|
|
|
break;
|
|
case stateSdCardEmmcBatchModeMenu:
|
|
menu = batchModeMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(batchModeMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Batch mode");
|
|
|
|
break;
|
|
case stateTicketMenu:
|
|
menu = ticketMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(ticketMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, sdCardEmmcMenuItems[3]);
|
|
|
|
break;
|
|
case stateUpdateMenu:
|
|
menu = updateMenuItems;
|
|
menuItemsCount = MAX_ELEMENTS(updateMenuItems);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, mainMenuItems[2]);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (menu && menuItemsCount)
|
|
{
|
|
breaks++;
|
|
|
|
if (scroll > 0) uiDrawString((FB_WIDTH / 2) - (uiGetStrWidth(upwardsArrow) / 2), STRING_Y_POS(breaks), FONT_COLOR_RGB, upwardsArrow);
|
|
|
|
breaks++;
|
|
|
|
j = 0;
|
|
highlight = false;
|
|
|
|
for(i = scroll; i < menuItemsCount; i++, j++)
|
|
{
|
|
if (j >= maxElements) break;
|
|
|
|
// Avoid printing the "Create directory with archive bit set" option if "Split output dump" is disabled
|
|
if (uiState == stateXciDumpMenu && i == 2 && !dumpCfg.xciDumpCfg.isFat32)
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Dump verification method" option if "CRC32 checksum calculation + dump verification" is disabled
|
|
if (uiState == stateXciDumpMenu && i == 6 && !dumpCfg.xciDumpCfg.calcCrc)
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Dump bundled update NSP" / "Dump installed update NSP" option in the NSP dump menu if we're dealing with a gamecard and it doesn't include any bundled updates, or if we're dealing with a SD/eMMC title without installed updates
|
|
// Also avoid printing the "Dump bundled DLC NSP" / "Dump installed DLC NSP" option in the NSP dump menu if we're dealing with a gamecard and it doesn't include any bundled DLCs, or if we're dealing with a SD/eMMC title without installed DLCs
|
|
if (uiState == stateNspDumpMenu && ((i == 1 && (!titlePatchCount || (menuType == MENUTYPE_SDCARD_EMMC && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)))) || (i == 2 && (!titleAddOnCount || (menuType == MENUTYPE_SDCARD_EMMC && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))))))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Verify dump using No-Intro database" option in the NSP dump menus if we're dealing with a gamecard title
|
|
// Also, avoid printing the "Remove console specific data" option in the NSP dump menus if we're dealing with a gamecard title
|
|
// Also, avoid printing the "Generate ticket-less dump" option in the NSP dump menus if we're dealing with a gamecard Application/AddOn title
|
|
if (menuType == MENUTYPE_GAMECARD && (((uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && (i == 2 || i == 3)) || ((uiState == stateNspAppDumpMenu || uiState == stateNspAddOnDumpMenu) && i == 4)))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Dump delta fragments" option in the update NSP dump menu if we're dealing with a gamecard update
|
|
if (menuType == MENUTYPE_GAMECARD && uiState == stateNspPatchDumpMenu && i == 6)
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Generate ticket-less dump" option in the NSP dump menus if we're dealing with a SD/eMMC title and the "Remove console specific data" option is disabled
|
|
if (menuType == MENUTYPE_SDCARD_EMMC && (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && i == 4 && !dumpCfg.nspDumpCfg.removeConsoleData)
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Dump base applications", "Dump updates" and/or "Dump DLCs" options in the batch mode menu if we're dealing with a storage source that doesn't hold any title belonging to the current category
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && ((!titleAppCount && i == 1) || (!titlePatchCount && i == 2) || (!titleAddOnCount && i == 3))) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && ((!sdCardTitleAppCount && i == 1) || (!sdCardTitlePatchCount && i == 2) || (!sdCardTitleAddOnCount && i == 3))) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && ((!emmcTitleAppCount && i == 1) || (!emmcTitlePatchCount && i == 2) || (!emmcTitleAddOnCount && i == 3)))))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Generate ticket-less dumps" option in the batch mode menu if the "Remove console specific data" option is disabled
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && i == 6 && !dumpCfg.batchDumpCfg.removeConsoleData)
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Dump delta fragments from updates" option in the batch mode menu if the "Dump updates" option is disabled
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && i == 8 && !dumpCfg.batchDumpCfg.dumpPatchTitles)
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Source storage" option in the batch mode menu if we only have titles available in a single source storage device
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && i == 13 && ((!sdCardTitleAppCount && !sdCardTitlePatchCount && !sdCardTitleAddOnCount) || (!emmcTitleAppCount && !emmcTitlePatchCount && !emmcTitleAddOnCount)))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Use update" option in the ExeFS menu if we're dealing with a gamecard and either its base application count is greater than 1 or it has no available patches
|
|
// Also avoid printing it if we're dealing with a SD/eMMC title and it has no available patches, or if we're dealing with an orphan Patch
|
|
if (uiState == stateExeFsMenu && i == 4 && ((menuType == MENUTYPE_GAMECARD && (titleAppCount > 1 || !checkIfBaseApplicationHasPatchOrAddOn(0, false))) || (menuType == MENUTYPE_SDCARD_EMMC && ((!orphanMode && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)) || orphanMode))))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Use update" option in the ExeFS data dump and browser menus if we're not dealing with a gamecard, if the base application count is equal to or less than 1, or if the selected base application has no available patches
|
|
if ((uiState == stateExeFsSectionDataDumpMenu || uiState == stateExeFsSectionBrowserMenu) && i == 2 && (menuType != MENUTYPE_GAMECARD || titleAppCount <= 1 || !checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false)))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Use update/DLC" option in the RomFS menu if we're dealing with a gamecard and either its base application count is greater than 1 or it has no available patches/DLCs
|
|
// Also avoid printing it if we're dealing with a SD/eMMC title and it has no available patches/DLCs (or if its an orphan title)
|
|
if (uiState == stateRomFsMenu && i == 4 && ((menuType == MENUTYPE_GAMECARD && (titleAppCount > 1 || (!checkIfBaseApplicationHasPatchOrAddOn(0, false) && !checkIfBaseApplicationHasPatchOrAddOn(0, true)))) || (menuType == MENUTYPE_SDCARD_EMMC && (orphanMode || (!checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false) && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))))))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Use update/DLC" option in the RomFS data dump and browser menus if we're not dealing with a gamecard, if the base application count is equal to or less than 1, or if the selected base application has no available patches/DLCs
|
|
if ((uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu) && i == 2 && (menuType != MENUTYPE_GAMECARD || titleAppCount <= 1 || (!checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false) && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true))))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "RomFS options" element in the SD card / eMMC title menu if we're dealing with an orphan Patch
|
|
// Also avoid printing the "ExeFS options" element in the SD card / eMMC title menu if we're dealing with an orphan DLC
|
|
if (uiState == stateSdCardEmmcTitleMenu && orphanMode && ((orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_PATCH && i == 2) || (orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_ADDON && i == 1)))
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
// Avoid printing the "Use ticket from title" element in the Ticket menu if we're dealing with an orphan title
|
|
if (uiState == stateTicketMenu && orphanMode && i == 2)
|
|
{
|
|
j--;
|
|
continue;
|
|
}
|
|
|
|
xpos = STRING_X_POS;
|
|
ypos = (8 + (breaks * LINE_HEIGHT) + (uiState == stateSdCardEmmcMenu ? (j * (NACP_ICON_DOWNSCALED + 12)) : (j * (font_height + 12))) + 6);
|
|
|
|
if (i == cursor)
|
|
{
|
|
highlight = true;
|
|
uiFill(0, ypos - 6, FB_WIDTH, (uiState == stateSdCardEmmcMenu ? (NACP_ICON_DOWNSCALED + 12) : (font_height + 12)), HIGHLIGHT_BG_COLOR_RGB);
|
|
}
|
|
|
|
if (uiState == stateSdCardEmmcMenu)
|
|
{
|
|
if (baseAppEntries[i].icon != NULL)
|
|
{
|
|
uiDrawIcon(baseAppEntries[i].icon, NACP_ICON_DOWNSCALED, NACP_ICON_DOWNSCALED, xpos, ypos);
|
|
|
|
xpos += (NACP_ICON_DOWNSCALED + 8);
|
|
}
|
|
|
|
ypos += ((NACP_ICON_DOWNSCALED / 2) - (font_height / 2));
|
|
} else
|
|
if (uiState == stateHfs0Browser || uiState == stateExeFsSectionBrowser || uiState == stateRomFsSectionBrowser)
|
|
{
|
|
u8 *icon = NULL;
|
|
|
|
if (uiState == stateRomFsSectionBrowser)
|
|
{
|
|
u32 idx = (strlen(curRomFsPath) <= 1 ? (i + 1) : i); // Adjust index if we're at the root directory
|
|
icon = (highlight ? (romFsBrowserEntries[idx].type == ROMFS_ENTRY_DIR ? dirHighlightIconBuf : fileHighlightIconBuf) : (romFsBrowserEntries[idx].type == ROMFS_ENTRY_DIR ? dirNormalIconBuf : fileNormalIconBuf));
|
|
} else {
|
|
icon = (highlight ? fileHighlightIconBuf : fileNormalIconBuf);
|
|
}
|
|
|
|
uiDrawIcon(icon, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, xpos, ypos);
|
|
|
|
xpos += (BROWSER_ICON_DIMENSION + 8);
|
|
}
|
|
|
|
if (highlight)
|
|
{
|
|
uiDrawString(xpos, ypos, HIGHLIGHT_FONT_COLOR_RGB, menu[i]);
|
|
|
|
u32 idx = ((uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) <= 1) ? (i + 1) : i); // Adjust index if we're at the root directory
|
|
|
|
if (uiState == stateHfs0Browser || uiState == stateExeFsSectionBrowser || (uiState == stateRomFsSectionBrowser && romFsBrowserEntries[idx].type == ROMFS_ENTRY_FILE))
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "(%s)", ((uiState == stateHfs0Browser || uiState == stateExeFsSectionBrowser) ? hfs0ExeFsEntriesSizes[idx].sizeStr : romFsBrowserEntries[idx].sizeInfo.sizeStr));
|
|
uiDrawString(FB_WIDTH - (8 + uiGetStrWidth(strbuf)), ypos, HIGHLIGHT_FONT_COLOR_RGB, strbuf);
|
|
}
|
|
} else {
|
|
uiDrawString(xpos, ypos, FONT_COLOR_RGB, menu[i]);
|
|
|
|
u32 idx = ((uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) <= 1) ? (i + 1) : i); // Adjust index if we're at the root directory
|
|
|
|
if (uiState == stateHfs0Browser || uiState == stateExeFsSectionBrowser || (uiState == stateRomFsSectionBrowser && romFsBrowserEntries[idx].type == ROMFS_ENTRY_FILE))
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "(%s)", ((uiState == stateHfs0Browser || uiState == stateExeFsSectionBrowser) ? hfs0ExeFsEntriesSizes[idx].sizeStr : romFsBrowserEntries[idx].sizeInfo.sizeStr));
|
|
uiDrawString(FB_WIDTH - (8 + uiGetStrWidth(strbuf)), ypos, FONT_COLOR_RGB, strbuf);
|
|
}
|
|
}
|
|
|
|
xpos = OPTIONS_X_START_POS;
|
|
|
|
bool leftArrowCondition = false;
|
|
bool rightArrowCondition = false;
|
|
|
|
// Print XCI dump menu settings values
|
|
if (uiState == stateXciDumpMenu && i > 0)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 1: // Split output dump (FAT32 support)
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.isFat32, !dumpCfg.xciDumpCfg.isFat32, (dumpCfg.xciDumpCfg.isFat32 ? 0 : 255), (dumpCfg.xciDumpCfg.isFat32 ? 255 : 0), 0, (dumpCfg.xciDumpCfg.isFat32 ? "Yes" : "No"));
|
|
break;
|
|
case 2: // Create directory with archive bit set
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.setXciArchiveBit, !dumpCfg.xciDumpCfg.setXciArchiveBit, (dumpCfg.xciDumpCfg.setXciArchiveBit ? 0 : 255), (dumpCfg.xciDumpCfg.setXciArchiveBit ? 255 : 0), 0, (dumpCfg.xciDumpCfg.setXciArchiveBit ? "Yes" : "No"));
|
|
break;
|
|
case 3: // Keep certificate
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.keepCert, !dumpCfg.xciDumpCfg.keepCert, (dumpCfg.xciDumpCfg.keepCert ? 0 : 255), (dumpCfg.xciDumpCfg.keepCert ? 255 : 0), 0, (dumpCfg.xciDumpCfg.keepCert ? "Yes" : "No"));
|
|
break;
|
|
case 4: // Trim output dump
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.trimDump, !dumpCfg.xciDumpCfg.trimDump, (dumpCfg.xciDumpCfg.trimDump ? 0 : 255), (dumpCfg.xciDumpCfg.trimDump ? 255 : 0), 0, (dumpCfg.xciDumpCfg.trimDump ? "Yes" : "No"));
|
|
break;
|
|
case 5: // CRC32 checksum calculation + dump verification
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.xciDumpCfg.calcCrc, !dumpCfg.xciDumpCfg.calcCrc, (dumpCfg.xciDumpCfg.calcCrc ? 0 : 255), (dumpCfg.xciDumpCfg.calcCrc ? 255 : 0), 0, (dumpCfg.xciDumpCfg.calcCrc ? "Yes" : "No"));
|
|
break;
|
|
case 6: // Dump verification method
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, dumpCfg.xciDumpCfg.useNoIntroLookup, !dumpCfg.xciDumpCfg.useNoIntroLookup, FONT_COLOR_RGB, (dumpCfg.xciDumpCfg.useNoIntroLookup ? xciChecksumLookupMethods[1] : xciChecksumLookupMethods[0]));
|
|
break;
|
|
case 7: // Output naming scheme
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, dumpCfg.xciDumpCfg.useBrackets, !dumpCfg.xciDumpCfg.useBrackets, FONT_COLOR_RGB, (dumpCfg.xciDumpCfg.useBrackets ? xciNamingSchemes[1] : xciNamingSchemes[0]));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print NSP dump menus settings values
|
|
if ((uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && i > 0)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 1: // Split output dump (FAT32 support)
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.isFat32, !dumpCfg.nspDumpCfg.isFat32, (dumpCfg.nspDumpCfg.isFat32 ? 0 : 255), (dumpCfg.nspDumpCfg.isFat32 ? 255 : 0), 0, (dumpCfg.nspDumpCfg.isFat32 ? "Yes" : "No"));
|
|
break;
|
|
case 2: // Verify dump using No-Intro database
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.useNoIntroLookup, !dumpCfg.nspDumpCfg.useNoIntroLookup, (dumpCfg.nspDumpCfg.useNoIntroLookup ? 0 : 255), (dumpCfg.nspDumpCfg.useNoIntroLookup ? 255 : 0), 0, (dumpCfg.nspDumpCfg.useNoIntroLookup ? "Yes" : "No"));
|
|
break;
|
|
case 3: // Remove console specific data
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.removeConsoleData, !dumpCfg.nspDumpCfg.removeConsoleData, (dumpCfg.nspDumpCfg.removeConsoleData ? 0 : 255), (dumpCfg.nspDumpCfg.removeConsoleData ? 255 : 0), 0, (dumpCfg.nspDumpCfg.removeConsoleData ? "Yes" : "No"));
|
|
break;
|
|
case 4: // Generate ticket-less dump
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.tiklessDump, !dumpCfg.nspDumpCfg.tiklessDump, (dumpCfg.nspDumpCfg.tiklessDump ? 0 : 255), (dumpCfg.nspDumpCfg.tiklessDump ? 255 : 0), 0, (dumpCfg.nspDumpCfg.tiklessDump ? "Yes" : "No"));
|
|
break;
|
|
case 5: // Change NPDM RSA key/sig in Program NCA || DLC to dump
|
|
if (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu)
|
|
{
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.npdmAcidRsaPatch, !dumpCfg.nspDumpCfg.npdmAcidRsaPatch, (dumpCfg.nspDumpCfg.npdmAcidRsaPatch ? 0 : 255), (dumpCfg.nspDumpCfg.npdmAcidRsaPatch ? 255 : 0), 0, (dumpCfg.nspDumpCfg.npdmAcidRsaPatch ? "Yes" : "No"));
|
|
} else
|
|
if (uiState == stateNspAddOnDumpMenu)
|
|
{
|
|
if (!strlen(titleSelectorStr))
|
|
{
|
|
// Find a matching application to print its name and Title ID
|
|
// Otherwise, just print the Title ID
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, (menuType == MENUTYPE_GAMECARD), NULL, titleSelectorStr, MAX_CHARACTERS(titleSelectorStr));
|
|
|
|
if (addOnEntries[selectedAddOnIndex].contentSize)
|
|
{
|
|
strcat(titleSelectorStr, " (");
|
|
strcat(titleSelectorStr, addOnEntries[selectedAddOnIndex].contentSizeStr);
|
|
strcat(titleSelectorStr, ")");
|
|
}
|
|
|
|
uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = ((menuType == MENUTYPE_GAMECARD && titleAddOnCount > 0 && selectedAddOnIndex > 0) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && retrievePreviousPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, selectedAppInfoIndex, true) != selectedAddOnIndex));
|
|
rightArrowCondition = ((menuType == MENUTYPE_GAMECARD && titleAddOnCount > 0 && selectedAddOnIndex < (titleAddOnCount - 1)) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, selectedAppInfoIndex, true) != selectedAddOnIndex));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, titleSelectorStr);
|
|
}
|
|
break;
|
|
case 6: // Application to dump || Dump delta fragments || Output naming scheme (DLC)
|
|
if (uiState == stateNspAppDumpMenu)
|
|
{
|
|
if (!strlen(titleSelectorStr))
|
|
{
|
|
// Print application name
|
|
snprintf(titleSelectorStr, MAX_CHARACTERS(titleSelectorStr), "%s v%s", baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
|
|
if (baseAppEntries[selectedAppIndex].contentSize)
|
|
{
|
|
strcat(titleSelectorStr, " (");
|
|
strcat(titleSelectorStr, baseAppEntries[selectedAppIndex].contentSizeStr);
|
|
strcat(titleSelectorStr, ")");
|
|
}
|
|
|
|
uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex > 0);
|
|
rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex < (titleAppCount - 1));
|
|
} else
|
|
if (uiState == stateNspPatchDumpMenu)
|
|
{
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.nspDumpCfg.dumpDeltaFragments, !dumpCfg.nspDumpCfg.dumpDeltaFragments, (dumpCfg.nspDumpCfg.dumpDeltaFragments ? 0 : 255), (dumpCfg.nspDumpCfg.dumpDeltaFragments ? 255 : 0), 0, (dumpCfg.nspDumpCfg.dumpDeltaFragments ? "Yes" : "No"));
|
|
} else
|
|
if (uiState == stateNspAddOnDumpMenu)
|
|
{
|
|
leftArrowCondition = dumpCfg.nspDumpCfg.useBrackets;
|
|
rightArrowCondition = !dumpCfg.nspDumpCfg.useBrackets;
|
|
}
|
|
|
|
if (uiState != stateNspPatchDumpMenu) uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, (uiState == stateNspAddOnDumpMenu ? (dumpCfg.nspDumpCfg.useBrackets ? nspNamingSchemes[1] : nspNamingSchemes[0]) : titleSelectorStr));
|
|
|
|
break;
|
|
case 7: // Output naming scheme (base application) || Update to dump
|
|
if (uiState == stateNspAppDumpMenu)
|
|
{
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, dumpCfg.nspDumpCfg.useBrackets, !dumpCfg.nspDumpCfg.useBrackets, FONT_COLOR_RGB, (dumpCfg.nspDumpCfg.useBrackets ? nspNamingSchemes[1] : nspNamingSchemes[0]));
|
|
} else
|
|
if (uiState == stateNspPatchDumpMenu)
|
|
{
|
|
if (!strlen(titleSelectorStr))
|
|
{
|
|
// Find a matching application to print its name
|
|
// Otherwise, just print the Title ID
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, (menuType == MENUTYPE_GAMECARD), NULL, titleSelectorStr, MAX_CHARACTERS(titleSelectorStr));
|
|
|
|
if (patchEntries[selectedPatchIndex].contentSize)
|
|
{
|
|
strcat(titleSelectorStr, " (");
|
|
strcat(titleSelectorStr, patchEntries[selectedPatchIndex].contentSizeStr);
|
|
strcat(titleSelectorStr, ")");
|
|
}
|
|
|
|
uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = ((menuType == MENUTYPE_GAMECARD && titlePatchCount > 0 && selectedPatchIndex > 0) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && retrievePreviousPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppInfoIndex, false) != selectedPatchIndex));
|
|
rightArrowCondition = ((menuType == MENUTYPE_GAMECARD && titlePatchCount > 0 && selectedPatchIndex < (titlePatchCount - 1)) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppInfoIndex, false) != selectedPatchIndex));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, titleSelectorStr);
|
|
}
|
|
|
|
break;
|
|
case 8: // Output naming scheme (update)
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, dumpCfg.nspDumpCfg.useBrackets, !dumpCfg.nspDumpCfg.useBrackets, FONT_COLOR_RGB, (dumpCfg.nspDumpCfg.useBrackets ? nspNamingSchemes[1] : nspNamingSchemes[0]));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (i == 2)
|
|
{
|
|
if (dumpCfg.nspDumpCfg.useNoIntroLookup)
|
|
{
|
|
uiDrawString(FB_WIDTH / 2, ypos, FONT_COLOR_RGB, "This requires a working Internet connection!");
|
|
} else {
|
|
if (highlight)
|
|
{
|
|
uiFill(FB_WIDTH / 2, ypos - 6, FB_WIDTH / 2, font_height + 12, HIGHLIGHT_BG_COLOR_RGB);
|
|
} else {
|
|
uiFill(FB_WIDTH / 2, ypos - 6, FB_WIDTH / 2, font_height + 12, BG_COLOR_RGB);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Print settings values for the batch mode menu
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && i > 0)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 1: // Dump base applications
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.dumpAppTitles, !dumpCfg.batchDumpCfg.dumpAppTitles, (dumpCfg.batchDumpCfg.dumpAppTitles ? 0 : 255), (dumpCfg.batchDumpCfg.dumpAppTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.dumpAppTitles ? "Yes" : "No"));
|
|
break;
|
|
case 2: // Dump updates
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.dumpPatchTitles, !dumpCfg.batchDumpCfg.dumpPatchTitles, (dumpCfg.batchDumpCfg.dumpPatchTitles ? 0 : 255), (dumpCfg.batchDumpCfg.dumpPatchTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.dumpPatchTitles ? "Yes" : "No"));
|
|
break;
|
|
case 3: // Dump DLCs
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.dumpAddOnTitles, !dumpCfg.batchDumpCfg.dumpAddOnTitles, (dumpCfg.batchDumpCfg.dumpAddOnTitles ? 0 : 255), (dumpCfg.batchDumpCfg.dumpAddOnTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.dumpAddOnTitles ? "Yes" : "No"));
|
|
break;
|
|
case 4: // Split output dumps (FAT32 support)
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.isFat32, !dumpCfg.batchDumpCfg.isFat32, (dumpCfg.batchDumpCfg.isFat32 ? 0 : 255), (dumpCfg.batchDumpCfg.isFat32 ? 255 : 0), 0, (dumpCfg.batchDumpCfg.isFat32 ? "Yes" : "No"));
|
|
break;
|
|
case 5: // Remove console specific data
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.removeConsoleData, !dumpCfg.batchDumpCfg.removeConsoleData, (dumpCfg.batchDumpCfg.removeConsoleData ? 0 : 255), (dumpCfg.batchDumpCfg.removeConsoleData ? 255 : 0), 0, (dumpCfg.batchDumpCfg.removeConsoleData ? "Yes" : "No"));
|
|
break;
|
|
case 6: // Generate ticket-less dumps
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.tiklessDump, !dumpCfg.batchDumpCfg.tiklessDump, (dumpCfg.batchDumpCfg.tiklessDump ? 0 : 255), (dumpCfg.batchDumpCfg.tiklessDump ? 255 : 0), 0, (dumpCfg.batchDumpCfg.tiklessDump ? "Yes" : "No"));
|
|
break;
|
|
case 7: // Change NPDM RSA key/sig in Program NCA
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.npdmAcidRsaPatch, !dumpCfg.batchDumpCfg.npdmAcidRsaPatch, (dumpCfg.batchDumpCfg.npdmAcidRsaPatch ? 0 : 255), (dumpCfg.batchDumpCfg.npdmAcidRsaPatch ? 255 : 0), 0, (dumpCfg.batchDumpCfg.npdmAcidRsaPatch ? "Yes" : "No"));
|
|
break;
|
|
case 8: // Dump delta fragments from updates
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.dumpDeltaFragments, !dumpCfg.batchDumpCfg.dumpDeltaFragments, (dumpCfg.batchDumpCfg.dumpDeltaFragments ? 0 : 255), (dumpCfg.batchDumpCfg.dumpDeltaFragments ? 255 : 0), 0, (dumpCfg.batchDumpCfg.dumpDeltaFragments ? "Yes" : "No"));
|
|
break;
|
|
case 9: // Skip dumped titles
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.skipDumpedTitles, !dumpCfg.batchDumpCfg.skipDumpedTitles, (dumpCfg.batchDumpCfg.skipDumpedTitles ? 0 : 255), (dumpCfg.batchDumpCfg.skipDumpedTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.skipDumpedTitles ? "Yes" : "No"));
|
|
break;
|
|
case 10: // Remember dumped titles
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.rememberDumpedTitles, !dumpCfg.batchDumpCfg.rememberDumpedTitles, (dumpCfg.batchDumpCfg.rememberDumpedTitles ? 0 : 255), (dumpCfg.batchDumpCfg.rememberDumpedTitles ? 255 : 0), 0, (dumpCfg.batchDumpCfg.rememberDumpedTitles ? "Yes" : "No"));
|
|
break;
|
|
case 11: // Halt dump process on errors
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.batchDumpCfg.haltOnErrors, !dumpCfg.batchDumpCfg.haltOnErrors, (dumpCfg.batchDumpCfg.haltOnErrors ? 0 : 255), (dumpCfg.batchDumpCfg.haltOnErrors ? 255 : 0), 0, (dumpCfg.batchDumpCfg.haltOnErrors ? "Yes" : "No"));
|
|
break;
|
|
case 12: // Output naming scheme
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, dumpCfg.batchDumpCfg.useBrackets, !dumpCfg.batchDumpCfg.useBrackets, FONT_COLOR_RGB, (dumpCfg.batchDumpCfg.useBrackets ? nspNamingSchemes[1] : nspNamingSchemes[0]));
|
|
break;
|
|
case 13: // Source storage
|
|
leftArrowCondition = (dumpCfg.batchDumpCfg.batchModeSrc != BATCH_SOURCE_ALL);
|
|
rightArrowCondition = (dumpCfg.batchDumpCfg.batchModeSrc != BATCH_SOURCE_EMMC);
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL ? "All (SD card + eMMC)" : (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD ? "SD card" : "eMMC")));
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print settings values for ExeFS menu
|
|
if (uiState == stateExeFsMenu && i > 1)
|
|
{
|
|
u32 appIndex = (menuType == MENUTYPE_GAMECARD ? 0 : selectedAppInfoIndex);
|
|
|
|
switch(i)
|
|
{
|
|
case 2: // Split files bigger than 4 GiB (FAT32 support)
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.exeFsDumpCfg.isFat32, !dumpCfg.exeFsDumpCfg.isFat32, (dumpCfg.exeFsDumpCfg.isFat32 ? 0 : 255), (dumpCfg.exeFsDumpCfg.isFat32 ? 255 : 0), 0, (dumpCfg.exeFsDumpCfg.isFat32 ? "Yes" : "No"));
|
|
break;
|
|
case 3: // Save data to CFW directory (LayeredFS)
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.exeFsDumpCfg.useLayeredFSDir, !dumpCfg.exeFsDumpCfg.useLayeredFSDir, (dumpCfg.exeFsDumpCfg.useLayeredFSDir ? 0 : 255), (dumpCfg.exeFsDumpCfg.useLayeredFSDir ? 255 : 0), 0, (dumpCfg.exeFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
break;
|
|
case 4: // Use update
|
|
if (exeFsUpdateFlag)
|
|
{
|
|
if (!strlen(exeFsAndRomFsSelectorStr))
|
|
{
|
|
// Find a matching application to print its name
|
|
// Otherwise, just print the Title ID
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, MAX_CHARACTERS(exeFsAndRomFsSelectorStr));
|
|
|
|
// Concatenate patch source storage
|
|
strcat(exeFsAndRomFsSelectorStr, (patchEntries[selectedPatchIndex].storageId == NcmStorageId_GameCard ? " (gamecard)" : (patchEntries[selectedPatchIndex].storageId == NcmStorageId_SdCard ? " (SD card)" : " (eMMC)")));
|
|
|
|
uiTruncateOptionStr(exeFsAndRomFsSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = true;
|
|
rightArrowCondition = (((menuType == MENUTYPE_GAMECARD && titleAppCount == 1) || menuType == MENUTYPE_SDCARD_EMMC) && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, appIndex, false) != selectedPatchIndex);
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, exeFsAndRomFsSelectorStr);
|
|
} else {
|
|
leftArrowCondition = false;
|
|
rightArrowCondition = (((menuType == MENUTYPE_GAMECARD && titleAppCount == 1) || menuType == MENUTYPE_SDCARD_EMMC) && checkIfBaseApplicationHasPatchOrAddOn(appIndex, false));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_ERROR_RGB, "No");
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print settings values for ExeFS submenus
|
|
if ((uiState == stateExeFsSectionDataDumpMenu || uiState == stateExeFsSectionBrowserMenu) && i > 0)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 1: // Bundled application to dump/browse
|
|
if (!strlen(titleSelectorStr))
|
|
{
|
|
// Print application name
|
|
snprintf(titleSelectorStr, MAX_CHARACTERS(titleSelectorStr), "%s v%s", baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex > 0);
|
|
rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex < (titleAppCount - 1));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, titleSelectorStr);
|
|
|
|
break;
|
|
case 2: // Use update
|
|
if (exeFsUpdateFlag)
|
|
{
|
|
if (!strlen(exeFsAndRomFsSelectorStr))
|
|
{
|
|
// Find a matching application to print its name
|
|
// Otherwise, just print the Title ID
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, MAX_CHARACTERS(exeFsAndRomFsSelectorStr));
|
|
|
|
// Concatenate patch source storage
|
|
strcat(exeFsAndRomFsSelectorStr, (patchEntries[selectedPatchIndex].storageId == NcmStorageId_GameCard ? " (gamecard)" : (patchEntries[selectedPatchIndex].storageId == NcmStorageId_SdCard ? " (SD card)" : " (eMMC)")));
|
|
|
|
uiTruncateOptionStr(exeFsAndRomFsSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = true;
|
|
rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppIndex, false) != selectedPatchIndex);
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, exeFsAndRomFsSelectorStr);
|
|
} else {
|
|
leftArrowCondition = false;
|
|
rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_ERROR_RGB, "No");
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print settings values for RomFS menu
|
|
if (uiState == stateRomFsMenu && i > 1)
|
|
{
|
|
u32 appIndex = (menuType == MENUTYPE_GAMECARD ? 0 : selectedAppInfoIndex);
|
|
|
|
switch(i)
|
|
{
|
|
case 2: // Split files bigger than 4 GiB (FAT32 support)
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.romFsDumpCfg.isFat32, !dumpCfg.romFsDumpCfg.isFat32, (dumpCfg.romFsDumpCfg.isFat32 ? 0 : 255), (dumpCfg.romFsDumpCfg.isFat32 ? 255 : 0), 0, (dumpCfg.romFsDumpCfg.isFat32 ? "Yes" : "No"));
|
|
break;
|
|
case 3: // Save data to CFW directory (LayeredFS)
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.romFsDumpCfg.useLayeredFSDir, !dumpCfg.romFsDumpCfg.useLayeredFSDir, (dumpCfg.romFsDumpCfg.useLayeredFSDir ? 0 : 255), (dumpCfg.romFsDumpCfg.useLayeredFSDir ? 255 : 0), 0, (dumpCfg.romFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
break;
|
|
case 4: // Use update/DLC
|
|
if (curRomFsType != ROMFS_TYPE_APP)
|
|
{
|
|
if (!strlen(exeFsAndRomFsSelectorStr))
|
|
{
|
|
// Find a matching application to print its name. Otherwise, just print the Title ID
|
|
// Concatenate title type
|
|
// Concatenate source storage
|
|
|
|
switch(curRomFsType)
|
|
{
|
|
case ROMFS_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, MAX_CHARACTERS(exeFsAndRomFsSelectorStr));
|
|
strcat(exeFsAndRomFsSelectorStr, " (UPD)");
|
|
strcat(exeFsAndRomFsSelectorStr, (patchEntries[selectedPatchIndex].storageId == NcmStorageId_GameCard ? " (gamecard)" : (patchEntries[selectedPatchIndex].storageId == NcmStorageId_SdCard ? " (SD card)" : " (eMMC)")));
|
|
break;
|
|
case ROMFS_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, (menuType == MENUTYPE_GAMECARD && titleAppCount > 1), NULL, exeFsAndRomFsSelectorStr, MAX_CHARACTERS(exeFsAndRomFsSelectorStr));
|
|
strcat(exeFsAndRomFsSelectorStr, " (DLC)");
|
|
strcat(exeFsAndRomFsSelectorStr, (addOnEntries[selectedAddOnIndex].storageId == NcmStorageId_GameCard ? " (gamecard)" : (addOnEntries[selectedAddOnIndex].storageId == NcmStorageId_SdCard ? " (SD card)" : " (eMMC)")));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiTruncateOptionStr(exeFsAndRomFsSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = true;
|
|
rightArrowCondition = (((menuType == MENUTYPE_GAMECARD && titleAppCount == 1) || menuType == MENUTYPE_SDCARD_EMMC) && ((curRomFsType == ROMFS_TYPE_PATCH && (retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, appIndex, false) != selectedPatchIndex || checkIfBaseApplicationHasPatchOrAddOn(appIndex, true))) || (curRomFsType == ROMFS_TYPE_ADDON && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, appIndex, true) != selectedAddOnIndex)));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, exeFsAndRomFsSelectorStr);
|
|
} else {
|
|
leftArrowCondition = false;
|
|
rightArrowCondition = (((menuType == MENUTYPE_GAMECARD && titleAppCount == 1) || menuType == MENUTYPE_SDCARD_EMMC) && (checkIfBaseApplicationHasPatchOrAddOn(appIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(appIndex, true)));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_ERROR_RGB, "No");
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print settings values for RomFS submenus
|
|
if ((uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu) && i > 0)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 1: // Bundled application to dump/browse
|
|
if (!strlen(titleSelectorStr))
|
|
{
|
|
// Print application name
|
|
snprintf(titleSelectorStr, MAX_CHARACTERS(titleSelectorStr), "%s v%s", baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex > 0);
|
|
rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && selectedAppIndex < (titleAppCount - 1));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, titleSelectorStr);
|
|
|
|
break;
|
|
case 2: // Use update/DLC
|
|
if (curRomFsType != ROMFS_TYPE_APP)
|
|
{
|
|
if (!strlen(exeFsAndRomFsSelectorStr))
|
|
{
|
|
// Find a matching application to print its name. Otherwise, just print the Title ID
|
|
// Concatenate title type
|
|
// Concatenate source storage
|
|
|
|
switch(curRomFsType)
|
|
{
|
|
case ROMFS_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, MAX_CHARACTERS(exeFsAndRomFsSelectorStr));
|
|
strcat(exeFsAndRomFsSelectorStr, " (UPD)");
|
|
strcat(exeFsAndRomFsSelectorStr, (patchEntries[selectedPatchIndex].storageId == NcmStorageId_GameCard ? " (gamecard)" : (patchEntries[selectedPatchIndex].storageId == NcmStorageId_SdCard ? " (SD card)" : " (eMMC)")));
|
|
break;
|
|
case ROMFS_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, (menuType == MENUTYPE_GAMECARD), NULL, exeFsAndRomFsSelectorStr, MAX_CHARACTERS(exeFsAndRomFsSelectorStr));
|
|
strcat(exeFsAndRomFsSelectorStr, " (DLC)");
|
|
strcat(exeFsAndRomFsSelectorStr, (addOnEntries[selectedAddOnIndex].storageId == NcmStorageId_GameCard ? " (gamecard)" : (addOnEntries[selectedAddOnIndex].storageId == NcmStorageId_SdCard ? " (SD card)" : " (eMMC)")));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiTruncateOptionStr(exeFsAndRomFsSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = true;
|
|
rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && ((curRomFsType == ROMFS_TYPE_PATCH && (retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppIndex, false) != selectedPatchIndex || checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true))) || (curRomFsType == ROMFS_TYPE_ADDON && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, selectedAppIndex, true) != selectedAddOnIndex)));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, exeFsAndRomFsSelectorStr);
|
|
} else {
|
|
leftArrowCondition = false;
|
|
rightArrowCondition = (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && (checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true)));
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_ERROR_RGB, "No");
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Print settings values for the Ticket menu
|
|
if (uiState == stateTicketMenu && i > 0)
|
|
{
|
|
switch(i)
|
|
{
|
|
case 1: // Remove console specific data
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS, dumpCfg.tikDumpCfg.removeConsoleData, !dumpCfg.tikDumpCfg.removeConsoleData, (dumpCfg.tikDumpCfg.removeConsoleData ? 0 : 255), (dumpCfg.tikDumpCfg.removeConsoleData ? 255 : 0), 0, (dumpCfg.tikDumpCfg.removeConsoleData ? "Yes" : "No"));
|
|
break;
|
|
case 2: // Use ticket from title
|
|
if (curTikType != TICKET_TYPE_APP)
|
|
{
|
|
if (!strlen(titleSelectorStr))
|
|
{
|
|
// Print update/DLC TID
|
|
switch(curTikType)
|
|
{
|
|
case TICKET_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, false, NULL, titleSelectorStr, MAX_CHARACTERS(titleSelectorStr));
|
|
strcat(titleSelectorStr, " (UPD)");
|
|
break;
|
|
case TICKET_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, false, NULL, titleSelectorStr, MAX_CHARACTERS(titleSelectorStr));
|
|
strcat(titleSelectorStr, " (DLC)");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = true;
|
|
rightArrowCondition = ((curTikType == TICKET_TYPE_PATCH && (retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppInfoIndex, false) != selectedPatchIndex || checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))) || (curTikType == TICKET_TYPE_ADDON && retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, selectedAppInfoIndex, true) != selectedAddOnIndex));
|
|
} else {
|
|
if (!strlen(titleSelectorStr))
|
|
{
|
|
// Print application TID
|
|
snprintf(titleSelectorStr, MAX_CHARACTERS(titleSelectorStr), "%016lX v%s (BASE)", baseAppEntries[selectedAppInfoIndex].titleId, baseAppEntries[selectedAppInfoIndex].versionStr);
|
|
uiTruncateOptionStr(titleSelectorStr, xpos, ypos, OPTIONS_X_END_POS_NSP);
|
|
}
|
|
|
|
leftArrowCondition = false;
|
|
rightArrowCondition = (checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true));
|
|
}
|
|
|
|
uiPrintOption(xpos, ypos, OPTIONS_X_END_POS_NSP, leftArrowCondition, rightArrowCondition, FONT_COLOR_RGB, titleSelectorStr);
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == cursor) highlight = false;
|
|
}
|
|
|
|
if ((scroll + maxElements) < menuItemsCount)
|
|
{
|
|
ypos = (8 + (breaks * LINE_HEIGHT) + (uiState == stateSdCardEmmcMenu ? (j * (NACP_ICON_DOWNSCALED + 12)) : (j * (font_height + 12))));
|
|
uiDrawString((FB_WIDTH / 2) - (uiGetStrWidth(downwardsArrow) / 2), ypos, FONT_COLOR_RGB, downwardsArrow);
|
|
}
|
|
|
|
j++;
|
|
if ((scroll + maxElements) < menuItemsCount) j++;
|
|
|
|
ypos = (8 + (breaks * LINE_HEIGHT) + (j * (font_height + 12)));
|
|
|
|
if (uiState == stateMainMenu)
|
|
{
|
|
// Print warning about missing Lockpick_RCM keys file
|
|
if (!keysFileAvailable)
|
|
{
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_ERROR_RGB, "Warning: missing keys file at \"%s\".", KEYS_FILE_PATH);
|
|
ypos += (LINE_HEIGHT + LINE_STRING_OFFSET);
|
|
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_ERROR_RGB, "This file is needed to deal with the encryption schemes used by Nintendo Switch content files.");
|
|
ypos += (LINE_HEIGHT + LINE_STRING_OFFSET);
|
|
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_ERROR_RGB, "SD/eMMC operations will be entirely disabled, along with NSP/ExeFS/RomFS related operations.");
|
|
ypos += (LINE_HEIGHT + LINE_STRING_OFFSET);
|
|
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_ERROR_RGB, "Please run Lockpick_RCM to generate this file. More info at: " LOCKPICK_RCM_URL);
|
|
}
|
|
|
|
// Print warning about running the application under applet mode
|
|
if (appletModeCheck())
|
|
{
|
|
if (!keysFileAvailable) ypos += ((LINE_HEIGHT * 2) + LINE_STRING_OFFSET);
|
|
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_ERROR_RGB, "Warning: running under applet mode.");
|
|
ypos += (LINE_HEIGHT + LINE_STRING_OFFSET);
|
|
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_ERROR_RGB, "It seems you used an applet (Album, Settings, etc.) to run the application. This mode greatly limits the amount of usable RAM.");
|
|
ypos += (LINE_HEIGHT + LINE_STRING_OFFSET);
|
|
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_ERROR_RGB, "If you ever get any memory allocation errors, please consider running the application through title override (hold R while launching a game).");
|
|
}
|
|
}
|
|
|
|
// Print information about the "Change NPDM RSA key/sig in Program NCA" option
|
|
if (((uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu) && cursor == 5) || (uiState == stateSdCardEmmcBatchModeMenu && cursor == 7))
|
|
{
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_RGB, "Replaces the public RSA key in the NPDM ACID section and the NPDM RSA signature in the Program NCA (only if it needs other modifications).");
|
|
ypos += (LINE_HEIGHT + LINE_STRING_OFFSET);
|
|
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_RGB, "Disabling this will make the output NSP require ACID patches to work under any CFW, but will also make the Program NCA verifiable by PC tools.");
|
|
}
|
|
|
|
// Print information about the "Dump delta fragments" option
|
|
if ((uiState == stateNspPatchDumpMenu && cursor == 6) || (uiState == stateSdCardEmmcBatchModeMenu && cursor == 8))
|
|
{
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_RGB, "Dumps delta fragments for the selected update(s), if available. These are commonly excluded - they serve no real purpose in output dumps.");
|
|
}
|
|
|
|
// Print information about the "Split files bigger than 4 GiB (FAT32 support)" option
|
|
if ((uiState == stateExeFsMenu || uiState == stateRomFsMenu) && cursor == 2)
|
|
{
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_RGB, "If FAT32 support is enabled, files bigger than 4 GiB will be split and stored in a subdirectory with the archive bit set (like NSPs).");
|
|
}
|
|
|
|
// Print information about the "Save data to CFW directory (LayeredFS)" option
|
|
if ((uiState == stateExeFsMenu || uiState == stateRomFsMenu) && cursor == 3)
|
|
{
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_RGB, "Enabling this option will save output data to \"%s[TitleID]/%s/\" (LayeredFS directory structure).", strchr(cfwDirStr, '/'), (uiState == stateExeFsMenu ? "exefs" : "romfs"));
|
|
}
|
|
|
|
// Print hint about dumping RomFS content from DLCs
|
|
if ((uiState == stateRomFsMenu && cursor == 4 && ((menuType == MENUTYPE_GAMECARD && titleAppCount <= 1 && checkIfBaseApplicationHasPatchOrAddOn(0, true)) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true)))) || ((uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu) && cursor == 2 && (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true))))
|
|
{
|
|
uiDrawString(STRING_X_POS, ypos, FONT_COLOR_RGB, "Hint: choosing a DLC will only access RomFS data from it, unlike updates (which share their RomFS data with its base application).");
|
|
}
|
|
} else {
|
|
breaks += 2;
|
|
|
|
if (uiState == stateRomFsSectionBrowser)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "No entries available in the selected directory! Press " NINTENDO_FONT_B " to go back.");
|
|
} else
|
|
if (uiState == stateSdCardEmmcOrphanPatchAddOnMenu)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_RGB, "No orphan updates/DLCs available in the SD card / eMMC storage!");
|
|
}
|
|
}
|
|
|
|
bool curGcStatus = gameCardInfo.isInserted;
|
|
|
|
while(true)
|
|
{
|
|
uiUpdateStatusMsg();
|
|
uiRefreshDisplay();
|
|
|
|
scanPads();
|
|
|
|
keysDown = getButtonsDown();
|
|
keysHeld = getButtonsHeld();
|
|
|
|
if (keysDown || keysHeld || (menuType == MENUTYPE_GAMECARD && gameCardInfo.isInserted != curGcStatus)) break;
|
|
}
|
|
|
|
// Exit
|
|
if (keysDown & HidNpadButton_Plus) res = resultExit;
|
|
|
|
// Process key inputs only if the UI state hasn't been changed
|
|
if (res == resultNone)
|
|
{
|
|
// Process base application info change
|
|
if (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && uiState != stateHfs0Browser && uiState != stateExeFsSectionBrowser && uiState != stateRomFsSectionBrowser)
|
|
{
|
|
if ((keysDown & HidNpadButton_L) || (keysDown & HidNpadButton_ZL))
|
|
{
|
|
if (selectedAppInfoIndex > 0)
|
|
{
|
|
selectedAppInfoIndex--;
|
|
dumpedContentInfoStr[0] = '\0';
|
|
}
|
|
}
|
|
|
|
if ((keysDown & HidNpadButton_R) || (keysDown & HidNpadButton_ZR))
|
|
{
|
|
if ((selectedAppInfoIndex + 1) < titleAppCount)
|
|
{
|
|
selectedAppInfoIndex++;
|
|
dumpedContentInfoStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uiState == stateXciDumpMenu)
|
|
{
|
|
// Select
|
|
if ((keysDown & HidNpadButton_A) && cursor == 0) res = resultDumpXci;
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B) res = resultShowGameCardMenu;
|
|
|
|
// Change option to false
|
|
if (keysDown & HidNpadButton_AnyLeft)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Split output dump (FAT32 support)
|
|
dumpCfg.xciDumpCfg.isFat32 = dumpCfg.xciDumpCfg.setXciArchiveBit = false;
|
|
break;
|
|
case 2: // Create directory with archive bit set
|
|
dumpCfg.xciDumpCfg.setXciArchiveBit = false;
|
|
break;
|
|
case 3: // Keep certificate
|
|
dumpCfg.xciDumpCfg.keepCert = false;
|
|
break;
|
|
case 4: // Trim output dump
|
|
dumpCfg.xciDumpCfg.trimDump = false;
|
|
break;
|
|
case 5: // CRC32 checksum calculation + dump verification
|
|
dumpCfg.xciDumpCfg.calcCrc = false;
|
|
break;
|
|
case 6: // Dump verification method
|
|
dumpCfg.xciDumpCfg.useNoIntroLookup = false;
|
|
break;
|
|
case 7: // Output naming scheme
|
|
dumpCfg.xciDumpCfg.useBrackets = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Save settings to configuration file
|
|
saveConfig();
|
|
}
|
|
|
|
// Change option to true
|
|
if (keysDown & HidNpadButton_AnyRight)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Split output dump (FAT32 support)
|
|
dumpCfg.xciDumpCfg.isFat32 = true;
|
|
break;
|
|
case 2: // Create directory with archive bit set
|
|
dumpCfg.xciDumpCfg.setXciArchiveBit = true;
|
|
break;
|
|
case 3: // Keep certificate
|
|
dumpCfg.xciDumpCfg.keepCert = true;
|
|
break;
|
|
case 4: // Trim output dump
|
|
dumpCfg.xciDumpCfg.trimDump = true;
|
|
break;
|
|
case 5: // CRC32 checksum calculation + dump verification
|
|
dumpCfg.xciDumpCfg.calcCrc = true;
|
|
break;
|
|
case 6: // Dump verification method
|
|
dumpCfg.xciDumpCfg.useNoIntroLookup = true;
|
|
break;
|
|
case 7: // Output naming scheme
|
|
dumpCfg.xciDumpCfg.useBrackets = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Save settings to configuration file
|
|
saveConfig();
|
|
}
|
|
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
} else
|
|
if (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu)
|
|
{
|
|
// Select
|
|
if ((keysDown & HidNpadButton_A) && cursor == 0)
|
|
{
|
|
selectedNspDumpType = (uiState == stateNspAppDumpMenu ? DUMP_APP_NSP : (uiState == stateNspPatchDumpMenu ? DUMP_PATCH_NSP : DUMP_ADDON_NSP));
|
|
res = resultDumpNsp;
|
|
}
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B)
|
|
{
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (uiState == stateNspAppDumpMenu && !titlePatchCount && !titleAddOnCount)
|
|
{
|
|
res = resultShowGameCardMenu;
|
|
} else {
|
|
res = resultShowNspDumpMenu;
|
|
}
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
if (!orphanMode)
|
|
{
|
|
if (uiState == stateNspAppDumpMenu && (!titlePatchCount || !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)) && (!titleAddOnCount || !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true)))
|
|
{
|
|
res = resultShowSdCardEmmcTitleMenu;
|
|
} else {
|
|
res = resultShowNspDumpMenu;
|
|
}
|
|
} else {
|
|
res = resultShowSdCardEmmcTitleMenu;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Change option to false
|
|
if (keysDown & HidNpadButton_AnyLeft)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Split output dump (FAT32 support)
|
|
dumpCfg.nspDumpCfg.isFat32 = false;
|
|
break;
|
|
case 2: // Verify dump using No-Intro database
|
|
dumpCfg.nspDumpCfg.useNoIntroLookup = false;
|
|
break;
|
|
case 3: // Remove console specific data
|
|
dumpCfg.nspDumpCfg.removeConsoleData = dumpCfg.nspDumpCfg.tiklessDump = false;
|
|
break;
|
|
case 4: // Generate ticket-less dump
|
|
dumpCfg.nspDumpCfg.tiklessDump = false;
|
|
break;
|
|
case 5: // Change NPDM RSA key/sig in Program NCA || DLC to dump
|
|
if (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu)
|
|
{
|
|
dumpCfg.nspDumpCfg.npdmAcidRsaPatch = false;
|
|
} else
|
|
if (uiState == stateNspAddOnDumpMenu)
|
|
{
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (selectedAddOnIndex > 0)
|
|
{
|
|
selectedAddOnIndex--;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
if (!orphanMode)
|
|
{
|
|
u32 newIndex = retrievePreviousPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, selectedAppInfoIndex, true);
|
|
if (newIndex != selectedAddOnIndex)
|
|
{
|
|
selectedAddOnIndex = newIndex;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 6: // Application to dump || Dump delta fragments || Output naming scheme (DLC)
|
|
if (uiState == stateNspAppDumpMenu)
|
|
{
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (selectedAppIndex > 0)
|
|
{
|
|
selectedAppIndex--;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
} else
|
|
if (uiState == stateNspPatchDumpMenu)
|
|
{
|
|
dumpCfg.nspDumpCfg.dumpDeltaFragments = false;
|
|
} else
|
|
if (uiState == stateNspAddOnDumpMenu)
|
|
{
|
|
dumpCfg.nspDumpCfg.useBrackets = false;
|
|
}
|
|
break;
|
|
case 7: // Output naming scheme (base application) || Update to dump
|
|
if (uiState == stateNspAppDumpMenu)
|
|
{
|
|
dumpCfg.nspDumpCfg.useBrackets = false;
|
|
} else
|
|
if (uiState == stateNspPatchDumpMenu)
|
|
{
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (selectedPatchIndex > 0)
|
|
{
|
|
selectedPatchIndex--;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
if (!orphanMode)
|
|
{
|
|
u32 newIndex = retrievePreviousPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppInfoIndex, false);
|
|
if (newIndex != selectedPatchIndex)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 8: // Output naming scheme (update)
|
|
dumpCfg.nspDumpCfg.useBrackets = false;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Save settings to configuration file
|
|
saveConfig();
|
|
}
|
|
|
|
// Change option to true
|
|
if (keysDown & HidNpadButton_AnyRight)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Split output dump (FAT32 support)
|
|
dumpCfg.nspDumpCfg.isFat32 = true;
|
|
break;
|
|
case 2: // Verify dump using No-Intro database
|
|
dumpCfg.nspDumpCfg.useNoIntroLookup = true;
|
|
break;
|
|
case 3: // Remove console specific data
|
|
dumpCfg.nspDumpCfg.removeConsoleData = true;
|
|
break;
|
|
case 4: // Generate ticket-less dump
|
|
dumpCfg.nspDumpCfg.tiklessDump = true;
|
|
break;
|
|
case 5: // Change NPDM RSA key/sig in Program NCA || DLC to dump
|
|
if (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu)
|
|
{
|
|
dumpCfg.nspDumpCfg.npdmAcidRsaPatch = true;
|
|
} else {
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (titleAddOnCount > 1 && (selectedAddOnIndex + 1) < titleAddOnCount)
|
|
{
|
|
selectedAddOnIndex++;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
if (!orphanMode)
|
|
{
|
|
u32 newIndex = retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedAddOnIndex, selectedAppInfoIndex, true);
|
|
if (newIndex != selectedAddOnIndex)
|
|
{
|
|
selectedAddOnIndex = newIndex;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 6: // Application to dump || Dump delta fragments || Output naming scheme (DLC)
|
|
if (uiState == stateNspAppDumpMenu)
|
|
{
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (titleAppCount > 1 && (selectedAppIndex + 1) < titleAppCount)
|
|
{
|
|
selectedAppIndex++;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
} else
|
|
if (uiState == stateNspPatchDumpMenu)
|
|
{
|
|
dumpCfg.nspDumpCfg.dumpDeltaFragments = true;
|
|
} else
|
|
if (uiState == stateNspAddOnDumpMenu)
|
|
{
|
|
dumpCfg.nspDumpCfg.useBrackets = true;
|
|
}
|
|
break;
|
|
case 7: // Output naming scheme (base application) || Update to dump
|
|
if (uiState == stateNspAppDumpMenu)
|
|
{
|
|
dumpCfg.nspDumpCfg.useBrackets = true;
|
|
} else
|
|
if (uiState == stateNspPatchDumpMenu)
|
|
{
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (titlePatchCount > 1 && (selectedPatchIndex + 1) < titlePatchCount)
|
|
{
|
|
selectedPatchIndex++;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
if (!orphanMode)
|
|
{
|
|
u32 newIndex = retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppInfoIndex, false);
|
|
if (newIndex != selectedPatchIndex)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case 8: // Output naming scheme (update)
|
|
dumpCfg.nspDumpCfg.useBrackets = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Save settings to configuration file
|
|
saveConfig();
|
|
}
|
|
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
} else
|
|
if (uiState == stateSdCardEmmcBatchModeMenu)
|
|
{
|
|
// Select
|
|
if ((keysDown & HidNpadButton_A) && cursor == 0 && (dumpCfg.batchDumpCfg.dumpAppTitles || dumpCfg.batchDumpCfg.dumpPatchTitles || dumpCfg.batchDumpCfg.dumpAddOnTitles)) res = resultSdCardEmmcBatchDump;
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B) res = resultShowSdCardEmmcMenu;
|
|
|
|
// Change option to false
|
|
if (keysDown & HidNpadButton_AnyLeft)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Dump base applications
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = false;
|
|
break;
|
|
case 2: // Dump updates
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = false;
|
|
break;
|
|
case 3: // Dump DLCs
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = false;
|
|
break;
|
|
case 4: // Split output dumps (FAT32 support)
|
|
dumpCfg.batchDumpCfg.isFat32 = false;
|
|
break;
|
|
case 5: // Remove console specific data
|
|
dumpCfg.batchDumpCfg.removeConsoleData = dumpCfg.batchDumpCfg.tiklessDump = false;
|
|
break;
|
|
case 6: // Generate ticket-less dumps
|
|
dumpCfg.batchDumpCfg.tiklessDump = false;
|
|
break;
|
|
case 7: // Change NPDM RSA key/sig in Program NCA
|
|
dumpCfg.batchDumpCfg.npdmAcidRsaPatch = false;
|
|
break;
|
|
case 8: // Dump delta fragments from updates
|
|
dumpCfg.batchDumpCfg.dumpDeltaFragments = false;
|
|
break;
|
|
case 9: // Skip already dumped titles
|
|
dumpCfg.batchDumpCfg.skipDumpedTitles = false;
|
|
break;
|
|
case 10: // Remember dumped titles
|
|
dumpCfg.batchDumpCfg.rememberDumpedTitles = false;
|
|
break;
|
|
case 11: // Halt dump process on errors
|
|
dumpCfg.batchDumpCfg.haltOnErrors = false;
|
|
break;
|
|
case 12: // Output naming scheme
|
|
dumpCfg.batchDumpCfg.useBrackets = false;
|
|
break;
|
|
case 13: // Source storage
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc != BATCH_SOURCE_ALL)
|
|
{
|
|
dumpCfg.batchDumpCfg.batchModeSrc--;
|
|
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL)
|
|
{
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = (titleAppCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = (titlePatchCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = (titleAddOnCount > 0);
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD)
|
|
{
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = (sdCardTitleAppCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = (sdCardTitlePatchCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = (sdCardTitleAddOnCount > 0);
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC)
|
|
{
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = (emmcTitleAppCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = (emmcTitlePatchCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = (emmcTitleAddOnCount > 0);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Save settings to configuration file
|
|
saveConfig();
|
|
}
|
|
|
|
// Change option to true
|
|
if (keysDown & HidNpadButton_AnyRight)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Dump base applications
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = true;
|
|
break;
|
|
case 2: // Dump updates
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = true;
|
|
break;
|
|
case 3: // Dump DLCs
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = true;
|
|
break;
|
|
case 4: // Split output dumps (FAT32 support)
|
|
dumpCfg.batchDumpCfg.isFat32 = true;
|
|
break;
|
|
case 5: // Remove console specific data
|
|
dumpCfg.batchDumpCfg.removeConsoleData = true;
|
|
break;
|
|
case 6: // Generate ticket-less dumps
|
|
dumpCfg.batchDumpCfg.tiklessDump = true;
|
|
break;
|
|
case 7: // Change NPDM RSA key/sig in Program NCA
|
|
dumpCfg.batchDumpCfg.npdmAcidRsaPatch = true;
|
|
break;
|
|
case 8: // Dump delta fragments from updates
|
|
dumpCfg.batchDumpCfg.dumpDeltaFragments = true;
|
|
break;
|
|
case 9: // Skip already dumped titles
|
|
dumpCfg.batchDumpCfg.skipDumpedTitles = true;
|
|
break;
|
|
case 10: // Remember dumped titles
|
|
dumpCfg.batchDumpCfg.rememberDumpedTitles = true;
|
|
break;
|
|
case 11: // Halt dump process on errors
|
|
dumpCfg.batchDumpCfg.haltOnErrors = true;
|
|
break;
|
|
case 12: // Output naming scheme
|
|
dumpCfg.batchDumpCfg.useBrackets = true;
|
|
break;
|
|
case 13: // Source storage
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc != BATCH_SOURCE_EMMC)
|
|
{
|
|
dumpCfg.batchDumpCfg.batchModeSrc++;
|
|
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL)
|
|
{
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = (titleAppCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = (titlePatchCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = (titleAddOnCount > 0);
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD)
|
|
{
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = (sdCardTitleAppCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = (sdCardTitlePatchCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = (sdCardTitleAddOnCount > 0);
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC)
|
|
{
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = (emmcTitleAppCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = (emmcTitlePatchCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = (emmcTitleAddOnCount > 0);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Save settings to configuration file
|
|
saveConfig();
|
|
}
|
|
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
} else
|
|
if (uiState == stateExeFsMenu)
|
|
{
|
|
// Select
|
|
if (keysDown & HidNpadButton_A)
|
|
{
|
|
// Reset option to its default value
|
|
selectedAppIndex = (menuType == MENUTYPE_GAMECARD ? 0 : selectedAppInfoIndex);
|
|
|
|
switch(cursor)
|
|
{
|
|
case 0:
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowExeFsSectionDataDumpMenu : resultDumpExeFsSectionData);
|
|
break;
|
|
case 1:
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowExeFsSectionBrowserMenu : resultExeFsSectionBrowserGetList);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B)
|
|
{
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
freeTitlesFromSdCardAndEmmc(NcmContentMetaType_Patch);
|
|
res = resultShowGameCardMenu;
|
|
} else {
|
|
res = resultShowSdCardEmmcTitleMenu;
|
|
}
|
|
}
|
|
|
|
// Go left
|
|
if (keysDown & HidNpadButton_AnyLeft)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 2: // Split files bigger than 4 GiB (FAT32 support)
|
|
dumpCfg.exeFsDumpCfg.isFat32 = false;
|
|
break;
|
|
case 3: // Save data to CFW directory (LayeredFS)
|
|
dumpCfg.exeFsDumpCfg.useLayeredFSDir = false;
|
|
break;
|
|
case 4: // Use update
|
|
if ((menuType == MENUTYPE_GAMECARD && titleAppCount == 1 && checkIfBaseApplicationHasPatchOrAddOn(0, false)) || (menuType == MENUTYPE_SDCARD_EMMC && checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)))
|
|
{
|
|
if (exeFsUpdateFlag)
|
|
{
|
|
u32 appIndex = (menuType == MENUTYPE_GAMECARD ? 0 : selectedAppInfoIndex);
|
|
u32 newIndex = retrievePreviousPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, appIndex, false);
|
|
|
|
if (newIndex != selectedPatchIndex)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
} else {
|
|
exeFsUpdateFlag = false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go right
|
|
if (keysDown & HidNpadButton_AnyRight)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 2: // Split files bigger than 4 GiB (FAT32 support)
|
|
dumpCfg.exeFsDumpCfg.isFat32 = true;
|
|
break;
|
|
case 3: // Save data to CFW directory (LayeredFS)
|
|
dumpCfg.exeFsDumpCfg.useLayeredFSDir = true;
|
|
break;
|
|
case 4: // Use update
|
|
if ((menuType == MENUTYPE_GAMECARD && titleAppCount == 1 && checkIfBaseApplicationHasPatchOrAddOn(0, false)) || (menuType == MENUTYPE_SDCARD_EMMC && checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)))
|
|
{
|
|
u32 appIndex = (menuType == MENUTYPE_GAMECARD ? 0 : selectedAppInfoIndex);
|
|
|
|
if (exeFsUpdateFlag)
|
|
{
|
|
u32 newIndex = retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, appIndex, false);
|
|
if (newIndex != selectedPatchIndex)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
} else {
|
|
exeFsUpdateFlag = true;
|
|
selectedPatchIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(appIndex, false);
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
} else
|
|
if (uiState == stateExeFsSectionDataDumpMenu || uiState == stateExeFsSectionBrowserMenu)
|
|
{
|
|
// Select
|
|
if ((keysDown & HidNpadButton_A) && cursor == 0) res = (uiState == stateExeFsSectionDataDumpMenu ? resultDumpExeFsSectionData : resultExeFsSectionBrowserGetList);
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B) res = resultShowExeFsMenu;
|
|
|
|
// Change option to false
|
|
if (keysDown & HidNpadButton_AnyLeft)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Bundled application to dump/browse
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (selectedAppIndex > 0)
|
|
{
|
|
selectedAppIndex--;
|
|
titleSelectorStr[0] = '\0';
|
|
exeFsUpdateFlag = false;
|
|
}
|
|
}
|
|
break;
|
|
case 2: // Use update
|
|
if (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false))
|
|
{
|
|
if (exeFsUpdateFlag)
|
|
{
|
|
u32 newIndex = retrievePreviousPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppIndex, false);
|
|
if (newIndex != selectedPatchIndex)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
} else {
|
|
exeFsUpdateFlag = false;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Change option to true
|
|
if (keysDown & HidNpadButton_AnyRight)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Bundled application to dump/browse
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (titleAppCount > 1 && (selectedAppIndex + 1) < titleAppCount)
|
|
{
|
|
selectedAppIndex++;
|
|
titleSelectorStr[0] = '\0';
|
|
exeFsUpdateFlag = false;
|
|
}
|
|
}
|
|
break;
|
|
case 2: // Use update
|
|
if (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false))
|
|
{
|
|
if (exeFsUpdateFlag)
|
|
{
|
|
u32 newIndex = retrieveNextPatchOrAddOnIndexFromBaseApplication(selectedPatchIndex, selectedAppIndex, false);
|
|
if (newIndex != selectedPatchIndex)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
} else {
|
|
exeFsUpdateFlag = true;
|
|
selectedPatchIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppIndex, false);
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
} else
|
|
if (uiState == stateRomFsMenu)
|
|
{
|
|
// Select
|
|
if (keysDown & HidNpadButton_A)
|
|
{
|
|
// Reset option to its default value
|
|
if (!orphanMode) selectedAppIndex = (menuType == MENUTYPE_GAMECARD ? 0 : selectedAppInfoIndex);
|
|
|
|
switch(cursor)
|
|
{
|
|
case 0:
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowRomFsSectionDataDumpMenu : resultDumpRomFsSectionData);
|
|
break;
|
|
case 1:
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowRomFsSectionBrowserMenu : resultRomFsSectionBrowserGetEntries);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B)
|
|
{
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
freeTitlesFromSdCardAndEmmc(NcmContentMetaType_Patch);
|
|
freeTitlesFromSdCardAndEmmc(NcmContentMetaType_AddOnContent);
|
|
res = resultShowGameCardMenu;
|
|
} else {
|
|
res = resultShowSdCardEmmcTitleMenu;
|
|
}
|
|
}
|
|
|
|
// Go left
|
|
if (keysDown & HidNpadButton_AnyLeft)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 2: // Split files bigger than 4 GiB (FAT32 support)
|
|
dumpCfg.romFsDumpCfg.isFat32 = false;
|
|
break;
|
|
case 3: // Save data to CFW directory (LayeredFS)
|
|
dumpCfg.romFsDumpCfg.useLayeredFSDir = false;
|
|
break;
|
|
case 4: // Use update/DLC
|
|
if ((menuType == MENUTYPE_GAMECARD && titleAppCount == 1 && (checkIfBaseApplicationHasPatchOrAddOn(0, false) || checkIfBaseApplicationHasPatchOrAddOn(0, true))) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && (checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))))
|
|
{
|
|
if (curRomFsType != ROMFS_TYPE_APP)
|
|
{
|
|
u32 appIndex = (menuType == MENUTYPE_GAMECARD ? 0 : selectedAppInfoIndex);
|
|
u32 curIndex = (curRomFsType == ROMFS_TYPE_PATCH ? selectedPatchIndex : selectedAddOnIndex);
|
|
u32 newIndex = retrievePreviousPatchOrAddOnIndexFromBaseApplication(curIndex, appIndex, (curRomFsType == ROMFS_TYPE_ADDON));
|
|
|
|
if (newIndex != curIndex)
|
|
{
|
|
if (curRomFsType == ROMFS_TYPE_PATCH)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
} else {
|
|
selectedAddOnIndex = newIndex;
|
|
}
|
|
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
} else {
|
|
if (curRomFsType == ROMFS_TYPE_ADDON)
|
|
{
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(appIndex, false))
|
|
{
|
|
curRomFsType = ROMFS_TYPE_PATCH;
|
|
selectedPatchIndex = retrieveLastPatchOrAddOnIndexFromBaseApplication(appIndex, false);
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
} else {
|
|
curRomFsType = ROMFS_TYPE_APP;
|
|
}
|
|
} else {
|
|
curRomFsType = ROMFS_TYPE_APP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go right
|
|
if (keysDown & HidNpadButton_AnyRight)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 2: // Split files bigger than 4 GiB (FAT32 support)
|
|
dumpCfg.romFsDumpCfg.isFat32 = true;
|
|
break;
|
|
case 3: // Save data to CFW directory (LayeredFS)
|
|
dumpCfg.romFsDumpCfg.useLayeredFSDir = true;
|
|
break;
|
|
case 4: // Use update/DLC
|
|
if ((menuType == MENUTYPE_GAMECARD && titleAppCount == 1 && (checkIfBaseApplicationHasPatchOrAddOn(0, false) || checkIfBaseApplicationHasPatchOrAddOn(0, true))) || (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && (checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))))
|
|
{
|
|
u32 appIndex = (menuType == MENUTYPE_GAMECARD ? 0 : selectedAppInfoIndex);
|
|
|
|
if (curRomFsType != ROMFS_TYPE_APP)
|
|
{
|
|
u32 curIndex = (curRomFsType == ROMFS_TYPE_PATCH ? selectedPatchIndex : selectedAddOnIndex);
|
|
u32 newIndex = retrieveNextPatchOrAddOnIndexFromBaseApplication(curIndex, appIndex, (curRomFsType == ROMFS_TYPE_ADDON));
|
|
|
|
if (newIndex != curIndex)
|
|
{
|
|
if (curRomFsType == ROMFS_TYPE_PATCH)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
} else {
|
|
selectedAddOnIndex = newIndex;
|
|
}
|
|
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
} else {
|
|
if (curRomFsType == ROMFS_TYPE_PATCH)
|
|
{
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(appIndex, true))
|
|
{
|
|
curRomFsType = ROMFS_TYPE_ADDON;
|
|
selectedAddOnIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(appIndex, true);
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(appIndex, false))
|
|
{
|
|
curRomFsType = ROMFS_TYPE_PATCH;
|
|
selectedPatchIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(appIndex, false);
|
|
} else {
|
|
curRomFsType = ROMFS_TYPE_ADDON;
|
|
selectedAddOnIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(appIndex, true);
|
|
}
|
|
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
} else
|
|
if (uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu)
|
|
{
|
|
// Select
|
|
if ((keysDown & HidNpadButton_A) && cursor == 0) res = (uiState == stateRomFsSectionDataDumpMenu ? resultDumpRomFsSectionData : resultRomFsSectionBrowserGetEntries);
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B) res = resultShowRomFsMenu;
|
|
|
|
// Change option to false
|
|
if (keysDown & HidNpadButton_AnyLeft)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Bundled application to dump/browse
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (selectedAppIndex > 0)
|
|
{
|
|
selectedAppIndex--;
|
|
titleSelectorStr[0] = '\0';
|
|
curRomFsType = ROMFS_TYPE_APP;
|
|
}
|
|
}
|
|
break;
|
|
case 2: // Use update/DLC
|
|
if (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && (checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true)))
|
|
{
|
|
if (curRomFsType != ROMFS_TYPE_APP)
|
|
{
|
|
u32 curIndex = (curRomFsType == ROMFS_TYPE_PATCH ? selectedPatchIndex : selectedAddOnIndex);
|
|
u32 newIndex = retrievePreviousPatchOrAddOnIndexFromBaseApplication(curIndex, selectedAppIndex, (curRomFsType == ROMFS_TYPE_ADDON));
|
|
|
|
if (newIndex != curIndex)
|
|
{
|
|
if (curRomFsType == ROMFS_TYPE_PATCH)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
} else {
|
|
selectedAddOnIndex = newIndex;
|
|
}
|
|
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
} else {
|
|
if (curRomFsType == ROMFS_TYPE_ADDON)
|
|
{
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false))
|
|
{
|
|
curRomFsType = ROMFS_TYPE_PATCH;
|
|
selectedPatchIndex = retrieveLastPatchOrAddOnIndexFromBaseApplication(selectedAppIndex, false);
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
} else {
|
|
curRomFsType = ROMFS_TYPE_APP;
|
|
}
|
|
} else {
|
|
curRomFsType = ROMFS_TYPE_APP;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Change option to true
|
|
if (keysDown & HidNpadButton_AnyRight)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Bundled application to dump/browse
|
|
if (menuType == MENUTYPE_GAMECARD)
|
|
{
|
|
if (titleAppCount > 1 && (selectedAppIndex + 1) < titleAppCount)
|
|
{
|
|
selectedAppIndex++;
|
|
titleSelectorStr[0] = '\0';
|
|
curRomFsType = ROMFS_TYPE_APP;
|
|
}
|
|
}
|
|
break;
|
|
case 2: // Use update
|
|
if (menuType == MENUTYPE_GAMECARD && titleAppCount > 1 && (checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true)))
|
|
{
|
|
if (curRomFsType != ROMFS_TYPE_APP)
|
|
{
|
|
u32 curIndex = (curRomFsType == ROMFS_TYPE_PATCH ? selectedPatchIndex : selectedAddOnIndex);
|
|
u32 newIndex = retrieveNextPatchOrAddOnIndexFromBaseApplication(curIndex, selectedAppIndex, (curRomFsType == ROMFS_TYPE_ADDON));
|
|
|
|
if (newIndex != curIndex)
|
|
{
|
|
if (curRomFsType == ROMFS_TYPE_PATCH)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
} else {
|
|
selectedAddOnIndex = newIndex;
|
|
}
|
|
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
} else {
|
|
if (curRomFsType == ROMFS_TYPE_PATCH)
|
|
{
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true))
|
|
{
|
|
curRomFsType = ROMFS_TYPE_ADDON;
|
|
selectedAddOnIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppIndex, true);
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false))
|
|
{
|
|
curRomFsType = ROMFS_TYPE_PATCH;
|
|
selectedPatchIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppIndex, false);
|
|
} else {
|
|
curRomFsType = ROMFS_TYPE_ADDON;
|
|
selectedAddOnIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppIndex, true);
|
|
}
|
|
|
|
exeFsAndRomFsSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
} else
|
|
if (uiState == stateTicketMenu)
|
|
{
|
|
// Select
|
|
if ((keysDown & HidNpadButton_A) && cursor == 0) res = resultDumpTicket;
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B) res = resultShowSdCardEmmcTitleMenu;
|
|
|
|
// Go left
|
|
if (keysDown & HidNpadButton_AnyLeft)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Remove console specific data
|
|
dumpCfg.tikDumpCfg.removeConsoleData = false;
|
|
saveConfig();
|
|
break;
|
|
case 2: // Use ticket from title
|
|
if (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && (checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true)))
|
|
{
|
|
if (curTikType != TICKET_TYPE_APP)
|
|
{
|
|
u32 curIndex = (curTikType == TICKET_TYPE_PATCH ? selectedPatchIndex : selectedAddOnIndex);
|
|
u32 newIndex = retrievePreviousPatchOrAddOnIndexFromBaseApplication(curIndex, selectedAppInfoIndex, (curTikType == TICKET_TYPE_ADDON));
|
|
|
|
if (newIndex != curIndex)
|
|
{
|
|
if (curTikType == TICKET_TYPE_PATCH)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
} else {
|
|
selectedAddOnIndex = newIndex;
|
|
}
|
|
|
|
titleSelectorStr[0] = '\0';
|
|
} else {
|
|
if (curTikType == TICKET_TYPE_ADDON)
|
|
{
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false))
|
|
{
|
|
curTikType = TICKET_TYPE_PATCH;
|
|
selectedPatchIndex = retrieveLastPatchOrAddOnIndexFromBaseApplication(selectedAppInfoIndex, false);
|
|
titleSelectorStr[0] = '\0';
|
|
} else {
|
|
curTikType = TICKET_TYPE_APP;
|
|
}
|
|
} else {
|
|
curTikType = TICKET_TYPE_APP;
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go right
|
|
if (keysDown & HidNpadButton_AnyRight)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 1: // Remove console specific data
|
|
dumpCfg.tikDumpCfg.removeConsoleData = true;
|
|
saveConfig();
|
|
break;
|
|
case 2: // Use update/DLC
|
|
if (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && (checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false) || checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true)))
|
|
{
|
|
if (curTikType != TICKET_TYPE_APP)
|
|
{
|
|
u32 curIndex = (curTikType == TICKET_TYPE_PATCH ? selectedPatchIndex : selectedAddOnIndex);
|
|
u32 newIndex = retrieveNextPatchOrAddOnIndexFromBaseApplication(curIndex, selectedAppInfoIndex, (curTikType == TICKET_TYPE_ADDON));
|
|
|
|
if (newIndex != curIndex)
|
|
{
|
|
if (curTikType == TICKET_TYPE_PATCH)
|
|
{
|
|
selectedPatchIndex = newIndex;
|
|
} else {
|
|
selectedAddOnIndex = newIndex;
|
|
}
|
|
|
|
titleSelectorStr[0] = '\0';
|
|
} else {
|
|
if (curTikType == TICKET_TYPE_PATCH)
|
|
{
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))
|
|
{
|
|
curTikType = TICKET_TYPE_ADDON;
|
|
selectedAddOnIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppInfoIndex, true);
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false))
|
|
{
|
|
curTikType = TICKET_TYPE_PATCH;
|
|
selectedPatchIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppInfoIndex, false);
|
|
} else {
|
|
curTikType = TICKET_TYPE_ADDON;
|
|
selectedAddOnIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppInfoIndex, true);
|
|
}
|
|
|
|
titleSelectorStr[0] = '\0';
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
} else {
|
|
// Select
|
|
if (keysDown & HidNpadButton_A)
|
|
{
|
|
if (uiState == stateMainMenu)
|
|
{
|
|
selectedAppInfoIndex = 0;
|
|
|
|
switch(cursor)
|
|
{
|
|
case 0:
|
|
res = resultShowGameCardMenu;
|
|
menuType = MENUTYPE_GAMECARD;
|
|
break;
|
|
case 1:
|
|
if (keysFileAvailable)
|
|
{
|
|
res = resultShowSdCardEmmcMenu;
|
|
menuType = MENUTYPE_SDCARD_EMMC;
|
|
} else {
|
|
uiStatusMsg("Keys file unavailable at \"%s\". Option disabled.", KEYS_FILE_PATH);
|
|
}
|
|
break;
|
|
case 2:
|
|
res = resultShowUpdateMenu;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else
|
|
if (uiState == stateGameCardMenu)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 0:
|
|
res = resultShowXciDumpMenu;
|
|
break;
|
|
case 1:
|
|
if (keysFileAvailable)
|
|
{
|
|
if (!titlePatchCount && !titleAddOnCount)
|
|
{
|
|
res = resultShowNspAppDumpMenu;
|
|
|
|
// Reset option to its default value
|
|
selectedAppIndex = 0;
|
|
} else {
|
|
res = resultShowNspDumpMenu;
|
|
}
|
|
} else {
|
|
uiStatusMsg("Keys file unavailable at \"%s\". Option disabled.", KEYS_FILE_PATH);
|
|
}
|
|
break;
|
|
case 2:
|
|
res = resultShowHfs0Menu;
|
|
break;
|
|
case 3:
|
|
if (keysFileAvailable)
|
|
{
|
|
loadTitlesFromSdCardAndEmmc(NcmContentMetaType_Patch);
|
|
|
|
res = resultShowExeFsMenu;
|
|
|
|
// Reset options to their default values
|
|
exeFsUpdateFlag = false;
|
|
selectedPatchIndex = 0;
|
|
} else {
|
|
uiStatusMsg("Keys file unavailable at \"%s\". Option disabled.", KEYS_FILE_PATH);
|
|
}
|
|
break;
|
|
case 4:
|
|
if (keysFileAvailable)
|
|
{
|
|
loadTitlesFromSdCardAndEmmc(NcmContentMetaType_Patch);
|
|
loadTitlesFromSdCardAndEmmc(NcmContentMetaType_AddOnContent);
|
|
|
|
res = resultShowRomFsMenu;
|
|
|
|
// Reset options to their default values
|
|
selectedPatchIndex = selectedAddOnIndex = 0;
|
|
curRomFsType = ROMFS_TYPE_APP;
|
|
} else {
|
|
uiStatusMsg("Keys file unavailable at \"%s\". Option disabled.", KEYS_FILE_PATH);
|
|
}
|
|
break;
|
|
case 5:
|
|
res = resultDumpGameCardCertificate;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else
|
|
if (uiState == stateNspDumpMenu)
|
|
{
|
|
// Reset options to their default values
|
|
selectedAppIndex = 0;
|
|
selectedPatchIndex = 0;
|
|
selectedAddOnIndex = 0;
|
|
|
|
switch(cursor)
|
|
{
|
|
case 0:
|
|
res = resultShowNspAppDumpMenu;
|
|
if (menuType == MENUTYPE_SDCARD_EMMC) selectedAppIndex = selectedAppInfoIndex;
|
|
break;
|
|
case 1:
|
|
res = resultShowNspPatchDumpMenu;
|
|
if (menuType == MENUTYPE_SDCARD_EMMC) selectedPatchIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppInfoIndex, false);
|
|
break;
|
|
case 2:
|
|
res = resultShowNspAddOnDumpMenu;
|
|
if (menuType == MENUTYPE_SDCARD_EMMC) selectedAddOnIndex = retrieveFirstPatchOrAddOnIndexFromBaseApplication(selectedAppInfoIndex, true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else
|
|
if (uiState == stateHfs0Menu)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 0:
|
|
res = resultShowRawHfs0PartitionDumpMenu;
|
|
break;
|
|
case 1:
|
|
res = resultShowHfs0PartitionDataDumpMenu;
|
|
break;
|
|
case 2:
|
|
res = resultShowHfs0BrowserMenu;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else
|
|
if (uiState == stateRawHfs0PartitionDumpMenu)
|
|
{
|
|
// Save selected partition index
|
|
selectedPartitionIndex = (u32)cursor;
|
|
res = resultDumpRawHfs0Partition;
|
|
} else
|
|
if (uiState == stateHfs0PartitionDataDumpMenu)
|
|
{
|
|
// Save selected partition index
|
|
selectedPartitionIndex = (u32)cursor;
|
|
res = resultDumpHfs0PartitionData;
|
|
} else
|
|
if (uiState == stateHfs0BrowserMenu)
|
|
{
|
|
// Save selected partition index
|
|
selectedPartitionIndex = (u32)cursor;
|
|
res = resultHfs0BrowserGetList;
|
|
} else
|
|
if (uiState == stateHfs0Browser)
|
|
{
|
|
if (menu && menuItemsCount)
|
|
{
|
|
// Save selected file index
|
|
selectedFileIndex = (u32)cursor;
|
|
res = resultHfs0BrowserCopyFile;
|
|
}
|
|
} else
|
|
if (uiState == stateExeFsSectionBrowser)
|
|
{
|
|
if (menu && menuItemsCount)
|
|
{
|
|
// Save selected file index
|
|
selectedFileIndex = (u32)cursor;
|
|
res = resultExeFsSectionBrowserCopyFile;
|
|
}
|
|
} else
|
|
if (uiState == stateRomFsSectionBrowser)
|
|
{
|
|
if (menu && menuItemsCount)
|
|
{
|
|
// Save selected file index
|
|
selectedFileIndex = (u32)cursor;
|
|
if (strlen(curRomFsPath) <= 1) selectedFileIndex++; // Adjust index if we're at the root directory
|
|
res = (romFsBrowserEntries[selectedFileIndex].type == ROMFS_ENTRY_DIR ? resultRomFsSectionBrowserChangeDir : resultRomFsSectionBrowserCopyFile);
|
|
}
|
|
} else
|
|
if (uiState == stateSdCardEmmcMenu)
|
|
{
|
|
// Save selected base application index
|
|
selectedAppInfoIndex = (u32)cursor;
|
|
res = resultShowSdCardEmmcTitleMenu;
|
|
} else
|
|
if (uiState == stateSdCardEmmcTitleMenu)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 0:
|
|
if (!orphanMode)
|
|
{
|
|
if ((!titlePatchCount || !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)) && (!titleAddOnCount || !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true)))
|
|
{
|
|
res = resultShowNspAppDumpMenu;
|
|
selectedAppIndex = selectedAppInfoIndex;
|
|
} else {
|
|
res = resultShowNspDumpMenu;
|
|
}
|
|
} else {
|
|
res = (orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_PATCH ? resultShowNspPatchDumpMenu : resultShowNspAddOnDumpMenu);
|
|
}
|
|
break;
|
|
case 1:
|
|
res = resultShowExeFsMenu;
|
|
|
|
if (!orphanMode)
|
|
{
|
|
// Reset options to their default values
|
|
exeFsUpdateFlag = false;
|
|
selectedPatchIndex = 0;
|
|
} else {
|
|
exeFsUpdateFlag = true;
|
|
}
|
|
|
|
break;
|
|
case 2:
|
|
res = resultShowRomFsMenu;
|
|
|
|
if (!orphanMode)
|
|
{
|
|
// Reset options to their default values
|
|
selectedPatchIndex = selectedAddOnIndex = 0;
|
|
curRomFsType = ROMFS_TYPE_APP;
|
|
} else {
|
|
curRomFsType = ROMFS_TYPE_ADDON;
|
|
}
|
|
|
|
break;
|
|
case 3:
|
|
res = resultShowTicketMenu;
|
|
|
|
if (!orphanMode)
|
|
{
|
|
// Reset options to their default values
|
|
selectedPatchIndex = selectedAddOnIndex = 0;
|
|
curTikType = TICKET_TYPE_APP;
|
|
} else {
|
|
curTikType = (orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_PATCH ? TICKET_TYPE_PATCH : TICKET_TYPE_ADDON);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} else
|
|
if (uiState == stateSdCardEmmcOrphanPatchAddOnMenu)
|
|
{
|
|
if (menu && menuItemsCount)
|
|
{
|
|
if (orphanEntries[cursor].type == ORPHAN_ENTRY_TYPE_PATCH)
|
|
{
|
|
selectedPatchIndex = orphanEntries[cursor].index;
|
|
} else
|
|
if (orphanEntries[cursor].type == ORPHAN_ENTRY_TYPE_ADDON)
|
|
{
|
|
selectedAddOnIndex = orphanEntries[cursor].index;
|
|
}
|
|
|
|
res = resultShowSdCardEmmcTitleMenu;
|
|
}
|
|
} else
|
|
if (uiState == stateUpdateMenu)
|
|
{
|
|
switch(cursor)
|
|
{
|
|
case 0:
|
|
res = resultUpdateNSWDBXml;
|
|
break;
|
|
case 1:
|
|
if (!updatePerformed)
|
|
{
|
|
res = resultUpdateApplication;
|
|
} else {
|
|
uiStatusMsg("Update already performed. Please restart the application.");
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Back
|
|
if (keysDown & HidNpadButton_B)
|
|
{
|
|
if (uiState == stateGameCardMenu || uiState == stateSdCardEmmcMenu || uiState == stateUpdateMenu)
|
|
{
|
|
res = resultShowMainMenu;
|
|
menuType = MENUTYPE_MAIN;
|
|
} else
|
|
if (menuType == MENUTYPE_GAMECARD && (uiState == stateNspDumpMenu || uiState == stateHfs0Menu))
|
|
{
|
|
res = resultShowGameCardMenu;
|
|
} else
|
|
if (uiState == stateRawHfs0PartitionDumpMenu || uiState == stateHfs0PartitionDataDumpMenu || uiState == stateHfs0BrowserMenu)
|
|
{
|
|
res = resultShowHfs0Menu;
|
|
} else
|
|
if (uiState == stateHfs0Browser)
|
|
{
|
|
freeHfs0ExeFsEntriesSizes();
|
|
freeFilenameBuffer();
|
|
|
|
res = resultShowHfs0BrowserMenu;
|
|
} else
|
|
if (uiState == stateExeFsSectionBrowser)
|
|
{
|
|
freeHfs0ExeFsEntriesSizes();
|
|
freeFilenameBuffer();
|
|
|
|
freeExeFsContext();
|
|
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowExeFsSectionBrowserMenu : resultShowExeFsMenu);
|
|
} else
|
|
if (uiState == stateRomFsSectionBrowser)
|
|
{
|
|
if (strlen(curRomFsPath) > 1)
|
|
{
|
|
// Point to the parent directory entry ("..")
|
|
selectedFileIndex = 0;
|
|
res = resultRomFsSectionBrowserChangeDir;
|
|
} else {
|
|
freeRomFsBrowserEntries();
|
|
freeFilenameBuffer();
|
|
if (curRomFsType == ROMFS_TYPE_PATCH) freeBktrContext();
|
|
freeRomFsContext();
|
|
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowRomFsSectionBrowserMenu : resultShowRomFsMenu);
|
|
}
|
|
} else
|
|
if (uiState == stateSdCardEmmcTitleMenu)
|
|
{
|
|
res = (!orphanMode ? resultShowSdCardEmmcMenu : resultShowSdCardEmmcOrphanPatchAddOnMenu);
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC && !orphanMode && uiState == stateNspDumpMenu)
|
|
{
|
|
res = resultShowSdCardEmmcTitleMenu;
|
|
} else
|
|
if (uiState == stateSdCardEmmcOrphanPatchAddOnMenu)
|
|
{
|
|
res = resultShowSdCardEmmcMenu;
|
|
orphanMode = false;
|
|
}
|
|
}
|
|
|
|
// Special action #1
|
|
if (keysDown & HidNpadButton_Y)
|
|
{
|
|
if (uiState == stateSdCardEmmcMenu && (calculateOrphanPatchOrAddOnCount(false) || calculateOrphanPatchOrAddOnCount(true)))
|
|
{
|
|
// SD/eMMC menu: Dump installed content with missing base application
|
|
res = resultShowSdCardEmmcOrphanPatchAddOnMenu;
|
|
orphanMode = true;
|
|
} else
|
|
if (uiState == stateRomFsSectionBrowser && strlen(curRomFsPath) > 1)
|
|
{
|
|
// RomFS section browser: dump current directory
|
|
res = resultRomFsSectionBrowserCopyDir;
|
|
}
|
|
}
|
|
|
|
// Special action #2
|
|
if (keysDown & HidNpadButton_X)
|
|
{
|
|
if (uiState == stateSdCardEmmcMenu && (titleAppCount || titlePatchCount || titleAddOnCount))
|
|
{
|
|
// Batch mode
|
|
res = resultShowSdCardEmmcBatchModeMenu;
|
|
|
|
// Check if we're using the default configuration
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && !dumpCfg.batchDumpCfg.dumpAppTitles && !dumpCfg.batchDumpCfg.dumpPatchTitles && !dumpCfg.batchDumpCfg.dumpAddOnTitles)
|
|
{
|
|
dumpCfg.batchDumpCfg.dumpAppTitles = (titleAppCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpPatchTitles = (titlePatchCount > 0);
|
|
dumpCfg.batchDumpCfg.dumpAddOnTitles = (titleAddOnCount > 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (menu && menuItemsCount)
|
|
{
|
|
// Go up
|
|
if ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp) || (keysHeld & HidNpadButton_StickRUp))
|
|
{
|
|
scrollAmount = -1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Up) || (keysDown & HidNpadButton_StickLUp));
|
|
}
|
|
|
|
if ((keysDown & HidNpadButton_Left) || (keysDown & HidNpadButton_StickLLeft) || (keysHeld & HidNpadButton_StickRLeft)) scrollAmount = -5;
|
|
|
|
// Go down
|
|
if ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown) || (keysHeld & HidNpadButton_StickRDown))
|
|
{
|
|
scrollAmount = 1;
|
|
scrollWithKeysDown = ((keysDown & HidNpadButton_Down) || (keysDown & HidNpadButton_StickLDown));
|
|
}
|
|
|
|
if ((keysDown & HidNpadButton_Right) || (keysDown & HidNpadButton_StickLRight) || (keysHeld & HidNpadButton_StickRRight)) scrollAmount = 5;
|
|
}
|
|
}
|
|
|
|
// Calculate scroll only if the UI state hasn't been changed
|
|
if (res == resultNone)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
if (scrollWithKeysDown && (cursor + scrollAmount) > (menuItemsCount - 1))
|
|
{
|
|
cursor = 0;
|
|
scroll = 0;
|
|
} else {
|
|
for(i = 0; i < scrollAmount; i++)
|
|
{
|
|
if (cursor >= (menuItemsCount - 1)) break;
|
|
cursor++;
|
|
if ((cursor - scroll) >= maxElements) scroll++;
|
|
}
|
|
}
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
if (scrollWithKeysDown && (cursor + scrollAmount) < 0)
|
|
{
|
|
cursor = (menuItemsCount - 1);
|
|
scroll = (menuItemsCount - maxElements);
|
|
if (scroll < 0) scroll = 0;
|
|
} else {
|
|
for(i = 0; i < -scrollAmount; i++)
|
|
{
|
|
if (cursor <= 0) break;
|
|
cursor--;
|
|
if ((cursor - scroll) < 0) scroll--;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Create directory with archive bit set" option in the XCI dump menu if "Split output dump" is disabled
|
|
if (uiState == stateXciDumpMenu && cursor == 2 && !dumpCfg.xciDumpCfg.isFat32)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor++;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor--;
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Dump verification method" option if "CRC32 checksum calculation + dump verification" is disabled
|
|
if (uiState == stateXciDumpMenu && cursor == 6 && !dumpCfg.xciDumpCfg.calcCrc)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor++;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor--;
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Dump bundled update NSP" / "Dump installed update NSP" option in the NSP dump menu if we're dealing with a gamecard and it doesn't include any bundled updates, or if we're dealing with a SD/eMMC title without installed updates
|
|
// Also avoid placing the cursor on the "Dump bundled DLC NSP" / "Dump installed DLC NSP" option in the NSP dump menu if we're dealing with a gamecard and it doesn't include any bundled DLCs, or if we're dealing with a SD/eMMC title without installed DLCs
|
|
if (uiState == stateNspDumpMenu && ((cursor == 1 && (!titlePatchCount || (menuType == MENUTYPE_SDCARD_EMMC && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)))) || (cursor == 2 && (!titleAddOnCount || (menuType == MENUTYPE_SDCARD_EMMC && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))))))
|
|
{
|
|
if (cursor == 1)
|
|
{
|
|
if ((!titleAddOnCount || (menuType == MENUTYPE_SDCARD_EMMC && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))))
|
|
{
|
|
// Just in case
|
|
cursor = 0;
|
|
} else {
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = 2;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = 0;
|
|
}
|
|
}
|
|
} else
|
|
if (cursor == 2)
|
|
{
|
|
if (!titlePatchCount || (menuType == MENUTYPE_SDCARD_EMMC && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)))
|
|
{
|
|
// Just in case
|
|
cursor = 0;
|
|
} else {
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 0 : 1);
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Verify dump using No-Intro database" option in the NSP dump menus if we're dealing with a gamecard title
|
|
// Also, avoid placing the cursor on the "Remove console specific data" option in the NSP dump menus if we're dealing with a gamecard title
|
|
// Also, avoid placing the cursor on the "Generate ticket-less dump" option in the NSP dump menus if we're dealing with a gamecard Application/AddOn title
|
|
if (menuType == MENUTYPE_GAMECARD && (((uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && (cursor == 2 || cursor == 3)) || ((uiState == stateNspAppDumpMenu || uiState == stateNspAddOnDumpMenu) && cursor == 4)))
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = ((uiState == stateNspPatchDumpMenu && cursor <= 3) ? 4 : 5);
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = 1;
|
|
}
|
|
}
|
|
|
|
// Avoid printing the "Dump delta fragments" option in the update NSP dump menu if we're dealing with a gamecard update
|
|
if (menuType == MENUTYPE_GAMECARD && uiState == stateNspPatchDumpMenu && cursor == 6)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor++;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor--;
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Generate ticket-less dump" option in the NSP dump menus if we're dealing with a SD/eMMC title and the "Remove console specific data" option is disabled
|
|
if (menuType == MENUTYPE_SDCARD_EMMC && (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu) && cursor == 4 && !dumpCfg.nspDumpCfg.removeConsoleData)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor++;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor--;
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Dump base applications", "Dump updates" and/or "Dump DLCs" options in the batch mode menu if we're dealing with a storage source that doesn't hold any title belonging to the current category
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && ((!titleAppCount && cursor == 1) || (!titlePatchCount && cursor == 2) || (!titleAddOnCount && cursor == 3))) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && ((!sdCardTitleAppCount && cursor == 1) || (!sdCardTitlePatchCount && cursor == 2) || (!sdCardTitleAddOnCount && cursor == 3))) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && ((!emmcTitleAppCount && cursor == 1) || (!emmcTitlePatchCount && cursor == 2) || (!emmcTitleAddOnCount && cursor == 3)))))
|
|
{
|
|
if (cursor == 1)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL)
|
|
{
|
|
cursor = (titlePatchCount ? 2 : (titleAddOnCount ? 3 : 4));
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD)
|
|
{
|
|
cursor = (sdCardTitlePatchCount ? 2 : (sdCardTitleAddOnCount ? 3 : 4));
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC)
|
|
{
|
|
cursor = (emmcTitlePatchCount ? 2 : (emmcTitleAddOnCount ? 3 : 4));
|
|
}
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = 0;
|
|
}
|
|
} else
|
|
if (cursor == 2)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL)
|
|
{
|
|
cursor = (titleAddOnCount ? 3 : 4);
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD)
|
|
{
|
|
cursor = (sdCardTitleAddOnCount ? 3 : 4);
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC)
|
|
{
|
|
cursor = (emmcTitleAddOnCount ? 3 : 4);
|
|
}
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL)
|
|
{
|
|
cursor = (titleAppCount ? 1 : 0);
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD)
|
|
{
|
|
cursor = (sdCardTitleAppCount ? 1 : 0);
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC)
|
|
{
|
|
cursor = (emmcTitleAppCount ? 1 : 0);
|
|
}
|
|
}
|
|
} else
|
|
if (cursor == 3)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = 4;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL)
|
|
{
|
|
cursor = (titlePatchCount ? 2 : (titleAppCount ? 1 : 0));
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD)
|
|
{
|
|
cursor = (sdCardTitlePatchCount ? 2 : (sdCardTitleAppCount ? 1 : 0));
|
|
} else
|
|
if (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC)
|
|
{
|
|
cursor = (emmcTitlePatchCount ? 2 : (emmcTitleAppCount ? 1 : 0));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Generate ticket-less dumps" option in the batch mode menu if the "Remove console specific data" option is disabled
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && cursor == 6 && !dumpCfg.batchDumpCfg.removeConsoleData)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor++;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor--;
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Dump delta fragments from updates" option in the batch mode menu if the "Dump updates" option is disabled
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && cursor == 8 && !dumpCfg.batchDumpCfg.dumpPatchTitles)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor++;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor--;
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Source storage" option in the batch mode menu if we only have titles available in a single source storage device
|
|
if (uiState == stateSdCardEmmcBatchModeMenu && cursor == 13 && ((!sdCardTitleAppCount && !sdCardTitlePatchCount && !sdCardTitleAddOnCount) || (!emmcTitleAppCount && !emmcTitlePatchCount && !emmcTitleAddOnCount)))
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 0 : 12);
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor--;
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Use update" option in the ExeFS menu if we're dealing with a gamecard and either its base application count is greater than 1 or it has no available patches
|
|
// Also avoid placing the cursor on it if we're dealing with a SD/eMMC title and it has no available patches, or if we're dealing with an orphan Patch
|
|
if (uiState == stateExeFsMenu && cursor == 4 && ((menuType == MENUTYPE_GAMECARD && (titleAppCount > 1 || !checkIfBaseApplicationHasPatchOrAddOn(0, false))) || (menuType == MENUTYPE_SDCARD_EMMC && ((!orphanMode && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false)) || orphanMode))))
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 0 : 1);
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Use update" option in the ExeFS data dump and browser menus if we're not dealing with a gamecard, if the base application count is equal to or less than 1, or if the selected base application has no available patches
|
|
if ((uiState == stateExeFsSectionDataDumpMenu || uiState == stateExeFsSectionBrowserMenu) && cursor == 2 && (menuType != MENUTYPE_GAMECARD || titleAppCount <= 1 || !checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false)))
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 0 : 1);
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Use update/DLC" option in the RomFS menu if we're dealing with a gamecard and either its base application count is greater than 1 or it has no available patches/DLCs
|
|
// Also avoid placing the cursor on it if we're dealing with a SD/eMMC title and it has no available patches/DLCs (or if its an orphan title)
|
|
if (uiState == stateRomFsMenu && cursor == 4 && ((menuType == MENUTYPE_GAMECARD && (titleAppCount > 1 || (!checkIfBaseApplicationHasPatchOrAddOn(0, false) && !checkIfBaseApplicationHasPatchOrAddOn(0, true)))) || (menuType == MENUTYPE_SDCARD_EMMC && (orphanMode || (!checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, false) && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppInfoIndex, true))))))
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 0 : 1);
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Use update/DLC" option in the RomFS data dump and browser menus if we're not dealing with a gamecard, if the base application count is equal to or less than 1, or if the selected base application has no available patches/DLCs
|
|
if ((uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu) && cursor == 2 && (menuType != MENUTYPE_GAMECARD || titleAppCount <= 1 || (!checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, false) && !checkIfBaseApplicationHasPatchOrAddOn(selectedAppIndex, true))))
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 0 : 1);
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "RomFS options" element in the SD card / eMMC title menu if we're dealing with an orphan Patch
|
|
// Also avoid placing the cursor on the "ExeFS options" element in the SD card / eMMC title menu if we're dealing with an orphan DLC
|
|
if (uiState == stateSdCardEmmcTitleMenu && orphanMode && ((orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_PATCH && cursor == 2) || (orphanEntries[orphanListCursor].type == ORPHAN_ENTRY_TYPE_ADDON && cursor == 1)))
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor++;
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor--;
|
|
}
|
|
}
|
|
|
|
// Avoid placing the cursor on the "Use ticket from title" element in the Ticket menu if we're dealing with an orphan title
|
|
if (uiState == stateTicketMenu && orphanMode && cursor == 2)
|
|
{
|
|
if (scrollAmount > 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 0 : 1);
|
|
} else
|
|
if (scrollAmount < 0)
|
|
{
|
|
cursor = (scrollWithKeysDown ? 1 : 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
if (uiState == stateDumpXci)
|
|
{
|
|
char tmp[128] = {'\0'};
|
|
strbuf[0] = '\0';
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[0]);
|
|
breaks++;
|
|
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s", xciDumpMenuItems[1], (dumpCfg.xciDumpCfg.isFat32 ? "Yes" : "No"));
|
|
|
|
if (dumpCfg.xciDumpCfg.isFat32)
|
|
{
|
|
strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", xciDumpMenuItems[2], (dumpCfg.xciDumpCfg.setXciArchiveBit ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", xciDumpMenuItems[3], (dumpCfg.xciDumpCfg.keepCert ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", xciDumpMenuItems[4], (dumpCfg.xciDumpCfg.trimDump ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s", xciDumpMenuItems[5], (dumpCfg.xciDumpCfg.calcCrc ? "Yes" : "No"));
|
|
|
|
if (dumpCfg.xciDumpCfg.calcCrc)
|
|
{
|
|
strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", xciDumpMenuItems[6], (dumpCfg.xciDumpCfg.useNoIntroLookup ? xciChecksumLookupMethods[1] : xciChecksumLookupMethods[0]));
|
|
strcat(strbuf, tmp);
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", xciDumpMenuItems[7], (dumpCfg.xciDumpCfg.useBrackets ? xciNamingSchemes[1] : xciNamingSchemes[0]));
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
dumpNXCardImage(&(dumpCfg.xciDumpCfg));
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowXciDumpMenu;
|
|
|
|
dumpedContentInfoStr[0] = '\0';
|
|
} else
|
|
if (uiState == stateDumpNsp)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (menuType == MENUTYPE_GAMECARD ? nspDumpGameCardMenuItems[selectedNspDumpType] : nspDumpSdCardEmmcMenuItems[selectedNspDumpType]));
|
|
breaks++;
|
|
|
|
menu = (selectedNspDumpType == DUMP_APP_NSP ? nspAppDumpMenuItems : (selectedNspDumpType == DUMP_PATCH_NSP ? nspPatchDumpMenuItems : nspAddOnDumpMenuItems));
|
|
|
|
char tmp[128] = {'\0'};
|
|
strbuf[0] = '\0';
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[1], (dumpCfg.nspDumpCfg.isFat32 ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
if (menuType != MENUTYPE_GAMECARD)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[2], (dumpCfg.nspDumpCfg.useNoIntroLookup ? "Yes" : "No"));
|
|
breaks++;
|
|
}
|
|
|
|
if (menuType == MENUTYPE_GAMECARD && selectedNspDumpType == DUMP_PATCH_NSP)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s", menu[4], (dumpCfg.nspDumpCfg.tiklessDump ? "Yes" : "No"));
|
|
} else
|
|
if (menuType == MENUTYPE_SDCARD_EMMC)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s", menu[3], (dumpCfg.nspDumpCfg.removeConsoleData ? "Yes" : "No"));
|
|
|
|
if (dumpCfg.nspDumpCfg.removeConsoleData)
|
|
{
|
|
strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", menu[4], (dumpCfg.nspDumpCfg.tiklessDump ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
}
|
|
}
|
|
|
|
if (selectedNspDumpType == DUMP_APP_NSP || selectedNspDumpType == DUMP_PATCH_NSP)
|
|
{
|
|
if ((menuType == MENUTYPE_GAMECARD && selectedNspDumpType == DUMP_PATCH_NSP) || menuType == MENUTYPE_SDCARD_EMMC) strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", menu[5], (dumpCfg.nspDumpCfg.npdmAcidRsaPatch ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
|
|
if (selectedNspDumpType == DUMP_PATCH_NSP)
|
|
{
|
|
strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", menu[6], (dumpCfg.nspDumpCfg.dumpDeltaFragments ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
}
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks++;
|
|
|
|
if (selectedNspDumpType == DUMP_APP_NSP)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s v%s", menu[6], baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
} else
|
|
if (selectedNspDumpType == DUMP_PATCH_NSP)
|
|
{
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, menu[7], strbuf, MAX_CHARACTERS(strbuf));
|
|
} else
|
|
if (selectedNspDumpType == DUMP_ADDON_NSP)
|
|
{
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, true, menu[5], strbuf, MAX_CHARACTERS(strbuf));
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", (selectedNspDumpType == DUMP_ADDON_NSP ? menu[6] : (selectedNspDumpType == DUMP_APP_NSP ? menu[7] : menu[8])), (dumpCfg.nspDumpCfg.useBrackets ? nspNamingSchemes[1] : nspNamingSchemes[0]));
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
dumpNintendoSubmissionPackage(selectedNspDumpType, (selectedNspDumpType == DUMP_APP_NSP ? selectedAppIndex : (selectedNspDumpType == DUMP_PATCH_NSP ? selectedPatchIndex : selectedAddOnIndex)), &(dumpCfg.nspDumpCfg), false);
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
|
|
res = (selectedNspDumpType == DUMP_APP_NSP ? resultShowNspAppDumpMenu : (selectedNspDumpType == DUMP_PATCH_NSP ? resultShowNspPatchDumpMenu : resultShowNspAddOnDumpMenu));
|
|
|
|
dumpedContentInfoStr[0] = '\0';
|
|
} else
|
|
if (uiState == stateSdCardEmmcBatchDump)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Batch dump");
|
|
breaks++;
|
|
|
|
menu = batchModeMenuItems;
|
|
|
|
char tmp[128] = {'\0'};
|
|
strbuf[0] = '\0';
|
|
|
|
if ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && titleAppCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitleAppCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && emmcTitleAppCount))
|
|
{
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", menu[1], (dumpCfg.batchDumpCfg.dumpAppTitles ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
}
|
|
|
|
if ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && titlePatchCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitlePatchCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && emmcTitlePatchCount))
|
|
{
|
|
if (strlen(strbuf)) strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", menu[2], (dumpCfg.batchDumpCfg.dumpPatchTitles ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
}
|
|
|
|
if ((dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL && titleAddOnCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD && sdCardTitleAddOnCount) || (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_EMMC && emmcTitleAddOnCount))
|
|
{
|
|
if (strlen(strbuf)) strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", menu[3], (dumpCfg.batchDumpCfg.dumpAddOnTitles ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
}
|
|
|
|
if (strlen(strbuf))
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks++;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[4], (dumpCfg.batchDumpCfg.isFat32 ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s", menu[5], (dumpCfg.batchDumpCfg.removeConsoleData ? "Yes" : "No"));
|
|
|
|
if (dumpCfg.batchDumpCfg.removeConsoleData)
|
|
{
|
|
strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", menu[6], (dumpCfg.batchDumpCfg.tiklessDump ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
}
|
|
|
|
strcat(strbuf, " | ");
|
|
snprintf(tmp, MAX_CHARACTERS(tmp), "%s%s", menu[7], (dumpCfg.batchDumpCfg.npdmAcidRsaPatch ? "Yes" : "No"));
|
|
strcat(strbuf, tmp);
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks++;
|
|
|
|
if (dumpCfg.batchDumpCfg.dumpPatchTitles)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[8], (dumpCfg.batchDumpCfg.dumpDeltaFragments ? "Yes" : "No"));
|
|
breaks++;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s | %s%s", menu[9], (dumpCfg.batchDumpCfg.skipDumpedTitles ? "Yes" : "No"), menu[10], (dumpCfg.batchDumpCfg.rememberDumpedTitles ? "Yes" : "No"), menu[11], (dumpCfg.batchDumpCfg.haltOnErrors ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[12], (dumpCfg.batchDumpCfg.useBrackets ? nspNamingSchemes[1] : nspNamingSchemes[0]));
|
|
breaks++;
|
|
|
|
if ((sdCardTitleAppCount || sdCardTitlePatchCount || sdCardTitleAddOnCount) && (emmcTitleAppCount || emmcTitlePatchCount || emmcTitleAddOnCount))
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[13], (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_ALL ? "All (SD card + eMMC)" : (dumpCfg.batchDumpCfg.batchModeSrc == BATCH_SOURCE_SDCARD ? "SD card" : "eMMC")));
|
|
breaks++;
|
|
}
|
|
|
|
breaks++;
|
|
uiRefreshDisplay();
|
|
|
|
int ret = dumpNintendoSubmissionPackageBatch(&(dumpCfg.batchDumpCfg));
|
|
|
|
if (ret == -2)
|
|
{
|
|
uiRefreshDisplay();
|
|
res = resultExit;
|
|
} else {
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowSdCardEmmcBatchModeMenu;
|
|
}
|
|
} else
|
|
if (uiState == stateDumpRawHfs0Partition)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Raw %s", (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems[selectedPartitionIndex] : hfs0PartitionDumpType2MenuItems[selectedPartitionIndex]));
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
dumpRawHfs0Partition(selectedPartitionIndex, true);
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowRawHfs0PartitionDumpMenu;
|
|
} else
|
|
if (uiState == stateDumpHfs0PartitionData)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Data %s", (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems[selectedPartitionIndex] : hfs0PartitionDumpType2MenuItems[selectedPartitionIndex]));
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
dumpHfs0PartitionData(selectedPartitionIndex, true);
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowHfs0PartitionDataDumpMenu;
|
|
} else
|
|
if (uiState == stateHfs0BrowserGetList)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, (gameCardInfo.hfs0PartitionCnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems[selectedPartitionIndex] : hfs0BrowserType2MenuItems[selectedPartitionIndex]));
|
|
breaks += 2;
|
|
|
|
uiPleaseWait(0);
|
|
breaks += 2;
|
|
|
|
if (getHfs0FileList(selectedPartitionIndex))
|
|
{
|
|
res = resultShowHfs0Browser;
|
|
} else {
|
|
waitForButtonPress();
|
|
res = resultShowHfs0BrowserMenu;
|
|
}
|
|
} else
|
|
if (uiState == stateHfs0BrowserCopyFile)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Manual File Dump: %s (HFS0 partition %u [%s])", filenameBuffer[selectedFileIndex], selectedPartitionIndex, GAMECARD_PARTITION_NAME(gameCardInfo.hfs0PartitionCnt, selectedPartitionIndex));
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
dumpFileFromHfs0Partition(selectedPartitionIndex, selectedFileIndex, filenameBuffer[selectedFileIndex], true);
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowHfs0Browser;
|
|
} else
|
|
if (uiState == stateDumpExeFsSectionData)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[0]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", exeFsMenuItems[2], (dumpCfg.exeFsDumpCfg.isFat32 ? "Yes" : "No"), exeFsMenuItems[3], (dumpCfg.exeFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
if (!exeFsUpdateFlag)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s v%s", exeFsSectionDumpMenuItems[1], baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
} else {
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update to dump: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
u32 curIndex = (!exeFsUpdateFlag ? selectedAppIndex : selectedPatchIndex);
|
|
|
|
dumpExeFsSectionData(curIndex, exeFsUpdateFlag, &(dumpCfg.exeFsDumpCfg));
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowExeFsSectionDataDumpMenu : resultShowExeFsMenu);
|
|
} else
|
|
if (uiState == stateExeFsSectionBrowserGetList)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, exeFsMenuItems[1]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", exeFsMenuItems[2], (dumpCfg.exeFsDumpCfg.isFat32 ? "Yes" : "No"), exeFsMenuItems[3], (dumpCfg.exeFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
if (!exeFsUpdateFlag)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s v%s", exeFsSectionBrowserMenuItems[1], baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
} else {
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update to browse: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiPleaseWait(0);
|
|
breaks += 2;
|
|
|
|
bool exefs_fail = false;
|
|
|
|
u32 curIndex = (!exeFsUpdateFlag ? selectedAppIndex : selectedPatchIndex);
|
|
|
|
if (readNcaExeFsSection(curIndex, exeFsUpdateFlag))
|
|
{
|
|
if (getExeFsFileList())
|
|
{
|
|
res = resultShowExeFsSectionBrowser;
|
|
} else {
|
|
freeExeFsContext();
|
|
exefs_fail = true;
|
|
}
|
|
} else {
|
|
exefs_fail = true;
|
|
}
|
|
|
|
if (exefs_fail)
|
|
{
|
|
breaks += 2;
|
|
waitForButtonPress();
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowExeFsSectionBrowserMenu : resultShowExeFsMenu);
|
|
}
|
|
} else
|
|
if (uiState == stateExeFsSectionBrowserCopyFile)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Manual File Dump: %s (ExeFS)", filenameBuffer[selectedFileIndex]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", exeFsMenuItems[2], (dumpCfg.exeFsDumpCfg.isFat32 ? "Yes" : "No"), exeFsMenuItems[3], (dumpCfg.exeFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
if (!exeFsUpdateFlag)
|
|
{
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "Base application: %s v%s", baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
} else {
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
u32 curIndex = (!exeFsUpdateFlag ? selectedAppIndex : selectedPatchIndex);
|
|
|
|
dumpFileFromExeFsSection(curIndex, selectedFileIndex, exeFsUpdateFlag, &(dumpCfg.exeFsDumpCfg));
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowExeFsSectionBrowser;
|
|
} else
|
|
if (uiState == stateDumpRomFsSectionData)
|
|
{
|
|
u32 curIndex = 0;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[0]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", romFsMenuItems[2], (dumpCfg.romFsDumpCfg.isFat32 ? "Yes" : "No"), romFsMenuItems[3], (dumpCfg.romFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
switch(curRomFsType)
|
|
{
|
|
case ROMFS_TYPE_APP:
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s v%s", romFsSectionDumpMenuItems[1], baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
curIndex = selectedAppIndex;
|
|
break;
|
|
case ROMFS_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update to dump: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
curIndex = selectedPatchIndex;
|
|
break;
|
|
case ROMFS_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, true, "DLC to dump: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
curIndex = selectedAddOnIndex;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
dumpRomFsSectionData(curIndex, curRomFsType, &(dumpCfg.romFsDumpCfg));
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowRomFsSectionDataDumpMenu : resultShowRomFsMenu);
|
|
} else
|
|
if (uiState == stateRomFsSectionBrowserGetEntries)
|
|
{
|
|
u32 curIndex = 0;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[1]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", romFsMenuItems[2], (dumpCfg.romFsDumpCfg.isFat32 ? "Yes" : "No"), romFsMenuItems[3], (dumpCfg.romFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
switch(curRomFsType)
|
|
{
|
|
case ROMFS_TYPE_APP:
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s v%s", romFsSectionBrowserMenuItems[1], baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
curIndex = selectedAppIndex;
|
|
break;
|
|
case ROMFS_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update to browse: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
curIndex = selectedPatchIndex;
|
|
break;
|
|
case ROMFS_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, true, "DLC to browse: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
curIndex = selectedAddOnIndex;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiPleaseWait(0);
|
|
breaks += 2;
|
|
|
|
bool romfs_fail = false;
|
|
|
|
if (readNcaRomFsSection(curIndex, curRomFsType, -1) == 0)
|
|
{
|
|
if (getRomFsFileList(0, (curRomFsType == ROMFS_TYPE_PATCH)))
|
|
{
|
|
res = resultShowRomFsSectionBrowser;
|
|
} else {
|
|
if (curRomFsType == ROMFS_TYPE_PATCH) freeBktrContext();
|
|
freeRomFsContext();
|
|
romfs_fail = true;
|
|
}
|
|
} else {
|
|
romfs_fail = true;
|
|
}
|
|
|
|
if (romfs_fail)
|
|
{
|
|
breaks += 2;
|
|
waitForButtonPress();
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowRomFsSectionBrowserMenu : resultShowRomFsMenu);
|
|
}
|
|
} else
|
|
if (uiState == stateRomFsSectionBrowserChangeDir)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, romFsMenuItems[1]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", romFsMenuItems[2], (dumpCfg.romFsDumpCfg.isFat32 ? "Yes" : "No"), romFsMenuItems[3], (dumpCfg.romFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
switch(curRomFsType)
|
|
{
|
|
case ROMFS_TYPE_APP:
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s v%s", romFsSectionBrowserMenuItems[1], baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
break;
|
|
case ROMFS_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update to browse: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
break;
|
|
case ROMFS_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, true, "DLC to browse: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
bool romfs_fail = false;
|
|
|
|
if (romFsBrowserEntries[selectedFileIndex].type == ROMFS_ENTRY_DIR)
|
|
{
|
|
if (getRomFsFileList(romFsBrowserEntries[selectedFileIndex].offset, (curRomFsType == ROMFS_TYPE_PATCH)))
|
|
{
|
|
res = resultShowRomFsSectionBrowser;
|
|
} else {
|
|
romfs_fail = true;
|
|
}
|
|
} else {
|
|
// Unexpected condition
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Error: the selected entry is not a directory!");
|
|
romfs_fail = true;
|
|
}
|
|
|
|
if (romfs_fail)
|
|
{
|
|
freeRomFsBrowserEntries();
|
|
freeFilenameBuffer();
|
|
if (curRomFsType == ROMFS_TYPE_PATCH) freeBktrContext();
|
|
freeRomFsContext();
|
|
|
|
breaks += 2;
|
|
waitForButtonPress();
|
|
res = ((menuType == MENUTYPE_GAMECARD && titleAppCount > 1) ? resultShowRomFsSectionBrowserMenu : resultShowRomFsMenu);
|
|
}
|
|
} else
|
|
if (uiState == stateRomFsSectionBrowserCopyFile)
|
|
{
|
|
u32 curIndex = 0;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Manual File Dump: %s (RomFS)", filenameBuffer[selectedFileIndex]);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", romFsMenuItems[2], (dumpCfg.romFsDumpCfg.isFat32 ? "Yes" : "No"), romFsMenuItems[3], (dumpCfg.romFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
switch(curRomFsType)
|
|
{
|
|
case ROMFS_TYPE_APP:
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "Base application: %s v%s", baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
curIndex = selectedAppIndex;
|
|
break;
|
|
case ROMFS_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
curIndex = selectedPatchIndex;
|
|
break;
|
|
case ROMFS_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, true, "DLC: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
curIndex = selectedAddOnIndex;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
if (romFsBrowserEntries[selectedFileIndex].type == ROMFS_ENTRY_FILE)
|
|
{
|
|
dumpFileFromRomFsSection(curIndex, romFsBrowserEntries[selectedFileIndex].offset, curRomFsType, &(dumpCfg.romFsDumpCfg));
|
|
} else {
|
|
// Unexpected condition
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Error: the selected entry is not a file!");
|
|
breaks += 2;
|
|
}
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowRomFsSectionBrowser;
|
|
} else
|
|
if (uiState == stateRomFsSectionBrowserCopyDir)
|
|
{
|
|
u32 curIndex = 0;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Manual Directory Dump: romfs:%s (RomFS)", curRomFsPath);
|
|
breaks++;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s | %s%s", romFsMenuItems[2], (dumpCfg.romFsDumpCfg.isFat32 ? "Yes" : "No"), romFsMenuItems[3], (dumpCfg.romFsDumpCfg.useLayeredFSDir ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
switch(curRomFsType)
|
|
{
|
|
case ROMFS_TYPE_APP:
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "Base application: %s v%s", baseAppEntries[selectedAppIndex].name, baseAppEntries[selectedAppIndex].versionStr);
|
|
curIndex = selectedAppIndex;
|
|
break;
|
|
case ROMFS_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, "Update: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
curIndex = selectedPatchIndex;
|
|
break;
|
|
case ROMFS_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, true, "DLC: ", strbuf, MAX_CHARACTERS(strbuf));
|
|
curIndex = selectedAddOnIndex;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
uiRefreshDisplay();
|
|
|
|
dumpCurrentDirFromRomFsSection(curIndex, curRomFsType, &(dumpCfg.romFsDumpCfg));
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowRomFsSectionBrowser;
|
|
} else
|
|
if (uiState == stateDumpGameCardCertificate)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, gameCardMenuItems[5]);
|
|
breaks += 2;
|
|
|
|
dumpGameCardCertificate();
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowGameCardMenu;
|
|
} else
|
|
if (uiState == stateDumpTicket)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "Dump ticket");
|
|
breaks++;
|
|
|
|
menu = ticketMenuItems;
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, "%s%s", menu[1], (dumpCfg.tikDumpCfg.removeConsoleData ? "Yes" : "No"));
|
|
breaks++;
|
|
|
|
switch(curTikType)
|
|
{
|
|
case TICKET_TYPE_APP:
|
|
snprintf(strbuf, MAX_CHARACTERS(strbuf), "%s%s | %016lX v%s (BASE)", menu[2], baseAppEntries[selectedAppInfoIndex].name, baseAppEntries[selectedAppInfoIndex].titleId, baseAppEntries[selectedAppInfoIndex].versionStr);
|
|
break;
|
|
case TICKET_TYPE_PATCH:
|
|
retrieveDescriptionForPatchOrAddOn(selectedPatchIndex, false, true, menu[2], strbuf, MAX_CHARACTERS(strbuf));
|
|
strcat(strbuf, " (UPD)");
|
|
break;
|
|
case TICKET_TYPE_ADDON:
|
|
retrieveDescriptionForPatchOrAddOn(selectedAddOnIndex, true, true, menu[2], strbuf, MAX_CHARACTERS(strbuf));
|
|
strcat(strbuf, " (DLC)");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, strbuf);
|
|
breaks += 2;
|
|
|
|
u32 titleIndex = (curTikType == TICKET_TYPE_APP ? selectedAppInfoIndex : (curTikType == TICKET_TYPE_PATCH ? selectedPatchIndex : selectedAddOnIndex));
|
|
|
|
dumpTicketFromTitle(titleIndex, curTikType, &(dumpCfg.tikDumpCfg));
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowTicketMenu;
|
|
} else
|
|
if (uiState == stateUpdateNSWDBXml)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, updateMenuItems[0]);
|
|
breaks += 2;
|
|
|
|
updateNSWDBXml();
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowUpdateMenu;
|
|
} else
|
|
if (uiState == stateUpdateApplication)
|
|
{
|
|
uiDrawString(STRING_X_POS, STRING_Y_POS(breaks), FONT_COLOR_TITLE_RGB, updateMenuItems[1]);
|
|
breaks += 2;
|
|
|
|
updatePerformed = updateApplication();
|
|
|
|
waitForButtonPress();
|
|
|
|
updateFreeSpace();
|
|
res = resultShowUpdateMenu;
|
|
}
|
|
|
|
return res;
|
|
}
|