2018-06-21 07:42:46 +01:00
# include <stdarg.h>
# include <stddef.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <sys/stat.h>
# include <switch.h>
# include "dumper.h"
# include "fsext.h"
# include "ui.h"
# include "util.h"
extern FsDeviceOperator fsOperatorInstance ;
extern bool gameCardInserted ;
extern u32 currentFBWidth , currentFBHeight ;
extern u8 * currentFB ;
2018-06-26 02:11:18 +01:00
extern char gameCardSizeStr [ 32 ] , trimmedCardSizeStr [ 32 ] ;
2018-06-21 07:42:46 +01:00
extern char * hfs0_header ;
extern u64 hfs0_offset , hfs0_size ;
extern u32 hfs0_partition_cnt ;
extern u64 gameCardTitleID ;
2018-06-30 07:37:42 +01:00
extern u32 gameCardVersion ;
extern char gameCardName [ 0x201 ] , fixedGameCardName [ 0x201 ] , gameCardAuthor [ 0x101 ] , gameCardVersionStr [ 64 ] ;
extern char gameCardUpdateVersionStr [ 128 ] ;
2018-06-21 07:42:46 +01:00
2018-06-26 02:11:18 +01:00
static bool isFat32 = false , dumpCert = false , trimDump = false , calcCrc = true ;
2018-06-24 01:48:34 +01:00
2018-06-21 07:42:46 +01:00
static u32 selectedOption ;
2018-07-21 06:56:00 +01:00
static FsFileSystem fs ;
2018-06-21 07:42:46 +01:00
static char statusMessage [ 2048 ] = { ' \0 ' } ;
static int statusMessageFadeout = 0 ;
static char fileCopyPath [ NAME_BUF_LEN * 2 ] = { ' \0 ' } ;
static char currentDirectory [ NAME_BUF_LEN ] = { ' \0 ' } ;
static int cursor = 0 ;
static int scroll = 0 ;
static int headlineCnt = 0 ;
int breaks = 0 ;
u64 freeSpace = 0 ;
static char freeSpaceStr [ 64 ] = { ' \0 ' } ;
static char titlebuf [ NAME_BUF_LEN * 2 ] = { ' \0 ' } ;
static const int maxListElements = 45 ;
static char * filenameBuffer = NULL ;
2018-06-22 01:46:17 +01:00
static char * filenames [ FILENAME_MAX_CNT ] ;
2018-06-21 07:42:46 +01:00
static int filenamesCount = 0 ;
static UIState uiState ;
static const char * appHeadline = " Nintendo Switch Game Card Dump Tool v " APP_VERSION " . \n Original code by MCMrARM. \n Additional modifications by DarkMatterCore. \n \n " ;
2018-07-21 06:56:00 +01:00
static const char * appControls = " [D-Pad / Analog Sticks] Move | [A] Select | [B] Back | [+] Exit " ;
2018-06-21 07:42:46 +01:00
2018-07-21 06:56:00 +01:00
static const char * mainMenuItems [ ] = { " Full XCI dump " , " Raw partition dump " , " Partition data dump " , " View game card files " , " Dump game card certificate " , " Update NSWDB.COM XML database " , " Update application (not working at this moment) " } ;
2018-06-26 02:11:18 +01:00
static const char * xciDumpMenuItems [ ] = { " Start XCI dump process " , " Split output dump (FAT32 support): " , " Dump certificate: " , " Trim output dump: " , " CRC32 checksum calculation + dump verification: " } ;
2018-07-21 06:56:00 +01:00
static const char * partitionDumpType1MenuItems [ ] = { " Dump partition 0 (Update) " , " Dump partition 1 (Normal) " , " Dump partition 2 (Secure) " } ;
static const char * partitionDumpType2MenuItems [ ] = { " Dump partition 0 (Update) " , " Dump partition 1 (Logo) " , " Dump partition 2 (Normal) " , " Dump partition 3 (Secure) " } ;
static const char * viewGameCardFsType1MenuItems [ ] = { " View files from partition 0 (Update) " , " View files from partition 1 (Normal) " , " View files from partition 2 (Secure) " } ;
static const char * viewGameCardFsType2MenuItems [ ] = { " View files from partition 0 (Update) " , " View files from partition 1 (Logo) " , " View files from partition 2 (Normal) " , " View files from partition 3 (Secure) " } ;
2018-06-21 07:42:46 +01:00
static unsigned char asciiData [ 128 ] [ 8 ] = {
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } , { 0x00 , 0x3E , 0x41 , 0x55 , 0x41 , 0x55 , 0x49 , 0x3E } ,
{ 0x00 , 0x3E , 0x7F , 0x6B , 0x7F , 0x6B , 0x77 , 0x3E } , { 0x00 , 0x22 , 0x77 , 0x7F , 0x7F , 0x3E , 0x1C , 0x08 } ,
{ 0x00 , 0x08 , 0x1C , 0x3E , 0x7F , 0x3E , 0x1C , 0x08 } , { 0x00 , 0x08 , 0x1C , 0x2A , 0x7F , 0x2A , 0x08 , 0x1C } ,
{ 0x00 , 0x08 , 0x1C , 0x3E , 0x7F , 0x3E , 0x08 , 0x1C } , { 0x00 , 0x00 , 0x1C , 0x3E , 0x3E , 0x3E , 0x1C , 0x00 } ,
{ 0xFF , 0xFF , 0xE3 , 0xC1 , 0xC1 , 0xC1 , 0xE3 , 0xFF } , { 0x00 , 0x00 , 0x1C , 0x22 , 0x22 , 0x22 , 0x1C , 0x00 } ,
{ 0xFF , 0xFF , 0xE3 , 0xDD , 0xDD , 0xDD , 0xE3 , 0xFF } , { 0x00 , 0x0F , 0x03 , 0x05 , 0x39 , 0x48 , 0x48 , 0x30 } ,
{ 0x00 , 0x08 , 0x3E , 0x08 , 0x1C , 0x22 , 0x22 , 0x1C } , { 0x00 , 0x18 , 0x14 , 0x10 , 0x10 , 0x30 , 0x70 , 0x60 } ,
{ 0x00 , 0x0F , 0x19 , 0x11 , 0x13 , 0x37 , 0x76 , 0x60 } , { 0x00 , 0x08 , 0x2A , 0x1C , 0x77 , 0x1C , 0x2A , 0x08 } ,
{ 0x00 , 0x60 , 0x78 , 0x7E , 0x7F , 0x7E , 0x78 , 0x60 } , { 0x00 , 0x03 , 0x0F , 0x3F , 0x7F , 0x3F , 0x0F , 0x03 } ,
{ 0x00 , 0x08 , 0x1C , 0x2A , 0x08 , 0x2A , 0x1C , 0x08 } , { 0x00 , 0x66 , 0x66 , 0x66 , 0x66 , 0x00 , 0x66 , 0x66 } ,
{ 0x00 , 0x3F , 0x65 , 0x65 , 0x3D , 0x05 , 0x05 , 0x05 } , { 0x00 , 0x0C , 0x32 , 0x48 , 0x24 , 0x12 , 0x4C , 0x30 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x7F , 0x7F , 0x7F } , { 0x00 , 0x08 , 0x1C , 0x2A , 0x08 , 0x2A , 0x1C , 0x3E } ,
{ 0x00 , 0x08 , 0x1C , 0x3E , 0x7F , 0x1C , 0x1C , 0x1C } , { 0x00 , 0x1C , 0x1C , 0x1C , 0x7F , 0x3E , 0x1C , 0x08 } ,
{ 0x00 , 0x08 , 0x0C , 0x7E , 0x7F , 0x7E , 0x0C , 0x08 } , { 0x00 , 0x08 , 0x18 , 0x3F , 0x7F , 0x3F , 0x18 , 0x08 } ,
{ 0x00 , 0x00 , 0x00 , 0x70 , 0x70 , 0x70 , 0x7F , 0x7F } , { 0x00 , 0x00 , 0x14 , 0x22 , 0x7F , 0x22 , 0x14 , 0x00 } ,
{ 0x00 , 0x08 , 0x1C , 0x1C , 0x3E , 0x3E , 0x7F , 0x7F } , { 0x00 , 0x7F , 0x7F , 0x3E , 0x3E , 0x1C , 0x1C , 0x08 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 } , { 0x00 , 0x18 , 0x3C , 0x3C , 0x18 , 0x18 , 0x00 , 0x18 } ,
{ 0x00 , 0x36 , 0x36 , 0x14 , 0x00 , 0x00 , 0x00 , 0x00 } , { 0x00 , 0x36 , 0x36 , 0x7F , 0x36 , 0x7F , 0x36 , 0x36 } ,
{ 0x00 , 0x08 , 0x1E , 0x20 , 0x1C , 0x02 , 0x3C , 0x08 } , { 0x00 , 0x60 , 0x66 , 0x0C , 0x18 , 0x30 , 0x66 , 0x06 } ,
{ 0x00 , 0x3C , 0x66 , 0x3C , 0x28 , 0x65 , 0x66 , 0x3F } , { 0x00 , 0x18 , 0x18 , 0x18 , 0x30 , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x06 , 0x0C , 0x18 , 0x18 , 0x18 , 0x0C , 0x06 } , { 0x00 , 0x60 , 0x30 , 0x18 , 0x18 , 0x18 , 0x30 , 0x60 } ,
{ 0x00 , 0x00 , 0x36 , 0x1C , 0x7F , 0x1C , 0x36 , 0x00 } , { 0x00 , 0x00 , 0x08 , 0x08 , 0x3E , 0x08 , 0x08 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x30 , 0x30 , 0x30 , 0x60 } , { 0x00 , 0x00 , 0x00 , 0x00 , 0x3C , 0x00 , 0x00 , 0x00 } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x60 , 0x60 } , { 0x00 , 0x00 , 0x06 , 0x0C , 0x18 , 0x30 , 0x60 , 0x00 } ,
{ 0x00 , 0x3C , 0x66 , 0x6E , 0x76 , 0x66 , 0x66 , 0x3C } , { 0x00 , 0x18 , 0x18 , 0x38 , 0x18 , 0x18 , 0x18 , 0x7E } ,
{ 0x00 , 0x3C , 0x66 , 0x06 , 0x0C , 0x30 , 0x60 , 0x7E } , { 0x00 , 0x3C , 0x66 , 0x06 , 0x1C , 0x06 , 0x66 , 0x3C } ,
{ 0x00 , 0x0C , 0x1C , 0x2C , 0x4C , 0x7E , 0x0C , 0x0C } , { 0x00 , 0x7E , 0x60 , 0x7C , 0x06 , 0x06 , 0x66 , 0x3C } ,
{ 0x00 , 0x3C , 0x66 , 0x60 , 0x7C , 0x66 , 0x66 , 0x3C } , { 0x00 , 0x7E , 0x66 , 0x0C , 0x0C , 0x18 , 0x18 , 0x18 } ,
{ 0x00 , 0x3C , 0x66 , 0x66 , 0x3C , 0x66 , 0x66 , 0x3C } , { 0x00 , 0x3C , 0x66 , 0x66 , 0x3E , 0x06 , 0x66 , 0x3C } ,
{ 0x00 , 0x00 , 0x18 , 0x18 , 0x00 , 0x18 , 0x18 , 0x00 } , { 0x00 , 0x00 , 0x18 , 0x18 , 0x00 , 0x18 , 0x18 , 0x30 } ,
{ 0x00 , 0x06 , 0x0C , 0x18 , 0x30 , 0x18 , 0x0C , 0x06 } , { 0x00 , 0x00 , 0x00 , 0x3C , 0x00 , 0x3C , 0x00 , 0x00 } ,
{ 0x00 , 0x60 , 0x30 , 0x18 , 0x0C , 0x18 , 0x30 , 0x60 } , { 0x00 , 0x3C , 0x66 , 0x06 , 0x1C , 0x18 , 0x00 , 0x18 } ,
{ 0x00 , 0x38 , 0x44 , 0x5C , 0x58 , 0x42 , 0x3C , 0x00 } , { 0x00 , 0x3C , 0x66 , 0x66 , 0x7E , 0x66 , 0x66 , 0x66 } ,
{ 0x00 , 0x7C , 0x66 , 0x66 , 0x7C , 0x66 , 0x66 , 0x7C } , { 0x00 , 0x3C , 0x66 , 0x60 , 0x60 , 0x60 , 0x66 , 0x3C } ,
{ 0x00 , 0x7C , 0x66 , 0x66 , 0x66 , 0x66 , 0x66 , 0x7C } , { 0x00 , 0x7E , 0x60 , 0x60 , 0x7C , 0x60 , 0x60 , 0x7E } ,
{ 0x00 , 0x7E , 0x60 , 0x60 , 0x7C , 0x60 , 0x60 , 0x60 } , { 0x00 , 0x3C , 0x66 , 0x60 , 0x60 , 0x6E , 0x66 , 0x3C } ,
{ 0x00 , 0x66 , 0x66 , 0x66 , 0x7E , 0x66 , 0x66 , 0x66 } , { 0x00 , 0x3C , 0x18 , 0x18 , 0x18 , 0x18 , 0x18 , 0x3C } ,
{ 0x00 , 0x1E , 0x0C , 0x0C , 0x0C , 0x6C , 0x6C , 0x38 } , { 0x00 , 0x66 , 0x6C , 0x78 , 0x70 , 0x78 , 0x6C , 0x66 } ,
{ 0x00 , 0x60 , 0x60 , 0x60 , 0x60 , 0x60 , 0x60 , 0x7E } , { 0x00 , 0x63 , 0x77 , 0x7F , 0x6B , 0x63 , 0x63 , 0x63 } ,
{ 0x00 , 0x63 , 0x73 , 0x7B , 0x6F , 0x67 , 0x63 , 0x63 } , { 0x00 , 0x3C , 0x66 , 0x66 , 0x66 , 0x66 , 0x66 , 0x3C } ,
{ 0x00 , 0x7C , 0x66 , 0x66 , 0x66 , 0x7C , 0x60 , 0x60 } , { 0x00 , 0x3C , 0x66 , 0x66 , 0x66 , 0x6E , 0x3C , 0x06 } ,
{ 0x00 , 0x7C , 0x66 , 0x66 , 0x7C , 0x78 , 0x6C , 0x66 } , { 0x00 , 0x3C , 0x66 , 0x60 , 0x3C , 0x06 , 0x66 , 0x3C } ,
{ 0x00 , 0x7E , 0x5A , 0x18 , 0x18 , 0x18 , 0x18 , 0x18 } , { 0x00 , 0x66 , 0x66 , 0x66 , 0x66 , 0x66 , 0x66 , 0x3E } ,
{ 0x00 , 0x66 , 0x66 , 0x66 , 0x66 , 0x66 , 0x3C , 0x18 } , { 0x00 , 0x63 , 0x63 , 0x63 , 0x6B , 0x7F , 0x77 , 0x63 } ,
{ 0x00 , 0x63 , 0x63 , 0x36 , 0x1C , 0x36 , 0x63 , 0x63 } , { 0x00 , 0x66 , 0x66 , 0x66 , 0x3C , 0x18 , 0x18 , 0x18 } ,
{ 0x00 , 0x7E , 0x06 , 0x0C , 0x18 , 0x30 , 0x60 , 0x7E } , { 0x00 , 0x1E , 0x18 , 0x18 , 0x18 , 0x18 , 0x18 , 0x1E } ,
{ 0x00 , 0x00 , 0x60 , 0x30 , 0x18 , 0x0C , 0x06 , 0x00 } , { 0x00 , 0x78 , 0x18 , 0x18 , 0x18 , 0x18 , 0x18 , 0x78 } ,
{ 0x00 , 0x08 , 0x14 , 0x22 , 0x41 , 0x00 , 0x00 , 0x00 } , { 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x7F } ,
{ 0x00 , 0x0C , 0x0C , 0x06 , 0x00 , 0x00 , 0x00 , 0x00 } , { 0x00 , 0x00 , 0x00 , 0x3C , 0x06 , 0x3E , 0x66 , 0x3E } ,
{ 0x00 , 0x60 , 0x60 , 0x60 , 0x7C , 0x66 , 0x66 , 0x7C } , { 0x00 , 0x00 , 0x00 , 0x3C , 0x66 , 0x60 , 0x66 , 0x3C } ,
{ 0x00 , 0x06 , 0x06 , 0x06 , 0x3E , 0x66 , 0x66 , 0x3E } , { 0x00 , 0x00 , 0x00 , 0x3C , 0x66 , 0x7E , 0x60 , 0x3C } ,
{ 0x00 , 0x1C , 0x36 , 0x30 , 0x30 , 0x7C , 0x30 , 0x30 } , { 0x00 , 0x00 , 0x3E , 0x66 , 0x66 , 0x3E , 0x06 , 0x3C } ,
{ 0x00 , 0x60 , 0x60 , 0x60 , 0x7C , 0x66 , 0x66 , 0x66 } , { 0x00 , 0x00 , 0x18 , 0x00 , 0x18 , 0x18 , 0x18 , 0x3C } ,
{ 0x00 , 0x0C , 0x00 , 0x0C , 0x0C , 0x6C , 0x6C , 0x38 } , { 0x00 , 0x60 , 0x60 , 0x66 , 0x6C , 0x78 , 0x6C , 0x66 } ,
{ 0x00 , 0x18 , 0x18 , 0x18 , 0x18 , 0x18 , 0x18 , 0x18 } , { 0x00 , 0x00 , 0x00 , 0x63 , 0x77 , 0x7F , 0x6B , 0x6B } ,
{ 0x00 , 0x00 , 0x00 , 0x7C , 0x7E , 0x66 , 0x66 , 0x66 } , { 0x00 , 0x00 , 0x00 , 0x3C , 0x66 , 0x66 , 0x66 , 0x3C } ,
{ 0x00 , 0x00 , 0x7C , 0x66 , 0x66 , 0x7C , 0x60 , 0x60 } , { 0x00 , 0x00 , 0x3C , 0x6C , 0x6C , 0x3C , 0x0D , 0x0F } ,
{ 0x00 , 0x00 , 0x00 , 0x7C , 0x66 , 0x66 , 0x60 , 0x60 } , { 0x00 , 0x00 , 0x00 , 0x3E , 0x40 , 0x3C , 0x02 , 0x7C } ,
{ 0x00 , 0x00 , 0x18 , 0x18 , 0x7E , 0x18 , 0x18 , 0x18 } , { 0x00 , 0x00 , 0x00 , 0x66 , 0x66 , 0x66 , 0x66 , 0x3E } ,
{ 0x00 , 0x00 , 0x00 , 0x00 , 0x66 , 0x66 , 0x3C , 0x18 } , { 0x00 , 0x00 , 0x00 , 0x63 , 0x6B , 0x6B , 0x6B , 0x3E } ,
{ 0x00 , 0x00 , 0x00 , 0x66 , 0x3C , 0x18 , 0x3C , 0x66 } , { 0x00 , 0x00 , 0x00 , 0x66 , 0x66 , 0x3E , 0x06 , 0x3C } ,
{ 0x00 , 0x00 , 0x00 , 0x3C , 0x0C , 0x18 , 0x30 , 0x3C } , { 0x00 , 0x0E , 0x18 , 0x18 , 0x30 , 0x18 , 0x18 , 0x0E } ,
{ 0x00 , 0x18 , 0x18 , 0x18 , 0x00 , 0x18 , 0x18 , 0x18 } , { 0x00 , 0x70 , 0x18 , 0x18 , 0x0C , 0x18 , 0x18 , 0x70 } ,
{ 0x00 , 0x00 , 0x00 , 0x3A , 0x6C , 0x00 , 0x00 , 0x00 } , { 0x00 , 0x08 , 0x1C , 0x36 , 0x63 , 0x41 , 0x41 , 0x7F }
} ;
int uiGetIndex ( int x , int y )
{
return ( x + y * currentFBWidth ) * 4 ;
}
void uiFill ( int x , int y , int width , int height , u8 r , u8 g , u8 b )
{
if ( currentFB = = NULL ) return ;
if ( x + width < 0 | | y + height < 0 | | x > = currentFBWidth | | y > = currentFBHeight ) return ;
if ( x < 0 )
{
width + = x ;
x = 0 ;
}
if ( y < 0 )
{
height + = y ;
y = 0 ;
}
if ( x + width > = currentFBWidth ) width = currentFBWidth - x ;
if ( y + height > = currentFBHeight ) height = currentFBHeight - y ;
u8 colorLine [ width * 4 ] ;
for ( int ly = 0 ; ly < width ; ly + + )
{
colorLine [ ly * 4 + 0 ] = r ;
colorLine [ ly * 4 + 1 ] = g ;
colorLine [ ly * 4 + 2 ] = b ;
colorLine [ ly * 4 + 3 ] = 255 ;
}
u8 * fbAddr = currentFB + uiGetIndex ( x , y ) ; // - (width * 4);
for ( int dx = 0 ; dx < height ; dx + + )
{
memcpy ( fbAddr , colorLine , ( size_t ) ( width * 4 ) ) ;
fbAddr + = currentFBWidth * 4 ;
}
}
void uiDrawChar ( char c , int x , int y , u8 r , u8 g , u8 b )
{
if ( currentFB = = NULL ) return ;
if ( c & 0x80 ) c = ' ? ' ; // Unicode chars
unsigned char * data = asciiData [ ( int ) c ] ;
for ( int cy = 0 ; cy < 8 ; cy + + )
{
if ( y + cy < 0 | | y + cy > = currentFBHeight ) continue ;
unsigned char l = data [ cy ] ;
for ( int cx = 0 ; cx < 8 ; cx + + )
{
if ( x + cx < 0 | | x + cx > = currentFBWidth ) continue ;
if ( ( 0 b10000000 > > cx ) & l )
{
u8 * ptr = & currentFB [ uiGetIndex ( x + cx , y + cy ) ] ;
* ( ptr + 0 ) = r ;
* ( ptr + 1 ) = g ;
* ( ptr + 2 ) = b ;
* ( ptr + 3 ) = 255 ;
}
}
}
}
void uiDrawString ( const char * string , int x , int y , u8 r , u8 g , u8 b )
{
if ( currentFB = = NULL ) return ;
int len = ( int ) strlen ( string ) ;
int cx = x ;
int cy = y ;
for ( int i = 0 ; i < len ; i + + )
{
char c = string [ i ] ;
if ( c = = ' \n ' )
{
cx = x ;
cy + = 8 ;
} else {
uiDrawChar ( c , cx , cy , r , g , b ) ;
cx + = 8 ;
}
if ( cx > ( currentFBWidth - 8 ) ) break ;
}
}
void uiStatusMsg ( const char * format , . . . )
{
2018-06-22 01:46:17 +01:00
statusMessageFadeout = 1000 ;
2018-07-26 02:57:44 +01:00
2018-06-21 07:42:46 +01:00
va_list args ;
va_start ( args , format ) ;
vsnprintf ( statusMessage , sizeof ( statusMessage ) / sizeof ( statusMessage [ 0 ] ) , format , args ) ;
va_end ( args ) ;
//printf("Status message: %s\n", statusMessage);
}
2018-06-22 01:46:17 +01:00
void uiUpdateStatusMsg ( )
{
if ( ! strlen ( statusMessage ) | | ! statusMessageFadeout ) return ;
uiFill ( 0 , currentFBHeight - 12 , currentFBWidth , 8 , 50 , 50 , 50 ) ;
2018-07-26 02:57:44 +01:00
int fadeout = ( statusMessageFadeout > 255 ? 255 : statusMessageFadeout ) ;
2018-06-22 01:46:17 +01:00
uiDrawString ( statusMessage , 4 , currentFBHeight - 12 , fadeout , fadeout , fadeout ) ;
2018-07-26 02:57:44 +01:00
syncDisplay ( ) ;
2018-06-22 01:46:17 +01:00
statusMessageFadeout - = 4 ;
}
void uiPleaseWait ( )
{
breaks = headlineCnt ;
uiDrawString ( " Please wait... " , 0 , breaks * 8 , 115 , 115 , 255 ) ;
syncDisplay ( ) ;
delay ( 2 ) ;
}
2018-06-21 07:42:46 +01:00
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 uiInit ( )
{
uiState = stateMainMenu ;
cursor = 0 ;
scroll = 0 ;
headlineCnt = 0 ;
2018-06-22 01:46:17 +01:00
filenameBuffer = ( char * ) malloc ( FILENAME_BUFFER_SIZE ) ;
2018-06-21 07:42:46 +01:00
int i , headlineLen = strlen ( appHeadline ) ;
for ( i = 0 ; i < headlineLen ; i + + )
{
if ( appHeadline [ i ] = = ' \n ' ) headlineCnt + + ;
}
if ( ! headlineCnt ) headlineCnt = 2 ;
uiUpdateFreeSpace ( ) ;
}
void uiDeinit ( )
{
if ( filenameBuffer ) free ( filenameBuffer ) ;
}
void uiSetState ( UIState state )
{
uiState = state ;
cursor = 0 ;
scroll = 0 ;
}
UIState uiGetState ( )
{
return uiState ;
}
void uiClearScreen ( )
{
uiFill ( 0 , 0 , currentFBWidth , currentFBHeight , 50 , 50 , 50 ) ;
}
2018-06-22 01:46:17 +01:00
void uiPrintHeadline ( )
{
uiClearScreen ( ) ;
uiDrawString ( appHeadline , 0 , 0 , 255 , 255 , 255 ) ;
}
2018-06-21 07:42:46 +01:00
static void enterDirectory ( const char * path )
{
snprintf ( currentDirectory , sizeof ( currentDirectory ) / sizeof ( currentDirectory [ 0 ] ) , " %s " , path ) ;
2018-06-22 01:46:17 +01:00
filenamesCount = FILENAME_MAX_CNT ;
2018-06-21 07:42:46 +01:00
getDirectoryContents ( filenameBuffer , & filenames [ 0 ] , & filenamesCount , currentDirectory , ( ! strcmp ( currentDirectory , " view:/ " ) & & strlen ( currentDirectory ) = = 6 ) ) ;
cursor = 0 ;
scroll = 0 ;
}
UIResult uiLoop ( u32 keysDown )
{
UIResult res = resultNone ;
int i ;
breaks = headlineCnt ;
if ( uiState = = stateMainMenu | | uiState = = stateXciDumpMenu | | uiState = = stateRawPartitionDumpMenu | | uiState = = statePartitionDataDumpMenu | | uiState = = stateViewGameCardFsMenu | | uiState = = stateViewGameCardFsBrowser )
{
// Exit
2018-07-21 06:56:00 +01:00
if ( keysDown & KEY_PLUS )
{
if ( uiState = = stateViewGameCardFsBrowser )
{
fsdevUnmountDevice ( " view " ) ;
fsFsClose ( & fs ) ;
}
return resultExit ;
}
2018-06-21 07:42:46 +01:00
uiDrawString ( appControls , 0 , breaks * 8 , 255 , 255 , 255 ) ;
breaks + = 2 ;
uiDrawString ( freeSpaceStr , 0 , breaks * 8 , 255 , 255 , 255 ) ;
breaks + = 2 ;
if ( uiState ! = stateViewGameCardFsBrowser )
{
2018-06-22 01:46:17 +01:00
if ( gameCardInserted & & hfs0_header ! = NULL & & ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT | | hfs0_partition_cnt = = GAMECARD_TYPE2_PARTITION_CNT ) & & gameCardTitleID ! = 0 )
2018-06-21 07:42:46 +01:00
{
uiDrawString ( " Game Card is inserted! " , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + = 2 ;
/*snprintf(titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]), "Root HFS0 header offset: 0x%016lX", hfs0_offset);
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + + ;
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Root HFS0 header size: 0x%016lX " , hfs0_size ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + + ; */
2018-06-30 07:37:42 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Name: %s " , gameCardName ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + + ;
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Developer: %s " , gameCardAuthor ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + + ;
2018-06-21 07:42:46 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Title ID: %016lX " , gameCardTitleID ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + + ;
2018-06-30 07:37:42 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Version: %s " , gameCardVersionStr ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + + ;
2018-06-21 07:42:46 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Size: %s " , gameCardSizeStr ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + + ;
2018-06-26 02:11:18 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Used space: %s " , trimmedCardSizeStr ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
breaks + + ;
2018-06-21 07:42:46 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Partition count: %u (%s) " , hfs0_partition_cnt , GAMECARD_TYPE ( hfs0_partition_cnt ) ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
2018-06-30 07:37:42 +01:00
if ( strlen ( gameCardUpdateVersionStr ) )
{
breaks + + ;
2018-07-21 06:56:00 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Bundled FW update: %s " , gameCardUpdateVersionStr ) ;
2018-06-30 07:37:42 +01:00
uiDrawString ( titlebuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
}
2018-06-21 07:42:46 +01:00
} else {
if ( gameCardInserted )
{
if ( hfs0_header ! = NULL )
{
2018-06-22 01:46:17 +01:00
if ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT | | hfs0_partition_cnt = = GAMECARD_TYPE2_PARTITION_CNT )
{
uiDrawString ( " Error: unable to retrieve the game card Title ID! " , 0 , breaks * 8 , 255 , 0 , 0 ) ;
2018-06-30 07:37:42 +01:00
if ( strlen ( gameCardUpdateVersionStr ) )
{
breaks + + ;
2018-07-21 06:56:00 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Bundled FW Update: %s " , gameCardUpdateVersionStr ) ;
2018-06-30 07:37:42 +01:00
uiDrawString ( titlebuf , 0 , breaks * 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. " , 0 , breaks * 8 , 255 , 255 , 255 ) ;
}
2018-06-22 01:46:17 +01:00
} else {
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Error: unknown root HFS0 header partition count! (%u) " , hfs0_partition_cnt ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 255 , 0 , 0 ) ;
}
2018-06-21 07:42:46 +01:00
} else {
uiDrawString ( " Error: unable to get root HFS0 header data! " , 0 , breaks * 8 , 255 , 0 , 0 ) ;
}
} else {
uiDrawString ( " Game Card is not inserted! " , 0 , breaks * 8 , 255 , 0 , 0 ) ;
}
res = resultShowMainMenu ;
}
breaks + = 2 ;
}
2018-06-22 01:46:17 +01:00
if ( gameCardInserted & & hfs0_header ! = NULL & & ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT | | hfs0_partition_cnt = = GAMECARD_TYPE2_PARTITION_CNT ) & & gameCardTitleID ! = 0 )
2018-06-21 07:42:46 +01:00
{
const char * * menu = NULL ;
int menuItemsCount ;
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 ] , 0 , breaks * 8 , 115 , 115 , 255 ) ;
break ;
case stateRawPartitionDumpMenu :
case statePartitionDataDumpMenu :
menu = ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? partitionDumpType1MenuItems : partitionDumpType2MenuItems ) ;
menuItemsCount = ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? ( sizeof ( partitionDumpType1MenuItems ) / sizeof ( partitionDumpType1MenuItems [ 0 ] ) ) : ( sizeof ( partitionDumpType2MenuItems ) / sizeof ( partitionDumpType2MenuItems [ 0 ] ) ) ) ;
uiDrawString ( ( uiState = = stateRawPartitionDumpMenu ? mainMenuItems [ 1 ] : mainMenuItems [ 2 ] ) , 0 , breaks * 8 , 115 , 115 , 255 ) ;
break ;
case stateViewGameCardFsMenu :
menu = ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? viewGameCardFsType1MenuItems : viewGameCardFsType2MenuItems ) ;
menuItemsCount = ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? ( sizeof ( viewGameCardFsType1MenuItems ) / sizeof ( viewGameCardFsType1MenuItems [ 0 ] ) ) : ( sizeof ( viewGameCardFsType2MenuItems ) / sizeof ( viewGameCardFsType2MenuItems [ 0 ] ) ) ) ;
uiDrawString ( mainMenuItems [ 3 ] , 0 , breaks * 8 , 115 , 115 , 255 ) ;
break ;
case stateViewGameCardFsBrowser :
menu = ( const char * * ) filenames ;
menuItemsCount = filenamesCount ;
uiDrawString ( ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? viewGameCardFsType1MenuItems [ selectedOption ] : viewGameCardFsType2MenuItems [ selectedOption ] ) , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Current directory: %s " , currentDirectory ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 255 , 255 , 255 ) ;
break ;
default :
break ;
}
if ( menu & & menuItemsCount )
{
if ( uiState ! = stateMainMenu ) breaks + = 2 ;
int scrollAmount = 0 ;
if ( ( keysDown & KEY_DOWN ) | | ( keysDown & KEY_RSTICK_DOWN ) | | ( keysDown & KEY_LSTICK_DOWN ) ) scrollAmount = 1 ;
if ( ( keysDown & KEY_UP ) | | ( keysDown & KEY_RSTICK_UP ) | | ( keysDown & KEY_LSTICK_UP ) ) scrollAmount = - 1 ;
2018-06-24 01:48:34 +01:00
if ( uiState ! = stateXciDumpMenu & & ( ( keysDown & KEY_LEFT ) | | ( keysDown & KEY_RSTICK_LEFT ) | | ( keysDown & KEY_LSTICK_LEFT ) ) ) scrollAmount = - 5 ;
if ( uiState ! = stateXciDumpMenu & & ( ( keysDown & KEY_RIGHT ) | | ( keysDown & KEY_RSTICK_RIGHT ) | | ( keysDown & KEY_LSTICK_RIGHT ) ) ) scrollAmount = 5 ;
2018-06-21 07:42:46 +01:00
if ( scrollAmount > 0 )
{
for ( i = 0 ; i < scrollAmount ; i + + )
{
if ( cursor < menuItemsCount - 1 )
{
cursor + + ;
if ( ( cursor - scroll ) > = maxListElements ) scroll + + ;
}
}
} else
if ( scrollAmount < 0 )
{
for ( i = 0 ; i < - scrollAmount ; i + + )
{
if ( cursor > 0 )
{
cursor - - ;
if ( ( cursor - scroll ) < 0 ) scroll - - ;
}
}
}
i = 0 ;
for ( int j = scroll ; j < menuItemsCount ; j + + )
{
u8 color = 255 ;
if ( i + scroll = = cursor )
{
uiFill ( 0 , ( breaks * 8 ) + ( i * 13 ) , currentFBWidth / 2 , 13 , 33 , 34 , 39 ) ;
2018-06-24 01:48:34 +01:00
uiDrawString ( menu [ j ] , 0 , ( breaks * 8 ) + ( i * 13 ) + 2 , 0 , 255 , 197 ) ;
2018-06-21 07:42:46 +01:00
} else {
uiDrawString ( menu [ j ] , 0 , ( breaks * 8 ) + ( i * 13 ) + 2 , color , color , color ) ;
}
2018-06-24 01:48:34 +01:00
// Print XCI dump menu settings values
2018-06-26 02:11:18 +01:00
if ( uiState = = stateXciDumpMenu & & i > 0 )
2018-06-24 01:48:34 +01:00
{
switch ( i )
{
case 1 : // Split output dump (FAT32 support)
if ( isFat32 )
{
uiDrawString ( " Yes " , strlen ( menu [ i ] ) * 8 , ( breaks * 8 ) + ( i * 13 ) + 2 , 0 , 255 , 0 ) ;
} else {
uiDrawString ( " No " , strlen ( menu [ i ] ) * 8 , ( breaks * 8 ) + ( i * 13 ) + 2 , 255 , 0 , 0 ) ;
}
break ;
case 2 : // Dump certificate
if ( dumpCert )
{
uiDrawString ( " Yes " , strlen ( menu [ i ] ) * 8 , ( breaks * 8 ) + ( i * 13 ) + 2 , 0 , 255 , 0 ) ;
} else {
uiDrawString ( " No " , strlen ( menu [ i ] ) * 8 , ( breaks * 8 ) + ( i * 13 ) + 2 , 255 , 0 , 0 ) ;
}
break ;
2018-06-26 02:11:18 +01:00
case 3 : // Trim output dump
if ( trimDump )
{
uiDrawString ( " Yes " , strlen ( menu [ i ] ) * 8 , ( breaks * 8 ) + ( i * 13 ) + 2 , 0 , 255 , 0 ) ;
} else {
uiDrawString ( " No " , strlen ( menu [ i ] ) * 8 , ( breaks * 8 ) + ( i * 13 ) + 2 , 255 , 0 , 0 ) ;
}
break ;
case 4 : // CRC32 checksum calculation + dump verification
if ( calcCrc )
2018-06-24 01:48:34 +01:00
{
uiDrawString ( " Yes " , strlen ( menu [ i ] ) * 8 , ( breaks * 8 ) + ( i * 13 ) + 2 , 0 , 255 , 0 ) ;
} else {
uiDrawString ( " No " , strlen ( menu [ i ] ) * 8 , ( breaks * 8 ) + ( i * 13 ) + 2 , 255 , 0 , 0 ) ;
}
break ;
default :
break ;
}
}
2018-06-21 07:42:46 +01:00
i + + ;
if ( i > = maxListElements ) break ;
}
2018-06-24 01:48:34 +01:00
if ( uiState = = stateXciDumpMenu )
2018-06-21 07:42:46 +01:00
{
2018-06-24 01:48:34 +01:00
// Select
if ( ( keysDown & KEY_A ) & & cursor = = 0 )
{
selectedOption = ( u32 ) cursor ;
res = resultDumpXci ;
}
// Back
if ( keysDown & KEY_B ) res = resultShowMainMenu ;
// Change option to false
if ( ( keysDown & KEY_LEFT ) | | ( keysDown & KEY_RSTICK_LEFT ) | | ( keysDown & KEY_LSTICK_LEFT ) )
2018-06-21 07:42:46 +01:00
{
switch ( cursor )
{
2018-06-24 01:48:34 +01:00
case 1 : // Split output dump (FAT32 support)
isFat32 = false ;
2018-06-21 07:42:46 +01:00
break ;
2018-06-24 01:48:34 +01:00
case 2 : // Dump certificate
dumpCert = false ;
2018-06-21 07:42:46 +01:00
break ;
2018-06-26 02:11:18 +01:00
case 3 : // Trim output dump
trimDump = false ;
break ;
case 4 : // CRC32 checksum calculation + dump verification
calcCrc = false ;
2018-06-21 07:42:46 +01:00
break ;
2018-06-24 01:48:34 +01:00
default :
break ;
}
}
// Change option to true
if ( ( keysDown & KEY_RIGHT ) | | ( keysDown & KEY_RSTICK_RIGHT ) | | ( keysDown & KEY_LSTICK_RIGHT ) )
{
switch ( cursor )
{
case 1 : // Split output dump (FAT32 support)
isFat32 = true ;
2018-06-21 07:42:46 +01:00
break ;
2018-06-24 01:48:34 +01:00
case 2 : // Dump certificate
dumpCert = true ;
break ;
2018-06-26 02:11:18 +01:00
case 3 : // Trim output dump
trimDump = true ;
break ;
case 4 : // CRC32 checksum calculation + dump verification
calcCrc = true ;
2018-06-21 07:42:46 +01:00
break ;
default :
break ;
}
2018-06-24 01:48:34 +01:00
}
} else {
// Select
if ( keysDown & KEY_A )
2018-06-21 07:42:46 +01:00
{
2018-06-24 01:48:34 +01:00
if ( uiState = = stateMainMenu )
2018-06-21 07:42:46 +01:00
{
2018-06-24 01:48:34 +01:00
switch ( cursor )
2018-06-21 07:42:46 +01:00
{
2018-06-24 01:48:34 +01:00
case 0 :
res = resultShowXciDumpMenu ;
break ;
case 1 :
res = resultShowRawPartitionDumpMenu ;
break ;
case 2 :
res = resultShowPartitionDataDumpMenu ;
break ;
case 3 :
res = resultShowViewGameCardFsMenu ;
break ;
case 4 :
res = resultDumpGameCardCertificate ;
break ;
2018-06-30 07:37:42 +01:00
case 5 :
res = resultUpdateNSWDBXml ;
break ;
case 6 :
//res = resultUpdateApplication;
break ;
2018-06-24 01:48:34 +01:00
default :
2018-06-21 07:42:46 +01:00
break ;
}
2018-06-24 01:48:34 +01:00
} else
if ( uiState = = stateRawPartitionDumpMenu )
2018-06-21 07:42:46 +01:00
{
2018-06-24 01:48:34 +01:00
selectedOption = ( u32 ) cursor ;
res = resultDumpRawPartition ;
} else
if ( uiState = = statePartitionDataDumpMenu )
2018-06-21 07:42:46 +01:00
{
2018-06-24 01:48:34 +01:00
selectedOption = ( u32 ) cursor ;
res = resultDumpPartitionData ;
} else
if ( uiState = = stateViewGameCardFsMenu )
{
selectedOption = ( u32 ) cursor ;
res = resultShowViewGameCardFsGetList ;
} else
if ( uiState = = stateViewGameCardFsBrowser )
{
char * selectedPath = ( char * ) malloc ( strlen ( currentDirectory ) + 1 + strlen ( filenames [ cursor ] ) + 2 ) ;
memset ( selectedPath , 0 , strlen ( currentDirectory ) + 1 + strlen ( filenames [ cursor ] ) + 2 ) ;
2018-06-21 07:42:46 +01:00
2018-06-24 01:48:34 +01:00
if ( strlen ( filenames [ cursor ] ) = = 2 & & ! strcmp ( filenames [ cursor ] , " .. " ) )
2018-06-21 07:42:46 +01:00
{
2018-06-24 01:48:34 +01:00
for ( i = ( strlen ( currentDirectory ) - 1 ) ; i > = 0 ; i - - )
2018-06-21 07:42:46 +01:00
{
2018-06-24 01:48:34 +01:00
if ( currentDirectory [ i ] = = ' / ' )
{
strncpy ( selectedPath , currentDirectory , i ) ;
selectedPath [ i ] = ' \0 ' ;
break ;
}
2018-06-21 07:42:46 +01:00
}
2018-06-24 01:48:34 +01:00
} else {
snprintf ( selectedPath , strlen ( currentDirectory ) + 1 + strlen ( filenames [ cursor ] ) + 2 , " %s/%s " , currentDirectory , filenames [ cursor ] ) ;
2018-06-21 07:42:46 +01:00
}
2018-06-24 01:48:34 +01:00
if ( isDirectory ( selectedPath ) )
{
enterDirectory ( selectedPath ) ;
} else {
snprintf ( fileCopyPath , sizeof ( fileCopyPath ) / sizeof ( fileCopyPath [ 0 ] ) , " %s " , selectedPath ) ;
res = resultViewGameCardFsBrowserCopyFile ;
}
2018-06-21 07:42:46 +01:00
free ( selectedPath ) ;
}
}
2018-06-24 01:48:34 +01:00
// Back
if ( keysDown & KEY_B )
{
if ( uiState = = stateRawPartitionDumpMenu | | uiState = = statePartitionDataDumpMenu | | uiState = = stateViewGameCardFsMenu )
{
res = resultShowMainMenu ;
} else
if ( uiState = = stateViewGameCardFsBrowser )
{
if ( ! strcmp ( currentDirectory , " view:/ " ) & & strlen ( currentDirectory ) = = 6 )
{
fsdevUnmountDevice ( " view " ) ;
2018-07-21 06:56:00 +01:00
fsFsClose ( & fs ) ;
2018-06-24 01:48:34 +01:00
res = resultShowViewGameCardFsMenu ;
} else {
char * selectedPath = ( char * ) malloc ( strlen ( currentDirectory ) + 1 ) ;
memset ( selectedPath , 0 , strlen ( currentDirectory ) + 1 ) ;
for ( i = ( strlen ( currentDirectory ) - 1 ) ; i > = 0 ; i - - )
{
if ( currentDirectory [ i ] = = ' / ' )
{
strncpy ( selectedPath , currentDirectory , i ) ;
selectedPath [ i ] = ' \0 ' ;
break ;
}
}
if ( isDirectory ( selectedPath ) ) enterDirectory ( selectedPath ) ;
free ( selectedPath ) ;
}
}
}
2018-06-21 07:42:46 +01:00
}
}
}
} else
if ( uiState = = stateDumpXci )
{
2018-06-24 01:48:34 +01:00
uiDrawString ( mainMenuItems [ 0 ] , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + + ;
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " %s%s " , xciDumpMenuItems [ 1 ] , ( isFat32 ? " Yes " : " No " ) ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + + ;
2018-06-21 07:42:46 +01:00
2018-06-24 01:48:34 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " %s%s " , xciDumpMenuItems [ 2 ] , ( dumpCert ? " Yes " : " No " ) ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + + ;
2018-06-26 02:11:18 +01:00
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " %s%s " , xciDumpMenuItems [ 3 ] , ( trimDump ? " Yes " : " No " ) ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + + ;
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " %s%s " , xciDumpMenuItems [ 4 ] , ( calcCrc ? " Yes " : " No " ) ) ;
2018-06-24 01:48:34 +01:00
uiDrawString ( titlebuf , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
2018-06-21 07:42:46 +01:00
2018-06-26 02:11:18 +01:00
dumpGameCartridge ( & fsOperatorInstance , isFat32 , dumpCert , trimDump , calcCrc ) ;
2018-06-21 07:42:46 +01:00
waitForButtonPress ( ) ;
uiUpdateFreeSpace ( ) ;
res = resultShowXciDumpMenu ;
} else
if ( uiState = = stateDumpRawPartition )
{
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Raw %s " , ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? partitionDumpType1MenuItems [ selectedOption ] : partitionDumpType2MenuItems [ selectedOption ] ) ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
dumpRawPartition ( & fsOperatorInstance , selectedOption , true ) ;
waitForButtonPress ( ) ;
uiUpdateFreeSpace ( ) ;
res = resultShowRawPartitionDumpMenu ;
} else
if ( uiState = = stateDumpPartitionData )
{
snprintf ( titlebuf , sizeof ( titlebuf ) / sizeof ( titlebuf [ 0 ] ) , " Data %s " , ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? partitionDumpType1MenuItems [ selectedOption ] : partitionDumpType2MenuItems [ selectedOption ] ) ) ;
uiDrawString ( titlebuf , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
dumpPartitionData ( & fsOperatorInstance , selectedOption ) ;
waitForButtonPress ( ) ;
uiUpdateFreeSpace ( ) ;
res = resultShowPartitionDataDumpMenu ;
} else
if ( uiState = = stateViewGameCardFsGetList )
{
uiDrawString ( ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? viewGameCardFsType1MenuItems [ selectedOption ] : viewGameCardFsType1MenuItems [ selectedOption ] ) , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
2018-07-21 06:56:00 +01:00
if ( mountViewPartition ( & fsOperatorInstance , & fs , selectedOption ) )
2018-06-21 07:42:46 +01:00
{
enterDirectory ( " view:/ " ) ;
res = resultShowViewGameCardFsBrowser ;
} else {
breaks + = 2 ;
waitForButtonPress ( ) ;
res = resultShowViewGameCardFsMenu ;
}
} else
if ( uiState = = stateViewGameCardFsBrowserCopyFile )
{
uiDrawString ( " Manual File Dump " , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
FILE * inFile = fopen ( fileCopyPath , " rb " ) ;
if ( inFile )
{
fseek ( inFile , 0L , SEEK_END ) ;
u64 input_filesize = ftell ( inFile ) ;
fclose ( inFile ) ;
if ( input_filesize < = freeSpace )
{
char destCopyPath [ NAME_BUF_LEN ] = { ' \0 ' } ;
for ( i = ( strlen ( fileCopyPath ) - 1 ) ; i > = 0 ; i - - )
{
if ( fileCopyPath [ i ] = = ' / ' )
{
2018-06-30 07:37:42 +01:00
snprintf ( destCopyPath , sizeof ( destCopyPath ) / sizeof ( destCopyPath [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s) " , fixedGameCardName , gameCardVersion , gameCardTitleID , selectedOption , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , selectedOption ) ) ;
mkdir ( destCopyPath , 0744 ) ;
snprintf ( destCopyPath , sizeof ( destCopyPath ) / sizeof ( destCopyPath [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s)/%.*s " , fixedGameCardName , gameCardVersion , gameCardTitleID , selectedOption , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , selectedOption ) , ( int ) ( strlen ( fileCopyPath ) - i ) , fileCopyPath + i + 1 ) ;
2018-06-21 07:42:46 +01:00
break ;
}
}
uiDrawString ( " Hold B to cancel " , 0 , breaks * 8 , 255 , 255 , 255 ) ;
breaks + = 2 ;
2018-06-26 02:11:18 +01:00
copyFile ( fileCopyPath , destCopyPath , true , true ) ;
2018-06-21 07:42:46 +01:00
} else {
uiDrawString ( " Error: not enough free space available in the SD card. " , 0 , breaks * 8 , 255 , 0 , 0 ) ;
}
} else {
uiDrawString ( " Error: unable to get input file size. " , 0 , breaks * 8 , 255 , 0 , 0 ) ;
}
breaks + = 2 ;
waitForButtonPress ( ) ;
uiUpdateFreeSpace ( ) ;
res = resultShowViewGameCardFsBrowser ;
} else
if ( uiState = = stateDumpGameCardCertificate )
{
uiDrawString ( mainMenuItems [ 4 ] , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
dumpGameCertificate ( & fsOperatorInstance ) ;
waitForButtonPress ( ) ;
2018-06-30 07:37:42 +01:00
uiUpdateFreeSpace ( ) ;
res = resultShowMainMenu ;
} else
if ( uiState = = stateUpdateNSWDBXml )
{
uiDrawString ( mainMenuItems [ 5 ] , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
updateNSWDBXml ( ) ;
waitForButtonPress ( ) ;
uiUpdateFreeSpace ( ) ;
res = resultShowMainMenu ;
} else
if ( uiState = = stateUpdateApplication )
{
uiDrawString ( mainMenuItems [ 6 ] , 0 , breaks * 8 , 115 , 115 , 255 ) ;
breaks + = 2 ;
updateApplication ( ) ;
waitForButtonPress ( ) ;
2018-06-21 07:42:46 +01:00
uiUpdateFreeSpace ( ) ;
res = resultShowMainMenu ;
}
2018-06-22 01:46:17 +01:00
uiUpdateStatusMsg ( ) ;
2018-06-21 07:42:46 +01:00
return res ;
}