2016-02-25 16:57:01 +01:00
# include "godmode.h"
2016-03-28 17:32:29 +02:00
# include "ui.h"
2016-02-25 16:57:01 +01:00
# include "hid.h"
2016-12-10 15:32:03 +01:00
# include "fsinit.h"
# include "fsdrive.h"
# include "fsutil.h"
# include "fsperm.h"
2016-12-10 15:40:36 +01:00
# include "gameutil.h"
2017-04-17 23:45:49 +02:00
# include "keydbutil.h"
2017-01-02 17:37:08 +01:00
# include "nandutil.h"
2016-12-11 16:28:51 +01:00
# include "filetype.h"
2017-02-26 13:35:37 +01:00
# include "unittype.h"
2016-03-11 01:29:14 +01:00
# include "nand.h"
2016-03-21 18:53:09 +01:00
# include "virtual.h"
2017-01-17 23:24:25 +01:00
# include "vcart.h"
2017-04-08 14:17:58 +02:00
# include "game.h"
2017-02-08 21:45:52 +01:00
# include "nandcmac.h"
2017-03-02 22:40:58 +01:00
# include "ctrtransfer.h"
2017-01-25 14:46:29 +01:00
# include "ncchinfo.h"
2016-04-04 22:45:49 +02:00
# include "image.h"
2017-01-25 16:13:50 -03:00
# include "chainload.h"
2017-03-09 14:35:36 +01:00
# include "qlzcomp.h"
# include "timer.h"
2017-03-10 13:47:16 +01:00
# include QLZ_SPLASH_H
2016-02-25 16:57:01 +01:00
2016-04-06 16:45:43 +02:00
# define N_PANES 2
2016-03-15 22:19:30 +01:00
2017-02-15 16:34:25 +01:00
# define COLOR_TOP_BAR (PERM_RED ? COLOR_RED : PERM_ORANGE ? COLOR_ORANGE : PERM_BLUE ? COLOR_BRIGHTBLUE : \
PERM_YELLOW ? COLOR_BRIGHTYELLOW : PERM_GREEN ? COLOR_GREEN : COLOR_WHITE )
2016-02-28 13:11:07 +01:00
# define COLOR_SIDE_BAR COLOR_DARKGREY
2016-02-27 19:58:41 +01:00
# define COLOR_MARKED COLOR_TINTEDYELLOW
# define COLOR_FILE COLOR_TINTEDGREEN
# define COLOR_DIR COLOR_TINTEDBLUE
# define COLOR_ROOT COLOR_GREY
2016-03-21 16:58:35 +01:00
# define COLOR_ENTRY(e) (((e)->marked) ? COLOR_MARKED : ((e)->type == T_DIR) ? COLOR_DIR : ((e)->type == T_FILE) ? COLOR_FILE : ((e)->type == T_ROOT) ? COLOR_ROOT : COLOR_GREY)
2016-02-27 19:58:41 +01:00
2016-04-08 21:03:40 +02:00
# define COLOR_HVOFFS RGB(0x40, 0x60, 0x50)
# define COLOR_HVOFFSI COLOR_DARKESTGREY
# define COLOR_HVASCII RGB(0x40, 0x80, 0x50)
# define COLOR_HVHEX(i) ((i % 2) ? RGB(0x30, 0x90, 0x30) : RGB(0x30, 0x80, 0x30))
2016-04-06 16:06:54 +02:00
typedef struct {
char path [ 256 ] ;
u32 cursor ;
u32 scroll ;
} PaneData ;
2016-03-16 01:26:17 +01:00
2016-04-06 16:06:54 +02:00
void DrawUserInterface ( const char * curr_path , DirEntry * curr_entry , DirStruct * clipboard , u32 curr_pane ) {
2016-03-02 19:36:20 +01:00
const u32 n_cb_show = 8 ;
2016-07-18 03:15:04 +02:00
const u32 bartxt_start = ( FONT_HEIGHT_EXT = = 10 ) ? 1 : 2 ;
const u32 bartxt_x = 2 ;
2017-04-26 01:53:13 +02:00
const u32 bartxt_rx = SCREEN_WIDTH_TOP - ( 19 * FONT_WIDTH_EXT ) - bartxt_x ;
const u32 info_start = ( MAIN_SCREEN = = TOP_SCREEN ) ? 18 : 2 ; // leave space for the topbar when required
const u32 instr_x = ( SCREEN_WIDTH_MAIN - ( 34 * FONT_WIDTH_EXT ) ) / 2 ;
const u32 len_path = SCREEN_WIDTH_TOP - 120 ;
const u32 len_info = ( SCREEN_WIDTH_MAIN - ( ( SCREEN_WIDTH_MAIN > = 400 ) ? 80 : 20 ) ) / 2 ;
2016-04-02 19:07:46 +02:00
char tempstr [ 64 ] ;
2016-03-01 02:00:48 +01:00
static u32 state_prev = 0xFFFFFFFF ;
u32 state_curr =
( ( * curr_path ) ? ( 1 < < 0 ) : 0 ) |
2016-04-05 20:41:40 +02:00
( ( clipboard - > n_entries ) ? ( 1 < < 1 ) : 0 ) |
2016-12-19 14:32:22 +01:00
( ( CheckSDMountState ( ) ) ? ( 1 < < 2 ) : 0 ) |
( ( GetMountState ( ) ) ? ( 1 < < 3 ) : 0 ) |
2017-02-16 02:44:49 +01:00
( ( GetWritePermissions ( ) > PERM_BASE ) ? ( 1 < < 4 ) : 0 ) |
( curr_pane < < 5 ) ;
2016-03-01 02:00:48 +01:00
if ( state_prev ! = state_curr ) {
ClearScreenF ( true , false , COLOR_STD_BG ) ;
state_prev = state_curr ;
}
2016-02-27 19:58:41 +01:00
// top bar - current path & free/total storage
2017-04-26 01:53:13 +02:00
DrawRectangle ( TOP_SCREEN , 0 , 0 , SCREEN_WIDTH_TOP , 12 , COLOR_TOP_BAR ) ;
2016-02-27 19:58:41 +01:00
if ( strncmp ( curr_path , " " , 256 ) ! = 0 ) {
2016-04-02 19:07:46 +02:00
char bytestr0 [ 32 ] ;
char bytestr1 [ 32 ] ;
2017-04-24 21:48:21 +02:00
TruncateString ( tempstr , curr_path , len_path / FONT_WIDTH_EXT , 8 ) ;
2017-04-26 01:53:13 +02:00
DrawStringF ( TOP_SCREEN , bartxt_x , bartxt_start , COLOR_STD_BG , COLOR_TOP_BAR , tempstr ) ;
DrawStringF ( TOP_SCREEN , bartxt_rx , bartxt_start , COLOR_STD_BG , COLOR_TOP_BAR , " %19.19s " , " LOADING... " ) ;
2016-02-27 19:58:41 +01:00
FormatBytes ( bytestr0 , GetFreeSpace ( curr_path ) ) ;
FormatBytes ( bytestr1 , GetTotalSpace ( curr_path ) ) ;
snprintf ( tempstr , 64 , " %s/%s " , bytestr0 , bytestr1 ) ;
2017-04-26 01:53:13 +02:00
DrawStringF ( TOP_SCREEN , bartxt_rx , bartxt_start , COLOR_STD_BG , COLOR_TOP_BAR , " %19.19s " , tempstr ) ;
2016-02-27 19:58:41 +01:00
} else {
2017-04-26 01:53:13 +02:00
DrawStringF ( TOP_SCREEN , bartxt_x , bartxt_start , COLOR_STD_BG , COLOR_TOP_BAR , " [root] " ) ;
DrawStringF ( TOP_SCREEN , bartxt_rx , bartxt_start , COLOR_STD_BG , COLOR_TOP_BAR , " %19.19s " , FLAVOR ) ;
2016-02-27 19:58:41 +01:00
}
// left top - current file info
2016-04-06 16:06:54 +02:00
if ( curr_pane ) snprintf ( tempstr , 63 , " PANE #%lu " , curr_pane ) ;
else snprintf ( tempstr , 63 , " CURRENT " ) ;
2017-04-24 21:48:21 +02:00
DrawStringF ( MAIN_SCREEN , 2 , info_start , COLOR_STD_FONT , COLOR_STD_BG , " [%s] " , tempstr ) ;
2016-04-06 16:06:54 +02:00
// file / entry name
2017-04-24 21:48:21 +02:00
ResizeString ( tempstr , curr_entry - > name , len_info / FONT_WIDTH_EXT , 8 , false ) ;
2016-03-02 19:36:20 +01:00
u32 color_current = COLOR_ENTRY ( curr_entry ) ;
2017-04-24 21:48:21 +02:00
DrawStringF ( MAIN_SCREEN , 4 , info_start + 12 , color_current , COLOR_STD_BG , " %s " , tempstr ) ;
2016-04-06 16:06:54 +02:00
// size (in Byte) or type desc
2016-03-21 16:58:35 +01:00
if ( curr_entry - > type = = T_DIR ) {
2017-04-24 21:48:21 +02:00
ResizeString ( tempstr , " (dir) " , len_info / FONT_WIDTH_EXT , 8 , false ) ;
2016-03-21 16:58:35 +01:00
} else if ( curr_entry - > type = = T_DOTDOT ) {
2016-03-02 19:36:20 +01:00
snprintf ( tempstr , 21 , " %20s " , " " ) ;
2016-10-29 16:02:07 +02:00
} else if ( curr_entry - > type = = T_ROOT ) {
int drvtype = DriveType ( curr_entry - > path ) ;
char drvstr [ 32 ] ;
snprintf ( drvstr , 31 , " (%s%s) " ,
2016-12-02 15:42:05 +01:00
( ( drvtype & DRV_SDCARD ) ? " SD " : ( drvtype & DRV_RAMDRIVE ) ? " RAMdrive " : ( drvtype & DRV_GAME ) ? " Game " :
2016-10-29 16:02:07 +02:00
( drvtype & DRV_SYSNAND ) ? " SysNAND " : ( drvtype & DRV_EMUNAND ) ? " EmuNAND " : ( drvtype & DRV_IMAGE ) ? " Image " :
2016-12-02 15:42:05 +01:00
( drvtype & DRV_XORPAD ) ? " XORpad " : ( drvtype & DRV_MEMORY ) ? " Memory " : ( drvtype & DRV_ALIAS ) ? " Alias " :
2017-01-13 14:20:42 +01:00
( drvtype & DRV_CART ) ? " Gamecart " : ( drvtype & DRV_SEARCH ) ? " Search " : " " ) ,
2016-10-29 16:02:07 +02:00
( ( drvtype & DRV_FAT ) ? " FAT " : ( drvtype & DRV_VIRTUAL ) ? " Virtual " : " " ) ) ;
2017-04-24 21:48:21 +02:00
ResizeString ( tempstr , drvstr , len_info / FONT_WIDTH_EXT , 8 , false ) ;
2017-02-23 14:33:09 +01:00
} else {
2016-04-05 21:13:23 +02:00
char numstr [ 32 ] ;
2016-04-02 19:07:46 +02:00
char bytestr [ 32 ] ;
2016-04-05 21:13:23 +02:00
FormatNumber ( numstr , curr_entry - > size ) ;
2016-04-06 12:52:23 +02:00
snprintf ( bytestr , 31 , " %s Byte " , numstr ) ;
2017-04-24 21:48:21 +02:00
ResizeString ( tempstr , bytestr , len_info / FONT_WIDTH_EXT , 8 , false ) ;
2016-02-27 19:58:41 +01:00
}
2017-04-24 21:48:21 +02:00
DrawStringF ( MAIN_SCREEN , 4 , info_start + 12 + 10 , color_current , COLOR_STD_BG , tempstr ) ;
2016-07-22 18:32:44 +02:00
// path of file (if in search results)
2016-12-10 15:32:03 +01:00
if ( ( DriveType ( curr_path ) & DRV_SEARCH ) & & strrchr ( curr_entry - > path , ' / ' ) ) {
2016-07-22 18:32:44 +02:00
char dirstr [ 256 ] ;
strncpy ( dirstr , curr_entry - > path , 256 ) ;
* ( strrchr ( dirstr , ' / ' ) + 1 ) = ' \0 ' ;
2017-04-24 21:48:21 +02:00
ResizeString ( tempstr , dirstr , len_info / FONT_WIDTH_EXT , 8 , false ) ;
DrawStringF ( MAIN_SCREEN , 4 , info_start + 12 + 10 + 10 , color_current , COLOR_STD_BG , tempstr ) ;
2016-07-22 18:32:44 +02:00
} else {
2017-04-24 21:48:21 +02:00
ResizeString ( tempstr , " " , len_info / FONT_WIDTH_EXT , 8 , false ) ;
DrawStringF ( MAIN_SCREEN , 4 , info_start + 12 + 10 + 10 , color_current , COLOR_STD_BG , tempstr ) ;
2016-07-22 18:32:44 +02:00
}
2016-02-29 22:27:04 +01:00
// right top - clipboard
2017-04-24 21:48:21 +02:00
DrawStringF ( MAIN_SCREEN , SCREEN_WIDTH_MAIN - len_info , info_start , COLOR_STD_FONT , COLOR_STD_BG , " %*s " ,
len_info / FONT_WIDTH_EXT , ( clipboard - > n_entries ) ? " [CLIPBOARD] " : " " ) ;
2016-03-02 19:36:20 +01:00
for ( u32 c = 0 ; c < n_cb_show ; c + + ) {
u32 color_cb = COLOR_ENTRY ( & ( clipboard - > entry [ c ] ) ) ;
2017-04-24 21:48:21 +02:00
ResizeString ( tempstr , ( clipboard - > n_entries > c ) ? clipboard - > entry [ c ] . name : " " , len_info / FONT_WIDTH_EXT , 8 , true ) ;
DrawStringF ( MAIN_SCREEN , SCREEN_WIDTH_MAIN - len_info - 4 , info_start + 12 + ( c * 10 ) , color_cb , COLOR_STD_BG , tempstr ) ;
2016-02-29 22:27:04 +01:00
}
* tempstr = ' \0 ' ;
2016-03-02 19:36:20 +01:00
if ( clipboard - > n_entries > n_cb_show ) snprintf ( tempstr , 60 , " + %lu more " , clipboard - > n_entries - n_cb_show ) ;
2017-04-24 21:48:21 +02:00
DrawStringF ( MAIN_SCREEN , SCREEN_WIDTH_MAIN - len_info - 4 , info_start + 12 + ( n_cb_show * 10 ) , COLOR_DARKGREY , COLOR_STD_BG ,
" %*s " , len_info / FONT_WIDTH_EXT , tempstr ) ;
2016-02-27 19:58:41 +01:00
// bottom: inctruction block
2016-07-27 00:19:12 +02:00
char instr [ 512 ] ;
2017-02-28 15:57:14 +01:00
snprintf ( instr , 512 , " %s \n %s%s%s%s%s%s%s%s " ,
FLAVOR " Explorer v " VERSION , // generic start part
2016-03-02 14:01:20 +01:00
( * curr_path ) ? ( ( clipboard - > n_entries = = 0 ) ? " L - MARK files (use with \x18 \x19 \x1A \x1B ) \n X - DELETE / [+R] RENAME file(s) \n Y - COPY file(s) / [+R] CREATE dir \n " :
" L - MARK files (use with \x18 \x19 \x1A \x1B ) \n X - DELETE / [+R] RENAME file(s) \n Y - PASTE file(s) / [+R] CREATE dir \n " ) :
2017-02-16 02:44:49 +01:00
( ( GetWritePermissions ( ) > PERM_BASE ) ? " R+Y - Relock write permissions \n " : " " ) ,
2016-12-19 14:32:22 +01:00
( * curr_path ) ? " " : ( GetMountState ( ) ) ? " R+X - Unmount image \n " : " " ,
2017-02-16 02:44:49 +01:00
( * curr_path ) ? " " : ( CheckSDMountState ( ) ) ? " R+B - Unmount SD card \n " : " R+B - Remount SD card \n " ,
2017-03-20 02:57:13 +01:00
( * curr_path ) ? " R+A - Directory options \n " : " R+A - Drive options \n " ,
2016-03-02 14:01:20 +01:00
" R+L - Make a Screenshot \n " ,
2016-04-06 16:06:54 +02:00
" R+ \x1B \x1A - Switch to prev/next pane \n " ,
2016-03-02 14:01:20 +01:00
( clipboard - > n_entries ) ? " SELECT - Clear Clipboard \n " : " SELECT - Restore Clipboard \n " , // only if clipboard is full
2017-02-22 23:44:57 +01:00
" START - Reboot / [+R] Poweroff \n HOME button for HOME menu " ) ; // generic end part
2017-04-24 21:48:21 +02:00
DrawStringF ( MAIN_SCREEN , instr_x , SCREEN_HEIGHT - 4 - GetDrawStringHeight ( instr ) , COLOR_STD_FONT , COLOR_STD_BG , instr ) ;
2016-02-27 19:58:41 +01:00
}
2016-03-15 22:19:30 +01:00
void DrawDirContents ( DirStruct * contents , u32 cursor , u32 * scroll ) {
2017-04-24 21:48:21 +02:00
const int str_width = ( SCREEN_WIDTH_ALT - 3 ) / FONT_WIDTH_EXT ;
2016-03-02 17:22:44 +01:00
const u32 bar_height_min = 32 ;
const u32 bar_width = 2 ;
2016-02-26 17:03:25 +01:00
const u32 stp_y = 12 ;
2017-04-26 01:53:13 +02:00
const u32 start_y = ( MAIN_SCREEN = = TOP_SCREEN ) ? 2 : 2 + stp_y ;
2016-02-26 17:03:25 +01:00
const u32 pos_x = 0 ;
2016-02-27 19:58:41 +01:00
const u32 lines = ( SCREEN_HEIGHT - start_y + stp_y - 1 ) / stp_y ;
u32 pos_y = start_y ;
2016-03-15 22:19:30 +01:00
if ( * scroll > cursor ) * scroll = cursor ;
else if ( * scroll + lines < = cursor ) * scroll = cursor - lines + 1 ;
2016-06-10 14:23:37 +02:00
if ( * scroll + lines > contents - > n_entries )
* scroll = ( contents - > n_entries > lines ) ? contents - > n_entries - lines : 0 ;
2016-02-25 16:57:01 +01:00
for ( u32 i = 0 ; pos_y < SCREEN_HEIGHT ; i + + ) {
char tempstr [ str_width + 1 ] ;
2016-03-15 22:19:30 +01:00
u32 offset_i = * scroll + i ;
2016-03-02 19:36:20 +01:00
u32 color_font = COLOR_WHITE ;
2016-02-25 16:57:01 +01:00
if ( offset_i < contents - > n_entries ) {
2016-04-05 21:30:05 +02:00
DirEntry * curr_entry = & ( contents - > entry [ offset_i ] ) ;
char namestr [ str_width - 10 + 1 ] ;
char bytestr [ 10 + 1 ] ;
color_font = ( cursor ! = offset_i ) ? COLOR_ENTRY ( curr_entry ) : COLOR_STD_FONT ;
FormatBytes ( bytestr , curr_entry - > size ) ;
ResizeString ( namestr , curr_entry - > name , str_width - 10 , str_width - 20 , false ) ;
snprintf ( tempstr , str_width + 1 , " %s%10.10s " , namestr ,
2016-04-06 12:52:23 +02:00
( curr_entry - > type = = T_DIR ) ? " (dir) " : ( curr_entry - > type = = T_DOTDOT ) ? " (..) " : bytestr ) ;
2016-03-02 19:36:20 +01:00
} else snprintf ( tempstr , str_width + 1 , " %-*.*s " , str_width , str_width , " " ) ;
2017-04-24 21:48:21 +02:00
DrawStringF ( ALT_SCREEN , pos_x , pos_y , color_font , COLOR_STD_BG , tempstr ) ;
2016-02-25 16:57:01 +01:00
pos_y + = stp_y ;
}
2016-02-28 13:11:07 +01:00
2016-03-02 17:22:44 +01:00
if ( contents - > n_entries > lines ) { // draw position bar at the right
2016-02-28 13:11:07 +01:00
u32 bar_height = ( lines * SCREEN_HEIGHT ) / contents - > n_entries ;
if ( bar_height < bar_height_min ) bar_height = bar_height_min ;
2016-03-15 22:19:30 +01:00
u32 bar_pos = ( ( u64 ) * scroll * ( SCREEN_HEIGHT - bar_height ) ) / ( contents - > n_entries - lines ) ;
2016-02-28 13:11:07 +01:00
2017-04-24 21:48:21 +02:00
DrawRectangle ( ALT_SCREEN , SCREEN_WIDTH_ALT - bar_width , 0 , bar_width , bar_pos , COLOR_STD_BG ) ;
DrawRectangle ( ALT_SCREEN , SCREEN_WIDTH_ALT - bar_width , bar_pos + bar_height , bar_width , SCREEN_WIDTH_ALT - ( bar_pos + bar_height ) , COLOR_STD_BG ) ;
DrawRectangle ( ALT_SCREEN , SCREEN_WIDTH_ALT - bar_width , bar_pos , bar_width , bar_height , COLOR_SIDE_BAR ) ;
} else DrawRectangle ( ALT_SCREEN , SCREEN_WIDTH_ALT - bar_width , 0 , bar_width , SCREEN_HEIGHT , COLOR_STD_BG ) ;
2016-02-25 16:57:01 +01:00
}
2016-07-13 19:59:36 +02:00
u32 SdFormatMenu ( void ) {
2016-07-13 20:58:14 +02:00
const u32 emunand_size_table [ 6 ] = { 0x0 , 0x0 , 0x3AF , 0x4D8 , 0x3FF , 0x7FF } ;
2016-09-07 00:18:49 +02:00
const u32 cluster_size_table [ 5 ] = { 0x0 , 0x0 , 0x4000 , 0x8000 , 0x10000 } ;
const char * option_emunand_size [ 6 ] = { " No EmuNAND " , " O3DS NAND size " , " N3DS NAND size " , " 1GB (legacy size) " , " 2GB (legacy size) " , " User input... " } ;
2017-01-18 18:07:12 +01:00
const char * option_cluster_size [ 4 ] = { " Auto " , " 16KB Clusters " , " 32KB Clusters " , " 64KB Clusters " } ;
char label [ 16 ] = " 0:GM9SD " ;
2016-09-07 00:18:49 +02:00
u32 cluster_size = 0 ;
2016-07-13 19:59:36 +02:00
u64 sdcard_size_mb = 0 ;
u64 emunand_size_mb = ( u64 ) - 1 ;
2016-09-07 00:18:49 +02:00
u32 user_select ;
2016-07-13 19:59:36 +02:00
// check actual SD card size
sdcard_size_mb = GetSDCardSize ( ) / 0x100000 ;
if ( ! sdcard_size_mb ) {
2016-12-08 13:18:25 +01:00
ShowPrompt ( false , " Error: SD card not detected. " ) ;
2016-07-13 19:59:36 +02:00
return 1 ;
}
2016-09-07 00:18:49 +02:00
user_select = ShowSelectPrompt ( 6 , option_emunand_size , " Format SD card (%lluMB)? \n Choose EmuNAND size: " , sdcard_size_mb ) ;
2016-07-13 19:59:36 +02:00
if ( user_select & & ( user_select < 6 ) ) {
emunand_size_mb = emunand_size_table [ user_select ] ;
} else if ( user_select = = 6 ) do {
emunand_size_mb = ShowNumberPrompt ( 0 , " SD card size is %lluMB. \n Enter EmuNAND size (MB) below: " , sdcard_size_mb ) ;
if ( emunand_size_mb = = ( u64 ) - 1 ) break ;
} while ( emunand_size_mb > sdcard_size_mb ) ;
if ( emunand_size_mb = = ( u64 ) - 1 ) return 1 ;
2016-09-07 00:18:49 +02:00
user_select = ShowSelectPrompt ( 4 , option_cluster_size , " Format SD card (%lluMB)? \n Choose cluster size: " , sdcard_size_mb ) ;
if ( ! user_select ) return 1 ;
else cluster_size = cluster_size_table [ user_select ] ;
2017-01-18 18:07:12 +01:00
if ( ! ShowStringPrompt ( label + 2 , 9 , " Format SD card (%lluMB)? \n Enter label: " , sdcard_size_mb ) )
return 1 ;
if ( ! FormatSDCard ( emunand_size_mb , cluster_size , label ) ) {
2016-07-13 19:59:36 +02:00
ShowPrompt ( false , " Format SD: failed! " ) ;
return 1 ;
}
2017-02-26 13:35:37 +01:00
if ( IS_A9LH ) {
2016-07-13 22:22:23 +02:00
InitSDCardFS ( ) ; // on A9LH: copy the payload from mem to SD root
2017-02-28 17:57:39 +01:00
FileSetData ( " 0:/arm9loaderhax_si.bin " , ( u8 * ) 0x23F00000 , SELF_MAX_SIZE , 0 , true ) ;
2016-07-13 22:22:23 +02:00
DeinitSDCardFS ( ) ;
}
2016-09-07 00:19:12 +02:00
VirtualFile nand ;
2016-11-15 23:34:21 +01:00
if ( ! GetVirtualFile ( & nand , " S:/nand_minsize.bin " ) )
2016-09-07 00:19:12 +02:00
return 0 ;
2017-01-16 19:59:04 +01:00
InitSDCardFS ( ) ; // this has to be initialized for EmuNAND to work
2016-09-07 00:19:12 +02:00
if ( ( nand . size / ( 1024 * 1024 ) < = emunand_size_mb ) & & ShowPrompt ( true , " Clone SysNAND to RedNAND now? " ) ) {
2017-02-22 23:27:34 +01:00
u32 flags = OVERRIDE_PERM ;
if ( ! PathCopy ( " E: " , " S:/nand_minsize.bin " , & flags ) )
2016-09-07 00:19:12 +02:00
ShowPrompt ( false , " Cloning SysNAND to EmuNAND: failed! " ) ;
}
2017-01-16 19:59:04 +01:00
DeinitSDCardFS ( ) ;
2016-09-07 00:19:12 +02:00
2016-07-13 19:59:36 +02:00
return 0 ;
}
2016-04-08 21:03:40 +02:00
u32 HexViewer ( const char * path ) {
2016-05-19 21:24:49 +02:00
static const u32 max_data = ( SCREEN_HEIGHT / 8 ) * 16 ;
2016-04-08 21:03:40 +02:00
static u32 mode = 0 ;
2016-12-07 15:12:35 +01:00
u8 * data = TEMP_BUFFER ;
u8 * bottom_cpy = TEMP_BUFFER + 0xC0000 ; // a copy of the bottom screen framebuffer
2016-04-10 15:55:39 +02:00
u32 fsize = FileGetSize ( path ) ;
2016-04-08 21:03:40 +02:00
2016-05-18 00:05:24 +02:00
bool dual_screen ;
2016-04-08 21:03:40 +02:00
int x_off , x_hex , x_ascii ;
u32 vpad , hlpad , hrpad ;
u32 rows , cols ;
u32 total_shown ;
u32 total_data ;
u32 last_mode = 0xFF ;
2016-05-18 23:46:22 +02:00
u32 last_offset = ( u32 ) - 1 ;
2016-04-08 21:03:40 +02:00
u32 offset = 0 ;
2016-07-12 18:33:52 +02:00
u32 found_offset = ( u32 ) - 1 ;
u32 found_size = 0 ;
2016-05-21 22:04:03 +02:00
static const u32 edit_bsize = 0x4000 ; // should be multiple of 0x200 * 2
2016-05-19 21:24:49 +02:00
bool edit_mode = false ;
2016-12-07 15:12:35 +01:00
u8 * edit_buffer = TEMP_BUFFER ;
u8 * edit_buffer_cpy = TEMP_BUFFER + edit_bsize ;
2016-05-21 22:04:03 +02:00
u32 edit_start ;
2016-05-19 21:24:49 +02:00
int cursor = 0 ;
2016-12-07 23:58:03 +01:00
static bool show_instr = true ;
2017-04-24 21:48:21 +02:00
static const char * instr = " Hexeditor Controls: \n \n \x18 \x19 \x1A \x1B (+R) - Scroll \n R+Y - Switch view \n X - Search / goto... \n A - Enter edit mode \n A+ \x18 \x19 \x1A \x1B - Edit value \n B - Exit \n " ;
2016-12-07 23:58:03 +01:00
if ( show_instr ) { // show one time instructions
2017-04-24 21:48:21 +02:00
ShowPrompt ( false , instr ) ;
2016-12-07 23:58:03 +01:00
show_instr = false ;
}
2017-04-24 21:48:21 +02:00
# ifdef SWITCH_SCREENS
ShowString ( instr ) ;
# endif
2016-05-18 23:46:22 +02:00
memcpy ( bottom_cpy , BOT_SCREEN , ( SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3 ) ) ;
2016-05-18 00:05:24 +02:00
2016-04-08 21:03:40 +02:00
while ( true ) {
if ( mode ! = last_mode ) {
switch ( mode ) { // display mode
2016-07-18 03:15:04 +02:00
# ifdef FONT_6X10
case 1 :
vpad = 0 ;
hlpad = hrpad = 1 ;
cols = 16 ;
x_off = 0 ;
x_ascii = SCREEN_WIDTH_TOP - ( FONT_WIDTH_EXT * cols ) ;
x_hex = x_off + ( 8 * FONT_WIDTH_EXT ) + 16 ;
dual_screen = false ;
break ;
default :
mode = 0 ;
vpad = 0 ;
hlpad = hrpad = 3 ;
cols = 8 ;
x_off = 30 + ( SCREEN_WIDTH_TOP - SCREEN_WIDTH_BOT ) / 2 ;
x_ascii = SCREEN_WIDTH_TOP - x_off - ( FONT_WIDTH_EXT * cols ) ;
x_hex = ( SCREEN_WIDTH_TOP - ( ( hlpad + ( 2 * FONT_WIDTH_EXT ) + hrpad ) * cols ) ) / 2 ;
dual_screen = true ;
break ;
# else
2016-04-08 21:03:40 +02:00
case 1 :
vpad = hlpad = hrpad = 1 ;
cols = 12 ;
x_off = 0 ;
x_ascii = SCREEN_WIDTH_TOP - ( 8 * cols ) ;
x_hex = x_off + ( 8 * 8 ) + 12 ;
2016-05-18 00:05:24 +02:00
dual_screen = false ;
2016-04-08 21:03:40 +02:00
break ;
case 2 :
vpad = 1 ;
hlpad = 0 ;
hrpad = 1 ;
cols = 16 ;
x_off = - 1 ;
x_ascii = SCREEN_WIDTH_TOP - ( 8 * cols ) ;
x_hex = 0 ;
2016-05-18 00:05:24 +02:00
dual_screen = false ;
2016-04-08 21:03:40 +02:00
break ;
case 3 :
vpad = hlpad = hrpad = 1 ;
cols = 16 ;
x_off = 20 ;
x_ascii = - 1 ;
x_hex = x_off + ( 8 * 8 ) + 12 ;
2016-05-18 00:05:24 +02:00
dual_screen = false ;
2016-04-08 21:03:40 +02:00
break ;
default :
2016-07-18 03:15:04 +02:00
mode = 0 ;
2016-04-08 21:03:40 +02:00
vpad = hlpad = hrpad = 2 ;
cols = 8 ;
x_off = ( SCREEN_WIDTH_TOP - SCREEN_WIDTH_BOT ) / 2 ;
x_ascii = SCREEN_WIDTH_TOP - x_off - ( 8 * cols ) ;
x_hex = ( SCREEN_WIDTH_TOP - ( ( hlpad + 16 + hrpad ) * cols ) ) / 2 ;
2016-05-18 00:05:24 +02:00
dual_screen = true ;
2016-04-08 21:03:40 +02:00
break ;
2016-07-18 03:15:04 +02:00
# endif
2016-04-08 21:03:40 +02:00
}
2016-07-18 03:15:04 +02:00
rows = ( dual_screen ? 2 : 1 ) * SCREEN_HEIGHT / ( FONT_HEIGHT_EXT + ( 2 * vpad ) ) ;
2016-04-08 21:03:40 +02:00
total_shown = rows * cols ;
last_mode = mode ;
2017-04-24 21:48:21 +02:00
ClearScreen ( TOP_SCREEN , COLOR_STD_BG ) ;
if ( dual_screen ) ClearScreen ( BOT_SCREEN , COLOR_STD_BG ) ;
else memcpy ( BOT_SCREEN , bottom_cpy , ( SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3 ) ) ;
2016-04-08 21:03:40 +02:00
}
2016-04-10 15:55:39 +02:00
// fix offset (if required)
2016-06-13 23:51:41 +02:00
if ( offset % cols ) offset - = ( offset % cols ) ; // fix offset (align to cols)
if ( offset + total_shown > fsize + cols ) // if offset too big
2016-04-10 15:55:39 +02:00
offset = ( total_shown > fsize ) ? 0 : ( fsize + cols - total_shown - ( fsize % cols ) ) ;
2016-05-19 21:24:49 +02:00
// get data, using max data size (if new offset)
2016-05-27 01:21:05 +02:00
if ( offset ! = last_offset ) {
2016-05-21 22:04:03 +02:00
if ( ! edit_mode ) {
total_data = FileGetData ( path , data , max_data , offset ) ;
} else { // edit mode - read from memory
if ( ( offset < edit_start ) | | ( offset + max_data > edit_start + edit_bsize ) )
offset = last_offset ; // we don't expect this to happen
total_data = ( fsize - offset > = max_data ) ? max_data : fsize - offset ;
data = edit_buffer + ( offset - edit_start ) ;
}
2016-05-19 21:24:49 +02:00
last_offset = offset ;
}
2016-04-08 21:03:40 +02:00
// display data on screen
for ( u32 row = 0 ; row < rows ; row + + ) {
char ascii [ 16 + 1 ] = { 0 } ;
2016-07-18 03:15:04 +02:00
u32 y = row * ( FONT_HEIGHT_EXT + ( 2 * vpad ) ) + vpad ;
2016-04-08 21:03:40 +02:00
u32 curr_pos = row * cols ;
u32 cutoff = ( curr_pos > = total_data ) ? 0 : ( total_data > = curr_pos + cols ) ? cols : total_data - curr_pos ;
2016-07-12 18:33:52 +02:00
u32 marked0 = ( found_size & & ( offset < = found_offset ) ) ? found_offset - offset : 0 ;
u32 marked1 = marked0 + found_size ;
2016-05-18 23:46:22 +02:00
u8 * screen = TOP_SCREEN ;
u32 x0 = 0 ;
2016-07-12 18:33:52 +02:00
// fix marked0 / marked1 offsets for current row
marked0 = ( marked0 < curr_pos ) ? 0 : ( marked0 > = curr_pos + cols ) ? cols : marked0 - curr_pos ;
marked1 = ( marked1 < curr_pos ) ? 0 : ( marked1 > = curr_pos + cols ) ? cols : marked1 - curr_pos ;
2016-05-18 23:46:22 +02:00
if ( y > = SCREEN_HEIGHT ) { // switch to bottom screen
y - = SCREEN_HEIGHT ;
screen = BOT_SCREEN ;
x0 = 40 ;
}
2016-04-08 21:03:40 +02:00
memcpy ( ascii , data + curr_pos , cols ) ;
for ( u32 col = 0 ; col < cols ; col + + )
if ( ( col > = cutoff ) | | ( ascii [ col ] = = 0x00 ) ) ascii [ col ] = ' ' ;
// draw offset / ASCII representation
2016-05-18 23:46:22 +02:00
if ( x_off > = 0 ) DrawStringF ( screen , x_off - x0 , y , cutoff ? COLOR_HVOFFS : COLOR_HVOFFSI ,
2016-05-18 00:05:24 +02:00
COLOR_STD_BG , " %08X " , ( unsigned int ) offset + curr_pos ) ;
2016-05-19 21:24:49 +02:00
if ( x_ascii > = 0 ) {
2016-05-18 23:46:22 +02:00
DrawString ( screen , ascii , x_ascii - x0 , y , COLOR_HVASCII , COLOR_STD_BG ) ;
2016-07-12 18:33:52 +02:00
for ( u32 i = marked0 ; i < marked1 ; i + + )
2016-07-18 03:15:04 +02:00
DrawCharacter ( screen , ascii [ i % cols ] , x_ascii - x0 + ( FONT_WIDTH_EXT * i ) , y , COLOR_MARKED , COLOR_STD_BG ) ;
2016-05-19 21:24:49 +02:00
if ( edit_mode & & ( ( u32 ) cursor / cols = = row ) ) DrawCharacter ( screen , ascii [ cursor % cols ] ,
2016-07-18 03:15:04 +02:00
x_ascii - x0 + FONT_WIDTH_EXT * ( cursor % cols ) , y , COLOR_RED , COLOR_STD_BG ) ;
2016-05-19 21:24:49 +02:00
}
2016-04-08 21:03:40 +02:00
// draw HEX values
for ( u32 col = 0 ; ( col < cols ) & & ( x_hex > = 0 ) ; col + + ) {
2016-07-18 03:15:04 +02:00
u32 x = ( x_hex + hlpad ) + ( ( ( 2 * FONT_WIDTH_EXT ) + hrpad + hlpad ) * col ) - x0 ;
2016-07-12 18:33:52 +02:00
u32 hex_color = ( edit_mode & & ( ( u32 ) cursor = = curr_pos + col ) ) ? COLOR_RED :
( ( col > = marked0 ) & & ( col < marked1 ) ) ? COLOR_MARKED : COLOR_HVHEX ( col ) ;
2016-04-08 21:03:40 +02:00
if ( col < cutoff )
2016-05-19 21:24:49 +02:00
DrawStringF ( screen , x , y , hex_color , COLOR_STD_BG , " %02X " , ( unsigned int ) data [ curr_pos + col ] ) ;
else DrawStringF ( screen , x , y , hex_color , COLOR_STD_BG , " " ) ;
2016-04-08 21:03:40 +02:00
}
}
// handle user input
u32 pad_state = InputWait ( ) ;
2016-05-19 21:24:49 +02:00
if ( ( pad_state & BUTTON_R1 ) & & ( pad_state & BUTTON_L1 ) ) CreateScreenshot ( ) ;
else if ( ! edit_mode ) { // standard viewer mode
u32 step_ud = ( pad_state & BUTTON_R1 ) ? ( 0x1000 - ( 0x1000 % cols ) ) : cols ;
u32 step_lr = ( pad_state & BUTTON_R1 ) ? ( 0x10000 - ( 0x10000 % cols ) ) : total_shown ;
if ( pad_state & BUTTON_DOWN ) offset + = step_ud ;
else if ( pad_state & BUTTON_RIGHT ) offset + = step_lr ;
else if ( pad_state & BUTTON_UP ) offset = ( offset > step_ud ) ? offset - step_ud : 0 ;
else if ( pad_state & BUTTON_LEFT ) offset = ( offset > step_lr ) ? offset - step_lr : 0 ;
2016-07-18 03:15:04 +02:00
else if ( ( pad_state & BUTTON_R1 ) & & ( pad_state & BUTTON_Y ) ) mode + + ;
2016-05-19 21:24:49 +02:00
else if ( pad_state & BUTTON_A ) edit_mode = true ;
2016-06-18 15:07:57 +02:00
else if ( pad_state & ( BUTTON_B | BUTTON_START ) ) break ;
2016-07-12 18:33:52 +02:00
else if ( found_size & & ( pad_state & BUTTON_R1 ) & & ( pad_state & BUTTON_X ) ) {
u8 data [ 64 ] = { 0 } ;
FileGetData ( path , data , found_size , found_offset ) ;
found_offset = FileFindData ( path , data , found_size , found_offset + 1 ) ;
2017-04-24 21:48:21 +02:00
ClearScreen ( TOP_SCREEN , COLOR_STD_BG ) ;
2016-07-12 18:33:52 +02:00
if ( found_offset = = ( u32 ) - 1 ) {
ShowPrompt ( false , " Not found! " ) ;
found_size = 0 ;
} else offset = found_offset ;
} else if ( pad_state & BUTTON_X ) {
const char * optionstr [ 3 ] = { " Go to offset " , " Search for string " , " Search for data " } ;
u32 user_select = ShowSelectPrompt ( 3 , optionstr , " Current offset: %08X \n Select action: " ,
2016-06-13 23:51:41 +02:00
( unsigned int ) offset ) ;
2016-07-12 18:33:52 +02:00
if ( user_select = = 1 ) { // -> goto offset
u64 new_offset = ShowHexPrompt ( offset , 8 , " Current offset: %08X \n Enter new offset below. " ,
( unsigned int ) offset ) ;
if ( new_offset ! = ( u64 ) - 1 ) offset = new_offset ;
} else if ( user_select = = 2 ) {
char string [ 64 + 1 ] = { 0 } ;
if ( found_size ) FileGetData ( path , ( u8 * ) string , ( found_size < = 64 ) ? found_size : 64 , found_offset ) ;
if ( ShowStringPrompt ( string , 64 + 1 , " Enter search string below. \n (R+X to repeat search) " , ( unsigned int ) offset ) ) {
found_size = strnlen ( string , 64 ) ;
found_offset = FileFindData ( path , ( u8 * ) string , found_size , offset ) ;
2017-04-24 21:48:21 +02:00
ClearScreen ( TOP_SCREEN , COLOR_STD_BG ) ;
2016-07-12 18:33:52 +02:00
if ( found_offset = = ( u32 ) - 1 ) {
ShowPrompt ( false , " Not found! " ) ;
found_size = 0 ;
} else offset = found_offset ;
}
} else if ( user_select = = 3 ) {
u8 data [ 64 ] = { 0 } ;
u32 size = 0 ;
if ( found_size ) size = FileGetData ( path , data , ( found_size < = 64 ) ? found_size : 64 , found_offset ) ;
if ( ShowDataPrompt ( data , & size , " Enter search data below. \n (R+X to repeat search) " , ( unsigned int ) offset ) ) {
found_size = size ;
found_offset = FileFindData ( path , data , size , offset ) ;
2017-04-24 21:48:21 +02:00
ClearScreen ( TOP_SCREEN , COLOR_STD_BG ) ;
2016-07-12 18:33:52 +02:00
if ( found_offset = = ( u32 ) - 1 ) {
ShowPrompt ( false , " Not found! " ) ;
found_size = 0 ;
} else offset = found_offset ;
}
}
2016-06-13 23:51:41 +02:00
}
2016-05-27 01:21:05 +02:00
if ( edit_mode & & CheckWritePermissions ( path ) ) { // setup edit mode
2016-07-12 18:33:52 +02:00
found_size = 0 ;
found_offset = ( u32 ) - 1 ;
2016-05-21 22:04:03 +02:00
cursor = 0 ;
edit_start = ( ( offset - ( offset % 0x200 ) < = ( edit_bsize / 2 ) ) | | ( fsize < edit_bsize ) ) ? 0 :
offset - ( offset % 0x200 ) - ( edit_bsize / 2 ) ;
FileGetData ( path , edit_buffer , edit_bsize , edit_start ) ;
memcpy ( edit_buffer_cpy , edit_buffer , edit_bsize ) ;
data = edit_buffer + ( offset - edit_start ) ;
2016-05-27 01:21:05 +02:00
} else edit_mode = false ;
2016-05-19 21:24:49 +02:00
} else { // editor mode
2016-06-18 15:07:57 +02:00
if ( pad_state & ( BUTTON_B | BUTTON_START ) ) {
2016-05-19 21:24:49 +02:00
edit_mode = false ;
2016-05-21 22:04:03 +02:00
// check for user edits
u32 diffs = 0 ;
for ( u32 i = 0 ; i < edit_bsize ; i + + ) if ( edit_buffer [ i ] ! = edit_buffer_cpy [ i ] ) diffs + + ;
if ( diffs & & ShowPrompt ( true , " You made edits in %i place(s). \n Write changes to file? " , diffs ) )
2016-06-17 17:12:11 +02:00
if ( ! FileSetData ( path , edit_buffer , min ( edit_bsize , ( fsize - edit_start ) ) , edit_start , false ) )
2016-05-26 21:09:01 +02:00
ShowPrompt ( false , " Failed writing to file! " ) ;
2016-12-07 15:12:35 +01:00
data = TEMP_BUFFER ;
2016-05-21 22:04:03 +02:00
last_offset = ( u32 ) - 1 ; // force reload from file
2016-05-19 21:24:49 +02:00
} else if ( pad_state & BUTTON_A ) {
if ( pad_state & BUTTON_DOWN ) data [ cursor ] - - ;
else if ( pad_state & BUTTON_UP ) data [ cursor ] + + ;
else if ( pad_state & BUTTON_RIGHT ) data [ cursor ] + = 0x10 ;
else if ( pad_state & BUTTON_LEFT ) data [ cursor ] - = 0x10 ;
} else {
if ( pad_state & BUTTON_DOWN ) cursor + = cols ;
else if ( pad_state & BUTTON_UP ) cursor - = cols ;
else if ( pad_state & BUTTON_RIGHT ) cursor + + ;
else if ( pad_state & BUTTON_LEFT ) cursor - - ;
// fix cursor position
if ( cursor < 0 ) {
if ( offset > = cols ) {
offset - = cols ;
cursor + = cols ;
} else cursor = 0 ;
} else if ( ( ( u32 ) cursor > = total_data ) & & ( total_data < total_shown ) ) {
cursor = total_data - 1 ;
} else if ( ( u32 ) cursor > = total_shown ) {
if ( offset + total_shown = = fsize ) {
cursor = total_shown - 1 ;
} else {
offset + = cols ;
cursor = ( offset + cursor > = fsize ) ? fsize - offset - 1 : cursor - cols ;
}
}
}
}
2016-04-08 21:03:40 +02:00
}
2017-04-24 21:48:21 +02:00
ClearScreen ( TOP_SCREEN , COLOR_STD_BG ) ;
# ifndef SWITCH_SCREENS
2016-05-18 23:46:22 +02:00
memcpy ( BOT_SCREEN , bottom_cpy , ( SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3 ) ) ;
2017-04-24 21:48:21 +02:00
# else
ClearScreen ( BOT_SCREEN , COLOR_STD_BG ) ;
# endif
2016-04-08 21:03:40 +02:00
return 0 ;
}
2016-12-07 23:58:03 +01:00
u32 Sha256Calculator ( const char * path ) {
u32 drvtype = DriveType ( path ) ;
char pathstr [ 32 + 1 ] ;
u8 sha256 [ 32 ] ;
TruncateString ( pathstr , path , 32 , 8 ) ;
if ( ! FileGetSha256 ( path , sha256 ) ) {
ShowPrompt ( false , " Calculating SHA-256: failed! " ) ;
return 1 ;
} else {
static char pathstr_prev [ 32 + 1 ] = { 0 } ;
static u8 sha256_prev [ 32 ] = { 0 } ;
char sha_path [ 256 ] ;
u8 sha256_file [ 32 ] ;
snprintf ( sha_path , 256 , " %s.sha " , path ) ;
bool have_sha = ( FileGetData ( sha_path , sha256_file , 32 , 0 ) = = 32 ) ;
bool write_sha = ! have_sha & & ( drvtype & DRV_SDCARD ) ; // writing only on SD
if ( ShowPrompt ( write_sha , " %s \n %016llX%016llX \n %016llX%016llX%s%s%s%s%s " ,
pathstr , getbe64 ( sha256 + 0 ) , getbe64 ( sha256 + 8 ) , getbe64 ( sha256 + 16 ) , getbe64 ( sha256 + 24 ) ,
( have_sha ) ? " \n SHA verification: " : " " ,
( have_sha ) ? ( ( memcmp ( sha256 , sha256_file , 32 ) = = 0 ) ? " passed! " : " failed! " ) : " " ,
( memcmp ( sha256 , sha256_prev , 32 ) = = 0 ) ? " \n \n Identical with previous file: \n " : " " ,
( memcmp ( sha256 , sha256_prev , 32 ) = = 0 ) ? pathstr_prev : " " ,
( write_sha ) ? " \n \n Write .SHA file? " : " " ) & & ! have_sha & & write_sha ) {
FileSetData ( sha_path , sha256 , 32 , 0 , true ) ;
}
strncpy ( pathstr_prev , pathstr , 32 + 1 ) ;
memcpy ( sha256_prev , sha256 , 32 ) ;
}
return 0 ;
}
2017-02-08 21:45:52 +01:00
u32 CmacCalculator ( const char * path ) {
char pathstr [ 32 + 1 ] ;
u8 cmac [ 16 ] ;
TruncateString ( pathstr , path , 32 , 8 ) ;
if ( CalculateFileCmac ( path , cmac ) ! = 0 ) {
ShowPrompt ( false , " Calculating CMAC: failed! " ) ;
return 1 ;
} else {
u8 cmac_file [ 16 ] ;
bool identical = ( ( ReadFileCmac ( path , cmac_file ) = = 0 ) & & ( memcmp ( cmac , cmac_file , 16 ) = = 0 ) ) ;
if ( ShowPrompt ( ! identical , " %s \n %016llX%016llX \n %s%s%s " ,
pathstr , getbe64 ( cmac + 0 ) , getbe64 ( cmac + 8 ) ,
" CMAC verification: " , ( identical ) ? " passed! " : " failed! " ,
( ! identical ) ? " \n \n Fix CMAC in file? " : " " ) & &
2017-03-31 18:01:30 +02:00
! identical & & ( WriteFileCmac ( path , cmac ) ! = 0 ) ) {
2017-02-08 21:45:52 +01:00
ShowPrompt ( false , " Fixing CMAC: failed! " ) ;
}
}
return 0 ;
}
2017-03-20 02:57:13 +01:00
u32 StandardCopy ( u32 * cursor , DirStruct * current_dir ) {
DirEntry * curr_entry = & ( current_dir - > entry [ * cursor ] ) ;
u32 n_marked = 0 ;
if ( curr_entry - > marked ) {
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + )
if ( current_dir - > entry [ i ] . marked ) n_marked + + ;
}
u32 flags = BUILD_PATH ;
if ( ( n_marked > 1 ) & & ShowPrompt ( true , " Copy all %lu selected items? " , n_marked ) ) {
u32 n_success = 0 ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
const char * path = current_dir - > entry [ i ] . path ;
if ( ! current_dir - > entry [ i ] . marked )
continue ;
flags | = ASK_ALL ;
current_dir - > entry [ i ] . marked = false ;
if ( PathCopy ( OUTPUT_PATH , path , & flags ) ) n_success + + ;
else { // on failure: set cursor on failed item, break;
char currstr [ 32 + 1 ] ;
TruncateString ( currstr , path , 32 , 12 ) ;
ShowPrompt ( false , " %s \n Failed copying item " , currstr ) ;
* cursor = i ;
break ;
}
}
if ( n_success ) ShowPrompt ( false , " %lu items copied to %s " , n_success , OUTPUT_PATH ) ;
} else {
char pathstr [ 32 + 1 ] ;
TruncateString ( pathstr , curr_entry - > path , 32 , 8 ) ;
if ( ! PathCopy ( OUTPUT_PATH , curr_entry - > path , & flags ) )
ShowPrompt ( false , " %s \n Failed copying item " , pathstr ) ;
else ShowPrompt ( false , " %s \n Copied to %s " , pathstr , OUTPUT_PATH ) ;
}
return 0 ;
}
2016-12-13 00:20:00 +01:00
u32 FileHandlerMenu ( char * current_path , u32 * cursor , u32 * scroll , DirStruct * current_dir , DirStruct * clipboard ) {
DirEntry * curr_entry = & ( current_dir - > entry [ * cursor ] ) ;
2017-01-31 16:16:26 +01:00
const char * optionstr [ 16 ] ;
2016-12-13 00:20:00 +01:00
2016-12-19 01:33:30 +01:00
// check for file lock
if ( ! FileUnlock ( curr_entry - > path ) ) return 1 ;
2016-12-13 00:20:00 +01:00
u32 filetype = IdentifyFileType ( curr_entry - > path ) ;
u32 drvtype = DriveType ( curr_entry - > path ) ;
2017-01-25 14:46:29 +01:00
bool in_output_path = ( strncmp ( current_path , OUTPUT_PATH , 256 ) = = 0 ) ;
2017-01-16 22:32:32 +01:00
// special stuff, only available for known filetypes (see int special below)
2017-02-28 16:10:09 +01:00
bool mountable = ( FTYPE_MOUNTABLE ( filetype ) & & ! ( drvtype & DRV_IMAGE ) ) ;
2017-01-30 01:49:01 +01:00
bool verificable = ( FYTPE_VERIFICABLE ( filetype ) ) ;
bool decryptable = ( FYTPE_DECRYPTABLE ( filetype ) ) ;
2017-01-31 16:16:26 +01:00
bool encryptable = ( FYTPE_ENCRYPTABLE ( filetype ) ) ;
2017-02-08 21:45:52 +01:00
bool cryptable_inplace = ( ( encryptable | | decryptable ) & & ! in_output_path & & ( drvtype & DRV_FAT ) ) ;
2017-04-08 14:17:58 +02:00
bool cia_buildable = ( FTYPE_CIABUILD ( filetype ) ) ;
bool cia_buildable_legit = ( FTYPE_CIABUILD_L ( filetype ) ) ;
bool tik_buildable = ( FTYPE_TIKBUILD ( filetype ) ) & & ! in_output_path ;
2017-04-17 23:45:49 +02:00
bool key_buildable = ( FTYPE_KEYBUILD ( filetype ) ) & & ! in_output_path ;
2017-03-29 02:23:06 +02:00
bool titleinfo = ( FTYPE_TITLEINFO ( filetype ) ) ;
2017-03-24 21:22:49 +01:00
bool transferable = ( FTYPE_TRANSFERABLE ( filetype ) & & IS_A9LH & & ( drvtype & DRV_FAT ) ) ;
2017-01-30 01:49:01 +01:00
bool hsinjectable = ( FTYPE_HSINJECTABLE ( filetype ) ) ;
2017-02-26 13:35:37 +01:00
bool restorable = ( FTYPE_RESTORABLE ( filetype ) & & IS_A9LH & & ! ( drvtype & DRV_SYSNAND ) ) ;
2017-02-17 03:28:53 +01:00
bool ebackupable = ( FTYPE_EBACKUP ( filetype ) ) ;
2017-01-30 01:49:01 +01:00
bool xorpadable = ( FTYPE_XORPAD ( filetype ) ) ;
bool launchable = ( ( FTYPE_PAYLOAD ( filetype ) ) & & ( drvtype & DRV_FAT ) ) ;
2017-04-08 14:17:58 +02:00
bool special_opt = mountable | | verificable | | decryptable | | encryptable | | cia_buildable | | cia_buildable_legit | |
2017-04-17 23:45:49 +02:00
tik_buildable | | key_buildable | | titleinfo | | hsinjectable | | restorable | | xorpadable | | launchable | | ebackupable ;
2016-12-13 00:20:00 +01:00
2017-03-20 02:57:13 +01:00
char pathstr [ 32 + 1 ] ;
2016-12-13 00:20:00 +01:00
TruncateString ( pathstr , curr_entry - > path , 32 , 8 ) ;
2016-12-13 17:10:39 +01:00
2017-01-24 01:46:28 +01:00
u32 n_marked = 0 ;
if ( curr_entry - > marked ) {
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + )
if ( current_dir - > entry [ i ] . marked ) n_marked + + ;
}
2016-12-13 00:20:00 +01:00
// main menu processing
int n_opt = 0 ;
2017-01-16 22:32:32 +01:00
int special = ( special_opt ) ? + + n_opt : - 1 ;
2016-12-13 00:20:00 +01:00
int hexviewer = + + n_opt ;
int calcsha = + + n_opt ;
2017-02-08 21:45:52 +01:00
int calccmac = ( CheckCmacPath ( curr_entry - > path ) = = 0 ) ? + + n_opt : - 1 ;
2017-01-25 14:46:29 +01:00
int copystd = ( ! in_output_path ) ? + + n_opt : - 1 ;
2016-12-13 00:20:00 +01:00
int inject = ( ( clipboard - > n_entries = = 1 ) & &
( clipboard - > entry [ 0 ] . type = = T_FILE ) & &
( drvtype & DRV_FAT ) & &
( strncmp ( clipboard - > entry [ 0 ] . path , curr_entry - > path , 256 ) ! = 0 ) ) ?
( int ) + + n_opt : - 1 ;
2016-12-20 13:10:07 +01:00
int searchdrv = ( DriveType ( current_path ) & DRV_SEARCH ) ? + + n_opt : - 1 ;
2016-12-13 00:20:00 +01:00
if ( special > 0 ) optionstr [ special - 1 ] =
2017-01-27 15:29:53 +01:00
( filetype & IMG_NAND ) ? " NAND image options... " :
2017-03-02 22:40:58 +01:00
( filetype & IMG_FAT ) ? ( transferable ) ? " CTRNAND options... " : " Mount as FAT image " :
2017-01-27 15:29:53 +01:00
( filetype & GAME_CIA ) ? " CIA image options... " :
( filetype & GAME_NCSD ) ? " NCSD image options... " :
( filetype & GAME_NCCH ) ? " NCCH image options... " :
( filetype & GAME_EXEFS ) ? " Mount as EXEFS image " :
( filetype & GAME_ROMFS ) ? " Mount as ROMFS image " :
2017-03-23 16:00:17 +01:00
( filetype & GAME_TMD ) ? " TMD file options... " :
( filetype & GAME_BOSS ) ? " BOSS file options... " :
2017-02-07 23:18:25 +01:00
( filetype & GAME_NUSCDN ) ? " Decrypt NUS/CDN file " :
2017-03-29 02:23:06 +02:00
( filetype & GAME_SMDH ) ? " Show SMDH title info " :
2017-04-08 14:17:58 +02:00
( filetype & GAME_NDS ) ? " Show NDS title info " :
( filetype & GAME_TICKET ) ? " Ticket options... " :
2017-03-23 16:00:17 +01:00
( filetype & SYS_FIRM ) ? " FIRM image options... " :
2017-04-08 14:17:58 +02:00
( filetype & SYS_TICKDB ) ? ( tik_buildable ) ? " Ticket.db options... " : " Mount as ticket.db " :
( filetype & BIN_TIKDB ) ? " Titlekey options... " :
2017-04-17 23:45:49 +02:00
( filetype & BIN_KEYDB ) ? " AESkeydb options... " :
( filetype & BIN_LEGKEY ) ? " Build " KEYDB_NAME :
2017-01-30 20:28:49 +01:00
( filetype & BIN_NCCHNFO ) ? " NCCHinfo options... " :
2017-01-27 15:29:53 +01:00
( filetype & BIN_LAUNCH ) ? " Launch as arm9 payload " : " ??? " ;
2016-12-13 00:20:00 +01:00
optionstr [ hexviewer - 1 ] = " Show in Hexeditor " ;
optionstr [ calcsha - 1 ] = " Calculate SHA-256 " ;
2017-03-23 12:50:16 +01:00
if ( calccmac > 0 ) optionstr [ calccmac - 1 ] = " Calculate CMAC " ;
2017-01-24 01:46:28 +01:00
if ( copystd > 0 ) optionstr [ copystd - 1 ] = " Copy to " OUTPUT_PATH ;
2016-12-13 00:20:00 +01:00
if ( inject > 0 ) optionstr [ inject - 1 ] = " Inject data @offset " ;
if ( searchdrv > 0 ) optionstr [ searchdrv - 1 ] = " Open containing folder " ;
2017-02-09 14:33:05 +01:00
int user_select = ShowSelectPrompt ( n_opt , optionstr , ( n_marked > 1 ) ?
" %s \n %(%lu files selected) " : " %s " , pathstr , n_marked ) ;
2016-12-13 00:20:00 +01:00
if ( user_select = = hexviewer ) { // -> show in hex viewer
HexViewer ( curr_entry - > path ) ;
return 0 ;
} else if ( user_select = = calcsha ) { // -> calculate SHA-256
Sha256Calculator ( curr_entry - > path ) ;
GetDirContents ( current_dir , current_path ) ;
return 0 ;
2017-02-08 21:45:52 +01:00
} else if ( user_select = = calccmac ) { // -> calculate CMAC
2017-02-09 14:07:57 +01:00
optionstr [ 0 ] = " Check current CMAC only " ;
optionstr [ 1 ] = " Verify CMAC for all " ;
optionstr [ 2 ] = " Fix CMAC for all " ;
user_select = ( n_marked > 1 ) ? ShowSelectPrompt ( 3 , optionstr , " %s \n %(%lu files selected) " , pathstr , n_marked ) : 1 ;
2017-02-08 21:45:52 +01:00
if ( user_select = = 1 ) {
CmacCalculator ( curr_entry - > path ) ;
return 0 ;
} else if ( ( user_select = = 2 ) | | ( user_select = = 3 ) ) {
bool fix = ( user_select = = 3 ) ;
u32 n_processed = 0 ;
u32 n_success = 0 ;
u32 n_fixed = 0 ;
u32 n_nocmac = 0 ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
const char * path = current_dir - > entry [ i ] . path ;
if ( ! current_dir - > entry [ i ] . marked ) continue ;
if ( ! ShowProgress ( n_processed + + , n_marked , path ) ) break ;
if ( CheckCmacPath ( path ) ! = 0 ) {
n_nocmac + + ;
continue ;
}
2017-02-09 14:07:57 +01:00
if ( CheckFileCmac ( path ) = = 0 ) n_success + + ;
2017-02-08 21:45:52 +01:00
else if ( fix & & ( FixFileCmac ( path ) = = 0 ) ) n_fixed + + ;
else { // on failure: set cursor on failed file
* cursor = i ;
continue ;
}
2017-02-09 14:07:57 +01:00
current_dir - > entry [ i ] . marked = false ;
2017-02-08 21:45:52 +01:00
}
if ( n_fixed ) {
if ( n_nocmac ) ShowPrompt ( false , " %lu/%lu/%lu files ok/fixed/total \n %lu/%lu have no CMAC " ,
n_success , n_fixed , n_marked , n_nocmac , n_marked ) ;
else ShowPrompt ( false , " %lu/%lu files verified ok \n %lu/%lu files fixed " ,
n_success , n_marked , n_fixed , n_marked ) ;
} else {
if ( n_nocmac ) ShowPrompt ( false , " %lu/%lu files verified ok \n %lu/%lu have no CMAC " ,
n_success , n_marked , n_nocmac , n_marked ) ;
else ShowPrompt ( false , " %lu/%lu files verified ok " , n_success , n_marked ) ;
}
return 0 ;
}
2017-02-10 15:23:46 +01:00
return FileHandlerMenu ( current_path , cursor , scroll , current_dir , clipboard ) ;
2017-01-24 01:46:28 +01:00
} else if ( user_select = = copystd ) { // -> copy to OUTPUT_PATH
2017-03-20 02:57:13 +01:00
StandardCopy ( cursor , current_dir ) ;
2017-01-24 01:46:28 +01:00
return 0 ;
2016-12-13 00:20:00 +01:00
} else if ( user_select = = inject ) { // -> inject data from clipboard
char origstr [ 18 + 1 ] ;
TruncateString ( origstr , clipboard - > entry [ 0 ] . name , 18 , 10 ) ;
u64 offset = ShowHexPrompt ( 0 , 8 , " Inject data from %s? \n Specifiy offset below. " , origstr ) ;
if ( offset ! = ( u64 ) - 1 ) {
if ( ! FileInjectFile ( curr_entry - > path , clipboard - > entry [ 0 ] . path , ( u32 ) offset ) )
ShowPrompt ( false , " Failed injecting %s " , origstr ) ;
clipboard - > n_entries = 0 ;
}
return 0 ;
} else if ( user_select = = searchdrv ) { // -> search drive, open containing path
char * last_slash = strrchr ( curr_entry - > path , ' / ' ) ;
if ( last_slash ) {
snprintf ( current_path , last_slash - curr_entry - > path + 1 , " %s " , curr_entry - > path ) ;
GetDirContents ( current_dir , current_path ) ;
* cursor = 1 ;
* scroll = 0 ;
}
return 0 ;
} else if ( user_select ! = special ) {
return 1 ;
}
// stuff for special menu starts here
n_opt = 0 ;
2017-03-29 02:23:06 +02:00
int show_info = ( titleinfo ) ? + + n_opt : - 1 ;
2016-12-13 00:20:00 +01:00
int mount = ( mountable ) ? + + n_opt : - 1 ;
2017-01-02 17:37:08 +01:00
int restore = ( restorable ) ? + + n_opt : - 1 ;
2017-02-17 03:28:53 +01:00
int ebackup = ( ebackupable ) ? + + n_opt : - 1 ;
2016-12-13 00:20:00 +01:00
int decrypt = ( decryptable ) ? + + n_opt : - 1 ;
2017-01-31 16:16:26 +01:00
int encrypt = ( encryptable ) ? + + n_opt : - 1 ;
2017-04-08 14:17:58 +02:00
int cia_build = ( cia_buildable ) ? + + n_opt : - 1 ;
int cia_build_legit = ( cia_buildable_legit ) ? + + n_opt : - 1 ;
int tik_build_enc = ( tik_buildable ) ? + + n_opt : - 1 ;
int tik_build_dec = ( tik_buildable ) ? + + n_opt : - 1 ;
2017-04-17 23:45:49 +02:00
int key_build = ( key_buildable ) ? + + n_opt : - 1 ;
2016-12-13 00:20:00 +01:00
int verify = ( verificable ) ? + + n_opt : - 1 ;
2017-03-23 16:00:17 +01:00
int ctrtransfer = ( transferable ) ? + + n_opt : - 1 ;
2017-01-30 01:49:01 +01:00
int hsinject = ( hsinjectable ) ? + + n_opt : - 1 ;
2017-01-25 14:46:29 +01:00
int xorpad = ( xorpadable ) ? + + n_opt : - 1 ;
int xorpad_inplace = ( xorpadable ) ? + + n_opt : - 1 ;
2017-01-25 16:13:50 -03:00
int launch = ( launchable ) ? + + n_opt : - 1 ;
2016-12-13 00:20:00 +01:00
if ( mount > 0 ) optionstr [ mount - 1 ] = " Mount image to drive " ;
2017-01-02 17:37:08 +01:00
if ( restore > 0 ) optionstr [ restore - 1 ] = " Restore SysNAND (safe) " ;
2017-02-17 03:28:53 +01:00
if ( ebackup > 0 ) optionstr [ ebackup - 1 ] = " Update embedded backup " ;
2017-03-29 02:23:06 +02:00
if ( show_info > 0 ) optionstr [ show_info - 1 ] = " Show title info " ;
2017-01-31 16:16:26 +01:00
if ( decrypt > 0 ) optionstr [ decrypt - 1 ] = ( cryptable_inplace ) ? " Decrypt file (...) " : " Decrypt file ( " OUTPUT_PATH " ) " ;
if ( encrypt > 0 ) optionstr [ encrypt - 1 ] = ( cryptable_inplace ) ? " Encrypt file (...) " : " Encrypt file ( " OUTPUT_PATH " ) " ;
2017-04-08 14:17:58 +02:00
if ( cia_build > 0 ) optionstr [ cia_build - 1 ] = ( cia_build_legit < 0 ) ? " Build CIA from file " : " Build CIA (standard) " ;
if ( cia_build_legit > 0 ) optionstr [ cia_build_legit - 1 ] = " Build CIA (legit) " ;
if ( tik_build_enc > 0 ) optionstr [ tik_build_enc - 1 ] = " Build " TIKDB_NAME_ENC ;
if ( tik_build_dec > 0 ) optionstr [ tik_build_dec - 1 ] = " Build " TIKDB_NAME_DEC ;
2017-04-17 23:45:49 +02:00
if ( key_build > 0 ) optionstr [ key_build - 1 ] = " Build " KEYDB_NAME ;
2016-12-13 00:20:00 +01:00
if ( verify > 0 ) optionstr [ verify - 1 ] = " Verify file " ;
2017-03-23 16:00:17 +01:00
if ( ctrtransfer > 0 ) optionstr [ ctrtransfer - 1 ] = " Transfer image to CTRNAND " ;
2017-01-30 01:49:01 +01:00
if ( hsinject > 0 ) optionstr [ hsinject - 1 ] = " Inject to H&S " ;
2017-01-25 14:46:29 +01:00
if ( xorpad > 0 ) optionstr [ xorpad - 1 ] = " Build XORpads (SD output) " ;
if ( xorpad_inplace > 0 ) optionstr [ xorpad_inplace - 1 ] = " Build XORpads (inplace) " ;
2017-01-25 16:13:50 -03:00
if ( launch > 0 ) optionstr [ launch - 1 ] = " Launch as ARM9 payload " ;
2016-12-13 00:20:00 +01:00
// auto select when there is only one option
2017-02-09 14:33:05 +01:00
user_select = ( n_opt < = 1 ) ? n_opt : ( int ) ShowSelectPrompt ( n_opt , optionstr , ( n_marked > 1 ) ?
" %s \n %(%lu files selected) " : " %s " , pathstr , n_marked ) ;
2016-12-13 00:20:00 +01:00
if ( user_select = = mount ) { // -> mount file as image
2016-12-13 16:00:14 +01:00
if ( clipboard - > n_entries & & ( DriveType ( clipboard - > entry [ 0 ] . path ) & DRV_IMAGE ) )
2016-12-13 00:20:00 +01:00
clipboard - > n_entries = 0 ; // remove last mounted image clipboard entries
InitImgFS ( curr_entry - > path ) ;
2017-04-18 17:27:44 +02:00
if ( ! ( DriveType ( " 7: " ) | | DriveType ( " G: " ) | | DriveType ( " K: " ) | | DriveType ( " T: " ) ) ) {
2016-12-13 00:20:00 +01:00
ShowPrompt ( false , " Mounting image: failed " ) ;
InitImgFS ( NULL ) ;
} else {
* cursor = 0 ;
* current_path = ' \0 ' ;
GetDirContents ( current_dir , current_path ) ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
2017-04-18 17:27:44 +02:00
if ( strspn ( current_dir - > entry [ i ] . path , " 7GKTI " ) = = 0 )
2016-12-13 00:20:00 +01:00
continue ;
strncpy ( current_path , current_dir - > entry [ i ] . path , 256 ) ;
GetDirContents ( current_dir , current_path ) ;
* cursor = 1 ;
* scroll = 0 ;
break ;
}
}
return 0 ;
2017-01-31 16:16:26 +01:00
} else if ( user_select = = decrypt ) { // -> decrypt game file
if ( cryptable_inplace ) {
optionstr [ 0 ] = " Decrypt to " OUTPUT_PATH ;
optionstr [ 1 ] = " Decrypt inplace " ;
2017-02-09 14:33:05 +01:00
user_select = ( int ) ShowSelectPrompt ( 2 , optionstr , ( n_marked > 1 ) ?
" %s \n %(%lu files selected) " : " %s " , pathstr , n_marked ) ;
2017-01-31 16:16:26 +01:00
} else user_select = 1 ;
bool inplace = ( user_select = = 2 ) ;
if ( ! user_select ) { // do nothing when no choice is made
} else if ( ( n_marked > 1 ) & & ShowPrompt ( true , " Try to decrypt all %lu selected files? " , n_marked ) ) {
2016-12-13 00:20:00 +01:00
u32 n_success = 0 ;
u32 n_unencrypted = 0 ;
u32 n_other = 0 ;
2017-01-31 16:16:26 +01:00
ShowString ( " Trying to decrypt %lu files... " , n_marked ) ;
2016-12-13 00:20:00 +01:00
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
const char * path = current_dir - > entry [ i ] . path ;
if ( ! current_dir - > entry [ i ] . marked )
continue ;
2017-01-31 16:16:26 +01:00
if ( ! ( IdentifyFileType ( path ) & filetype & TYPE_BASE ) ) {
2016-12-13 00:20:00 +01:00
n_other + + ;
continue ;
}
2017-04-17 23:45:49 +02:00
if ( ! ( filetype & BIN_KEYDB ) & & ( CheckEncryptedGameFile ( path ) ! = 0 ) ) {
2016-12-13 00:20:00 +01:00
n_unencrypted + + ;
continue ;
}
current_dir - > entry [ i ] . marked = false ;
2017-04-17 23:45:49 +02:00
if ( ! ( filetype & BIN_KEYDB ) & & ( CryptGameFile ( path , inplace , false ) = = 0 ) ) n_success + + ;
else if ( ( filetype & BIN_KEYDB ) & & ( CryptAesKeyDb ( path , inplace , false ) = = 0 ) ) n_success + + ;
2016-12-13 00:20:00 +01:00
else { // on failure: set cursor on failed title, break;
2017-05-08 20:38:44 +02:00
TruncateString ( pathstr , path , 32 , 8 ) ;
ShowPrompt ( false , " %s \n Decryption failed%s " , pathstr , ( filetype & ( GAME_NCCH | GAME_NCSD | GAME_CIA ) ) ? " \n \n Hint: Did you provide \n " KEYDB_NAME " & " SEEDDB_NAME " ? " : " " ) ;
2016-12-13 00:20:00 +01:00
* cursor = i ;
break ;
}
}
if ( n_other | | n_unencrypted ) {
ShowPrompt ( false , " %lu/%lu files decrypted ok \n %lu/%lu not encrypted \n %lu/%lu not of same type " ,
2017-01-31 16:16:26 +01:00
n_success , n_marked , n_unencrypted , n_marked , n_other , n_marked ) ;
2016-12-13 00:20:00 +01:00
} else ShowPrompt ( false , " %lu/%lu files decrypted ok " , n_success , n_marked ) ;
if ( ! inplace & & n_success ) ShowPrompt ( false , " %lu files written to %s " , n_success , OUTPUT_PATH ) ;
} else {
2017-04-17 23:45:49 +02:00
if ( ! ( filetype & BIN_KEYDB ) & & ( CheckEncryptedGameFile ( curr_entry - > path ) ! = 0 ) ) {
2016-12-13 00:20:00 +01:00
ShowPrompt ( false , " %s \n File is not encrypted " , pathstr ) ;
} else {
2017-04-17 23:45:49 +02:00
u32 ret = ( filetype & BIN_KEYDB ) ? CryptAesKeyDb ( curr_entry - > path , inplace , false ) :
CryptGameFile ( curr_entry - > path , inplace , false ) ;
2017-05-08 20:38:44 +02:00
if ( inplace | | ( ret ! = 0 ) ) ShowPrompt ( false , " %s \n Decryption %s " , pathstr , ( ret = = 0 ) ? " success " : ( filetype & ( GAME_NCCH | GAME_NCSD | GAME_CIA ) ) ?
" failed \n \n Hint: Did you provide \n " KEYDB_NAME " & " SEEDDB_NAME " ? " : " failed " ) ;
2016-12-13 00:20:00 +01:00
else ShowPrompt ( false , " %s \n Decrypted to %s " , pathstr , OUTPUT_PATH ) ;
}
}
return 0 ;
2017-01-31 16:16:26 +01:00
} else if ( user_select = = encrypt ) { // -> encrypt game file
if ( cryptable_inplace ) {
optionstr [ 0 ] = " Encrypt to " OUTPUT_PATH ;
optionstr [ 1 ] = " Encrypt inplace " ;
2017-02-09 14:33:05 +01:00
user_select = ( int ) ShowSelectPrompt ( 2 , optionstr , ( n_marked > 1 ) ?
" %s \n %(%lu files selected) " : " %s " , pathstr , n_marked ) ;
2017-01-31 16:16:26 +01:00
} else user_select = 1 ;
bool inplace = ( user_select = = 2 ) ;
if ( ! user_select ) { // do nothing when no choice is made
} else if ( ( n_marked > 1 ) & & ShowPrompt ( true , " Try to encrypt all %lu selected files? " , n_marked ) ) {
u32 n_success = 0 ;
u32 n_other = 0 ;
ShowString ( " Trying to encrypt %lu files... " , n_marked ) ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
const char * path = current_dir - > entry [ i ] . path ;
if ( ! current_dir - > entry [ i ] . marked )
continue ;
if ( ! ( IdentifyFileType ( path ) & filetype & TYPE_BASE ) ) {
n_other + + ;
continue ;
}
current_dir - > entry [ i ] . marked = false ;
2017-04-17 23:45:49 +02:00
if ( ! ( filetype & BIN_KEYDB ) & & ( CryptGameFile ( path , inplace , true ) = = 0 ) ) n_success + + ;
else if ( ( filetype & BIN_KEYDB ) & & ( CryptAesKeyDb ( path , inplace , true ) = = 0 ) ) n_success + + ;
2017-01-31 16:16:26 +01:00
else { // on failure: set cursor on failed title, break;
2017-05-08 20:38:44 +02:00
TruncateString ( pathstr , path , 32 , 8 ) ;
ShowPrompt ( false , " %s \n Encryption failed " , pathstr ) ;
2017-01-31 16:16:26 +01:00
* cursor = i ;
break ;
}
}
if ( n_other ) {
ShowPrompt ( false , " %lu/%lu files encrypted ok \n %lu/%lu not of same type " ,
n_success , n_marked , n_other , n_marked ) ;
} else ShowPrompt ( false , " %lu/%lu files encrypted ok " , n_success , n_marked ) ;
if ( ! inplace & & n_success ) ShowPrompt ( false , " %lu files written to %s " , n_success , OUTPUT_PATH ) ;
} else {
2017-04-17 23:45:49 +02:00
u32 ret = ( filetype & BIN_KEYDB ) ? CryptAesKeyDb ( curr_entry - > path , inplace , true ) :
CryptGameFile ( curr_entry - > path , inplace , true ) ;
2017-01-31 16:16:26 +01:00
if ( inplace | | ( ret ! = 0 ) ) ShowPrompt ( false , " %s \n Encryption %s " , pathstr , ( ret = = 0 ) ? " success " : " failed " ) ;
else ShowPrompt ( false , " %s \n Encrypted to %s " , pathstr , OUTPUT_PATH ) ;
}
return 0 ;
2017-04-08 14:17:58 +02:00
} else if ( ( user_select = = cia_build ) | | ( user_select = = cia_build_legit ) ) { // -> build CIA
bool force_legit = ( user_select = = cia_build_legit ) ;
2016-12-15 11:46:00 +01:00
if ( ( n_marked > 1 ) & & ShowPrompt ( true , " Try to process all %lu selected files? " , n_marked ) ) {
u32 n_success = 0 ;
u32 n_other = 0 ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
const char * path = current_dir - > entry [ i ] . path ;
if ( ! current_dir - > entry [ i ] . marked )
continue ;
2017-01-31 16:16:26 +01:00
if ( ! ( IdentifyFileType ( path ) & filetype & TYPE_BASE ) ) {
2016-12-15 11:46:00 +01:00
n_other + + ;
continue ;
}
current_dir - > entry [ i ] . marked = false ;
if ( BuildCiaFromGameFile ( path , force_legit ) = = 0 ) n_success + + ;
else { // on failure: set *cursor on failed title, break;
2017-05-08 20:38:44 +02:00
TruncateString ( pathstr , path , 32 , 8 ) ;
ShowPrompt ( false , " %s \n Build CIA failed " , pathstr ) ;
2016-12-15 11:46:00 +01:00
* cursor = i ;
break ;
}
}
if ( n_other ) ShowPrompt ( false , " %lu/%lu CIAs built ok \n %lu/%lu not of same type " ,
n_success , n_marked , n_other , n_marked ) ;
else ShowPrompt ( false , " %lu/%lu CIAs built ok " , n_success , n_marked ) ;
if ( n_success ) ShowPrompt ( false , " %lu files written to %s " , n_success , OUTPUT_PATH ) ;
2017-01-25 14:46:29 +01:00
if ( n_success & & in_output_path ) GetDirContents ( current_dir , current_path ) ;
2016-12-15 11:46:00 +01:00
} else {
2017-01-25 14:46:29 +01:00
if ( BuildCiaFromGameFile ( curr_entry - > path , force_legit ) = = 0 ) {
2016-12-15 11:46:00 +01:00
ShowPrompt ( false , " %s \n CIA built to %s " , pathstr , OUTPUT_PATH ) ;
2017-01-25 14:46:29 +01:00
if ( in_output_path ) GetDirContents ( current_dir , current_path ) ;
} else ShowPrompt ( false , " %s \n CIA build failed " , pathstr ) ;
2016-12-15 11:46:00 +01:00
}
return 0 ;
2017-01-02 17:37:08 +01:00
} else if ( user_select = = verify ) { // -> verify game / nand file
2016-12-13 00:20:00 +01:00
if ( ( n_marked > 1 ) & & ShowPrompt ( true , " Try to verify all %lu selected files? " , n_marked ) ) {
u32 n_success = 0 ;
u32 n_other = 0 ;
u32 n_processed = 0 ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
const char * path = current_dir - > entry [ i ] . path ;
if ( ! current_dir - > entry [ i ] . marked )
continue ;
2017-02-08 21:45:52 +01:00
if ( ! ( filetype & ( GAME_CIA | GAME_TMD ) ) & &
! ShowProgress ( n_processed + + , n_marked , path ) ) break ;
2017-01-31 16:16:26 +01:00
if ( ! ( IdentifyFileType ( path ) & filetype & TYPE_BASE ) ) {
2016-12-13 00:20:00 +01:00
n_other + + ;
continue ;
}
current_dir - > entry [ i ] . marked = false ;
2017-01-02 17:37:08 +01:00
if ( filetype & IMG_NAND ) {
if ( ValidateNandDump ( path ) = = 0 ) n_success + + ;
} else if ( VerifyGameFile ( path ) = = 0 ) n_success + + ;
2016-12-13 00:20:00 +01:00
else { // on failure: set *cursor on failed title, break;
2017-05-08 20:38:44 +02:00
TruncateString ( pathstr , path , 32 , 8 ) ;
ShowPrompt ( false , " %s \n Verification failed " , pathstr ) ;
2016-12-13 00:20:00 +01:00
* cursor = i ;
break ;
}
}
if ( n_other ) ShowPrompt ( false , " %lu/%lu files verified ok \n %lu/%lu not of same type " ,
n_success , n_marked , n_other , n_marked ) ;
else ShowPrompt ( false , " %lu/%lu files verified ok " , n_success , n_marked ) ;
} else {
ShowString ( " %s \n Verifying file, please wait... " , pathstr ) ;
2017-01-02 17:37:08 +01:00
if ( filetype & IMG_NAND ) {
ShowPrompt ( false , " %s \n NAND validation %s " , pathstr ,
( ValidateNandDump ( curr_entry - > path ) = = 0 ) ? " success " : " failed " ) ;
} else ShowPrompt ( false , " %s \n Verification %s " , pathstr ,
2016-12-13 00:20:00 +01:00
( VerifyGameFile ( curr_entry - > path ) = = 0 ) ? " success " : " failed " ) ;
}
return 0 ;
2017-04-08 14:17:58 +02:00
} else if ( ( user_select = = tik_build_enc ) | | ( user_select = = tik_build_dec ) ) { // -> (Re)Build titlekey database
bool dec = ( user_select = = tik_build_dec ) ;
const char * path_out = ( dec ) ? OUTPUT_PATH " / " TIKDB_NAME_DEC : OUTPUT_PATH " / " TIKDB_NAME_ENC ;
if ( BuildTitleKeyInfo ( NULL , dec , false ) ! = 0 ) return 1 ; // init database
ShowString ( " Building %s... " , ( dec ) ? TIKDB_NAME_DEC : TIKDB_NAME_ENC ) ;
if ( n_marked > 1 ) {
u32 n_success = 0 ;
u32 n_other = 0 ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
const char * path = current_dir - > entry [ i ] . path ;
if ( ! current_dir - > entry [ i ] . marked )
continue ;
if ( ! FTYPE_TIKBUILD ( IdentifyFileType ( path ) ) ) {
n_other + + ;
continue ;
}
current_dir - > entry [ i ] . marked = false ;
if ( BuildTitleKeyInfo ( path , dec , false ) = = 0 ) n_success + + ; // ignore failures for now
}
if ( BuildTitleKeyInfo ( NULL , dec , true ) = = 0 ) {
if ( n_other ) ShowPrompt ( false , " %s \n %lu/%lu files processed \n %lu/%lu files ignored " ,
path_out , n_success , n_marked , n_other , n_marked ) ;
else ShowPrompt ( false , " %s \n %lu/%lu files processed " , path_out , n_success , n_marked ) ;
2017-04-17 23:45:49 +02:00
} else ShowPrompt ( false , " %s \n Build database failed. " , path_out ) ;
2017-04-08 14:17:58 +02:00
} else ShowPrompt ( false , " %s \n Build database %s. " , path_out ,
( BuildTitleKeyInfo ( curr_entry - > path , dec , true ) = = 0 ) ? " success " : " failed " ) ;
return 0 ;
2017-04-17 23:45:49 +02:00
} else if ( user_select = = key_build ) { // -> (Re)Build AES key database
const char * path_out = OUTPUT_PATH " / " KEYDB_NAME ;
if ( BuildKeyDb ( NULL , false ) ! = 0 ) return 1 ; // init database
ShowString ( " Building %s... " , KEYDB_NAME ) ;
if ( n_marked > 1 ) {
u32 n_success = 0 ;
u32 n_other = 0 ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
const char * path = current_dir - > entry [ i ] . path ;
if ( ! current_dir - > entry [ i ] . marked )
continue ;
if ( ! FTYPE_KEYBUILD ( IdentifyFileType ( path ) ) ) {
n_other + + ;
continue ;
}
current_dir - > entry [ i ] . marked = false ;
if ( BuildKeyDb ( path , false ) = = 0 ) n_success + + ; // ignore failures for now
}
if ( BuildKeyDb ( NULL , true ) = = 0 ) {
if ( n_other ) ShowPrompt ( false , " %s \n %lu/%lu files processed \n %lu/%lu files ignored " ,
path_out , n_success , n_marked , n_other , n_marked ) ;
else ShowPrompt ( false , " %s \n %lu/%lu files processed " , path_out , n_success , n_marked ) ;
} else ShowPrompt ( false , " %s \n Build database failed. " , path_out ) ;
} else ShowPrompt ( false , " %s \n Build database %s. " , path_out ,
( BuildKeyDb ( curr_entry - > path , true ) = = 0 ) ? " success " : " failed " ) ;
return 0 ;
2017-03-29 02:23:06 +02:00
} else if ( user_select = = show_info ) { // -> Show title info
if ( ShowGameFileTitleInfo ( curr_entry - > path ) ! = 0 )
ShowPrompt ( false , " Title info: not found " ) ;
return 0 ;
2017-01-30 01:49:01 +01:00
} else if ( user_select = = hsinject ) { // -> Inject to Health & Safety
char * destdrv [ 2 ] = { NULL } ;
n_opt = 0 ;
if ( DriveType ( " 1: " ) ) {
optionstr [ n_opt ] = " SysNAND H&S inject " ;
destdrv [ n_opt + + ] = " 1: " ;
}
if ( DriveType ( " 4: " ) ) {
optionstr [ n_opt ] = " EmuNAND H&S inject " ;
destdrv [ n_opt + + ] = " 4: " ;
}
user_select = ( n_opt > 1 ) ? ( int ) ShowSelectPrompt ( n_opt , optionstr , pathstr ) : n_opt ;
if ( user_select ) {
ShowPrompt ( false , " %s \n H&S inject %s " , pathstr ,
2017-05-08 20:38:44 +02:00
( InjectHealthAndSafety ( curr_entry - > path , destdrv [ user_select - 1 ] ) = = 0 ) ? " success " : " failed \n \n Hint: Did you provide \n " KEYDB_NAME " ? " ) ;
2017-01-30 01:49:01 +01:00
}
2017-02-17 03:28:53 +01:00
return 0 ;
2017-04-08 14:17:58 +02:00
} else if ( user_select = = ctrtransfer ) { // -> transfer CTRNAND image to SysNAND
2017-03-23 16:00:17 +01:00
char * destdrv [ 2 ] = { NULL } ;
n_opt = 0 ;
if ( DriveType ( " 1: " ) ) {
optionstr [ n_opt ] = " Transfer to SysNAND " ;
destdrv [ n_opt + + ] = " 1: " ;
}
if ( DriveType ( " 4: " ) ) {
optionstr [ n_opt ] = " Transfer to EmuNAND " ;
destdrv [ n_opt + + ] = " 4: " ;
}
user_select = ( n_opt > 1 ) ? ( int ) ShowSelectPrompt ( n_opt , optionstr , pathstr ) : n_opt ;
if ( user_select ) {
ShowPrompt ( false , " %s \n CTRNAND transfer %s " , pathstr ,
( TransferCtrNandImage ( curr_entry - > path , destdrv [ user_select - 1 ] ) = = 0 ) ? " success " : " failed " ) ;
}
2017-03-02 22:40:58 +01:00
return 0 ;
2017-01-02 17:37:08 +01:00
} else if ( user_select = = restore ) { // -> restore SysNAND (A9LH preserving)
ShowPrompt ( false , " %s \n NAND restore %s " , pathstr ,
( SafeRestoreNandDump ( curr_entry - > path ) = = 0 ) ? " success " : " failed " ) ;
2017-02-17 03:28:53 +01:00
return 0 ;
2017-01-25 14:46:29 +01:00
} else if ( ( user_select = = xorpad ) | | ( user_select = = xorpad_inplace ) ) {
bool inplace = ( user_select = = xorpad_inplace ) ;
bool success = ( BuildNcchInfoXorpads ( ( inplace ) ? current_path : OUTPUT_PATH , curr_entry - > path ) = = 0 ) ;
ShowPrompt ( false , " %s \n NCCHinfo padgen %s%s " , pathstr ,
( success ) ? " success " : " failed " ,
( ! success | | inplace ) ? " " : " \n Output dir: " OUTPUT_PATH ) ;
GetDirContents ( current_dir , current_path ) ;
for ( ; * cursor < current_dir - > n_entries ; ( * cursor ) + + ) {
curr_entry = & ( current_dir - > entry [ * cursor ] ) ;
if ( strncasecmp ( curr_entry - > name , NCCHINFO_NAME , 32 ) = = 0 ) break ;
}
if ( * cursor > = current_dir - > n_entries ) {
* scroll = 0 ;
* cursor = 1 ;
}
2017-02-17 03:28:53 +01:00
return 0 ;
} else if ( user_select = = ebackup ) {
ShowString ( " %s \n Updating embedded backup... " , pathstr ) ;
bool required = ( CheckEmbeddedBackup ( curr_entry - > path ) ! = 0 ) ;
bool success = ( required & & ( EmbedEssentialBackup ( curr_entry - > path ) = = 0 ) ) ;
ShowPrompt ( false , " %s \n Backup update: %s " , pathstr , ( ! required ) ? " not required " :
( success ) ? " completed " : " failed! " ) ;
2017-02-23 19:08:15 +01:00
GetDirContents ( current_dir , current_path ) ;
2017-02-17 03:28:53 +01:00
return 0 ;
2017-01-25 16:13:50 -03:00
} else if ( ( user_select = = launch ) ) {
size_t payload_size = FileGetSize ( curr_entry - > path ) ;
2017-01-30 01:49:54 +01:00
if ( ShowUnlockSequence ( 3 , " %s (%dkB) \n Launch as arm9 payload? " , pathstr , payload_size / 1024 ) ) {
2017-01-25 16:13:50 -03:00
if ( FileGetData ( curr_entry - > path , TEMP_BUFFER , payload_size , 0 ) = = payload_size ) {
Chainload ( TEMP_BUFFER , payload_size ) ;
while ( 1 ) ;
} // failed load is basically impossible here
}
2017-02-17 03:28:53 +01:00
return 0 ;
2016-12-13 00:20:00 +01:00
}
2017-02-10 15:23:46 +01:00
return FileHandlerMenu ( current_path , cursor , scroll , current_dir , clipboard ) ;
2016-12-13 00:20:00 +01:00
}
2017-02-28 04:10:23 +01:00
u32 HomeMoreMenu ( char * current_path , DirStruct * current_dir , DirStruct * clipboard ) {
const char * optionstr [ 8 ] ;
2017-03-13 20:20:20 +01:00
const char * promptstr = " HOME more... menu. \n Select action: " ;
2017-02-28 04:10:23 +01:00
u32 n_opt = 0 ;
int sdformat = + + n_opt ;
int bonus = ( GetNandUnusedSectors ( NAND_SYSNAND ) > 0x2000 ) ? ( int ) + + n_opt : - 1 ; // 4MB minsize
int multi = ( CheckMultiEmuNand ( ) ) ? ( int ) + + n_opt : - 1 ;
2017-04-12 00:27:02 +02:00
int bsupport = + + n_opt ;
2017-02-28 04:10:23 +01:00
int hsrestore = ( ( CheckHealthAndSafetyInject ( " 1: " ) = = 0 ) | | ( CheckHealthAndSafetyInject ( " 4: " ) = = 0 ) ) ? ( int ) + + n_opt : - 1 ;
int nandbak = + + n_opt ;
if ( sdformat > 0 ) optionstr [ sdformat - 1 ] = " SD format menu " ;
if ( bonus > 0 ) optionstr [ bonus - 1 ] = " Bonus drive setup " ;
if ( multi > 0 ) optionstr [ multi - 1 ] = " Switch EmuNAND " ;
2017-04-12 00:27:02 +02:00
if ( bsupport > 0 ) optionstr [ bsupport - 1 ] = " Build support files " ;
2017-02-28 04:10:23 +01:00
if ( hsrestore > 0 ) optionstr [ hsrestore - 1 ] = " Restore H&S " ;
if ( nandbak > 0 ) optionstr [ nandbak - 1 ] = " Backup NAND " ;
int user_select = ShowSelectPrompt ( n_opt , optionstr , promptstr ) ;
if ( user_select = = sdformat ) { // format SD card
bool sd_state = CheckSDMountState ( ) ;
if ( clipboard - > n_entries & & ( DriveType ( clipboard - > entry [ 0 ] . path ) & ( DRV_SDCARD | DRV_ALIAS | DRV_EMUNAND | DRV_IMAGE ) ) )
clipboard - > n_entries = 0 ; // remove SD clipboard entries
DeinitExtFS ( ) ;
DeinitSDCardFS ( ) ;
if ( ( SdFormatMenu ( ) = = 0 ) | | sd_state ) { ;
while ( ! InitSDCardFS ( ) & &
2017-04-20 12:31:05 +02:00
ShowPrompt ( true , " Initializing SD card failed! Retry? " ) ) ;
2017-02-28 04:10:23 +01:00
}
ClearScreenF ( true , true , COLOR_STD_BG ) ;
InitEmuNandBase ( true ) ;
InitExtFS ( ) ;
GetDirContents ( current_dir , current_path ) ;
return 0 ;
} else if ( user_select = = bonus ) { // setup bonus drive
if ( clipboard - > n_entries & & ( DriveType ( clipboard - > entry [ 0 ] . path ) & ( DRV_BONUS | DRV_IMAGE ) ) )
clipboard - > n_entries = 0 ; // remove bonus drive clipboard entries
if ( ! SetupBonusDrive ( ) ) ShowPrompt ( false , " Setup failed! " ) ;
ClearScreenF ( true , true , COLOR_STD_BG ) ;
GetDirContents ( current_dir , current_path ) ;
return 0 ;
} else if ( user_select = = multi ) { // switch EmuNAND offset
while ( ShowPrompt ( true , " Current EmuNAND offset is %06X. \n Switch to next offset? " , GetEmuNandBase ( ) ) ) {
if ( clipboard - > n_entries & & ( DriveType ( clipboard - > entry [ 0 ] . path ) & DRV_EMUNAND ) )
clipboard - > n_entries = 0 ; // remove EmuNAND clipboard entries
DismountDriveType ( DRV_EMUNAND ) ;
InitEmuNandBase ( false ) ;
InitExtFS ( ) ;
}
GetDirContents ( current_dir , current_path ) ;
return 0 ;
2017-04-12 00:27:02 +02:00
} else if ( user_select = = bsupport ) { // build support files
bool tik_enc_sys = false ;
bool tik_enc_emu = false ;
if ( BuildTitleKeyInfo ( NULL , false , false ) = = 0 ) {
ShowString ( " Building " TIKDB_NAME_ENC " ... " ) ;
tik_enc_sys = ( BuildTitleKeyInfo ( " 1:/dbs/ticket.db " , false , false ) = = 0 ) ;
tik_enc_emu = ( BuildTitleKeyInfo ( " 4:/dbs/ticket.db " , false , false ) = = 0 ) ;
if ( BuildTitleKeyInfo ( NULL , false , true ) ! = 0 )
tik_enc_sys = tik_enc_emu = false ;
}
bool tik_dec_sys = false ;
bool tik_dec_emu = false ;
if ( BuildTitleKeyInfo ( NULL , true , false ) = = 0 ) {
ShowString ( " Building " TIKDB_NAME_DEC " ... " ) ;
tik_dec_sys = ( BuildTitleKeyInfo ( " 1:/dbs/ticket.db " , true , false ) = = 0 ) ;
tik_dec_emu = ( BuildTitleKeyInfo ( " 4:/dbs/ticket.db " , true , false ) = = 0 ) ;
if ( ! tik_dec_sys | | BuildTitleKeyInfo ( NULL , true , true ) ! = 0 )
tik_dec_sys = tik_dec_emu = false ;
}
bool seed_sys = false ;
bool seed_emu = false ;
if ( BuildSeedInfo ( NULL , false ) = = 0 ) {
ShowString ( " Building " SEEDDB_NAME " ... " ) ;
seed_sys = ( BuildSeedInfo ( " 1: " , false ) = = 0 ) ;
seed_emu = ( BuildSeedInfo ( " 4: " , false ) = = 0 ) ;
if ( ! seed_sys | | BuildSeedInfo ( NULL , true ) ! = 0 )
seed_sys = seed_emu = false ;
}
bool lsector = false ;
u8 legit_sector [ 0x200 ] ;
if ( GetLegitSector0x96 ( legit_sector ) = = 0 ) {
ShowString ( " Searching secret sector... " ) ;
const char * path_sector = OUTPUT_PATH " / " SECRET_NAME ;
2017-04-26 01:53:13 +02:00
// we can safely assume the output path exists at that point
2017-04-12 00:27:02 +02:00
lsector = FileSetData ( path_sector , legit_sector , 0x200 , 0 , true ) ;
}
ShowPrompt ( false , " Built in " OUTPUT_PATH " : \n \n %18.18-s %s \n %18.18-s %s \n %18.18-s %s \n %18.18-s %s " ,
TIKDB_NAME_ENC , tik_enc_sys ? tik_enc_emu ? " OK (Sys&Emu) " : " OK (Sys) " : " Failed " ,
TIKDB_NAME_DEC , tik_dec_sys ? tik_dec_emu ? " OK (Sys&Emu) " : " OK (Sys) " : " Failed " ,
SEEDDB_NAME , seed_sys ? seed_emu ? " OK (Sys&Emu) " : " OK (Sys) " : " Failed " ,
SECRET_NAME , lsector ? " OK (legit) " : " Failed " ) ;
GetDirContents ( current_dir , current_path ) ;
return 0 ;
2017-02-28 04:10:23 +01:00
} else if ( user_select = = hsrestore ) { // restore Health & Safety
n_opt = 0 ;
int sys = ( CheckHealthAndSafetyInject ( " 1: " ) = = 0 ) ? ( int ) + + n_opt : - 1 ;
int emu = ( CheckHealthAndSafetyInject ( " 4: " ) = = 0 ) ? ( int ) + + n_opt : - 1 ;
if ( sys > 0 ) optionstr [ sys - 1 ] = " Restore H&S (SysNAND) " ;
if ( emu > 0 ) optionstr [ emu - 1 ] = " Restore H&S (EmuNAND) " ;
user_select = ( n_opt > 1 ) ? ShowSelectPrompt ( n_opt , optionstr , promptstr ) : n_opt ;
if ( user_select > 0 ) {
InjectHealthAndSafety ( NULL , ( user_select = = sys ) ? " 1: " : " 4: " ) ;
GetDirContents ( current_dir , current_path ) ;
return 0 ;
}
} else if ( user_select = = nandbak ) { // dump NAND backup
n_opt = 0 ;
int sys = ( DriveType ( " 1: " ) ) ? ( int ) + + n_opt : - 1 ;
int emu = ( DriveType ( " 4: " ) ) ? ( int ) + + n_opt : - 1 ;
if ( sys > 0 ) optionstr [ sys - 1 ] = " Backup SysNAND " ;
2017-02-28 16:46:24 +01:00
if ( emu > 0 ) optionstr [ emu - 1 ] = " Backup EmuNAND " ;
2017-02-28 04:10:23 +01:00
user_select = ( n_opt > 1 ) ? ShowSelectPrompt ( n_opt , optionstr , promptstr ) : n_opt ;
if ( user_select > 0 ) {
2017-02-28 17:58:48 +01:00
u32 flags = BUILD_PATH | CALC_SHA ;
2017-03-13 20:20:20 +01:00
if ( PathCopy ( OUTPUT_PATH , ( user_select = = sys ) ? " S:/nand.bin " : " E:/nand.bin " , & flags ) )
ShowPrompt ( false , " NAND backup written to " OUTPUT_PATH ) ;
else ShowPrompt ( false , " NAND backup failed " ) ;
2017-02-28 04:10:23 +01:00
GetDirContents ( current_dir , current_path ) ;
return 0 ;
}
} else return 1 ;
return HomeMoreMenu ( current_path , current_dir , clipboard ) ;
}
2017-03-09 14:35:36 +01:00
u32 SplashInit ( ) {
const char * namestr = FLAVOR " Explorer v " VERSION ;
const char * loadstr = " loading... " ;
const u32 pos_xb = 10 ;
const u32 pos_yb = 10 ;
const u32 pos_xu = SCREEN_WIDTH_BOT - 10 - GetDrawStringWidth ( loadstr ) ;
const u32 pos_yu = SCREEN_HEIGHT - 10 - GetDrawStringHeight ( loadstr ) ;
ClearScreenF ( true , true , COLOR_STD_BG ) ;
QlzDecompress ( TOP_SCREEN , QLZ_SPLASH , 0 ) ;
2017-03-10 13:47:16 +01:00
DrawStringF ( BOT_SCREEN , pos_xb , pos_yb , COLOR_STD_FONT , COLOR_STD_BG , " %s \n %*.*s \n %s \n \n %s \n %s \n \n %s \n %s " ,
namestr , strnlen ( namestr , 64 ) , strnlen ( namestr , 64 ) ,
" ------------------------------ " , " https://github.com/d0k3/GodMode9 " ,
" Releases: " , " https://github.com/d0k3/GodMode9/releases/ " , // this won't fit with a 8px width font
" Hourlies: " , " https://d0k3.secretalgorithm.com/ " ) ;
2017-03-09 14:35:36 +01:00
DrawStringF ( BOT_SCREEN , pos_xu , pos_yu , COLOR_STD_FONT , COLOR_STD_BG , loadstr ) ;
2017-02-28 04:10:23 +01:00
2017-03-09 14:35:36 +01:00
return 0 ;
}
2016-02-25 16:57:01 +01:00
u32 GodMode ( ) {
2016-02-27 19:58:41 +01:00
static const u32 quick_stp = 20 ;
2016-02-25 16:57:01 +01:00
u32 exit_mode = GODMODE_EXIT_REBOOT ;
2016-02-29 16:14:39 +01:00
2016-04-06 16:06:54 +02:00
// reserve 480kB for DirStruct, 64kB for PaneData, just to be safe
2016-11-21 20:04:46 +01:00
static DirStruct * current_dir = ( DirStruct * ) ( DIR_BUFFER + 0x00000 ) ;
static DirStruct * clipboard = ( DirStruct * ) ( DIR_BUFFER + 0x78000 ) ;
static PaneData * panedata = ( PaneData * ) ( DIR_BUFFER + 0xF0000 ) ;
2016-04-06 16:06:54 +02:00
PaneData * pane = panedata ;
2016-02-26 18:52:30 +01:00
char current_path [ 256 ] = { 0x00 } ;
2016-02-29 16:14:39 +01:00
2016-07-22 04:28:11 +02:00
int mark_next = - 1 ;
2016-03-02 14:01:20 +01:00
u32 last_clipboard_size = 0 ;
2016-02-26 18:52:30 +01:00
u32 cursor = 0 ;
2016-03-15 22:19:30 +01:00
u32 scroll = 0 ;
2016-02-25 16:57:01 +01:00
2016-03-01 02:00:48 +01:00
ClearScreenF ( true , true , COLOR_STD_BG ) ;
2016-04-06 16:06:54 +02:00
if ( ( sizeof ( DirStruct ) > 0x78000 ) | | ( N_PANES * sizeof ( PaneData ) > 0x10000 ) ) {
ShowPrompt ( false , " Out of memory! " ) ; // just to be safe
return exit_mode ;
}
2017-03-09 14:35:36 +01:00
SplashInit ( ) ;
timer_start ( ) ; // show splash for at least 1 sec
InitSDCardFS ( ) ;
2017-01-17 21:44:16 +01:00
InitEmuNandBase ( true ) ;
2016-03-11 01:29:14 +01:00
InitNandCrypto ( ) ;
2016-04-05 15:20:48 +02:00
InitExtFS ( ) ;
2016-02-25 16:57:01 +01:00
2017-03-10 13:47:16 +01:00
// this takes long - do it while splash is displayed
2017-03-09 14:35:36 +01:00
GetFreeSpace ( " 0: " ) ;
2016-04-06 12:52:23 +02:00
// could also check for a9lh via this: ((*(vu32*) 0x101401C0) == 0)
2017-02-26 13:35:37 +01:00
if ( ( ! IS_O3DS ) & & ! CheckSlot0x05Crypto ( ) ) {
2016-04-06 12:52:23 +02:00
if ( ! ShowPrompt ( true , " Warning: slot0x05 crypto fail! \n Could not set up slot0x05keyY. \n Continue? " ) ) {
2016-04-05 15:20:48 +02:00
DeinitExtFS ( ) ;
2016-03-22 19:24:21 +01:00
DeinitSDCardFS ( ) ;
return exit_mode ;
}
}
2016-02-29 16:14:39 +01:00
GetDirContents ( current_dir , " " ) ;
clipboard - > n_entries = 0 ;
2016-04-06 16:06:54 +02:00
memset ( panedata , 0x00 , 0x10000 ) ;
2017-03-09 14:35:36 +01:00
while ( timer_sec ( ) < 1 ) ; // show splash for at least 1 sec
ClearScreenF ( true , true , COLOR_STD_BG ) ; // clear splash
2016-03-11 01:29:14 +01:00
while ( true ) { // this is the main loop
2016-10-29 16:02:07 +02:00
int curr_drvtype = DriveType ( current_path ) ;
2016-04-07 14:29:37 +02:00
// basic sanity checking
if ( ! current_dir - > n_entries ) { // current dir is empty -> revert to root
* current_path = ' \0 ' ;
2016-10-29 17:14:24 +02:00
DeinitExtFS ( ) ; // deinit and...
InitExtFS ( ) ; // reinitialize extended file system
2016-04-07 14:29:37 +02:00
GetDirContents ( current_dir , current_path ) ;
cursor = 0 ;
if ( ! current_dir - > n_entries ) { // should not happen, if it does fail gracefully
ShowPrompt ( false , " Invalid directory object " ) ;
return exit_mode ;
}
}
if ( cursor > = current_dir - > n_entries ) // cursor beyond allowed range
cursor = current_dir - > n_entries - 1 ;
2016-04-07 14:44:25 +02:00
DirEntry * curr_entry = & ( current_dir - > entry [ cursor ] ) ;
2016-07-22 04:28:11 +02:00
if ( ( mark_next > = 0 ) & & ( curr_entry - > type ! = T_DOTDOT ) ) {
curr_entry - > marked = mark_next ;
mark_next = - 2 ;
}
2016-03-15 22:19:30 +01:00
DrawDirContents ( current_dir , cursor , & scroll ) ;
2017-04-26 01:53:13 +02:00
DrawUserInterface ( current_path , curr_entry , clipboard , N_PANES ? pane - panedata + 1 : 0 ) ;
2016-02-26 18:52:30 +01:00
u32 pad_state = InputWait ( ) ;
2016-04-02 19:07:46 +02:00
bool switched = ( pad_state & BUTTON_R1 ) ;
2016-03-01 02:00:48 +01:00
2016-04-07 14:44:25 +02:00
// basic navigation commands
2016-07-21 00:29:48 +02:00
if ( ( pad_state & BUTTON_A ) & & ( curr_entry - > type ! = T_FILE ) & & ( curr_entry - > type ! = T_DOTDOT ) ) { // for dirs
2016-12-10 15:32:03 +01:00
if ( switched & & ! ( DriveType ( curr_entry - > path ) & DRV_SEARCH ) ) { // search directory
2017-03-20 02:57:13 +01:00
const char * optionstr [ 3 ] = { " Search for files... " , " Directory info " , " Copy to " OUTPUT_PATH } ;
u32 n_opt = ( * current_path & & ( strncmp ( current_path , OUTPUT_PATH , 256 ) ! = 0 ) ) ? 3 : 2 ;
2017-02-27 02:07:27 +01:00
char namestr [ 32 + 1 ] ;
TruncateString ( namestr , ( * current_path ) ? curr_entry - > path : curr_entry - > name , 32 , 8 ) ;
2017-03-20 02:57:13 +01:00
u32 user_select = ShowSelectPrompt ( n_opt , optionstr , " %s " , namestr ) ;
2017-02-27 02:07:27 +01:00
if ( user_select = = 1 ) {
char searchstr [ 256 ] ;
snprintf ( searchstr , 256 , " * " ) ;
TruncateString ( namestr , curr_entry - > name , 20 , 8 ) ;
if ( ShowStringPrompt ( searchstr , 256 , " Search %s? \n Enter search below. " , namestr ) ) {
SetFSSearch ( searchstr , curr_entry - > path ) ;
snprintf ( current_path , 256 , " Z: " ) ;
GetDirContents ( current_dir , current_path ) ;
if ( current_dir - > n_entries ) ShowPrompt ( false , " Found %lu results. " , current_dir - > n_entries - 1 ) ;
cursor = 1 ;
scroll = 0 ;
}
} else if ( user_select = = 2 ) {
u64 tsize = 0 ;
u32 tdirs = 0 ;
u32 tfiles = 0 ;
if ( DirInfo ( curr_entry - > path , & tsize , & tdirs , & tfiles ) ) {
char bytestr [ 32 ] ;
FormatBytes ( bytestr , tsize ) ;
ShowPrompt ( false , " %s \n %lu files & %lu subdirs \n %s total " , namestr , tfiles , tdirs , bytestr ) ;
} else ShowPrompt ( false , " Analyze dir: failed! " ) ;
2017-03-20 02:57:13 +01:00
} else if ( user_select = = 3 ) {
StandardCopy ( & cursor , current_dir ) ;
2016-07-21 00:29:48 +02:00
}
} else { // one level up
2016-07-26 18:50:58 +02:00
u32 user_select = 1 ;
2016-10-29 16:02:07 +02:00
if ( curr_drvtype & DRV_SEARCH ) { // special menu for search drive
2016-07-26 18:50:58 +02:00
const char * optionstr [ 2 ] = { " Open this folder " , " Open containing folder " } ;
char pathstr [ 32 + 1 ] ;
TruncateString ( pathstr , curr_entry - > path , 32 , 8 ) ;
user_select = ShowSelectPrompt ( 2 , optionstr , pathstr ) ;
}
if ( user_select ) {
strncpy ( current_path , curr_entry - > path , 256 ) ;
if ( user_select = = 2 ) {
char * last_slash = strrchr ( current_path , ' / ' ) ;
if ( last_slash ) * last_slash = ' \0 ' ;
}
GetDirContents ( current_dir , current_path ) ;
if ( * current_path & & ( current_dir - > n_entries > 1 ) ) {
cursor = 1 ;
scroll = 0 ;
} else cursor = 0 ;
}
2016-07-21 00:29:48 +02:00
}
2016-04-08 21:03:40 +02:00
} else if ( ( pad_state & BUTTON_A ) & & ( curr_entry - > type = = T_FILE ) ) { // process a file
2016-12-13 00:20:00 +01:00
FileHandlerMenu ( current_path , & cursor , & scroll , current_dir , clipboard ) ; // processed externally
2016-04-07 14:44:25 +02:00
} else if ( * current_path & & ( ( pad_state & BUTTON_B ) | | // one level down
2016-07-26 18:51:16 +02:00
( ( pad_state & BUTTON_A ) & & ( curr_entry - > type = = T_DOTDOT ) ) ) ) {
if ( switched ) { // use R+B to return to root fast
* current_path = ' \0 ' ;
GetDirContents ( current_dir , current_path ) ;
cursor = scroll = 0 ;
} else {
char old_path [ 256 ] ;
char * last_slash = strrchr ( current_path , ' / ' ) ;
strncpy ( old_path , current_path , 256 ) ;
if ( last_slash ) * last_slash = ' \0 ' ;
else * current_path = ' \0 ' ;
GetDirContents ( current_dir , current_path ) ;
if ( * old_path & & current_dir - > n_entries ) {
for ( cursor = current_dir - > n_entries - 1 ;
( cursor > 0 ) & & ( strncmp ( current_dir - > entry [ cursor ] . path , old_path , 256 ) ! = 0 ) ; cursor - - ) ;
if ( * current_path & & ! cursor & & ( current_dir - > n_entries > 1 ) ) cursor = 1 ; // don't set it on the dotdot
scroll = 0 ;
}
2016-03-15 22:19:30 +01:00
}
2016-04-05 20:41:40 +02:00
} else if ( switched & & ( pad_state & BUTTON_B ) ) { // unmount SD card
2016-04-05 15:20:48 +02:00
DeinitExtFS ( ) ;
2016-12-19 14:32:22 +01:00
if ( ! CheckSDMountState ( ) ) {
while ( ! InitSDCardFS ( ) & &
2017-03-01 17:03:27 +01:00
ShowPrompt ( true , " Initialising SD card failed! Retry? " ) ) ;
2016-12-19 14:32:22 +01:00
} else {
DeinitSDCardFS ( ) ;
if ( clipboard - > n_entries & & ( DriveType ( clipboard - > entry [ 0 ] . path ) &
( DRV_SDCARD | DRV_ALIAS | DRV_EMUNAND | DRV_IMAGE ) ) )
clipboard - > n_entries = 0 ; // remove SD clipboard entries
2016-03-16 01:26:17 +01:00
}
2016-12-13 17:10:39 +01:00
ClearScreenF ( true , true , COLOR_STD_BG ) ;
2017-01-17 21:44:16 +01:00
InitEmuNandBase ( true ) ;
2016-04-05 15:20:48 +02:00
InitExtFS ( ) ;
2016-03-16 01:26:17 +01:00
GetDirContents ( current_dir , current_path ) ;
2016-03-17 19:41:27 +01:00
if ( cursor > = current_dir - > n_entries ) cursor = 0 ;
2016-04-06 16:06:54 +02:00
} else if ( ( pad_state & BUTTON_DOWN ) & & ( cursor + 1 < current_dir - > n_entries ) ) { // cursor down
2016-07-22 04:28:11 +02:00
if ( pad_state & BUTTON_L1 ) mark_next = curr_entry - > marked ;
2016-02-26 18:52:30 +01:00
cursor + + ;
2016-04-06 16:06:54 +02:00
} else if ( ( pad_state & BUTTON_UP ) & & cursor ) { // cursor up
2016-07-22 04:28:11 +02:00
if ( pad_state & BUTTON_L1 ) mark_next = curr_entry - > marked ;
2016-02-26 18:52:30 +01:00
cursor - - ;
2016-04-06 16:06:54 +02:00
} else if ( switched & & ( pad_state & ( BUTTON_RIGHT | BUTTON_LEFT ) ) ) { // switch pane
memcpy ( pane - > path , current_path , 256 ) ; // store state in current pane
pane - > cursor = cursor ;
pane - > scroll = scroll ;
( pad_state & BUTTON_LEFT ) ? pane - - : pane + + ; // switch to next
if ( pane < panedata ) pane + = N_PANES ;
else if ( pane > = panedata + N_PANES ) pane - = N_PANES ;
memcpy ( current_path , pane - > path , 256 ) ; // get state from next pane
cursor = pane - > cursor ;
scroll = pane - > scroll ;
GetDirContents ( current_dir , current_path ) ;
2016-07-22 04:28:11 +02:00
} else if ( ( pad_state & BUTTON_RIGHT ) & & ! ( pad_state & BUTTON_L1 ) ) { // cursor down (quick)
2016-02-27 19:58:41 +01:00
cursor + = quick_stp ;
2016-07-22 04:28:11 +02:00
} else if ( ( pad_state & BUTTON_LEFT ) & & ! ( pad_state & BUTTON_L1 ) ) { // cursor up (quick)
2016-02-27 19:58:41 +01:00
cursor = ( cursor > = quick_stp ) ? cursor - quick_stp : 0 ;
2016-02-29 16:47:38 +01:00
} else if ( pad_state & BUTTON_RIGHT ) { // mark all entries
2016-03-02 19:36:20 +01:00
for ( u32 c = 1 ; c < current_dir - > n_entries ; c + + ) current_dir - > entry [ c ] . marked = 1 ;
2016-07-22 04:28:11 +02:00
mark_next = 1 ;
2016-02-29 16:47:38 +01:00
} else if ( pad_state & BUTTON_LEFT ) { // unmark all entries
2016-03-02 19:36:20 +01:00
for ( u32 c = 1 ; c < current_dir - > n_entries ; c + + ) current_dir - > entry [ c ] . marked = 0 ;
2016-07-22 04:28:11 +02:00
mark_next = 0 ;
2016-03-01 02:00:48 +01:00
} else if ( switched & & ( pad_state & BUTTON_L1 ) ) { // switched L -> screenshot
CreateScreenshot ( ) ;
ClearScreenF ( true , true , COLOR_STD_BG ) ;
2016-07-22 04:28:11 +02:00
} else if ( * current_path & & ( pad_state & BUTTON_L1 ) & & ( curr_entry - > type ! = T_DOTDOT ) ) {
// unswitched L - mark/unmark single entry
if ( mark_next < - 1 ) mark_next = - 1 ;
else curr_entry - > marked ^ = 0x1 ;
2016-03-02 14:01:20 +01:00
} else if ( pad_state & BUTTON_SELECT ) { // clear/restore clipboard
clipboard - > n_entries = ( clipboard - > n_entries > 0 ) ? 0 : last_clipboard_size ;
2016-02-26 18:52:30 +01:00
}
2016-03-01 02:00:48 +01:00
// highly specific commands
2016-05-29 14:45:12 +02:00
if ( ! * current_path ) { // in the root folder...
2016-12-19 14:32:22 +01:00
if ( switched & & ( pad_state & BUTTON_X ) ) { // unmount image
2016-12-13 16:00:14 +01:00
if ( clipboard - > n_entries & & ( DriveType ( clipboard - > entry [ 0 ] . path ) & DRV_IMAGE ) )
2016-11-02 15:24:22 +01:00
clipboard - > n_entries = 0 ; // remove last mounted image clipboard entries
2016-12-19 14:32:22 +01:00
InitImgFS ( NULL ) ;
2016-12-13 16:00:14 +01:00
ClearScreenF ( false , true , COLOR_STD_BG ) ;
2016-04-05 20:41:40 +02:00
GetDirContents ( current_dir , current_path ) ;
2016-05-29 14:45:12 +02:00
} else if ( switched & & ( pad_state & BUTTON_Y ) ) {
2017-02-16 02:44:49 +01:00
SetWritePermissions ( PERM_BASE , false ) ;
ClearScreenF ( false , true , COLOR_STD_BG ) ;
2016-03-01 02:00:48 +01:00
}
} else if ( ! switched ) { // standard unswitched command set
2016-10-29 16:02:07 +02:00
if ( ( curr_drvtype & DRV_VIRTUAL ) & & ( pad_state & BUTTON_X ) ) {
2016-03-21 18:53:09 +01:00
ShowPrompt ( false , " Not allowed in virtual path " ) ;
} else if ( pad_state & BUTTON_X ) { // delete a file
2016-03-02 14:01:20 +01:00
u32 n_marked = 0 ;
2016-12-26 18:21:05 +01:00
if ( curr_entry - > marked ) {
for ( u32 c = 0 ; c < current_dir - > n_entries ; c + + )
if ( current_dir - > entry [ c ] . marked ) n_marked + + ;
}
2016-03-02 14:01:20 +01:00
if ( n_marked ) {
if ( ShowPrompt ( true , " Delete %u path(s)? " , n_marked ) ) {
u32 n_errors = 0 ;
2017-03-02 22:40:58 +01:00
ShowString ( " Deleting files, please wait... " ) ;
2016-03-02 14:01:20 +01:00
for ( u32 c = 0 ; c < current_dir - > n_entries ; c + + )
if ( current_dir - > entry [ c ] . marked & & ! PathDelete ( current_dir - > entry [ c ] . path ) )
n_errors + + ;
2016-06-10 16:21:25 +02:00
ClearScreenF ( true , false , COLOR_STD_BG ) ;
2016-03-02 14:01:20 +01:00
if ( n_errors ) ShowPrompt ( false , " Failed deleting %u/%u path(s) " , n_errors , n_marked ) ;
}
2016-04-07 14:44:25 +02:00
} else if ( curr_entry - > type ! = T_DOTDOT ) {
2016-03-02 17:22:44 +01:00
char namestr [ 36 + 1 ] ;
2016-06-15 16:30:37 +02:00
TruncateString ( namestr , curr_entry - > name , 28 , 12 ) ;
2016-06-10 16:21:25 +02:00
if ( ShowPrompt ( true , " Delete \" %s \" ? " , namestr ) ) {
2017-03-02 22:40:58 +01:00
ShowString ( " Deleting files, please wait... " ) ;
2016-06-10 16:21:25 +02:00
if ( ! PathDelete ( curr_entry - > path ) )
ShowPrompt ( false , " Failed deleting: \n %s " , namestr ) ;
ClearScreenF ( true , false , COLOR_STD_BG ) ;
}
2016-03-02 14:01:20 +01:00
}
GetDirContents ( current_dir , current_path ) ;
2016-03-01 02:00:48 +01:00
} else if ( ( pad_state & BUTTON_Y ) & & ( clipboard - > n_entries = = 0 ) ) { // fill clipboard
for ( u32 c = 0 ; c < current_dir - > n_entries ; c + + ) {
if ( current_dir - > entry [ c ] . marked ) {
current_dir - > entry [ c ] . marked = 0 ;
2016-03-02 19:36:20 +01:00
DirEntryCpy ( & ( clipboard - > entry [ clipboard - > n_entries ] ) , & ( current_dir - > entry [ c ] ) ) ;
2016-03-01 02:00:48 +01:00
clipboard - > n_entries + + ;
}
}
2016-04-07 14:44:25 +02:00
if ( ( clipboard - > n_entries = = 0 ) & & ( curr_entry - > type ! = T_DOTDOT ) ) {
DirEntryCpy ( & ( clipboard - > entry [ 0 ] ) , curr_entry ) ;
2016-03-01 02:00:48 +01:00
clipboard - > n_entries = 1 ;
}
2016-03-02 19:36:20 +01:00
if ( clipboard - > n_entries )
last_clipboard_size = clipboard - > n_entries ;
2016-10-29 16:02:07 +02:00
} else if ( ( curr_drvtype & DRV_SEARCH ) & & ( pad_state & BUTTON_Y ) ) {
2016-10-28 21:30:10 +02:00
ShowPrompt ( false , " Not allowed in search drive " ) ;
2016-12-02 12:49:41 +01:00
} else if ( ( curr_drvtype & DRV_GAME ) & & ( pad_state & BUTTON_Y ) ) {
ShowPrompt ( false , " Not allowed in virtual game path " ) ;
2016-12-02 15:42:05 +01:00
} else if ( ( curr_drvtype & DRV_XORPAD ) & & ( pad_state & BUTTON_Y ) ) {
ShowPrompt ( false , " Not allowed in XORpad drive " ) ;
2017-01-13 14:20:42 +01:00
} else if ( ( curr_drvtype & DRV_CART ) & & ( pad_state & BUTTON_Y ) ) {
ShowPrompt ( false , " Not allowed in gamecart drive " ) ;
} else if ( pad_state & BUTTON_Y ) { // paste files
2016-04-29 02:21:36 +02:00
const char * optionstr [ 2 ] = { " Copy path(s) " , " Move path(s) " } ;
2016-03-02 14:01:20 +01:00
char promptstr [ 64 ] ;
2016-07-27 00:19:12 +02:00
u32 flags = 0 ;
2016-04-29 02:21:36 +02:00
u32 user_select ;
2016-03-02 14:01:20 +01:00
if ( clipboard - > n_entries = = 1 ) {
char namestr [ 20 + 1 ] ;
TruncateString ( namestr , clipboard - > entry [ 0 ] . name , 20 , 12 ) ;
2016-06-18 15:26:11 +02:00
snprintf ( promptstr , 64 , " Paste \" %s \" here? " , namestr ) ;
} else snprintf ( promptstr , 64 , " Paste %lu paths here? " , clipboard - > n_entries ) ;
2016-10-29 16:02:07 +02:00
user_select = ( ( DriveType ( clipboard - > entry [ 0 ] . path ) & curr_drvtype & DRV_STDFAT ) ) ?
2016-06-18 15:26:11 +02:00
ShowSelectPrompt ( 2 , optionstr , promptstr ) : ( ShowPrompt ( true , promptstr ) ? 1 : 0 ) ;
if ( user_select ) {
2016-03-02 14:01:20 +01:00
for ( u32 c = 0 ; c < clipboard - > n_entries ; c + + ) {
2016-04-29 02:21:36 +02:00
char namestr [ 36 + 1 ] ;
TruncateString ( namestr , clipboard - > entry [ c ] . name , 36 , 12 ) ;
2016-07-27 00:19:12 +02:00
flags & = ~ ASK_ALL ;
if ( c < clipboard - > n_entries - 1 ) flags | = ASK_ALL ;
if ( ( user_select = = 1 ) & & ! PathCopy ( current_path , clipboard - > entry [ c ] . path , & flags ) ) {
2016-03-02 14:01:20 +01:00
if ( c + 1 < clipboard - > n_entries ) {
2016-03-02 17:22:44 +01:00
if ( ! ShowPrompt ( true , " Failed copying path: \n %s \n Process remaining? " , namestr ) ) break ;
} else ShowPrompt ( false , " Failed copying path: \n %s " , namestr ) ;
2016-07-27 00:19:12 +02:00
} else if ( ( user_select = = 2 ) & & ! PathMove ( current_path , clipboard - > entry [ c ] . path , & flags ) ) {
2016-04-29 02:21:36 +02:00
if ( c + 1 < clipboard - > n_entries ) {
if ( ! ShowPrompt ( true , " Failed moving path: \n %s \n Process remaining? " , namestr ) ) break ;
} else ShowPrompt ( false , " Failed moving path: \n %s " , namestr ) ;
2016-03-02 14:01:20 +01:00
}
2016-06-18 15:26:11 +02:00
}
clipboard - > n_entries = 0 ;
GetDirContents ( current_dir , current_path ) ;
2016-03-02 14:01:20 +01:00
}
ClearScreenF ( true , false , COLOR_STD_BG ) ;
2016-03-01 02:00:48 +01:00
}
} else { // switched command set
2016-10-29 16:02:07 +02:00
if ( ( curr_drvtype & DRV_VIRTUAL ) & & ( pad_state & ( BUTTON_X | BUTTON_Y ) ) ) {
2016-03-21 18:53:09 +01:00
ShowPrompt ( false , " Not allowed in virtual path " ) ;
2016-10-29 17:14:02 +02:00
} else if ( ( curr_drvtype & DRV_ALIAS ) & & ( pad_state & ( BUTTON_X ) ) ) {
2016-10-28 21:30:10 +02:00
ShowPrompt ( false , " Not allowed in alias path " ) ;
2016-04-07 14:44:25 +02:00
} else if ( ( pad_state & BUTTON_X ) & & ( curr_entry - > type ! = T_DOTDOT ) ) { // rename a file
2016-03-14 23:38:43 +01:00
char newname [ 256 ] ;
char namestr [ 20 + 1 ] ;
2016-04-07 14:44:25 +02:00
TruncateString ( namestr , curr_entry - > name , 20 , 12 ) ;
snprintf ( newname , 255 , curr_entry - > name ) ;
2016-06-13 23:51:41 +02:00
if ( ShowStringPrompt ( newname , 256 , " Rename %s? \n Enter new name below. " , namestr ) ) {
2016-04-07 14:44:25 +02:00
if ( ! PathRename ( curr_entry - > path , newname ) )
2016-03-14 23:38:43 +01:00
ShowPrompt ( false , " Failed renaming path: \n %s " , namestr ) ;
2016-03-15 22:19:30 +01:00
else {
GetDirContents ( current_dir , current_path ) ;
2016-04-07 14:44:25 +02:00
for ( cursor = ( current_dir - > n_entries ) ? current_dir - > n_entries - 1 : 0 ;
2016-03-15 22:19:30 +01:00
( cursor > 1 ) & & ( strncmp ( current_dir - > entry [ cursor ] . name , newname , 256 ) ! = 0 ) ; cursor - - ) ;
}
2016-03-14 23:38:43 +01:00
}
} else if ( pad_state & BUTTON_Y ) { // create a folder
char dirname [ 256 ] ;
snprintf ( dirname , 255 , " newdir " ) ;
2016-06-13 23:51:41 +02:00
if ( ShowStringPrompt ( dirname , 256 , " Create a new folder here? \n Enter name below. " ) ) {
2016-03-14 23:58:25 +01:00
if ( ! DirCreate ( current_path , dirname ) ) {
2016-03-15 22:19:30 +01:00
char namestr [ 36 + 1 ] ;
TruncateString ( namestr , dirname , 36 , 12 ) ;
ShowPrompt ( false , " Failed creating folder: \n %s " , namestr ) ;
} else {
GetDirContents ( current_dir , current_path ) ;
2016-04-07 14:44:25 +02:00
for ( cursor = ( current_dir - > n_entries ) ? current_dir - > n_entries - 1 : 0 ;
2016-03-15 22:19:30 +01:00
( cursor > 1 ) & & ( strncmp ( current_dir - > entry [ cursor ] . name , dirname , 256 ) ! = 0 ) ; cursor - - ) ;
}
2016-03-14 23:58:25 +01:00
}
2016-03-14 23:38:43 +01:00
}
2016-02-29 16:47:38 +01:00
}
2016-03-01 02:00:48 +01:00
2016-02-26 18:52:30 +01:00
if ( pad_state & BUTTON_START ) {
2016-05-19 22:26:28 +02:00
exit_mode = ( switched | | ( pad_state & BUTTON_LEFT ) ) ? GODMODE_EXIT_POWEROFF : GODMODE_EXIT_REBOOT ;
2016-02-26 18:52:30 +01:00
break ;
2016-10-17 23:45:22 +02:00
} else if ( pad_state & BUTTON_POWER ) {
exit_mode = GODMODE_EXIT_POWEROFF ;
break ;
2016-10-22 18:07:20 +02:00
} else if ( pad_state & BUTTON_HOME ) { // Home menu
2017-02-28 04:10:23 +01:00
const char * optionstr [ ] = { " Poweroff system " , " Reboot system " , " More... " } ;
const char * promptstr = " HOME button pressed. \n Select action: " ;
2017-02-20 22:59:03 +01:00
u32 n_opt = 3 ;
2017-02-28 04:10:23 +01:00
u32 user_select = 0 ;
while ( ( ( user_select = ShowSelectPrompt ( n_opt , optionstr , promptstr ) ) = = 3 ) & &
( HomeMoreMenu ( current_path , current_dir , clipboard ) = = 1 ) ) ; // more... menu
2016-10-22 18:07:20 +02:00
if ( user_select = = 1 ) {
exit_mode = GODMODE_EXIT_POWEROFF ;
break ;
} else if ( user_select = = 2 ) {
exit_mode = GODMODE_EXIT_REBOOT ;
break ;
}
2017-01-17 23:24:25 +01:00
} else if ( pad_state & ( CART_INSERT | CART_EJECT ) ) {
if ( ! InitVCartDrive ( ) & & ( pad_state & CART_INSERT ) ) // reinit virtual cart drive
ShowPrompt ( false , " Cart init failed! " ) ;
if ( ! ( * current_path ) | | ( curr_drvtype & DRV_CART ) )
GetDirContents ( current_dir , current_path ) ; // refresh dir contents
2017-03-01 17:03:27 +01:00
} else if ( pad_state & SD_INSERT ) {
while ( ! InitSDCardFS ( ) & & ShowPrompt ( true , " Initialising SD card failed! Retry? " ) ) ;
ClearScreenF ( true , true , COLOR_STD_BG ) ;
InitEmuNandBase ( true ) ;
InitExtFS ( ) ;
GetDirContents ( current_dir , current_path ) ;
} else if ( ( pad_state & SD_EJECT ) & & CheckSDMountState ( ) ) {
ShowPrompt ( false , " !Unexpected SD card removal! \n \n To prevent data loss, unmount \n before ejecting the SD card. " ) ;
DeinitExtFS ( ) ;
DeinitSDCardFS ( ) ;
InitExtFS ( ) ;
if ( clipboard - > n_entries & & ( DriveType ( clipboard - > entry [ 0 ] . path ) &
( DRV_SDCARD | DRV_ALIAS | DRV_EMUNAND | DRV_IMAGE ) ) )
clipboard - > n_entries = 0 ; // remove SD clipboard entries
GetDirContents ( current_dir , current_path ) ;
2016-10-17 23:45:22 +02:00
}
2016-02-26 18:52:30 +01:00
}
2016-02-25 16:57:01 +01:00
2016-04-05 15:20:48 +02:00
DeinitExtFS ( ) ;
2016-03-21 23:18:33 +01:00
DeinitSDCardFS ( ) ;
2016-02-25 16:57:01 +01:00
return exit_mode ;
}