1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-23 02:36:41 +00:00
nxdumptool/source/ui.c
2019-06-05 18:44:18 -04:00

2240 lines
93 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"
/* Extern variables */
extern FsDeviceOperator fsOperatorInstance;
extern AppletType programAppletType;
extern bool gameCardInserted;
extern char gameCardSizeStr[32], trimmedCardSizeStr[32];
extern u8 *hfs0_header;
extern u64 hfs0_offset, hfs0_size;
extern u32 hfs0_partition_cnt;
extern u8 *partitionHfs0Header;
extern u64 partitionHfs0HeaderOffset, partitionHfs0HeaderSize;
extern u32 partitionHfs0FileCount, partitionHfs0StrTableSize;
extern u32 gameCardAppCount;
extern u64 *gameCardTitleID;
extern u32 *gameCardVersion;
extern u32 gameCardPatchCount;
extern u64 *gameCardPatchTitleID;
extern u32 *gameCardPatchVersion;
extern u32 gameCardAddOnCount;
extern u64 *gameCardAddOnTitleID;
extern u32 *gameCardAddOnVersion;
extern char **gameCardName;
extern char **gameCardAuthor;
extern char **gameCardVersionStr;
extern u8 **gameCardIcon;
extern char gameCardUpdateVersionStr[128];
extern char *filenameBuffer;
extern char *filenames[FILENAME_MAX_CNT];
extern int filenamesCount;
extern char curRomFsPath[NAME_BUF_LEN];
extern romfs_browser_entry *romFsBrowserEntries;
extern char strbuf[NAME_BUF_LEN * 4];
/* Statically allocated variables */
static PlFontData standardFont;
static PlFontData nintendoExtFont;
static FT_Library library;
static FT_Face standardFontFace;
static FT_Face nintendoExtFontFace;
static Framebuffer fb;
static u32 *framebuf = NULL;
static u32 framebuf_width = 0;
int cursor = 0;
int scroll = 0;
int breaks = 0;
int font_height = 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 highlight = false;
static bool isFat32 = false, dumpCert = false, trimDump = false, calcCrc = true, setXciArchiveBit = false;
static char statusMessage[2048] = {'\0'};
static int statusMessageFadeout = 0;
u64 freeSpace = 0;
static char freeSpaceStr[64] = {'\0'};
static UIState uiState;
static const char *dirNormalIconPath = "romfs:/dir_normal.jpg";
static u8 *dirNormalIconBuf = NULL;
static const char *dirHighlightIconPath = "romfs:/dir_highlight.jpg";
static u8 *dirHighlightIconBuf = NULL;
static const char *fileNormalIconPath = "romfs:/file_normal.jpg";
static u8 *fileNormalIconBuf = NULL;
static const char *fileHighlightIconPath = "romfs:/file_highlight.jpg";
static u8 *fileHighlightIconBuf = NULL;
static const char *appHeadline = "Nintendo Switch Game Card Dump Tool v" APP_VERSION ".\nOriginal codebase by MCMrARM.\nUpdated and maintained by DarkMatterCore.\n\n";
static const char *appControlsNoGameCard = "[ " NINTENDO_FONT_PLUS " ] Exit";
static const char *appControlsSingleApp = "[ " 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 *appControlsMultiApp = "[ " 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 " ] Change displayed base application info | [ " NINTENDO_FONT_PLUS " ] Exit";
static const char *mainMenuItems[] = { "Cartridge Image (XCI) dump", "Nintendo Submission Package (NSP) dump", "HFS0 options", "RomFS options", "Dump game card certificate", "Update options" };
static const char *xciDumpMenuItems[] = { "Start XCI dump process", "Split output dump (FAT32 support): ", "Create directory with archive bit set: ", "Dump certificate: ", "Trim output dump: ", "CRC32 checksum calculation + dump verification: " };
static const char *nspDumpMenuItems[] = { "Dump base application NSP", "Dump bundled update NSP", "Dump bundled DLC NSP" };
static const char *nspAppDumpMenuItems[] = { "Start NSP dump process", "Split output dump (FAT32 support): ", "CRC32 checksum calculation: ", "Bundled application to dump: " };
static const char *nspPatchDumpMenuItems[] = { "Start NSP dump process", "Split output dump (FAT32 support): ", "CRC32 checksum calculation: ", "Bundled update to dump: " };
static const char *nspAddOnDumpMenuItems[] = { "Start NSP dump process", "Split output dump (FAT32 support): ", "CRC32 checksum calculation: ", "Bundled DLC to dump: " };
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 *romFsMenuItems[] = { "RomFS section data dump", "Browse RomFS section" };
static const char *romFsSectionDumpMenuItems[] = { "Start RomFS data dump process", "Bundled application to dump: " };
static const char *romFsSectionBrowserMenuItems[] = { "Browse RomFS section", "Bundled application to browse: " };
static const char *updateMenuItems[] = { "Update NSWDB.COM XML database", "Update application" };
void uiFill(int x, int y, int width, int height, u8 r, u8 g, u8 b)
{
/* Perform validity checks */
if ((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));
}
u32 lx, ly;
u32 framex, framey;
for (ly = 0; ly < height; ly++)
{
for (lx = 0; lx < width; lx++)
{
framex = (x + lx);
framey = (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));
}
u32 lx, ly;
u32 framex, framey;
u32 pos = 0;
for (ly = 0; ly < height; ly++)
{
for (lx = 0; lx < width; lx++)
{
framex = (x + lx);
framey = (y + ly);
pos = (((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, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: invalid parameters to process JPG image buffer.");
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)
{
ret = tjDecompressHeader2(_jpegDecompressor, rawJpg, rawJpgSize, &w, &h, &samp);
if (ret != -1)
{
if (w == expectedWidth && h == expectedHeight)
{
scalingFactors = tjGetScalingFactors(&numScalingFactors);
if (scalingFactors)
{
for(i = 0; i < numScalingFactors; i++)
{
if (TJSCALED(expectedWidth, scalingFactors[i]) == desiredWidth && TJSCALED(expectedHeight, scalingFactors[i]) == desiredHeight)
{
foundScalingFactor = true;
break;
}
}
if (foundScalingFactor)
{
pitch = TJPAD(desiredWidth * tjPixelSize[TJPF_RGB]);
jpgScaledBuf = malloc(pitch * desiredHeight);
if (jpgScaledBuf)
{
ret = tjDecompress2(_jpegDecompressor, rawJpg, rawJpgSize, jpgScaledBuf, desiredWidth, 0, desiredHeight, TJPF_RGB, TJFLAG_ACCURATEDCT);
if (ret != -1)
{
*outBuf = jpgScaledBuf;
success = true;
} else {
free(jpgScaledBuf);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: tjDecompress2 failed (%d).", ret);
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: unable to allocated memory for the scaled RGB image output.");
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: unable to find a valid scaling factor.");
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: error retrieving scaling factors.");
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: invalid image width/height.");
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: tjDecompressHeader2 failed (%d).", ret);
}
tjDestroy(_jpegDecompressor);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromMem: tjInitDecompress failed.");
}
return success;
}
bool uiLoadJpgFromFile(const char *filename, int expectedWidth, int expectedHeight, int desiredWidth, int desiredHeight, u8 **outBuf)
{
if (!filename || !desiredWidth || !desiredHeight || !outBuf)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: invalid parameters to process JPG image file.\n");
return false;
}
u8 *buf = NULL;
FILE *fp = NULL;
size_t filesize = 0, read = 0;
fp = fopen(filename, "rb");
if (!fp)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: failed to open file \"%s\".\n", filename);
return false;
}
fseek(fp, 0, SEEK_END);
filesize = ftell(fp);
rewind(fp);
if (!filesize)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: file \"%s\" is empty.\n", filename);
fclose(fp);
return false;
}
buf = malloc(filesize);
if (!buf)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: error allocating memory for image \"%s\".\n", filename);
fclose(fp);
return false;
}
read = fread(buf, 1, filesize, fp);
fclose(fp);
if (read != filesize)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "uiLoadJpgFromFile: error reading image \"%s\".\n", filename);
free(buf);
return false;
}
bool ret = uiLoadJpgFromMem(buf, filesize, expectedWidth, expectedHeight, desiredWidth, desiredHeight, outBuf);
free(buf);
if (!ret) strcat(strbuf, "\n");
return ret;
}
void uiDrawChar(FT_Bitmap *bitmap, int x, int y, u8 r, u8 g, u8 b)
{
if (framebuf == NULL) return;
u32 framex, framey;
u32 tmpx, tmpy;
u8 *imageptr = bitmap->buffer;
u8 src_val;
float opacity;
u8 fontR;
u8 fontG;
u8 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;
src_val = imageptr[tmpx];
if (!src_val)
{
/* Render background color */
if (highlight)
{
framebuf[(framey * framebuf_width) + framex] = RGBA8_MAXALPHA(HIGHLIGHT_BG_COLOR_R, HIGHLIGHT_BG_COLOR_G, HIGHLIGHT_BG_COLOR_B);
} else {
framebuf[(framey * framebuf_width) + framex] = RGBA8_MAXALPHA(BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB);
}
} else {
/* Calculate alpha (opacity) */
opacity = (src_val / 255.0);
if (highlight)
{
fontR = (r * opacity + (1 - opacity) * HIGHLIGHT_BG_COLOR_R);
fontG = (g * opacity + (1 - opacity) * HIGHLIGHT_BG_COLOR_G);
fontB = (b * opacity + (1 - opacity) * HIGHLIGHT_BG_COLOR_B);
} else {
fontR = (r * opacity + (1 - opacity) * BG_COLOR_RGB);
fontG = (g * opacity + (1 - opacity) * BG_COLOR_RGB);
fontB = (b * opacity + (1 - opacity) * BG_COLOR_RGB);
}
framebuf[(framey * framebuf_width) + framex] = RGBA8_MAXALPHA(fontR, fontG, fontB);
}
}
imageptr += bitmap->pitch;
}
}
void uiScroll()
{
if (framebuf == NULL)
{
/* Begin new frame */
u32 stride;
framebuf = (u32*)framebufferBegin(&fb, &stride);
framebuf_width = (stride / sizeof(u32));
}
u32 lx, ly;
for (ly = 0; ly < (FB_HEIGHT - font_height - 8); ly++)
{
for (lx = 0; lx < FB_WIDTH; lx++)
{
framebuf[(ly * framebuf_width) + lx] = framebuf[((ly + font_height) * framebuf_width) + lx];
}
}
uiFill(0, FB_HEIGHT - (8 + (font_height + (font_height / 4))), FB_WIDTH, (8 + (font_height + (font_height / 4))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB);
breaks = (FB_HEIGHT - (8 + (font_height + (font_height / 4))) + (font_height / 8));
}
void uiDrawString(const char *string, int x, int y, u8 r, u8 g, u8 b)
{
u32 tmpx = (x <= 8 ? 8 : (x + 8));
u32 tmpy = (font_height + (y <= 8 ? 8 : (y + 8)));
FT_Error ret = 0;
FT_UInt glyph_index;
FT_GlyphSlot standardFontSlot = standardFontFace->glyph;
FT_GlyphSlot nintendoExtFontSlot = nintendoExtFontFace->glyph;
u32 i;
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));
}
if (tmpy >= FB_HEIGHT)
{
tmpy = (FB_HEIGHT - (8 + (font_height + (font_height / 4))) + (font_height / 8));
uiScroll();
}
for(i = 0; i < str_size;)
{
bool useNintendoExt = (string[i] == 0xE0);
if (useNintendoExt)
{
tmpchar = (((string[i] << 8) & 0xFF00) | string[i + 1]);
i += 2;
} else {
unitcount = decode_utf8(&tmpchar, (const u8*)&string[i]);
if (unitcount <= 0) break;
i += unitcount;
if (tmpchar == '\n')
{
tmpx = 8;
tmpy += ((font_height + (font_height / 4)) + (font_height / 8));
breaks++;
continue;
} else
if (tmpchar == '\t')
{
tmpx += (font_height * TAB_WIDTH);
continue;
} else
if (tmpchar == '\r')
{
continue;
}
}
if (useNintendoExt)
{
glyph_index = FT_Get_Char_Index(nintendoExtFontFace, tmpchar);
ret = FT_Load_Glyph(nintendoExtFontFace, glyph_index, FT_LOAD_DEFAULT);
if (ret == 0) ret = FT_Render_Glyph(nintendoExtFontFace->glyph, FT_RENDER_MODE_NORMAL);
if (ret) break;
if ((tmpx + (nintendoExtFontSlot->advance.x >> 6)) >= FB_WIDTH)
{
tmpx = 8;
tmpy += ((font_height + (font_height / 4)) + (font_height / 8));
breaks++;
}
uiDrawChar(&nintendoExtFontSlot->bitmap, tmpx + nintendoExtFontSlot->bitmap_left, tmpy - nintendoExtFontSlot->bitmap_top, r, g, b);
tmpx += (nintendoExtFontSlot->advance.x >> 6);
tmpy += (nintendoExtFontSlot->advance.y >> 6);
} else {
glyph_index = FT_Get_Char_Index(standardFontFace, tmpchar);
ret = FT_Load_Glyph(standardFontFace, glyph_index, FT_LOAD_DEFAULT);
if (ret == 0) ret = FT_Render_Glyph(standardFontFace->glyph, FT_RENDER_MODE_NORMAL);
if (ret) break;
if ((tmpx + (standardFontSlot->advance.x >> 6)) >= FB_WIDTH)
{
tmpx = 8;
tmpy += ((font_height + (font_height / 4)) + (font_height / 8));
breaks++;
}
uiDrawChar(&standardFontSlot->bitmap, tmpx + standardFontSlot->bitmap_left, tmpy - standardFontSlot->bitmap_top, r, g, b);
tmpx += (standardFontSlot->advance.x >> 6);
tmpy += (standardFontSlot->advance.y >> 6);
}
}
}
void uiRefreshDisplay()
{
if (framebuf != NULL)
{
framebufferEnd(&fb);
framebuf = NULL;
framebuf_width = 0;
}
}
void uiStatusMsg(const char *fmt, ...)
{
statusMessageFadeout = 1000;
va_list args;
va_start(args, fmt);
vsnprintf(statusMessage, sizeof(statusMessage) / sizeof(statusMessage[0]), fmt, args);
va_end(args);
}
void uiUpdateStatusMsg()
{
if (!strlen(statusMessage) || !statusMessageFadeout) return;
if ((statusMessageFadeout - 4) > BG_COLOR_RGB)
{
int fadeout = (statusMessageFadeout > 255 ? 255 : statusMessageFadeout);
uiFill(0, FB_HEIGHT - (8 + (font_height + (font_height / 4))), FB_WIDTH, (8 + (font_height + (font_height / 4))), BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB);
uiDrawString(statusMessage, 8, (FB_HEIGHT - (16 + (font_height + (font_height / 4))) + (font_height / 8)), fadeout, fadeout, fadeout);
statusMessageFadeout -= 4;
} else {
statusMessageFadeout = 0;
}
}
void uiPleaseWait(u8 wait)
{
uiDrawString("Please wait...", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
uiRefreshDisplay();
if (wait) delay(wait);
}
void uiUpdateFreeSpace()
{
getSdCardFreeSpace(&freeSpace);
char tmp[32] = {'\0'};
convertSize(freeSpace, tmp, sizeof(tmp) / sizeof(tmp[0]));
snprintf(freeSpaceStr, sizeof(freeSpaceStr) / sizeof(freeSpaceStr[0]), "Free SD card space: %s.", tmp);
}
void uiClearScreen()
{
uiFill(0, 0, FB_WIDTH, FB_HEIGHT, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB);
}
void uiPrintHeadline()
{
breaks = 0;
uiClearScreen();
uiDrawString(appHeadline, 8, 8, 255, 255, 255);
}
void error_screen(const char *fmt, ...)
{
consoleInit(NULL);
va_list va;
va_start(va, fmt);
vprintf(fmt, va);
va_end(va);
printf("Press any button to exit.\n");
while(appletMainLoop())
{
hidScanInput();
u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
if (keysDown && !((keysDown & KEY_TOUCH) || (keysDown & KEY_LSTICK_LEFT) || (keysDown & KEY_LSTICK_RIGHT) || (keysDown & KEY_LSTICK_UP) || (keysDown & KEY_LSTICK_DOWN) || \
(keysDown & KEY_RSTICK_LEFT) || (keysDown & KEY_RSTICK_RIGHT) || (keysDown & KEY_RSTICK_UP) || (keysDown & KEY_RSTICK_DOWN))) break;
consoleUpdate(NULL);
}
consoleExit(NULL);
}
int uiInit()
{
Result rc = 0;
FT_Error ret = 0;
int status = 0;
bool pl_init = false, romfs_init = false, ft_lib_init = false, ft_std_face_init = false, ft_nintendo_face_init = false;
/* Set initial UI state */
uiState = stateMainMenu;
cursor = 0;
scroll = 0;
/* Initialize pl service */
rc = plInitialize();
if (R_FAILED(rc))
{
error_screen("plInitialize() failed (0x%08X).\n", rc);
goto out;
}
pl_init = true;
/* Retrieve standard shared font */
rc = plGetSharedFontByType(&standardFont, PlSharedFontType_Standard);
if (R_FAILED(rc))
{
error_screen("plGetSharedFontByType() failed to retrieve standard shared font (0x%08X).\n", rc);
goto out;
}
/* Retrieve Nintendo shared font */
rc = plGetSharedFontByType(&nintendoExtFont, PlSharedFontType_NintendoExt);
if (R_FAILED(rc))
{
error_screen("plGetSharedFontByType() failed to retrieve Nintendo shared font (0x%08X).\n", rc);
goto out;
}
/* Initialize FreeType */
ret = FT_Init_FreeType(&library);
if (ret)
{
error_screen("FT_Init_FreeType() failed (%d).\n", ret);
goto out;
}
ft_lib_init = true;
/* Create memory face for the standard shared font */
ret = FT_New_Memory_Face(library, standardFont.address, standardFont.size, 0, &standardFontFace);
if (ret)
{
error_screen("FT_New_Memory_Face() failed to create memory face for the standard shared font (%d).\n", ret);
goto out;
}
ft_std_face_init = true;
/* Create memory face for the Nintendo shared font */
ret = FT_New_Memory_Face(library, nintendoExtFont.address, nintendoExtFont.size, 0, &nintendoExtFontFace);
if (ret)
{
error_screen("FT_New_Memory_Face() failed to create memory face for the Nintendo shared font (%d).\n", ret);
goto out;
}
ft_nintendo_face_init = true;
/* Set standard shared font character size */
ret = FT_Set_Char_Size(standardFontFace, 0, CHAR_PT_SIZE * 64, SCREEN_DPI_CNT, SCREEN_DPI_CNT);
if (ret)
{
error_screen("FT_Set_Char_Size() failed to set character size for the standard shared font (%d).\n", ret);
goto out;
}
/* Set Nintendo shared font character size */
ret = FT_Set_Char_Size(nintendoExtFontFace, 0, CHAR_PT_SIZE * 64, SCREEN_DPI_CNT, SCREEN_DPI_CNT);
if (ret)
{
error_screen("FT_Set_Char_Size() failed to set character size for the Nintendo shared font (%d).\n", ret);
goto out;
}
/* Store font height and max width */
font_height = (standardFontFace->size->metrics.height / 64);
/* Prepare additional data needed by the UI functions */
filenameBuffer = calloc(FILENAME_BUFFER_SIZE, sizeof(char));
if (!filenameBuffer)
{
error_screen("Failed to allocate memory for the filename buffer.\n");
goto out;
}
/* Mount Application's RomFS */
rc = romfsInit();
if (R_FAILED(rc))
{
error_screen("romfsInit() failed (0x%08X).\n", rc);
goto out;
}
romfs_init = true;
if (!uiLoadJpgFromFile(dirNormalIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &dirNormalIconBuf))
{
strcat(strbuf, "Failed to load directory icon (normal).\n");
error_screen(strbuf);
goto out;
}
if (!uiLoadJpgFromFile(dirHighlightIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &dirHighlightIconBuf))
{
strcat(strbuf, "Failed to load directory icon (highlighted).\n");
error_screen(strbuf);
goto out;
}
if (!uiLoadJpgFromFile(fileNormalIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &fileNormalIconBuf))
{
strcat(strbuf, "Failed to load file icon (normal).\n");
error_screen(strbuf);
goto out;
}
if (!uiLoadJpgFromFile(fileHighlightIconPath, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, &fileHighlightIconBuf))
{
strcat(strbuf, "Failed to load file icon (highlighted).\n");
error_screen(strbuf);
goto out;
}
/* Unmount Application's RomFS */
romfsExit();
romfs_init = false;
/* Reinitialize FS stuff */
/* Fixes a problem where the file descriptor for the application NRO isn't properly closed */
/* We'll need to have write access to the NRO if the user runs the update procedure */
if (!envIsNso())
{
fsdevUnmountAll();
fsExit();
rc = fsInitialize();
if (R_FAILED(rc))
{
error_screen("fsInitialize() failed (0x%08X).\n", rc);
goto out;
}
rc = fsdevMountSdmc();
if (R_FAILED(rc))
{
error_screen("fsdevMountSdmc() failed (0x%08X).\n", rc);
goto out;
}
}
/* Create framebuffer */
framebufferCreate(&fb, nwindowGetDefault(), FB_WIDTH, FB_HEIGHT, PIXEL_FORMAT_RGBA_8888, 2);
framebufferMakeLinear(&fb);
/* Disable screen dimming and auto sleep */
appletSetMediaPlaybackState(true);
/* Get applet type */
programAppletType = appletGetAppletType();
/* Block HOME menu button presses if we're running as a regular application or a system application */
if (programAppletType == AppletType_Application || programAppletType == AppletType_SystemApplication) appletBeginBlockingHomeButton(0);
/* Clear screen */
uiClearScreen();
/* Update free space */
uiUpdateFreeSpace();
/* Set output status */
status = 1;
out:
if (!status)
{
if (fileHighlightIconBuf) free(fileHighlightIconBuf);
if (fileNormalIconBuf) free(fileNormalIconBuf);
if (dirHighlightIconBuf) free(dirHighlightIconBuf);
if (dirNormalIconBuf) free(dirNormalIconBuf);
if (romfs_init) romfsExit();
if (filenameBuffer) free(filenameBuffer);
if (ft_nintendo_face_init) FT_Done_Face(nintendoExtFontFace);
if (ft_std_face_init) FT_Done_Face(standardFontFace);
if (ft_lib_init) FT_Done_FreeType(library);
if (pl_init) plExit();
}
return status;
}
void uiDeinit()
{
/* Unblock HOME menu button presses if we're running as a regular application or a system application */
if (programAppletType == AppletType_Application || programAppletType == AppletType_SystemApplication) appletEndBlockingHomeButton();
/* Enable screen dimming and auto sleep */
appletSetMediaPlaybackState(false);
/* Free framebuffer object */
framebufferClose(&fb);
/* Free directory/file icons */
free(fileHighlightIconBuf);
free(fileNormalIconBuf);
free(dirHighlightIconBuf);
free(dirNormalIconBuf);
/* Free filename buffer */
free(filenameBuffer);
/* Free FreeType resources */
FT_Done_Face(nintendoExtFontFace);
FT_Done_Face(standardFontFace);
FT_Done_FreeType(library);
/* Deinitialize pl service */
plExit();
}
void uiSetState(UIState state)
{
uiState = state;
cursor = 0;
scroll = 0;
}
UIState uiGetState()
{
return uiState;
}
UIResult uiProcess()
{
UIResult res = resultNone;
int i, j;
const char **menu = NULL;
int menuItemsCount = 0;
u32 keysDown;
u32 keysHeld;
int scrollAmount = 0;
u32 patch, addon, xpos, ypos, startYPos;
char versionStr[128] = {'\0'};
uiPrintHeadline();
loadGameCardInfo();
if (uiState == stateMainMenu || uiState == stateXciDumpMenu || uiState == stateNspDumpMenu || uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu || uiState == stateHfs0Menu || uiState == stateRawHfs0PartitionDumpMenu || uiState == stateHfs0PartitionDataDumpMenu || uiState == stateHfs0BrowserMenu || uiState == stateHfs0Browser || uiState == stateRomFsMenu || uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu || uiState == stateRomFsSectionBrowser || uiState == stateUpdateMenu)
{
uiDrawString((!gameCardInserted ? appControlsNoGameCard : (gameCardAppCount > 1 ? appControlsMultiApp : appControlsSingleApp)), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
breaks += 2;
uiDrawString(freeSpaceStr, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
breaks += 2;
}
if (!gameCardInserted || hfs0_header == NULL || (hfs0_partition_cnt != GAMECARD_TYPE1_PARTITION_CNT && hfs0_partition_cnt != GAMECARD_TYPE2_PARTITION_CNT) || !gameCardAppCount || gameCardTitleID == NULL)
{
if (gameCardInserted)
{
if (hfs0_header != NULL)
{
if (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT || hfs0_partition_cnt == GAMECARD_TYPE2_PARTITION_CNT)
{
if (gameCardAppCount > 0)
{
uiDrawString("Error: unable to retrieve the game card Title ID!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
if (strlen(gameCardUpdateVersionStr))
{
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Bundled FW Update: %s", gameCardUpdateVersionStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
breaks++;
uiDrawString("In order to be able to dump data from this cartridge, make sure your console is at least on this FW version.", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
}
} else {
uiDrawString("Error: gamecard application count is zero!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: unknown root HFS0 header partition count! (%u)", hfs0_partition_cnt);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
}
} else {
uiDrawString("Error: unable to get root HFS0 header data!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
}
} else {
uiDrawString("Game card is not inserted!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 0, 0);
}
uiUpdateStatusMsg();
uiRefreshDisplay();
res = resultShowMainMenu;
hidScanInput();
keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
// Exit
if (keysDown & KEY_PLUS) res = resultExit;
return res;
}
if (uiState == stateMainMenu || uiState == stateXciDumpMenu || uiState == stateNspDumpMenu || uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu || uiState == stateHfs0Menu || uiState == stateRawHfs0PartitionDumpMenu || uiState == stateHfs0PartitionDataDumpMenu || uiState == stateHfs0BrowserMenu || uiState == stateHfs0Browser || uiState == stateRomFsMenu || uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu || uiState == stateRomFsSectionBrowser || uiState == stateUpdateMenu)
{
if (uiState != stateHfs0Browser && uiState != stateRomFsSectionBrowser)
{
uiDrawString("Game card is inserted!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
breaks += 2;
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Root HFS0 header offset: 0x%016lX", hfs0_offset);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Root HFS0 header size: 0x%016lX", hfs0_size);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
breaks++;*/
/* Print application info */
xpos = 8;
ypos = ((breaks * (font_height + (font_height / 4))) + (font_height / 8));
startYPos = ypos;
/* Draw icon */
if (gameCardIcon != NULL && gameCardIcon[selectedAppInfoIndex] != NULL)
{
uiDrawIcon(gameCardIcon[selectedAppInfoIndex], NACP_ICON_DOWNSCALED, NACP_ICON_DOWNSCALED, xpos, ypos + 8);
xpos += (NACP_ICON_DOWNSCALED + 8);
ypos += 8;
}
if (gameCardName != NULL && gameCardName[selectedAppInfoIndex] != NULL && strlen(gameCardName[selectedAppInfoIndex]))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Name: %s", gameCardName[selectedAppInfoIndex]);
uiDrawString(strbuf, xpos, ypos, 0, 255, 0);
ypos += (font_height + (font_height / 4));
}
if (gameCardAuthor != NULL && gameCardAuthor[selectedAppInfoIndex] != NULL && strlen(gameCardAuthor[selectedAppInfoIndex]))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Developer: %s", gameCardAuthor[selectedAppInfoIndex]);
uiDrawString(strbuf, xpos, ypos, 0, 255, 0);
ypos += (font_height + (font_height / 4));
}
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Title ID: %016lX", gameCardTitleID[selectedAppInfoIndex]);
uiDrawString(strbuf, xpos, ypos, 0, 255, 0);
if (gameCardPatchCount > 0)
{
u32 patchCnt = 0;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Bundled update(s): v");
for(patch = 0; patch < gameCardPatchCount; patch++)
{
if ((gameCardTitleID[selectedAppInfoIndex] | APPLICATION_PATCH_BITMASK) == gameCardPatchTitleID[patch])
{
if (patchCnt > 0) strcat(strbuf, ", v");
convertTitleVersionToDecimal(gameCardPatchVersion[patch], versionStr, sizeof(versionStr));
strcat(strbuf, versionStr);
patchCnt++;
}
}
if (patchCnt > 0) uiDrawString(strbuf, (FB_WIDTH / 2) - (FB_WIDTH / 8), ypos, 0, 255, 0);
}
ypos += (font_height + (font_height / 4));
if (gameCardVersionStr != NULL && gameCardVersionStr[selectedAppInfoIndex] != NULL && strlen(gameCardVersionStr[selectedAppInfoIndex]))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Version: %s", gameCardVersionStr[selectedAppInfoIndex]);
uiDrawString(strbuf, xpos, ypos, 0, 255, 0);
if (!gameCardAddOnCount) ypos += (font_height + (font_height / 4));
}
if (gameCardAddOnCount > 0)
{
u32 addOnCnt = 0;
for(addon = 0; addon < gameCardAddOnCount; addon++)
{
if ((gameCardTitleID[selectedAppInfoIndex] & APPLICATION_ADDON_BITMASK) == (gameCardAddOnTitleID[addon] & APPLICATION_ADDON_BITMASK)) addOnCnt++;
}
if (addOnCnt > 0)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Bundled DLC(s): %u", addOnCnt);
uiDrawString(strbuf, (FB_WIDTH / 2) - (FB_WIDTH / 8), ypos, 0, 255, 0);
ypos += (font_height + (font_height / 4));
}
}
ypos += 8;
if (xpos > 8 && (ypos - NACP_ICON_DOWNSCALED) < startYPos) ypos += (NACP_ICON_DOWNSCALED - (ypos - startYPos));
ypos += (font_height + (font_height / 4));
breaks += (int)round((double)(ypos - startYPos) / (double)(font_height + (font_height / 4)));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Size: %s | Used space: %s", gameCardSizeStr, trimmedCardSizeStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
if (gameCardAppCount > 1)
{
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Base application count: %u | Base application currently displayed: %u", gameCardAppCount, selectedAppInfoIndex + 1);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
}
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition count: %u (%s)", hfs0_partition_cnt, GAMECARD_TYPE(hfs0_partition_cnt));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
if (strlen(gameCardUpdateVersionStr))
{
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Bundled FW update: %s", gameCardUpdateVersionStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
}
if (gameCardPatchCount > 0)
{
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Total bundled update(s): %u", gameCardPatchCount);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
}
if (gameCardAddOnCount > 0)
{
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Total bundled DLC(s): %u", gameCardAddOnCount);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 0, 255, 0);
}
breaks += 2;
}
switch(uiState)
{
case stateMainMenu:
menu = mainMenuItems;
menuItemsCount = sizeof(mainMenuItems) / sizeof(mainMenuItems[0]);
break;
case stateXciDumpMenu:
menu = xciDumpMenuItems;
menuItemsCount = sizeof(xciDumpMenuItems) / sizeof(xciDumpMenuItems[0]);
uiDrawString(mainMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateNspDumpMenu:
menu = nspDumpMenuItems;
menuItemsCount = sizeof(nspDumpMenuItems) / sizeof(nspDumpMenuItems[0]);
uiDrawString(mainMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateNspAppDumpMenu:
menu = nspAppDumpMenuItems;
menuItemsCount = sizeof(nspAppDumpMenuItems) / sizeof(nspAppDumpMenuItems[0]);
uiDrawString(nspDumpMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateNspPatchDumpMenu:
menu = nspPatchDumpMenuItems;
menuItemsCount = sizeof(nspPatchDumpMenuItems) / sizeof(nspPatchDumpMenuItems[0]);
uiDrawString(nspDumpMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateNspAddOnDumpMenu:
menu = nspAddOnDumpMenuItems;
menuItemsCount = sizeof(nspAddOnDumpMenuItems) / sizeof(nspAddOnDumpMenuItems[0]);
uiDrawString(nspDumpMenuItems[2], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateHfs0Menu:
menu = hfs0MenuItems;
menuItemsCount = sizeof(hfs0MenuItems) / sizeof(hfs0MenuItems[0]);
uiDrawString(mainMenuItems[2], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateRawHfs0PartitionDumpMenu:
case stateHfs0PartitionDataDumpMenu:
menu = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems : hfs0PartitionDumpType2MenuItems);
menuItemsCount = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? (sizeof(hfs0PartitionDumpType1MenuItems) / sizeof(hfs0PartitionDumpType1MenuItems[0])) : (sizeof(hfs0PartitionDumpType2MenuItems) / sizeof(hfs0PartitionDumpType2MenuItems[0])));
uiDrawString((uiState == stateRawHfs0PartitionDumpMenu ? hfs0MenuItems[0] : hfs0MenuItems[1]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateHfs0BrowserMenu:
menu = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems : hfs0BrowserType2MenuItems);
menuItemsCount = (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? (sizeof(hfs0BrowserType1MenuItems) / sizeof(hfs0BrowserType1MenuItems[0])) : (sizeof(hfs0BrowserType2MenuItems) / sizeof(hfs0BrowserType2MenuItems[0])));
uiDrawString(hfs0MenuItems[2], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateHfs0Browser:
menu = (const char**)filenames;
menuItemsCount = filenamesCount;
uiDrawString((hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems[selectedPartitionIndex] : hfs0BrowserType2MenuItems[selectedPartitionIndex]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "File count: %d | Current file: %d", menuItemsCount, cursor + 1);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
break;
case stateRomFsMenu:
menu = romFsMenuItems;
menuItemsCount = sizeof(romFsMenuItems) / sizeof(romFsMenuItems[0]);
uiDrawString(mainMenuItems[3], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateRomFsSectionDataDumpMenu:
menu = romFsSectionDumpMenuItems;
menuItemsCount = sizeof(romFsSectionDumpMenuItems) / sizeof(romFsSectionDumpMenuItems[0]);
uiDrawString(romFsMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateRomFsSectionBrowserMenu:
menu = romFsSectionBrowserMenuItems;
menuItemsCount = sizeof(romFsSectionBrowserMenuItems) / sizeof(romFsSectionBrowserMenuItems[0]);
uiDrawString(romFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
case stateRomFsSectionBrowser:
menu = (const char**)filenames;
menuItemsCount = filenamesCount;
uiDrawString(romFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
convertTitleVersionToDecimal(gameCardVersion[selectedAppIndex], versionStr, sizeof(versionStr));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", romFsSectionBrowserMenuItems[1], gameCardName[selectedAppIndex], versionStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Path: romfs:%s", curRomFsPath);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
if (cursor > 0)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Entry count: %d | Current entry: %d", menuItemsCount - 1, cursor);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Entry count: %d", menuItemsCount - 1);
}
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 255, 255, 255);
break;
case stateUpdateMenu:
menu = updateMenuItems;
menuItemsCount = sizeof(updateMenuItems) / sizeof(updateMenuItems[0]);
uiDrawString(mainMenuItems[5], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
break;
default:
break;
}
if (menu && menuItemsCount)
{
if (uiState != stateMainMenu) breaks += 2;
j = 0;
highlight = false;
for(i = scroll; i < menuItemsCount; i++, j++)
{
if (((uiState != stateHfs0Browser && uiState != stateRomFsSectionBrowser) && j >= COMMON_MAX_ELEMENTS) || (uiState == stateHfs0Browser && j >= HFS0_MAX_ELEMENTS) || (uiState == stateRomFsSectionBrowser && j >= ROMFS_MAX_ELEMENTS)) break;
// Avoid printing the "Create directory with archive bit set" option if "Split output dump" is disabled
if (uiState == stateXciDumpMenu && i == 2 && !isFat32)
{
j--;
continue;
}
// Avoid printing the "Dump bundled update NSP" option in the NSP dump menu if our current gamecard doesn't include any bundled updates
// Also avoid printing the "Dump bundled DLC NSP" option in the NSP dump menu if our current gamecard doesn't include any bundled DLCs
if (uiState == stateNspDumpMenu)
{
if (i == 1)
{
if (!gameCardPatchCount) continue;
} else
if (i == 2)
{
if (!gameCardAddOnCount)
{
continue;
} else {
if (!gameCardPatchCount) j--;
}
}
}
// Avoid printing the parent directory entry ("..") in the RomFS browser if we're currently at the root directory
if (uiState == stateRomFsSectionBrowser && i == 0 && strlen(curRomFsPath) <= 1)
{
j--;
continue;
}
xpos = 8;
int font_r = 255, font_g = 255, font_b = 255;
if (i == cursor)
{
highlight = true;
uiFill(0, 8 + (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)), FB_WIDTH, font_height + 12, HIGHLIGHT_BG_COLOR_R, HIGHLIGHT_BG_COLOR_G, HIGHLIGHT_BG_COLOR_B);
font_r = HIGHLIGHT_FONT_COLOR_R;
font_g = HIGHLIGHT_FONT_COLOR_G;
font_b = HIGHLIGHT_FONT_COLOR_B;
}
if (uiState == stateHfs0Browser || uiState == stateRomFsSectionBrowser)
{
u8 *icon = (highlight ? (uiState == stateRomFsSectionBrowser ? (romFsBrowserEntries[i].type == ROMFS_ENTRY_DIR ? dirHighlightIconBuf : fileHighlightIconBuf) : fileHighlightIconBuf) : (uiState == stateRomFsSectionBrowser ? (romFsBrowserEntries[i].type == ROMFS_ENTRY_DIR ? dirNormalIconBuf : fileNormalIconBuf) : fileNormalIconBuf));
uiDrawIcon(icon, BROWSER_ICON_DIMENSION, BROWSER_ICON_DIMENSION, xpos, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 14);
xpos += (BROWSER_ICON_DIMENSION + 8);
}
uiDrawString(menu[i], xpos, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, font_r, font_g, font_b);
// Print XCI dump menu settings values
if (uiState == stateXciDumpMenu && i > 0)
{
switch(i)
{
case 1: // Split output dump (FAT32 support)
uiDrawString((isFat32 ? "Yes" : "No"), OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, (isFat32 ? 0 : 255), (isFat32 ? 255 : 0), 0);
break;
case 2: // Create directory with archive bit set
uiDrawString((setXciArchiveBit ? "Yes" : "No"), OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, (setXciArchiveBit ? 0 : 255), (setXciArchiveBit ? 255 : 0), 0);
break;
case 3: // Dump certificate
uiDrawString((dumpCert ? "Yes" : "No"), OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, (dumpCert ? 0 : 255), (dumpCert ? 255 : 0), 0);
break;
case 4: // Trim output dump
uiDrawString((trimDump ? "Yes" : "No"), OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, (trimDump ? 0 : 255), (trimDump ? 255 : 0), 0);
break;
case 5: // CRC32 checksum calculation + dump verification
uiDrawString((calcCrc ? "Yes" : "No"), OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, (calcCrc ? 0 : 255), (calcCrc ? 255 : 0), 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)
uiDrawString((isFat32 ? "Yes" : "No"), OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, (isFat32 ? 0 : 255), (isFat32 ? 255 : 0), 0);
break;
case 2: // CRC32 checksum calculation
uiDrawString((calcCrc ? "Yes" : "No"), OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, (calcCrc ? 0 : 255), (calcCrc ? 255 : 0), 0);
break;
case 3: // Bundled application/update/DLC to dump
if (uiState == stateNspAppDumpMenu)
{
// Print application name
convertTitleVersionToDecimal(gameCardVersion[selectedAppIndex], versionStr, sizeof(versionStr));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s v%s", gameCardName[selectedAppIndex], versionStr);
} else
if (uiState == stateNspPatchDumpMenu)
{
// Find a matching application to print its name
// Otherwise, just print the Title ID
retrieveDescriptionForPatchOrAddOn(gameCardPatchTitleID[selectedPatchIndex], gameCardPatchVersion[selectedPatchIndex], false, NULL);
} else
if (uiState == stateNspAddOnDumpMenu)
{
// Find a matching application to print its name and Title ID
// Otherwise, just print the Title ID
retrieveDescriptionForPatchOrAddOn(gameCardAddOnTitleID[selectedAddOnIndex], gameCardAddOnVersion[selectedAddOnIndex], true, NULL);
}
uiDrawString(strbuf, OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, 255, 255, 255);
break;
default:
break;
}
if (i == 2)
{
if (calcCrc)
{
uiDrawString("This takes extra time after the NSP dump has been completed!", FB_WIDTH / 2, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, 255, 255, 255);
} else {
uiFill(FB_WIDTH / 2, 8 + (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)), FB_WIDTH / 2, font_height + 12, (highlight ? HIGHLIGHT_BG_COLOR_R : BG_COLOR_RGB), (highlight ? HIGHLIGHT_BG_COLOR_G : BG_COLOR_RGB), (highlight ? HIGHLIGHT_BG_COLOR_B : BG_COLOR_RGB));
}
}
}
// Print RomFS menus settings values
if ((uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu) && i > 0)
{
switch(i)
{
case 1: // Bundled application to dump/browse
// Print application name
convertTitleVersionToDecimal(gameCardVersion[selectedAppIndex], versionStr, sizeof(versionStr));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s v%s", gameCardName[selectedAppIndex], versionStr);
uiDrawString(strbuf, OPTIONS_X_POS, (breaks * (font_height + (font_height / 4))) + (j * (font_height + 12)) + 6, 255, 255, 255);
break;
default:
break;
}
}
if (i == cursor) highlight = false;
}
}
uiUpdateStatusMsg();
uiRefreshDisplay();
hidScanInput();
keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
keysHeld = hidKeysHeld(CONTROLLER_P1_AUTO);
// Exit
if (keysDown & KEY_PLUS) res = resultExit;
// Process key inputs only if the UI state hasn't been changed
if (res == resultNone)
{
// Process base application info change
if (gameCardAppCount > 1 && uiState != stateHfs0Browser && uiState != stateRomFsSectionBrowser)
{
if ((keysDown & KEY_L) || (keysDown & KEY_ZL))
{
if (selectedAppInfoIndex > 0)
{
selectedAppInfoIndex--;
} else {
selectedAppInfoIndex = 0;
}
}
if ((keysDown & KEY_R) || (keysDown & KEY_ZR))
{
selectedAppInfoIndex++;
if (selectedAppInfoIndex >= gameCardAppCount) selectedAppInfoIndex = (gameCardAppCount - 1);
}
}
if (uiState == stateXciDumpMenu)
{
// Select
if ((keysDown & KEY_A) && cursor == 0) res = resultDumpXci;
// Back
if (keysDown & KEY_B) res = resultShowMainMenu;
// Change option to false
if (keysDown & KEY_LEFT)
{
switch(cursor)
{
case 1: // Split output dump (FAT32 support)
isFat32 = setXciArchiveBit = false;
break;
case 2: // Create directory with archive bit set
setXciArchiveBit = false;
break;
case 3: // Dump certificate
dumpCert = false;
break;
case 4: // Trim output dump
trimDump = false;
break;
case 5: // CRC32 checksum calculation + dump verification
calcCrc = false;
break;
default:
break;
}
}
// Change option to true
if (keysDown & KEY_RIGHT)
{
switch(cursor)
{
case 1: // Split output dump (FAT32 support)
isFat32 = true;
break;
case 2: // Create directory with archive bit set
setXciArchiveBit = true;
break;
case 3: // Dump certificate
dumpCert = true;
break;
case 4: // Trim output dump
trimDump = true;
break;
case 5: // CRC32 checksum calculation + dump verification
calcCrc = true;
break;
default:
break;
}
}
// Go up
if ((keysDown & KEY_DUP) || (keysDown & KEY_LSTICK_UP) || (keysHeld & KEY_RSTICK_UP)) scrollAmount = -1;
// Go down
if ((keysDown & KEY_DDOWN) || (keysDown & KEY_LSTICK_DOWN) || (keysHeld & KEY_RSTICK_DOWN)) scrollAmount = 1;
} else
if (uiState == stateNspAppDumpMenu || uiState == stateNspPatchDumpMenu || uiState == stateNspAddOnDumpMenu)
{
// Select
if ((keysDown & KEY_A) && cursor == 0)
{
selectedNspDumpType = (uiState == stateNspAppDumpMenu ? DUMP_APP_NSP : (uiState == stateNspPatchDumpMenu ? DUMP_PATCH_NSP : DUMP_ADDON_NSP));
res = resultDumpNsp;
}
// Back
if (keysDown & KEY_B)
{
if (uiState == stateNspAppDumpMenu && !gameCardPatchCount && !gameCardAddOnCount)
{
res = resultShowMainMenu;
} else {
res = resultShowNspDumpMenu;
}
}
// Change option to false
if (keysDown & KEY_LEFT)
{
switch(cursor)
{
case 1: // Split output dump (FAT32 support)
isFat32 = false;
break;
case 2: // CRC32 checksum calculation
calcCrc = false;
break;
case 3: // Bundled application/update/DLC to dump
if (uiState == stateNspAppDumpMenu)
{
if (selectedAppIndex > 0)
{
selectedAppIndex--;
} else {
selectedAppIndex = 0;
}
} else
if (uiState == stateNspPatchDumpMenu)
{
if (selectedPatchIndex > 0)
{
selectedPatchIndex--;
} else {
selectedPatchIndex = 0;
}
} else
if (uiState == stateNspAddOnDumpMenu)
{
if (selectedAddOnIndex > 0)
{
selectedAddOnIndex--;
} else {
selectedAddOnIndex = 0;
}
}
break;
default:
break;
}
}
// Change option to true
if (keysDown & KEY_RIGHT)
{
switch(cursor)
{
case 1: // Split output dump (FAT32 support)
isFat32 = true;
break;
case 2: // CRC32 checksum calculation
calcCrc = true;
break;
case 3: // Bundled application/update/DLC to dump
if (uiState == stateNspAppDumpMenu)
{
if (gameCardAppCount > 1)
{
selectedAppIndex++;
if (selectedAppIndex >= gameCardAppCount) selectedAppIndex = (gameCardAppCount - 1);
}
} else
if (uiState == stateNspPatchDumpMenu)
{
if (gameCardPatchCount > 1)
{
selectedPatchIndex++;
if (selectedPatchIndex >= gameCardPatchCount) selectedPatchIndex = (gameCardPatchCount - 1);
}
} else
if (uiState == stateNspAddOnDumpMenu)
{
if (gameCardAddOnCount > 1)
{
selectedAddOnIndex++;
if (selectedAddOnIndex >= gameCardAddOnCount) selectedAddOnIndex = (gameCardAddOnCount - 1);
}
}
break;
default:
break;
}
}
// Go up
if ((keysDown & KEY_DUP) || (keysDown & KEY_LSTICK_UP) || (keysHeld & KEY_RSTICK_UP)) scrollAmount = -1;
// Go down
if ((keysDown & KEY_DDOWN) || (keysDown & KEY_LSTICK_DOWN) || (keysHeld & KEY_RSTICK_DOWN)) scrollAmount = 1;
} else
if (uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu)
{
// Select
if ((keysDown & KEY_A) && cursor == 0) res = (uiState == stateRomFsSectionDataDumpMenu ? resultDumpRomFsSectionData : resultRomFsSectionBrowserGetEntries);
// Back
if (keysDown & KEY_B) res = resultShowMainMenu;
// Change option to false
if (keysDown & KEY_LEFT)
{
switch(cursor)
{
case 1: // Bundled application to dump/browse
if (selectedAppIndex > 0)
{
selectedAppIndex--;
} else {
selectedAppIndex = 0;
}
break;
default:
break;
}
}
// Change option to true
if (keysDown & KEY_RIGHT)
{
switch(cursor)
{
case 1: // Bundled application to dump/browse
if (gameCardAppCount > 1)
{
selectedAppIndex++;
if (selectedAppIndex >= gameCardAppCount) selectedAppIndex = (gameCardAppCount - 1);
}
break;
default:
break;
}
}
// Go up
if ((keysDown & KEY_DUP) || (keysDown & KEY_LSTICK_UP) || (keysHeld & KEY_RSTICK_UP)) scrollAmount = -1;
// Go down
if ((keysDown & KEY_DDOWN) || (keysDown & KEY_LSTICK_DOWN) || (keysHeld & KEY_RSTICK_DOWN)) scrollAmount = 1;
} else {
// Select
if (keysDown & KEY_A)
{
if (uiState == stateMainMenu)
{
switch(cursor)
{
case 0:
res = resultShowXciDumpMenu;
// Reset options to their default values
isFat32 = false;
dumpCert = false;
trimDump = false;
calcCrc = true;
break;
case 1:
if (!gameCardPatchCount && !gameCardAddOnCount)
{
res = resultShowNspAppDumpMenu;
// Reset options to their default values
isFat32 = false;
calcCrc = false;
selectedAppIndex = 0;
} else {
res = resultShowNspDumpMenu;
}
break;
case 2:
res = resultShowHfs0Menu;
break;
case 3:
res = resultShowRomFsMenu;
break;
case 4:
res = resultDumpGameCardCertificate;
break;
case 5:
res = resultShowUpdateMenu;
break;
default:
break;
}
} else
if (uiState == stateNspDumpMenu)
{
switch(cursor)
{
case 0:
res = resultShowNspAppDumpMenu;
break;
case 1:
res = (gameCardPatchCount > 0 ? resultShowNspPatchDumpMenu : resultShowNspAddOnDumpMenu);
break;
case 2:
res = resultShowNspAddOnDumpMenu;
break;
default:
break;
}
// Reset options to their default values
isFat32 = false;
calcCrc = false;
selectedAppIndex = 0;
selectedPatchIndex = 0;
selectedAddOnIndex = 0;
} 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)
{
// Save selected file index
selectedFileIndex = (u32)cursor;
res = resultHfs0BrowserCopyFile;
} else
if (uiState == stateRomFsMenu)
{
switch(cursor)
{
case 0:
res = (gameCardAppCount > 1 ? resultShowRomFsSectionDataDumpMenu : resultDumpRomFsSectionData);
// Reset options to their default values
selectedAppIndex = 0;
break;
case 1:
res = (gameCardAppCount > 1 ? resultShowRomFsSectionBrowserMenu : resultRomFsSectionBrowserGetEntries);
// Reset options to their default values
selectedAppIndex = 0;
break;
default:
break;
}
} else
if (uiState == stateRomFsSectionBrowser)
{
if (menu && menuItemsCount)
{
// Save selected file index
selectedFileIndex = (u32)cursor;
res = (romFsBrowserEntries[cursor].type == ROMFS_ENTRY_DIR ? resultRomFsSectionBrowserChangeDir : resultRomFsSectionBrowserCopyFile);
}
} else
if (uiState == stateUpdateMenu)
{
switch(cursor)
{
case 0:
res = resultUpdateNSWDBXml;
break;
case 1:
res = resultUpdateApplication;
break;
default:
break;
}
}
}
// Back
if (keysDown & KEY_B)
{
if (uiState == stateNspDumpMenu || uiState == stateHfs0Menu || uiState == stateRomFsMenu || uiState == stateUpdateMenu)
{
res = resultShowMainMenu;
} else
if (uiState == stateRawHfs0PartitionDumpMenu || uiState == stateHfs0PartitionDataDumpMenu || uiState == stateHfs0BrowserMenu)
{
res = resultShowHfs0Menu;
} else
if (uiState == stateHfs0Browser)
{
free(partitionHfs0Header);
partitionHfs0Header = NULL;
partitionHfs0HeaderOffset = 0;
partitionHfs0HeaderSize = 0;
partitionHfs0FileCount = 0;
partitionHfs0StrTableSize = 0;
res = resultShowHfs0BrowserMenu;
} else
if (uiState == stateRomFsSectionDataDumpMenu || uiState == stateRomFsSectionBrowserMenu)
{
res = resultShowRomFsMenu;
} else
if (uiState == stateRomFsSectionBrowser)
{
if (strlen(curRomFsPath) > 1)
{
// Point to the parent directory entry ("..")
selectedFileIndex = 0;
res = resultRomFsSectionBrowserChangeDir;
} else {
if (romFsBrowserEntries != NULL)
{
free(romFsBrowserEntries);
romFsBrowserEntries = NULL;
}
freeRomFsContext();
res = (gameCardAppCount > 1 ? resultShowRomFsSectionBrowserMenu : resultShowRomFsMenu);
}
}
}
if (menu && menuItemsCount)
{
// Go up
if ((keysDown & KEY_DUP) || (keysDown & KEY_LSTICK_UP) || (keysHeld & KEY_RSTICK_UP)) scrollAmount = -1;
if ((keysDown & KEY_DLEFT) || (keysDown & KEY_LSTICK_LEFT) || (keysHeld & KEY_RSTICK_LEFT)) scrollAmount = -5;
// Go down
if ((keysDown & KEY_DDOWN) || (keysDown & KEY_LSTICK_DOWN) || (keysHeld & KEY_RSTICK_DOWN)) scrollAmount = 1;
if ((keysDown & KEY_DRIGHT) || (keysDown & KEY_LSTICK_RIGHT) || (keysHeld & KEY_RSTICK_RIGHT)) scrollAmount = 5;
}
}
// Calculate scroll only if the UI state hasn't been changed
if (res == resultNone)
{
if (scrollAmount > 0)
{
for(i = 0; i < scrollAmount; i++)
{
if (cursor < menuItemsCount - 1)
{
cursor++;
if (((uiState != stateHfs0Browser && uiState != stateRomFsSectionBrowser) && (cursor - scroll) >= COMMON_MAX_ELEMENTS) || (uiState == stateHfs0Browser && (cursor - scroll) >= HFS0_MAX_ELEMENTS) || (uiState == stateRomFsSectionBrowser && (cursor - scroll) >= ROMFS_MAX_ELEMENTS)) scroll++;
}
}
} else
if (scrollAmount < 0)
{
for(i = 0; i < -scrollAmount; i++)
{
if (cursor > 0)
{
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 && !isFat32)
{
if (scrollAmount > 0)
{
cursor++;
} else
if (scrollAmount < 0)
{
cursor--;
}
}
// Avoid placing the cursor on the "Dump bundled update NSP" option in the NSP dump menu if our current gamecard doesn't include any bundled updates
// Also avoid placing the cursor on the "Dump bundled DLC NSP" option in the NSP dump menu if our current gamecard doesn't include any bundled DLCs
if (uiState == stateNspDumpMenu)
{
if ((gameCardPatchCount && !gameCardAddOnCount) || (!gameCardPatchCount && gameCardAddOnCount))
{
if (cursor >= 2) cursor = 1;
} else
if (!gameCardPatchCount && !gameCardAddOnCount)
{
// Just in case
cursor = 0;
}
}
// Avoid placing the cursor on the parent directory entry ("..") in the RomFS browser if we're currently at the root directory
if (uiState == stateRomFsSectionBrowser && cursor == 0 && strlen(curRomFsPath) <= 1) cursor = 1;
}
}
} else
if (uiState == stateDumpXci)
{
uiDrawString(mainMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[1], (isFat32 ? "Yes" : "No"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
if (isFat32)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[2], (setXciArchiveBit ? "Yes" : "No"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
}
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[3], (dumpCert ? "Yes" : "No"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[4], (trimDump ? "Yes" : "No"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", xciDumpMenuItems[5], (calcCrc ? "Yes" : "No"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiRefreshDisplay();
dumpCartridgeImage(&fsOperatorInstance, isFat32, setXciArchiveBit, dumpCert, trimDump, calcCrc);
waitForButtonPress();
uiUpdateFreeSpace();
res = resultShowXciDumpMenu;
} else
if (uiState == stateDumpNsp)
{
uiDrawString(nspDumpMenuItems[selectedNspDumpType], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
menu = (selectedNspDumpType == DUMP_APP_NSP ? nspAppDumpMenuItems : (selectedNspDumpType == DUMP_PATCH_NSP ? nspPatchDumpMenuItems : nspAddOnDumpMenuItems));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[1], (isFat32 ? "Yes" : "No"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s", menu[2], (calcCrc ? "Yes" : "No"));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
if (selectedNspDumpType == DUMP_APP_NSP)
{
convertTitleVersionToDecimal(gameCardVersion[selectedAppIndex], versionStr, sizeof(versionStr));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", menu[3], gameCardName[selectedAppIndex], versionStr);
} else
if (selectedNspDumpType == DUMP_PATCH_NSP)
{
retrieveDescriptionForPatchOrAddOn(gameCardPatchTitleID[selectedPatchIndex], gameCardPatchVersion[selectedPatchIndex], false, menu[3]);
} else
if (selectedNspDumpType == DUMP_ADDON_NSP)
{
retrieveDescriptionForPatchOrAddOn(gameCardAddOnTitleID[selectedAddOnIndex], gameCardAddOnVersion[selectedAddOnIndex], true, menu[3]);
}
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiRefreshDisplay();
dumpNintendoSubmissionPackage(&fsOperatorInstance, selectedNspDumpType, (selectedNspDumpType == DUMP_APP_NSP ? selectedAppIndex : (selectedNspDumpType == DUMP_PATCH_NSP ? selectedPatchIndex : selectedAddOnIndex)), isFat32, calcCrc);
waitForButtonPress();
uiUpdateFreeSpace();
res = (selectedNspDumpType == DUMP_APP_NSP ? resultShowNspAppDumpMenu : (selectedNspDumpType == DUMP_PATCH_NSP ? resultShowNspPatchDumpMenu : resultShowNspAddOnDumpMenu));
} else
if (uiState == stateDumpRawHfs0Partition)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Raw %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems[selectedPartitionIndex] : hfs0PartitionDumpType2MenuItems[selectedPartitionIndex]));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiRefreshDisplay();
dumpRawHfs0Partition(&fsOperatorInstance, selectedPartitionIndex, true);
waitForButtonPress();
uiUpdateFreeSpace();
res = resultShowRawHfs0PartitionDumpMenu;
} else
if (uiState == stateDumpHfs0PartitionData)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Data %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0PartitionDumpType1MenuItems[selectedPartitionIndex] : hfs0PartitionDumpType2MenuItems[selectedPartitionIndex]));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiRefreshDisplay();
dumpHfs0PartitionData(&fsOperatorInstance, selectedPartitionIndex);
waitForButtonPress();
uiUpdateFreeSpace();
res = resultShowHfs0PartitionDataDumpMenu;
} else
if (uiState == stateHfs0BrowserGetList)
{
uiDrawString((hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? hfs0BrowserType1MenuItems[selectedPartitionIndex] : hfs0BrowserType2MenuItems[selectedPartitionIndex]), 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiPleaseWait(0);
breaks += 2;
if (getHfs0FileList(selectedPartitionIndex))
{
cursor = 0;
scroll = 0;
res = resultShowHfs0Browser;
} else {
waitForButtonPress();
res = resultShowHfs0BrowserMenu;
}
} else
if (uiState == stateHfs0BrowserCopyFile)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Manual File Dump: %s (HFS0 partition %u [%s])", filenames[selectedFileIndex], selectedPartitionIndex, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, selectedPartitionIndex));
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiRefreshDisplay();
dumpFileFromHfs0Partition(&fsOperatorInstance, selectedPartitionIndex, selectedFileIndex, filenames[selectedFileIndex]);
waitForButtonPress();
uiUpdateFreeSpace();
res = resultShowHfs0Browser;
} else
if (uiState == stateDumpRomFsSectionData)
{
uiDrawString(romFsMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
convertTitleVersionToDecimal(gameCardVersion[selectedAppIndex], versionStr, sizeof(versionStr));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", romFsSectionDumpMenuItems[1], gameCardName[selectedAppIndex], versionStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiRefreshDisplay();
dumpRomFsSectionData(&fsOperatorInstance, selectedAppIndex);
waitForButtonPress();
uiUpdateFreeSpace();
res = (gameCardAppCount > 1 ? resultShowRomFsSectionDataDumpMenu : resultShowRomFsMenu);
} else
if (uiState == stateRomFsSectionBrowserGetEntries)
{
uiDrawString(romFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
convertTitleVersionToDecimal(gameCardVersion[selectedAppIndex], versionStr, sizeof(versionStr));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", romFsSectionBrowserMenuItems[1], gameCardName[selectedAppIndex], versionStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiPleaseWait(0);
breaks += 2;
bool romfs_fail = false;
initRomFsContext();
if (readProgramNcaRomFs(selectedAppIndex))
{
if (getRomFsFileList(0))
{
cursor = 0;
scroll = 0;
res = resultShowRomFsSectionBrowser;
} else {
freeRomFsContext();
romfs_fail = true;
}
} else {
romfs_fail = true;
}
if (romfs_fail)
{
breaks += 2;
waitForButtonPress();
res = (gameCardAppCount > 1 ? resultShowRomFsSectionBrowserMenu : resultShowRomFsMenu);
}
} else
if (uiState == stateRomFsSectionBrowserChangeDir)
{
uiDrawString(romFsMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
convertTitleVersionToDecimal(gameCardVersion[selectedAppIndex], versionStr, sizeof(versionStr));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "%s%s v%s", romFsSectionBrowserMenuItems[1], gameCardName[selectedAppIndex], versionStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
bool romfs_fail = false;
if (romFsBrowserEntries[selectedFileIndex].type == ROMFS_ENTRY_DIR)
{
if (getRomFsFileList(romFsBrowserEntries[selectedFileIndex].offset))
{
cursor = 0;
scroll = 0;
res = resultShowRomFsSectionBrowser;
} else {
romfs_fail = true;
}
} else {
// Unexpected condition
uiDrawString("Error: the selected entry is not a directory!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
romfs_fail = true;
}
if (romfs_fail)
{
if (romFsBrowserEntries != NULL)
{
free(romFsBrowserEntries);
romFsBrowserEntries = NULL;
}
freeRomFsContext();
breaks += 2;
waitForButtonPress();
res = (gameCardAppCount > 1 ? resultShowRomFsSectionBrowserMenu : resultShowRomFsMenu);
}
} else
if (uiState == stateRomFsSectionBrowserCopyFile)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Manual File Dump: %s (RomFS)", filenames[selectedFileIndex]);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks++;
convertTitleVersionToDecimal(gameCardVersion[selectedAppIndex], versionStr, sizeof(versionStr));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Base application: %s v%s", gameCardName[selectedAppIndex], versionStr);
uiDrawString(strbuf, 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
uiRefreshDisplay();
if (romFsBrowserEntries[selectedFileIndex].type == ROMFS_ENTRY_FILE)
{
dumpFileFromRomFsSection(selectedAppIndex, romFsBrowserEntries[selectedFileIndex].offset, true);
} else {
// Unexpected condition
uiDrawString("Error: the selected entry is not a file!", 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
}
waitForButtonPress();
uiUpdateFreeSpace();
res = resultShowRomFsSectionBrowser;
} else
if (uiState == stateDumpGameCardCertificate)
{
uiDrawString(mainMenuItems[4], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
dumpGameCertificate(&fsOperatorInstance);
waitForButtonPress();
uiUpdateFreeSpace();
res = resultShowMainMenu;
} else
if (uiState == stateUpdateNSWDBXml)
{
uiDrawString(updateMenuItems[0], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
updateNSWDBXml();
waitForButtonPress();
uiUpdateFreeSpace();
res = resultShowUpdateMenu;
} else
if (uiState == stateUpdateApplication)
{
uiDrawString(updateMenuItems[1], 8, (breaks * (font_height + (font_height / 4))) + (font_height / 8), 115, 115, 255);
breaks += 2;
updateApplication();
waitForButtonPress();
uiUpdateFreeSpace();
res = resultShowUpdateMenu;
}
return res;
}