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"
2017-05-19 02:29:23 +02:00
# include "fsgame.h"
2017-09-04 16:18:07 +02:00
# include "scripting.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"
2017-09-13 16:18:02 +02:00
# include "sysinfo.h"
2016-04-04 22:45:49 +02:00
# include "image.h"
2017-07-30 22:51:49 +02:00
# include "bootfirm.h"
2017-03-09 14:35:36 +01:00
# include "qlzcomp.h"
# include "timer.h"
2017-08-04 02:08:56 +02:00
# include "rtc.h"
2017-09-13 16:18:02 +02:00
# include "power.h"
# include "vff.h"
2017-03-10 13:47:16 +01:00
# include QLZ_SPLASH_H
2017-09-08 15:39:06 +02:00
# ifdef AUTORUN_SCRIPT
# include "autorun_gm9.h"
# endif
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))
2017-09-19 15:58:43 +02:00
# define BOOTMENU_KEY BUTTON_R1|BUTTON_LEFT
# define BOOTFIRM_PATHS "0: / bootonce.firm", "0: / boot.firm", "1: / boot.firm"
2017-09-16 13:48:31 +02:00
# define BOOTFIRM_TEMPS 0x1 // bits mark paths as temporary
2017-09-13 19:40:06 +02:00
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
2017-09-16 16:46:30 +02:00
void GetTimeString ( char * timestr , bool forced_update , bool full_year ) {
2017-08-04 02:08:56 +02:00
static DsTime dstime ;
static u64 timer = ( u64 ) - 1 ; // this ensures we don't check the time too often
if ( forced_update | | ( timer = = ( u64 ) - 1 ) | | ( timer_sec ( timer ) > 30 ) ) {
get_dstime ( & dstime ) ;
timer = timer_start ( ) ;
}
2017-09-16 16:46:30 +02:00
if ( timestr ) snprintf ( timestr , 31 , " %s%02lX-%02lX-%02lX %02lX:%02lX " , full_year ? " 20 " : " " ,
2017-08-08 12:30:33 +02:00
( u32 ) dstime . bcd_Y , ( u32 ) dstime . bcd_M , ( u32 ) dstime . bcd_D , ( u32 ) dstime . bcd_h , ( u32 ) dstime . bcd_m ) ;
2017-08-04 02:08:56 +02:00
}
2017-09-18 19:57:43 +02:00
void CheckBattery ( u32 * battery , bool * is_charging ) {
if ( battery ) {
static u32 battery_l = 0 ;
static u64 timer_b = ( u64 ) - 1 ; // this ensures we don't check too often
if ( ( timer_b = = ( u64 ) - 1 ) | | ( timer_sec ( timer_b ) > = 120 ) ) {
battery_l = GetBatteryPercent ( ) ;
timer_b = timer_start ( ) ;
}
* battery = battery_l ;
}
if ( is_charging ) {
static bool is_charging_l = false ;
static u64 timer_c = ( u64 ) - 1 ;
if ( ( timer_c = = ( u64 ) - 1 ) | | ( timer_sec ( timer_c ) > = 1 ) ) {
is_charging_l = IsCharging ( ) ;
timer_c = timer_start ( ) ;
}
* is_charging = is_charging_l ;
}
}
void GenerateBatteryBitmap ( u8 * bitmap , u32 width , u32 height , u32 color_bg ) {
const u32 color_outline = COLOR_BLACK ;
2017-09-21 01:31:48 +02:00
const u32 color_inline = COLOR_LIGHTGREY ;
const u32 color_inside = COLOR_LIGHTERGREY ;
2017-09-18 19:57:43 +02:00
if ( ( width < 8 ) | | ( height < 6 ) ) return ;
u32 battery ;
bool is_charging ;
CheckBattery ( & battery , & is_charging ) ;
u32 color_battery = ( is_charging ) ? COLOR_BLUE :
( battery > 70 ) ? COLOR_GREEN : ( battery > 30 ) ? COLOR_YELLOW : COLOR_RED ;
u32 nub_size = ( height < 12 ) ? 1 : 2 ;
u32 width_inside = width - 4 - nub_size ;
u32 width_battery = ( battery > = 100 ) ? width_inside : ( ( battery * width_inside ) + 50 ) / 100 ;
for ( u32 y = 0 ; y < height ; y + + ) {
const u32 mirror_y = ( y > = ( height + 1 ) / 2 ) ? height - 1 - y : y ;
for ( u32 x = 0 ; x < width ; x + + ) {
const u32 rev_x = width - x - 1 ;
u32 color = 0 ;
if ( mirror_y = = 0 ) color = ( rev_x > = nub_size ) ? color_outline : color_bg ;
else if ( mirror_y = = 1 ) color = ( ( x = = 0 ) | | ( rev_x = = nub_size ) ) ? color_outline : ( rev_x < nub_size ) ? color_bg : color_inline ;
else if ( mirror_y = = 2 ) color = ( ( x = = 0 ) | | ( rev_x < = nub_size ) ) ? color_outline : ( ( x = = 1 ) | | ( rev_x = = ( nub_size + 1 ) ) ) ? color_inline : color_inside ;
else color = ( ( x = = 0 ) | | ( rev_x = = 0 ) ) ? color_outline : ( ( x = = 1 ) | | ( rev_x < = ( nub_size + 1 ) ) ) ? color_inline : color_inside ;
if ( ( color = = color_inside ) & & ( x < ( 2 + width_battery ) ) ) color = color_battery ;
* ( bitmap + + ) = color > > 16 ; // B
* ( bitmap + + ) = color > > 8 ; // G
* ( bitmap + + ) = color & 0xFF ; // R
}
2017-09-16 16:46:30 +02:00
}
}
2017-08-29 03:29:29 +02:00
void DrawTopBar ( const char * curr_path ) {
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 len_path = SCREEN_WIDTH_TOP - 120 ;
2016-04-02 19:07:46 +02:00
char tempstr [ 64 ] ;
2016-03-01 02:00:48 +01:00
2017-08-29 03:29:29 +02:00
// top bar - current path
2017-04-26 01:53:13 +02:00
DrawRectangle ( TOP_SCREEN , 0 , 0 , SCREEN_WIDTH_TOP , 12 , COLOR_TOP_BAR ) ;
2017-08-15 16:39:15 +02:00
if ( * curr_path ) TruncateString ( tempstr , curr_path , len_path / FONT_WIDTH_EXT , 8 ) ;
else snprintf ( tempstr , 16 , " [root] " ) ;
DrawStringF ( TOP_SCREEN , bartxt_x , bartxt_start , COLOR_STD_BG , COLOR_TOP_BAR , tempstr ) ;
bool show_time = true ;
2017-08-29 03:29:29 +02:00
2017-08-15 16:39:15 +02:00
# ifdef SHOW_FREE
2017-08-29 03:29:29 +02:00
if ( * curr_path ) { // free & total storage
2017-09-18 19:57:43 +02:00
const u32 bartxt_rx = SCREEN_WIDTH_TOP - ( 19 * FONT_WIDTH_EXT ) - bartxt_x ;
2016-04-02 19:07:46 +02:00
char bytestr0 [ 32 ] ;
char bytestr1 [ 32 ] ;
2017-08-16 15:50:46 +02:00
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 ) ;
2017-08-15 16:39:15 +02:00
show_time = false ;
}
# endif
2017-08-29 03:29:29 +02:00
2017-09-16 16:46:30 +02:00
if ( show_time ) { // clock & battery
2017-09-18 19:57:43 +02:00
const u32 battery_width = 16 ;
const u32 battery_height = 9 ;
const u32 battery_x = SCREEN_WIDTH_TOP - battery_width - bartxt_x ;
const u32 battery_y = ( 12 - battery_height ) / 2 ;
const u32 clock_x = battery_x - ( 15 * FONT_WIDTH_EXT ) ;
2017-08-04 02:08:56 +02:00
char timestr [ 32 ] ;
2017-09-16 16:46:30 +02:00
GetTimeString ( timestr , false , false ) ;
2017-09-18 19:57:43 +02:00
DrawStringF ( TOP_SCREEN , clock_x , bartxt_start , COLOR_STD_BG , COLOR_TOP_BAR , " %14.14s " , timestr ) ;
u8 bitmap [ battery_width * battery_height * BYTES_PER_PIXEL ] ;
GenerateBatteryBitmap ( bitmap , battery_width , battery_height , COLOR_TOP_BAR ) ;
DrawBitmap ( TOP_SCREEN , battery_x , battery_y , battery_width , battery_height , bitmap ) ;
2016-02-27 19:58:41 +01:00
}
2017-08-29 03:29:29 +02:00
}
void DrawUserInterface ( const char * curr_path , DirEntry * curr_entry , DirStruct * clipboard , u32 curr_pane ) {
const u32 n_cb_show = 8 ;
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_info = ( SCREEN_WIDTH_MAIN - ( ( SCREEN_WIDTH_MAIN > = 400 ) ? 80 : 20 ) ) / 2 ;
char tempstr [ 64 ] ;
static u32 state_prev = 0xFFFFFFFF ;
u32 state_curr =
( ( * curr_path ) ? ( 1 < < 0 ) : 0 ) |
( ( clipboard - > n_entries ) ? ( 1 < < 1 ) : 0 ) |
( ( CheckSDMountState ( ) ) ? ( 1 < < 2 ) : 0 ) |
( ( GetMountState ( ) ) ? ( 1 < < 3 ) : 0 ) |
( ( GetWritePermissions ( ) > PERM_BASE ) ? ( 1 < < 4 ) : 0 ) |
( curr_pane < < 5 ) ;
if ( state_prev ! = state_curr ) {
ClearScreenF ( true , false , COLOR_STD_BG ) ;
state_prev = state_curr ;
}
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 " ,
2017-08-16 15:50:46 +02:00
FLAVOR " " VERSION , // generic start part
2017-08-01 02:03:05 +02: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 files / [+R] CREATE entry \n " :
" L - MARK files (use with \x18 \x19 \x1A \x1B ) \n X - DELETE / [+R] RENAME file(s) \n Y - PASTE files / [+R] CREATE entry \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-02-26 17:03:25 +01:00
const u32 stp_y = 12 ;
2017-08-29 03:29:29 +02:00
const u32 start_y = ( MAIN_SCREEN = = TOP_SCREEN ) ? 0 : stp_y ;
2016-02-26 17:03:25 +01:00
const u32 pos_x = 0 ;
2017-08-29 03:29:29 +02:00
const u32 lines = ( SCREEN_HEIGHT - ( start_y + 2 ) + ( stp_y - 1 ) ) / stp_y ;
u32 pos_y = start_y + 2 ;
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
2017-08-29 03:29:29 +02:00
const u32 flist_height = ( SCREEN_HEIGHT - start_y ) ;
const u32 bar_width = 2 ;
if ( contents - > n_entries > lines ) { // draw position bar at the right
const u32 bar_height_min = 32 ;
u32 bar_height = ( lines * flist_height ) / contents - > n_entries ;
2016-02-28 13:11:07 +01:00
if ( bar_height < bar_height_min ) bar_height = bar_height_min ;
2017-08-29 03:29:29 +02:00
const u32 bar_pos = ( ( u64 ) * scroll * ( flist_height - bar_height ) ) / ( contents - > n_entries - lines ) + start_y ;
2016-02-28 13:11:07 +01:00
2017-08-29 03:29:29 +02:00
DrawRectangle ( ALT_SCREEN , SCREEN_WIDTH_ALT - bar_width , start_y , bar_width , ( bar_pos - start_y ) , COLOR_STD_BG ) ;
DrawRectangle ( ALT_SCREEN , SCREEN_WIDTH_ALT - bar_width , bar_pos + bar_height , bar_width , SCREEN_HEIGHT - ( bar_pos + bar_height ) , COLOR_STD_BG ) ;
2017-04-24 21:48:21 +02:00
DrawRectangle ( ALT_SCREEN , SCREEN_WIDTH_ALT - bar_width , bar_pos , bar_width , bar_height , COLOR_SIDE_BAR ) ;
2017-08-29 03:29:29 +02:00
} else DrawRectangle ( ALT_SCREEN , SCREEN_WIDTH_ALT - bar_width , start_y , bar_width , flist_height , COLOR_STD_BG ) ;
2016-02-25 16:57:01 +01:00
}
2016-07-13 19:59:36 +02:00
u32 SdFormatMenu ( void ) {
2016-09-07 00:18:49 +02:00
const u32 cluster_size_table [ 5 ] = { 0x0 , 0x0 , 0x4000 , 0x8000 , 0x10000 } ;
2017-06-16 13:18:29 +02:00
const char * option_emunand_size [ 6 ] = { " No EmuNAND " , " RedNAND size (min) " , " EmuNAND size (full) " , " User input... " } ;
2017-01-18 18:07:12 +01:00
const char * option_cluster_size [ 4 ] = { " Auto " , " 16KB Clusters " , " 32KB Clusters " , " 64KB Clusters " } ;
2017-05-31 23:26:45 +02:00
u64 sysnand_size_mb = ( ( ( u64 ) GetNandSizeSectors ( NAND_SYSNAND ) * 0x200 ) + 0xFFFFF ) / 0x100000 ;
u64 sysnand_min_size_mb = ( ( ( u64 ) GetNandMinSizeSectors ( NAND_SYSNAND ) * 0x200 ) + 0xFFFFF ) / 0x100000 ;
2017-01-18 18:07:12 +01:00
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 ;
}
2017-06-16 13:18:29 +02:00
user_select = ShowSelectPrompt ( 4 , option_emunand_size , " Format SD card (%lluMB)? \n Choose EmuNAND size: " , sdcard_size_mb ) ;
if ( user_select & & ( user_select < 4 ) ) {
emunand_size_mb = ( user_select = = 2 ) ? sysnand_min_size_mb : ( user_select = = 3 ) ? sysnand_size_mb : 0 ;
} else if ( user_select = = 4 ) do {
emunand_size_mb = ShowNumberPrompt ( sysnand_min_size_mb , " SD card size is %lluMB. \n Enter EmuNAND size (MB) below: " , sdcard_size_mb ) ;
2016-07-13 19:59:36 +02:00
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-05-31 23:26:45 +02:00
if ( emunand_size_mb > = sysnand_min_size_mb ) {
const char * option_emunand_type [ 3 ] = { " RedNAND type " , " GW EmuNAND type " , " Don't set up " } ;
if ( emunand_size_mb > = sysnand_size_mb )
user_select = ShowSelectPrompt ( 3 , option_emunand_type , " Choose EmuNAND type to set up: " ) ;
else user_select = ShowPrompt ( true , " Clone SysNAND to RedNAND now? " ) ? 1 : 0 ;
if ( ! user_select | | ( user_select > 2 ) ) return 0 ;
u8 ncsd [ 0x200 ] ;
2017-02-22 23:27:34 +01:00
u32 flags = OVERRIDE_PERM ;
2017-05-31 23:26:45 +02:00
InitSDCardFS ( ) ; // this has to be initialized for EmuNAND to work
SetEmuNandBase ( ( user_select = = 2 ) ? 0 : 1 ) ; // 0 -> GW EmuNAND
if ( ( ReadNandSectors ( ncsd , 0 , 1 , 0xFF , NAND_SYSNAND ) ! = 0 ) | |
( WriteNandSectors ( ncsd , 0 , 1 , 0xFF , NAND_EMUNAND ) ! = 0 ) | |
( ! PathCopy ( " E: " , " S:/nand_minsize.bin " , & flags ) ) )
2016-09-07 00:19:12 +02:00
ShowPrompt ( false , " Cloning SysNAND to EmuNAND: failed! " ) ;
2017-05-31 23:26:45 +02:00
DeinitSDCardFS ( ) ;
2016-09-07 00:19:12 +02:00
}
2016-07-13 19:59:36 +02:00
return 0 ;
}
2017-06-26 01:44:16 +02:00
u32 FileHexViewer ( 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-05-17 01:14:09 +02:00
if ( MAIN_SCREEN ! = TOP_SCREEN ) ShowString ( instr ) ;
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
2017-08-04 02:08:56 +02:00
u32 pad_state = InputWait ( 0 ) ;
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 ) ;
if ( found_offset = = ( u32 ) - 1 ) {
ShowPrompt ( false , " Not found! " ) ;
found_size = 0 ;
} else offset = found_offset ;
2017-05-17 01:14:09 +02:00
if ( MAIN_SCREEN = = TOP_SCREEN ) ClearScreen ( TOP_SCREEN , COLOR_STD_BG ) ;
else if ( dual_screen ) ClearScreen ( BOT_SCREEN , COLOR_STD_BG ) ;
else memcpy ( BOT_SCREEN , bottom_cpy , ( SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3 ) ) ;
2016-07-12 18:33:52 +02:00
} 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 ) ;
if ( found_offset = = ( u32 ) - 1 ) {
ShowPrompt ( false , " Not found! " ) ;
found_size = 0 ;
} else offset = found_offset ;
2017-05-17 01:14:09 +02:00
if ( MAIN_SCREEN = = TOP_SCREEN ) ClearScreen ( TOP_SCREEN , COLOR_STD_BG ) ;
else if ( dual_screen ) ClearScreen ( BOT_SCREEN , COLOR_STD_BG ) ;
else memcpy ( BOT_SCREEN , bottom_cpy , ( SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3 ) ) ;
2016-07-12 18:33:52 +02:00
}
} 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 ) ;
if ( found_offset = = ( u32 ) - 1 ) {
ShowPrompt ( false , " Not found! " ) ;
found_size = 0 ;
} else offset = found_offset ;
2017-05-17 01:14:09 +02:00
if ( MAIN_SCREEN = = TOP_SCREEN ) ClearScreen ( TOP_SCREEN , COLOR_STD_BG ) ;
else if ( dual_screen ) ClearScreen ( BOT_SCREEN , COLOR_STD_BG ) ;
else memcpy ( BOT_SCREEN , bottom_cpy , ( SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3 ) ) ;
2016-07-12 18:33:52 +02:00
}
}
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 ) ;
2017-05-17 01:14:09 +02:00
if ( MAIN_SCREEN = = TOP_SCREEN ) memcpy ( BOT_SCREEN , bottom_cpy , ( SCREEN_HEIGHT * SCREEN_WIDTH_BOT * 3 ) ) ;
else ClearScreen ( BOT_SCREEN , COLOR_STD_BG ) ;
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 ) ;
2017-08-23 02:04:53 +02:00
if ( ! FileGetSha256 ( path , sha256 , 0 , 0 ) ) {
2016-12-07 23:58:03 +01:00
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-08-29 03:29:29 +02:00
u32 StandardCopy ( u32 * cursor , u32 * scroll , DirStruct * current_dir ) {
2017-03-20 02:57:13 +01:00
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 ;
2017-08-29 02:52:22 +02:00
DrawDirContents ( current_dir , ( * cursor = i ) , scroll ) ;
2017-03-20 02:57:13 +01:00
if ( PathCopy ( OUTPUT_PATH , path , & flags ) ) n_success + + ;
2017-08-29 02:52:22 +02:00
else { // on failure: show error, break
2017-03-20 02:57:13 +01:00
char currstr [ 32 + 1 ] ;
TruncateString ( currstr , path , 32 , 12 ) ;
ShowPrompt ( false , " %s \n Failed copying item " , currstr ) ;
break ;
}
2017-08-29 02:52:22 +02:00
current_dir - > entry [ i ] . marked = false ;
2017-03-20 02:57:13 +01:00
}
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 ;
}
2017-09-16 13:48:31 +02:00
u32 BootFirmHandler ( const char * bootpath , bool verbose , bool delete ) {
2017-09-13 19:40:06 +02:00
char pathstr [ 32 + 1 ] ;
TruncateString ( pathstr , bootpath , 32 , 8 ) ;
size_t firm_size = FileGetSize ( bootpath ) ;
2017-09-14 13:38:19 +02:00
if ( ! firm_size ) return 1 ;
2017-09-13 19:40:06 +02:00
if ( firm_size > TEMP_BUFFER_SIZE ) {
if ( verbose ) ShowPrompt ( false , " %s \n FIRM too big, can't boot " , pathstr ) ; // unlikely
return 1 ;
}
if ( verbose & & ! ShowUnlockSequence ( 3 , " %s (%dkB) \n Boot FIRM via chainloader? " , pathstr , firm_size / 1024 ) )
return 1 ;
2017-09-14 13:38:19 +02:00
if ( ( FileGetData ( bootpath , TEMP_BUFFER , firm_size , 0 ) ! = firm_size ) | |
2017-09-13 19:40:06 +02:00
( ValidateFirm ( TEMP_BUFFER , firm_size , false ) ! = 0 ) ) {
if ( verbose ) ShowPrompt ( false , " %s \n Not a bootable FIRM " , pathstr ) ;
return 1 ;
}
// unsupported location handling
char fixpath [ 256 ] = { 0 } ;
if ( verbose & & ( * bootpath ! = ' 0 ' ) & & ( * bootpath ! = ' 1 ' ) ) {
const char * optionstr [ 2 ] = { " Make a copy at " OUTPUT_PATH " /temp.firm " , " Try to boot anyways " } ;
u32 user_select = ShowSelectPrompt ( 2 , optionstr , " %s \n Warning: Trying to boot from \n an unsupported location. " , pathstr ) ;
if ( user_select = = 1 ) {
FileSetData ( OUTPUT_PATH " /temp.firm " , TEMP_BUFFER , firm_size , 0 , true ) ;
bootpath = OUTPUT_PATH " /temp.firm " ;
} else if ( ! user_select ) bootpath = " " ;
}
// fix the boot path ("sdmc"/"nand" for Luma et al, hacky af)
if ( ( * bootpath = = ' 0 ' ) | | ( * bootpath = = ' 1 ' ) )
snprintf ( fixpath , 256 , " %s%s " , ( * bootpath = = ' 0 ' ) ? " sdmc " : " nand " , bootpath + 1 ) ;
else strncpy ( fixpath , bootpath , 256 ) ;
// boot the FIRM (if we got a proper fixpath)
if ( * fixpath ) {
2017-09-16 13:48:31 +02:00
if ( delete ) PathDelete ( bootpath ) ;
2017-09-13 19:40:06 +02:00
BootFirm ( ( FirmHeader * ) ( void * ) TEMP_BUFFER , fixpath ) ;
while ( 1 ) ;
}
// a return was not intended
return 1 ;
}
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-09-22 17:17:48 +02:00
bool mountable = ( FTYPE_MOUNTABLE ( filetype ) & & ! ( drvtype & DRV_IMAGE ) & &
! ( ( drvtype & ( DRV_SYSNAND | DRV_EMUNAND ) ) & & ( drvtype & DRV_VIRTUAL ) & & ( filetype & IMG_FAT ) ) ) ;
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 ) ) ;
2017-05-17 01:16:24 +02:00
bool cxi_dumpable = ( FTYPE_CXIDUMP ( filetype ) ) ;
2017-04-08 14:17:58 +02:00
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-05-19 02:29:23 +02:00
bool renamable = ( FTYPE_RENAMABLE ( filetype ) ) ;
2017-03-24 21:22:49 +01:00
bool transferable = ( FTYPE_TRANSFERABLE ( filetype ) & & IS_A9LH & & ( drvtype & DRV_FAT ) ) ;
2017-09-08 03:12:04 +02:00
bool hsinjectable = ( FTYPE_HASCODE ( filetype ) ) ;
bool extrcodeable = ( FTYPE_HASCODE ( 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-09-10 14:13:45 +02:00
bool ncsdfixable = ( FTYPE_NCSDFIXABLE ( filetype ) ) ;
2017-01-30 01:49:01 +01:00
bool xorpadable = ( FTYPE_XORPAD ( filetype ) ) ;
2017-08-15 02:16:53 +02:00
bool keyinitable = ( FTYPE_KEYINIT ( filetype ) ) ;
2017-06-09 01:45:00 +02:00
bool scriptable = ( FTYPE_SCRIPT ( filetype ) ) ;
2017-07-30 22:31:13 +02:00
bool bootable = ( FTYPE_BOOTABLE ( filetype ) & & ! ( drvtype & DRV_VIRTUAL ) ) ;
2017-08-16 18:02:41 +02:00
bool installable = ( FTYPE_INSTALLABLE ( filetype ) & & ! ( drvtype & DRV_VIRTUAL ) ) ;
2017-09-20 23:41:07 +02:00
bool agbexportable = ( FTPYE_AGBSAVE ( filetype ) ) ;
bool agbimportable = ( FTPYE_AGBSAVE ( filetype ) & & ( drvtype & DRV_VIRTUAL ) & & ( drvtype & DRV_SYSNAND ) ) ;
bool special_opt = mountable | | verificable | | decryptable | | encryptable | | cia_buildable | | cia_buildable_legit | | cxi_dumpable | | tik_buildable | | key_buildable | | titleinfo | | renamable | | transferable | | hsinjectable | | restorable | | xorpadable | | ebackupable | | ncsdfixable | | extrcodeable | | keyinitable | | bootable | | scriptable | | installable | | agbexportable | | agbimportable ;
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 ;
2017-06-26 01:44:16 +02:00
int textviewer = ( filetype & TXT_GENERIC ) ? + + n_opt : - 1 ;
2016-12-13 00:20:00 +01:00
int calcsha = + + n_opt ;
2017-02-08 21:45:52 +01:00
int calccmac = ( CheckCmacPath ( curr_entry - > path ) = = 0 ) ? + + n_opt : - 1 ;
2017-09-13 16:18:02 +02:00
int fileinfo = + + n_opt ;
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 ) & &
( 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-05-19 02:29:23 +02:00
( filetype & GAME_NDS ) ? " NDS image options... " :
2017-04-08 14:17:58 +02:00
( filetype & GAME_TICKET ) ? " Ticket options... " :
2017-03-23 16:00:17 +01:00
( filetype & SYS_FIRM ) ? " FIRM image options... " :
2017-09-20 23:41:07 +02:00
( filetype & SYS_AGBSAVE ) ? ( agbimportable ) ? " AGBSAVE options... " : " Dump GBA VC save " :
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-09-10 14:13:45 +02:00
( filetype & TXT_SCRIPT ) ? " Execute GM9 script " :
( filetype & HDR_NAND ) ? " Rebuild NCSD header " :
( filetype & NOIMG_NAND ) ? " Rebuild NCSD header " : " ??? " ;
2016-12-13 00:20:00 +01:00
optionstr [ hexviewer - 1 ] = " Show in Hexeditor " ;
optionstr [ calcsha - 1 ] = " Calculate SHA-256 " ;
2017-09-13 16:18:02 +02:00
optionstr [ fileinfo - 1 ] = " Show file info " ;
2017-06-26 01:44:16 +02:00
if ( textviewer > 0 ) optionstr [ textviewer - 1 ] = " Show in Textviewer " ;
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
2017-06-26 01:44:16 +02:00
FileHexViewer ( curr_entry - > path ) ;
2017-09-10 14:13:45 +02:00
GetDirContents ( current_dir , current_path ) ;
2017-06-26 01:44:16 +02:00
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = textviewer ) { // -> show in text viewer
2017-09-06 00:46:01 +02:00
FileTextViewer ( curr_entry - > path , scriptable ) ;
2016-12-13 00:20:00 +01:00
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = calcsha ) { // -> calculate SHA-256
2016-12-13 00:20:00 +01:00
Sha256Calculator ( curr_entry - > path ) ;
GetDirContents ( current_dir , current_path ) ;
return 0 ;
2017-09-20 23:41:07 +02: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-09-20 23:41:07 +02:00
}
else if ( user_select = = fileinfo ) { // -> show file info
2017-09-13 16:18:02 +02:00
FILINFO fno ;
if ( fvx_stat ( curr_entry - > path , & fno ) ! = FR_OK ) {
ShowPrompt ( false , " %s \n File info failed! " , pathstr ) ;
return 0 ;
} ;
char sizestr [ 32 ] ;
char namestr [ 32 ] ;
FormatNumber ( sizestr , fno . fsize ) ;
TruncateString ( namestr , fno . fname , 32 , 8 ) ;
ShowPrompt ( false , " %s \n \n filesize: %s byte \n modified: %04lu-%02lu-%02lu %02lu:%02lu:%02lu \n \n [%c] read-only [%c] hidden \n [%c] system [%c] archive \n [%c] virtual " ,
namestr , sizestr ,
1980 + ( ( fno . fdate > > 9 ) & 0x7F ) , ( fno . fdate > > 5 ) & 0x0F , ( fno . fdate > > 0 ) & 0x1F ,
( fno . ftime > > 11 ) & 0x1F , ( fno . ftime > > 5 ) & 0x3F , ( ( fno . ftime > > 0 ) & 0x1F ) < < 1 ,
( fno . fattrib & AM_RDO ) ? ' X ' : ' ' , ( fno . fattrib & AM_HID ) ? ' X ' : ' ' , ( fno . fattrib & AM_SYS ) ? ' X ' : ' ' ,
( fno . fattrib & AM_ARC ) ? ' X ' : ' ' , ( fno . fattrib & AM_VRT ) ? ' X ' : ' ' ) ;
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = copystd ) { // -> copy to OUTPUT_PATH
2017-08-29 03:29:29 +02:00
StandardCopy ( cursor , scroll , current_dir ) ;
2017-01-24 01:46:28 +01:00
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = inject ) { // -> inject data from clipboard
2016-12-13 00:20:00 +01:00
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 ) {
2017-06-09 01:45:00 +02:00
if ( ! FileInjectFile ( curr_entry - > path , clipboard - > entry [ 0 ] . path , ( u32 ) offset , 0 , 0 , NULL ) )
2016-12-13 00:20:00 +01:00
ShowPrompt ( false , " Failed injecting %s " , origstr ) ;
clipboard - > n_entries = 0 ;
}
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = searchdrv ) { // -> search drive, open containing path
2016-12-13 00:20:00 +01:00
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 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select ! = special ) {
2016-12-13 00:20:00 +01:00
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 ;
2017-09-10 14:13:45 +02:00
int ncsdfix = ( ncsdfixable ) ? + + 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 ;
2017-05-17 01:16:24 +02:00
int cxi_dump = ( cxi_dumpable ) ? + + n_opt : - 1 ;
2017-04-08 14:17:58 +02:00
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-09-08 03:12:04 +02:00
int extrcode = ( extrcodeable ) ? + + n_opt : - 1 ;
2017-05-19 02:29:23 +02:00
int rename = ( renamable ) ? + + 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-08-15 02:16:53 +02:00
int keyinit = ( keyinitable ) ? + + n_opt : - 1 ;
2017-08-16 18:02:41 +02:00
int install = ( installable ) ? + + n_opt : - 1 ;
2017-07-04 02:22:35 +02:00
int boot = ( bootable ) ? + + n_opt : - 1 ;
2017-06-09 01:45:00 +02:00
int script = ( scriptable ) ? + + n_opt : - 1 ;
2017-09-20 23:41:07 +02:00
int agbexport = ( agbexportable ) ? + + n_opt : - 1 ;
int agbimport = ( agbimportable ) ? + + 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-09-10 14:13:45 +02:00
if ( ncsdfix > 0 ) optionstr [ ncsdfix - 1 ] = " Rebuild NCSD header " ;
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) " ;
2017-05-17 01:16:24 +02:00
if ( cxi_dump > 0 ) optionstr [ cxi_dump - 1 ] = " Dump CXI/NDS file " ;
2017-04-08 14:17:58 +02:00
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-05-19 02:29:23 +02:00
if ( rename > 0 ) optionstr [ rename - 1 ] = " Rename file " ;
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-09-08 03:12:04 +02:00
if ( extrcode > 0 ) optionstr [ extrcode - 1 ] = " Extract " EXEFS_CODE_NAME ;
2017-08-15 02:16:53 +02:00
if ( keyinit > 0 ) optionstr [ keyinit - 1 ] = " Init " KEYDB_NAME ;
2017-08-16 18:02:41 +02:00
if ( install > 0 ) optionstr [ install - 1 ] = " Install FIRM " ;
2017-07-10 01:48:54 +02:00
if ( boot > 0 ) optionstr [ boot - 1 ] = " Boot FIRM " ;
2017-06-09 01:45:00 +02:00
if ( script > 0 ) optionstr [ script - 1 ] = " Execute GM9 script " ;
2017-09-20 23:41:07 +02:00
if ( agbexport > 0 ) optionstr [ agbexport - 1 ] = " Dump GBA VC save " ;
if ( agbimport > 0 ) optionstr [ agbimport - 1 ] = " Inject GBA VC save " ;
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-09-20 23:41:07 +02:00
}
else if ( user_select = = decrypt ) { // -> decrypt game file
2017-01-31 16:16:26 +01:00
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 ;
}
2017-08-29 02:52:22 +02:00
DrawDirContents ( current_dir , ( * cursor = i ) , scroll ) ;
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 + + ;
2017-08-29 02:52:22 +02:00
else { // on failure: show error, break
2017-05-08 20:38:44 +02:00
TruncateString ( pathstr , path , 32 , 8 ) ;
2017-05-19 02:36:23 +02:00
ShowPrompt ( false , " %s \n Decryption failed " , pathstr ) ;
2016-12-13 00:20:00 +01:00
break ;
}
2017-08-29 02:52:22 +02:00
current_dir - > entry [ i ] . marked = false ;
2016-12-13 00:20:00 +01:00
}
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-19 02:36:23 +02:00
if ( inplace | | ( ret ! = 0 ) ) ShowPrompt ( false , " %s \n Decryption %s " , pathstr , ( ret = = 0 ) ? " success " : " failed " ) ;
2016-12-13 00:20:00 +01:00
else ShowPrompt ( false , " %s \n Decrypted to %s " , pathstr , OUTPUT_PATH ) ;
}
}
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = encrypt ) { // -> encrypt game file
2017-01-31 16:16:26 +01:00
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 ;
}
2017-08-29 02:52:22 +02:00
DrawDirContents ( current_dir , ( * cursor = i ) , scroll ) ;
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-08-29 02:52:22 +02:00
else { // on failure: show error, 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
break ;
}
2017-08-29 02:52:22 +02:00
current_dir - > entry [ i ] . marked = false ;
2017-01-31 16:16:26 +01:00
}
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-09-20 23:41:07 +02:00
}
else if ( ( user_select = = cia_build ) | | ( user_select = = cia_build_legit ) | | ( user_select = = cxi_dump ) ) { // -> build CIA / dump CXI
2017-05-17 01:16:24 +02:00
char * type = ( user_select = = cxi_dump ) ? " CXI " : " CIA " ;
2017-04-08 14:17:58 +02:00
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 ;
}
2017-08-29 02:52:22 +02:00
DrawDirContents ( current_dir , ( * cursor = i ) , scroll ) ;
2017-05-17 01:16:24 +02:00
if ( ( ( user_select ! = cxi_dump ) & & ( BuildCiaFromGameFile ( path , force_legit ) = = 0 ) ) | |
( ( user_select = = cxi_dump ) & & ( DumpCxiSrlFromTmdFile ( path ) = = 0 ) ) ) n_success + + ;
2017-08-29 02:52:22 +02:00
else { // on failure: show error, break
2017-05-08 20:38:44 +02:00
TruncateString ( pathstr , path , 32 , 8 ) ;
2017-05-17 01:16:24 +02:00
ShowPrompt ( false , " %s \n Build %s failed " , pathstr , type ) ;
2016-12-15 11:46:00 +01:00
break ;
}
2017-08-29 02:52:22 +02:00
current_dir - > entry [ i ] . marked = false ;
2016-12-15 11:46:00 +01:00
}
2017-05-17 01:16:24 +02:00
if ( n_other ) ShowPrompt ( false , " %lu/%lu %ss built ok \n %lu/%lu not of same type " ,
n_success , n_marked , type , n_other , n_marked ) ;
else ShowPrompt ( false , " %lu/%lu %ss built ok " , n_success , n_marked , type ) ;
2016-12-15 11:46:00 +01:00
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-05-17 01:16:24 +02:00
if ( ( ( user_select ! = cxi_dump ) & & ( BuildCiaFromGameFile ( curr_entry - > path , force_legit ) = = 0 ) ) | |
( ( user_select = = cxi_dump ) & & ( DumpCxiSrlFromTmdFile ( curr_entry - > path ) = = 0 ) ) ) {
ShowPrompt ( false , " %s \n %s built to %s " , pathstr , type , OUTPUT_PATH ) ;
2017-01-25 14:46:29 +01:00
if ( in_output_path ) GetDirContents ( current_dir , current_path ) ;
2017-05-17 01:16:24 +02:00
} else ShowPrompt ( false , " %s \n %s build failed " , pathstr , type ) ;
2016-12-15 11:46:00 +01:00
}
return 0 ;
2017-09-20 23:41:07 +02: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 ;
}
2017-08-29 02:52:22 +02:00
DrawDirContents ( current_dir , ( * cursor = i ) , scroll ) ;
if ( ( filetype & IMG_NAND ) & & ( ValidateNandDump ( path ) = = 0 ) ) n_success + + ;
else if ( VerifyGameFile ( path ) = = 0 ) n_success + + ;
else { // on failure: show error, 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
break ;
}
2017-08-29 02:52:22 +02:00
current_dir - > entry [ i ] . marked = false ;
2016-12-13 00:20:00 +01:00
}
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-09-20 23:41:07 +02:00
}
else if ( ( user_select = = tik_build_enc ) | | ( user_select = = tik_build_dec ) ) { // -> (Re)Build titlekey database
2017-04-08 14:17:58 +02:00
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-09-20 23:41:07 +02:00
}
else if ( user_select = = key_build ) { // -> (Re)Build AES key database
2017-04-17 23:45:49 +02:00
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-09-20 23:41:07 +02:00
}
else if ( user_select = = rename ) { // -> Game file renamer
2017-05-19 02:29:23 +02:00
if ( ( n_marked > 1 ) & & ShowPrompt ( true , " Try to rename all %lu selected files? " , n_marked ) ) {
u32 n_success = 0 ;
ShowProgress ( 0 , 0 , " " ) ;
for ( u32 i = 0 ; i < current_dir - > n_entries ; i + + ) {
DirEntry * entry = & ( current_dir - > entry [ i ] ) ;
if ( ! current_dir - > entry [ i ] . marked ) continue ;
ShowProgress ( i + 1 , current_dir - > n_entries , entry - > name ) ;
if ( ! GoodRenamer ( entry , false ) ) continue ;
n_success + + ;
current_dir - > entry [ i ] . marked = false ;
}
ShowPrompt ( false , " %lu/%lu renamed ok " , n_success , n_marked ) ;
} else if ( ! GoodRenamer ( & ( current_dir - > entry [ * cursor ] ) , true ) ) {
2017-07-10 01:48:54 +02:00
ShowPrompt ( false , " %s \n Could not rename \n (Maybe try decrypt?) " , pathstr ) ;
2017-05-19 02:29:23 +02:00
}
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = show_info ) { // -> Show title info
2017-03-29 02:23:06 +02:00
if ( ShowGameFileTitleInfo ( curr_entry - > path ) ! = 0 )
ShowPrompt ( false , " Title info: not found " ) ;
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = hsinject ) { // -> Inject to Health & Safety
2017-01-30 01:49:01 +01:00
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-19 02:36:23 +02:00
( InjectHealthAndSafety ( curr_entry - > path , destdrv [ user_select - 1 ] ) = = 0 ) ? " success " : " failed " ) ;
2017-01-30 01:49:01 +01:00
}
2017-02-17 03:28:53 +01:00
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = extrcode ) { // -> Extract code
2017-09-08 13:30:16 +02:00
ShowString ( " %s \n Extracting .code, please wait... " , pathstr ) ;
2017-09-08 15:27:10 +02:00
if ( ExtractCodeFromCxiFile ( curr_entry - > path , NULL ) = = 0 ) {
2017-09-08 03:12:04 +02:00
ShowPrompt ( false , " %s \n .code extracted to " OUTPUT_PATH , pathstr ) ;
} else ShowPrompt ( false , " %s \n .code extract failed " , pathstr ) ;
return 0 ;
2017-09-20 23:41:07 +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: " ;
}
2017-08-25 00:21:09 +02:00
if ( n_opt ) {
user_select = ( n_opt > 1 ) ? ( int ) ShowSelectPrompt ( n_opt , optionstr , pathstr ) : 1 ;
if ( user_select ) {
ShowPrompt ( false , " %s \n CTRNAND transfer %s " , pathstr ,
( TransferCtrNandImage ( curr_entry - > path , destdrv [ user_select - 1 ] ) = = 0 ) ? " success " : " failed " ) ;
}
} else ShowPrompt ( false , " %s \n No valid destination found " ) ;
2017-03-02 22:40:58 +01:00
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = restore ) { // -> restore SysNAND (A9LH preserving)
2017-01-02 17:37:08 +01:00
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-09-20 23:41:07 +02:00
}
else if ( user_select = = ncsdfix ) { // -> inject sighaxed NCSD
2017-09-10 14:13:45 +02:00
ShowPrompt ( false , " %s \n Rebuild NCSD %s " , pathstr ,
( FixNandHeader ( curr_entry - > path , ! ( filetype = = HDR_NAND ) ) = = 0 ) ? " success " : " failed " ) ;
GetDirContents ( current_dir , current_path ) ;
InitExtFS ( ) ; // this might have fixed something, so try this
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( ( user_select = = xorpad ) | | ( user_select = = xorpad_inplace ) ) { // -> build xorpads
2017-01-25 14:46:29 +01:00
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 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = ebackup ) { // -> update embedded backup
2017-02-17 03:28:53 +01:00
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-09-20 23:41:07 +02:00
}
else if ( user_select = = keyinit ) { // -> initialise keys from aeskeydb.bin
2017-08-15 02:16:53 +02:00
if ( ShowPrompt ( true , " Warning: Keys are not verified. \n Continue on your own risk? " ) )
ShowPrompt ( false , " %s \n AESkeydb init %s " , pathstr , ( InitKeyDb ( curr_entry - > path ) = = 0 ) ? " success " : " failed " ) ;
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = install ) { // -> install FIRM
2017-08-16 18:02:41 +02:00
size_t firm_size = FileGetSize ( curr_entry - > path ) ;
u32 slots = 1 ;
if ( GetNandPartitionInfo ( NULL , NP_TYPE_FIRM , NP_SUBTYPE_CTR , 1 , NAND_SYSNAND ) = = 0 ) {
optionstr [ 0 ] = " Install to FIRM0 " ;
optionstr [ 1 ] = " Install to FIRM1 " ;
optionstr [ 2 ] = " Install to both " ;
// this only works up to FIRM1;
slots = ShowSelectPrompt ( 3 , optionstr , " %s (%dkB) \n Install to SysNAND? " , pathstr , firm_size / 1024 ) ;
} else slots = ShowPrompt ( true , " %s (%dkB) \n Install to SysNAND? " , pathstr , firm_size / 1024 ) ? 1 : 0 ;
2017-08-23 13:33:31 +02:00
if ( slots ) ShowPrompt ( false , " %s (%dkB) \n Install %s " , pathstr , firm_size / 1024 ,
( SafeInstallFirm ( curr_entry - > path , slots ) = = 0 ) ? " success " : " failed! " ) ;
2017-08-16 18:02:41 +02:00
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = boot ) { // -> boot FIRM
2017-09-16 13:48:31 +02:00
BootFirmHandler ( curr_entry - > path , true , false ) ;
2017-07-04 02:22:35 +02:00
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = script ) { // execute script
2017-08-26 13:14:38 +02:00
if ( ShowPrompt ( true , " %s \n Warning: Do not run scripts \n from untrusted sources. \n \n Execute script? " , pathstr ) )
2017-06-09 01:45:00 +02:00
ShowPrompt ( false , " %s \n Script execute %s " , pathstr , ExecuteGM9Script ( curr_entry - > path ) ? " success " : " failure " ) ;
GetDirContents ( current_dir , current_path ) ;
2017-09-06 00:46:01 +02:00
ClearScreenF ( true , true , COLOR_STD_BG ) ;
2017-06-09 01:45:00 +02:00
return 0 ;
2016-12-13 00:20:00 +01:00
}
2017-09-20 23:41:07 +02:00
else if ( user_select = = agbexport ) { // export GBA VC save
if ( DumpGbaVcSavegame ( curr_entry - > path ) = = 0 )
ShowPrompt ( false , " Savegame dumped to " OUTPUT_PATH ) ;
else ShowPrompt ( false , " Savegame dump failed! " ) ;
return 0 ;
}
else if ( user_select = = agbimport ) { // import GBA VC save
if ( clipboard - > n_entries ! = 1 ) {
ShowPrompt ( false , " GBA VC savegame has to \n be in the clipboard. " ) ;
} else ShowPrompt ( false , " Savegame inject %s " ,
( InjectGbaVcSavegame ( curr_entry - > path , clipboard - > entry [ 0 ] . path ) = = 0 ) ? " success " : " failed! " ) ;
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 ) {
2017-05-31 15:48:14 +02:00
NandPartitionInfo np_info ;
if ( GetNandPartitionInfo ( & np_info , NP_TYPE_BONUS , NP_SUBTYPE_CTR , 0 , NAND_SYSNAND ) ! = 0 ) np_info . count = 0 ;
2017-02-28 04:10:23 +01:00
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 ;
2017-05-31 15:48:14 +02:00
int bonus = ( np_info . count > 0x2000 ) ? ( int ) + + n_opt : - 1 ; // 4MB minsize
2017-02-28 04:10:23 +01:00
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 ;
2017-08-04 02:08:56 +02:00
int clock = + + n_opt ;
2017-08-25 02:11:46 +02:00
int sysinfo = + + n_opt ;
2017-02-28 04:10:23 +01:00
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 " ;
2017-08-08 12:30:33 +02:00
if ( clock > 0 ) optionstr [ clock - 1 ] = " Set RTC date&time " ;
2017-08-25 02:11:46 +02:00
if ( sysinfo > 0 ) optionstr [ sysinfo - 1 ] = " System info " ;
2017-02-28 04:10:23 +01:00
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 ) ;
2017-05-31 23:26:45 +02:00
AutoEmuNandBase ( true ) ;
2017-02-28 04:10:23 +01:00
InitExtFS ( ) ;
GetDirContents ( current_dir , current_path ) ;
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = bonus ) { // setup bonus drive
2017-02-28 04:10:23 +01:00
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 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = multi ) { // switch EmuNAND offset
2017-02-28 04:10:23 +01:00
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 ) ;
2017-05-31 23:26:45 +02:00
AutoEmuNandBase ( false ) ;
2017-02-28 04:10:23 +01:00
InitExtFS ( ) ;
}
GetDirContents ( current_dir , current_path ) ;
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = bsupport ) { // build support files
2017-04-12 00:27:02 +02:00
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 ;
}
2017-06-07 12:42:24 +02:00
ShowPrompt ( false , " Built in " OUTPUT_PATH " : \n \n %18.18-s %s \n %18.18-s %s \n %18.18-s %s " ,
2017-04-12 00:27:02 +02:00
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 " ,
2017-06-07 12:42:24 +02:00
SEEDDB_NAME , seed_sys ? seed_emu ? " OK (Sys&Emu) " : " OK (Sys) " : " Failed " ) ;
2017-04-12 00:27:02 +02:00
GetDirContents ( current_dir , current_path ) ;
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = hsrestore ) { // restore Health & Safety
2017-02-28 04:10:23 +01:00
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 ;
}
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = clock ) { // RTC clock setter
2017-08-04 02:08:56 +02:00
DsTime dstime ;
get_dstime ( & dstime ) ;
2017-08-08 12:30:33 +02:00
if ( ShowRtcSetterPrompt ( & dstime , " Set RTC date&time: " ) ) {
2017-08-04 02:08:56 +02:00
char timestr [ 32 ] ;
set_dstime ( & dstime ) ;
2017-09-16 16:46:30 +02:00
GetTimeString ( timestr , true , true ) ;
2017-08-08 12:30:33 +02:00
ShowPrompt ( false , " New RTC date&time is: \n %s \n \n Hint: HOMEMENU time needs \n manual adjustment after \n setting the RTC. " ,
2017-08-04 02:08:56 +02:00
timestr ) ;
}
return 0 ;
2017-09-20 23:41:07 +02:00
}
else if ( user_select = = sysinfo ) { // Myria's system info
2017-08-25 02:11:46 +02:00
char * sysinfo_txt = ( char * ) TEMP_BUFFER ;
MyriaSysinfo ( sysinfo_txt ) ;
2017-09-06 00:46:01 +02:00
MemTextViewer ( sysinfo_txt , strnlen ( sysinfo_txt , TEMP_BUFFER_SIZE ) , false ) ;
2017-08-25 02:11:46 +02:00
return 0 ;
2017-02-28 04:10:23 +01:00
} else return 1 ;
return HomeMoreMenu ( current_path , current_dir , clipboard ) ;
}
2017-03-09 14:35:36 +01:00
2017-09-13 19:40:06 +02:00
u32 SplashInit ( const char * modestr ) {
2017-08-16 15:50:46 +02:00
const char * namestr = FLAVOR " " VERSION ;
2017-03-09 14:35:36 +01:00
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-09-13 19:40:06 +02:00
if ( modestr ) DrawStringF ( TOP_SCREEN , SCREEN_WIDTH_TOP - 10 - GetDrawStringWidth ( modestr ) ,
SCREEN_HEIGHT - 10 - GetDrawStringHeight ( loadstr ) , COLOR_STD_FONT , COLOR_TRANSPARENT , modestr ) ;
2017-09-01 18:01:33 +02:00
DrawStringF ( BOT_SCREEN , pos_xb , pos_yb , COLOR_STD_FONT , COLOR_STD_BG , " %s \n %*.*s \n %s \n \n \n %s \n %s \n \n %s \n %s " ,
2017-03-10 13:47:16 +01:00
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-09-01 18:01:33 +02:00
DrawStringF ( BOT_SCREEN , pos_xb , pos_yu , COLOR_STD_FONT , COLOR_STD_BG , " built: " DBUILTL ) ;
2017-02-28 04:10:23 +01:00
2017-03-09 14:35:36 +01:00
return 0 ;
}
2017-08-18 18:52:42 +02:00
u32 GodMode ( bool is_b9s ) {
2017-05-17 01:14:09 +02:00
const u32 quick_stp = ( MAIN_SCREEN = = TOP_SCREEN ) ? 20 : 19 ;
2017-09-13 19:40:06 +02:00
u32 exit_mode = GODMODE_EXIT_POWEROFF ;
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 ;
2017-06-23 00:59:51 +02:00
u32 last_write_perm = GetWritePermissions ( ) ;
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
2017-09-23 13:09:50 +02:00
u32 boot_origin = GetBootOrigin ( ) ;
bool bootloader = ! is_b9s & & IS_SIGHAX & & ( boot_origin & BOOT_NAND ) ;
bool bootmenu = bootloader & & CheckButton ( BOOTMENU_KEY ) ;
2017-09-19 15:58:43 +02:00
bool godmode9 = ! bootloader ;
2017-09-27 01:32:26 +02:00
FirmHeader * firm_in_mem = ( FirmHeader * ) ( void * ) ( TEMP_BUFFER + TEMP_BUFFER_SIZE ) ; // should be safe here
2017-09-21 01:31:48 +02:00
memcpy ( firm_in_mem , " NOPE " , 4 ) ; // to prevent bootloops
2017-09-19 15:58:43 +02:00
if ( bootloader ) { // check for FIRM in FCRAM, but prevent bootloops
2017-09-17 17:50:31 +02:00
for ( u8 * addr = ( u8 * ) 0x20000200 ; addr < ( u8 * ) 0x24000000 ; addr + = 0x400000 ) {
2017-09-19 01:00:44 +02:00
if ( memcmp ( addr - 0x200 , " A9NC " , 4 ) ! = 0 ) continue ;
2017-09-27 01:32:26 +02:00
u32 firm_size = GetFirmSize ( ( FirmHeader * ) ( void * ) addr ) ;
if ( ! firm_size | | ( firm_size > ( 0x400000 - 0x200 ) ) ) continue ;
if ( memcmp ( firm_in_mem , " FIRM " , 4 ) ! = 0 ) memmove ( firm_in_mem , addr , firm_size ) ;
2017-09-19 01:00:44 +02:00
if ( memcmp ( addr , " FIRM " , 4 ) = = 0 ) memcpy ( addr , " NOPE " , 4 ) ; // prevent bootloops
2017-09-17 17:50:31 +02:00
}
}
2017-09-13 19:40:06 +02:00
2017-09-23 13:09:50 +02:00
// get mode string for splash screen
const char * disp_mode = NULL ;
if ( bootloader ) disp_mode = " bootloader mode " ;
else if ( ! is_b9s & & ! IS_SIGHAX ) disp_mode = " oldloader mode " ;
else if ( ! is_b9s & & IS_SIGHAX & & ( boot_origin & BOOT_NTRBOOT ) ) disp_mode = " ntrboot mode " ;
// else if (!is_b9s || !IS_SIGHAX) disp_mode = "unknown mode";
2017-09-13 19:40:06 +02:00
2016-03-01 02:00:48 +01:00
ClearScreenF ( true , true , COLOR_STD_BG ) ;
2017-09-21 01:31:48 +02:00
SplashInit ( disp_mode ) ;
2017-09-13 19:40:06 +02:00
u64 timer = timer_start ( ) ; // show splash
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
InitSDCardFS ( ) ;
2017-05-31 23:26:45 +02:00
AutoEmuNandBase ( true ) ;
2017-08-18 18:52:42 +02:00
InitNandCrypto ( ! is_b9s ) ;
2016-04-05 15:20:48 +02:00
InitExtFS ( ) ;
2016-02-25 16:57:01 +01:00
2017-06-23 17:28:09 +02:00
// check for embedded essential backup
if ( IS_SIGHAX & & ! PathExist ( " S:/essential.exefs " ) & & CheckGenuineNandNcsd ( ) & &
ShowPrompt ( true , " Essential files backup not found. \n Create one now? " ) ) {
if ( EmbedEssentialBackup ( " S:/nand.bin " ) = = 0 ) {
u32 flags = BUILD_PATH | SKIP_ALL ;
PathCopy ( OUTPUT_PATH , " S:/essential.exefs " , & flags ) ;
ShowPrompt ( false , " Backup embedded in SysNAND \n and written to " OUTPUT_PATH " . " ) ;
}
}
2017-09-05 14:26:18 +02:00
// check internal clock
if ( IS_SIGHAX ) { // we could actually do this on any entrypoint
DsTime dstime ;
get_dstime ( & dstime ) ;
if ( ( DSTIMEGET ( & dstime , bcd_Y ) < 17 ) & &
ShowPrompt ( true , " RTC date&time seems to be \n wrong. Set it now? " ) & &
ShowRtcSetterPrompt ( & dstime , " Set RTC date&time: " ) ) {
char timestr [ 32 ] ;
set_dstime ( & dstime ) ;
2017-09-16 16:46:30 +02:00
GetTimeString ( timestr , true , true ) ;
2017-09-05 14:26:18 +02:00
ShowPrompt ( false , " New RTC date&time is: \n %s \n \n Hint: HOMEMENU time needs \n manual adjustment after \n setting the RTC. " , timestr ) ;
}
}
2017-09-08 20:25:48 +02:00
// check aeskeydb.bin / key state
if ( ! is_b9s & & ( CheckRecommendedKeyDb ( NULL ) ! = 0 ) ) {
ShowPrompt ( false , " WARNING: \n Not running from a boot9strap \n compatible entrypoint. Not \n everything may work as expected. \n \n Provide the recommended \n aeskeydb.bin file to make this \n warning go away. " ) ;
}
2017-09-19 15:58:43 +02:00
# ifndef AL3X10MODE
2017-09-25 00:50:16 +02:00
bootmenu = bootmenu | | ( bootloader & & CheckButton ( BOOTMENU_KEY ) ) ; // second check for boot menu keys
2017-09-13 19:40:06 +02:00
while ( HID_STATE & BUTTON_ANY ) ; // don't continue while any button is held
2017-09-19 15:58:43 +02:00
# endif
2017-08-15 16:39:15 +02:00
while ( timer_msec ( timer ) < 500 ) ; // show splash for at least 0.5 sec
2017-03-09 14:35:36 +01:00
2017-09-13 19:40:06 +02:00
2017-09-17 17:50:31 +02:00
// bootmenu handler
2017-09-13 19:40:06 +02:00
if ( bootmenu ) {
2017-09-14 13:38:19 +02:00
bootloader = false ;
while ( ! bootloader & & ! godmode9 ) {
const char * optionstr [ 6 ] = { " Resume bootloader " , " Resume GodMode9 " , " Select payload... " , " Select script... " ,
2017-09-13 19:40:06 +02:00
" Poweroff system " , " Reboot system " } ;
2017-09-14 13:38:19 +02:00
int user_select = ShowSelectPrompt ( 6 , optionstr , FLAVOR " bootloader menu. \n Select action: " ) ;
2017-09-13 19:40:06 +02:00
char loadpath [ 256 ] ;
if ( user_select = = 1 ) {
2017-09-14 13:38:19 +02:00
bootloader = true ;
} else if ( user_select = = 2 ) {
godmode9 = true ;
} else if ( ( user_select = = 3 ) & & ( FileSelector ( loadpath , " Bootloader payloads menu. \n Select payload: " , PAYLOAD_PATH , " *.firm " , true , false ) ) ) {
2017-09-16 13:48:31 +02:00
BootFirmHandler ( loadpath , false , false ) ;
2017-09-14 13:38:19 +02:00
} else if ( ( user_select = = 4 ) & & ( FileSelector ( loadpath , " Bootloader scripts menu. \n Select script: " , SCRIPT_PATH , " *.gm9 " , true , false ) ) ) {
2017-09-13 19:40:06 +02:00
ExecuteGM9Script ( loadpath ) ;
} else if ( user_select = = 5 ) {
2017-09-14 13:38:19 +02:00
exit_mode = GODMODE_EXIT_POWEROFF ;
} else if ( user_select = = 6 ) {
2017-09-13 19:40:06 +02:00
exit_mode = GODMODE_EXIT_REBOOT ;
} else if ( user_select ) continue ;
break ;
}
2017-09-14 13:38:19 +02:00
}
if ( bootloader ) {
const char * bootfirm_paths [ ] = { BOOTFIRM_PATHS } ;
2017-09-27 01:32:26 +02:00
if ( ValidateFirm ( firm_in_mem , FIRM_MAX_SIZE , false ) = = 0 ) BootFirm ( firm_in_mem , " 0:/bootonce.firm " ) ;
2017-09-14 13:38:19 +02:00
for ( u32 i = 0 ; i < sizeof ( bootfirm_paths ) / sizeof ( char * ) ; i + + ) {
2017-09-16 13:48:31 +02:00
BootFirmHandler ( bootfirm_paths [ i ] , false , ( BOOTFIRM_TEMPS > > i ) & 0x1 ) ;
2017-09-14 13:38:19 +02:00
}
2017-09-13 19:40:06 +02:00
}
2017-09-17 17:50:31 +02:00
GetDirContents ( current_dir , " " ) ;
clipboard - > n_entries = 0 ;
memset ( panedata , 0x00 , 0x10000 ) ;
2017-09-13 19:40:06 +02:00
ClearScreenF ( true , true , COLOR_STD_BG ) ; // clear splash
2017-09-17 17:50:31 +02:00
2017-09-14 13:38:19 +02:00
while ( godmode9 ) { // 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
2017-06-23 00:59:51 +02:00
ShowPrompt ( false , " Invalid directory object " ) ;
2016-04-07 14:29:37 +02:00
* 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
2017-06-23 00:59:51 +02:00
ShowPrompt ( false , " Invalid root directory " ) ;
2016-04-07 14:29:37 +02:00
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 ) ;
2017-09-24 12:10:09 +02:00
DrawTopBar ( current_path ) ;
2017-06-23 00:59:51 +02:00
// check write permissions
if ( ~ last_write_perm & GetWritePermissions ( ) ) {
2017-07-24 14:08:22 +02:00
if ( ShowPrompt ( true , " Write permissions were changed. \n Relock them? " ) ) SetWritePermissions ( last_write_perm , false ) ;
2017-06-23 00:59:51 +02:00
last_write_perm = GetWritePermissions ( ) ;
continue ;
}
// handle user input
2017-09-18 19:57:43 +02:00
u32 pad_state = InputWait ( 3 ) ;
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-08-16 01:28:32 +02:00
const char * optionstr [ 8 ] = { NULL } ;
2017-08-16 01:13:17 +02:00
char tpath [ 16 ] = { 0 } ;
if ( ! * current_path ) snprintf ( tpath , 15 , " %s/title " , curr_entry - > path ) ;
2017-05-15 23:25:39 +02:00
int n_opt = 0 ;
2017-08-16 01:13:17 +02:00
int srch_t = ( ( strncmp ( curr_entry - > path + 1 , " :/title " , 7 ) = = 0 ) | |
( * tpath & & PathExist ( tpath ) ) ) ? + + n_opt : - 1 ;
2017-05-15 23:25:39 +02:00
int srch_f = + + n_opt ;
2017-09-13 14:53:19 +02:00
int fixcmac = ( ! * current_path & & ( strspn ( curr_entry - > path , " 14AB " ) = = 1 ) ) ? + + n_opt : - 1 ;
2017-05-15 23:25:39 +02:00
int dirnfo = + + n_opt ;
2017-08-16 01:13:17 +02:00
int stdcpy = ( * current_path & & strncmp ( current_path , OUTPUT_PATH , 256 ) ! = 0 ) ? + + n_opt : - 1 ;
2017-05-15 23:25:39 +02:00
if ( srch_t > 0 ) optionstr [ srch_t - 1 ] = " Search for titles " ;
if ( srch_f > 0 ) optionstr [ srch_f - 1 ] = " Search for files... " ;
2017-09-13 14:53:19 +02:00
if ( fixcmac > 0 ) optionstr [ fixcmac - 1 ] = " Fix CMACs for drive " ;
2017-09-13 16:23:44 +02:00
if ( dirnfo > 0 ) optionstr [ dirnfo - 1 ] = ( * current_path ) ? " Show directory info " : " Show drive info " ;
2017-05-15 23:25:39 +02:00
if ( stdcpy > 0 ) optionstr [ stdcpy - 1 ] = " Copy to " OUTPUT_PATH ;
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-05-15 23:25:39 +02:00
int user_select = ShowSelectPrompt ( n_opt , optionstr , " %s " , namestr ) ;
if ( ( user_select = = srch_f ) | | ( user_select = = srch_t ) ) {
2017-02-27 02:07:27 +01:00
char searchstr [ 256 ] ;
2017-05-15 23:25:39 +02:00
snprintf ( searchstr , 256 , ( user_select = = srch_t ) ? " *.tmd " : " * " ) ;
2017-02-27 02:07:27 +01:00
TruncateString ( namestr , curr_entry - > name , 20 , 8 ) ;
2017-05-15 23:25:39 +02:00
if ( ( user_select = = srch_t ) | | ShowStringPrompt ( searchstr , 256 , " Search %s? \n Enter search below. " , namestr ) ) {
SetFSSearch ( searchstr , curr_entry - > path , ( user_select = = srch_t ) ) ;
2017-02-27 02:07:27 +01:00
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 ;
}
2017-09-13 14:53:19 +02:00
} else if ( user_select = = fixcmac ) {
ShowString ( " %s \n Fixing CMACs for drive... " , namestr ) ;
RecursiveFixFileCmac ( curr_entry - > path ) ;
2017-05-15 23:25:39 +02:00
} else if ( user_select = = dirnfo ) {
2017-09-13 16:23:44 +02:00
bool is_drive = ( ! * current_path ) ;
FILINFO fno ;
2017-02-27 02:07:27 +01:00
u64 tsize = 0 ;
u32 tdirs = 0 ;
u32 tfiles = 0 ;
2017-09-13 16:23:44 +02:00
ShowString ( " Analyzing %s, please wait... " , is_drive ? " drive " : " dir " ) ;
if ( ( is_drive | | ( fvx_stat ( curr_entry - > path , & fno ) = = FR_OK ) ) & &
DirInfo ( curr_entry - > path , & tsize , & tdirs , & tfiles ) ) {
2017-02-27 02:07:27 +01:00
char bytestr [ 32 ] ;
FormatBytes ( bytestr , tsize ) ;
2017-09-13 16:23:44 +02:00
if ( is_drive ) {
char freestr [ 32 ] ;
char drvsstr [ 32 ] ;
char usedstr [ 32 ] ;
FormatBytes ( freestr , GetFreeSpace ( curr_entry - > path ) ) ;
FormatBytes ( drvsstr , GetTotalSpace ( curr_entry - > path ) ) ;
FormatBytes ( usedstr , GetTotalSpace ( curr_entry - > path ) - GetFreeSpace ( curr_entry - > path ) ) ;
ShowPrompt ( false , " %s \n \n %lu files & %lu subdirs \n %s total size \n \n space free: %s \n space used: %s \n space total: %s " ,
namestr , tfiles , tdirs , bytestr , freestr , usedstr , drvsstr ) ;
} else {
ShowPrompt ( false , " %s \n \n created: %04lu-%02lu-%02lu %02lu:%02lu:%02lu \n %lu files & %lu subdirs \n %s total size \n \n [%c] read-only [%c] hidden \n [%c] system [%c] archive \n [%c] virtual " ,
namestr ,
1980 + ( ( fno . fdate > > 9 ) & 0x7F ) , ( fno . fdate > > 5 ) & 0x0F , ( fno . fdate > > 0 ) & 0x1F ,
( fno . ftime > > 11 ) & 0x1F , ( fno . ftime > > 5 ) & 0x3F , ( ( fno . ftime > > 0 ) & 0x1F ) < < 1 ,
tfiles , tdirs , bytestr ,
( fno . fattrib & AM_RDO ) ? ' X ' : ' ' , ( fno . fattrib & AM_HID ) ? ' X ' : ' ' , ( fno . fattrib & AM_SYS ) ? ' X ' : ' ' ,
( fno . fattrib & AM_ARC ) ? ' X ' : ' ' , ( fno . fattrib & AM_VRT ) ? ' X ' : ' ' ) ;
}
} else ShowPrompt ( false , " Analyze %s: failed! " , is_drive ? " drive " : " dir " ) ;
2017-09-15 14:11:43 +02:00
} else if ( user_select = = stdcpy ) {
StandardCopy ( & cursor , & scroll , 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-05-31 23:26:45 +02:00
AutoEmuNandBase ( 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 ;
2017-09-22 17:18:06 +02:00
} else if ( ! switched & & ( 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 + + ;
2017-09-22 17:18:06 +02:00
} else if ( ! switched & & ( 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 ) ;
2017-09-22 17:18:06 +02:00
} else if ( switched & & ( pad_state & BUTTON_DOWN ) ) { // force reload file list
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
}
2017-08-01 02:03:05 +02:00
} else if ( pad_state & BUTTON_Y ) { // create an entry
const char * optionstr [ ] = { " Create a folder " , " Create a dummy file " } ;
u32 type = ShowSelectPrompt ( 2 , optionstr , " Create a new entry here? \n Select type. " ) ;
if ( type ) {
const char * typestr = ( type = = 1 ) ? " folder " : ( type = = 2 ) ? " file " : NULL ;
char ename [ 256 ] ;
u64 fsize = 0 ;
snprintf ( ename , 255 , ( type = = 1 ) ? " newdir " : " dummy.bin " ) ;
if ( ( ShowStringPrompt ( ename , 256 , " Create a new %s here? \n Enter name below. " , typestr ) ) & &
( ( type ! = 2 ) | | ( ( fsize = ShowNumberPrompt ( 0 , " Create a new %s here? \n Enter file size below. " , typestr ) ) ! = ( u64 ) - 1 ) ) ) {
if ( ( ( type = = 1 ) & & ! DirCreate ( current_path , ename ) ) | |
( ( type = = 2 ) & & ! FileCreateDummy ( current_path , ename , fsize ) ) ) {
char namestr [ 36 + 1 ] ;
TruncateString ( namestr , ename , 36 , 12 ) ;
ShowPrompt ( false , " Failed creating %s: \n %s " , typestr , namestr ) ;
} else {
GetDirContents ( current_dir , current_path ) ;
for ( cursor = ( current_dir - > n_entries ) ? current_dir - > n_entries - 1 : 0 ;
( cursor > 1 ) & & ( strncmp ( current_dir - > entry [ cursor ] . name , ename , 256 ) ! = 0 ) ; cursor - - ) ;
}
2016-03-15 22:19:30 +01:00
}
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 ;
2017-08-01 17:04:06 +02:00
} else if ( pad_state & ( BUTTON_HOME | BUTTON_POWER ) ) { // Home menu
2017-08-14 13:10:16 +02:00
const char * optionstr [ 8 ] ;
2017-08-01 17:04:06 +02:00
const char * buttonstr = ( pad_state & BUTTON_HOME ) ? " HOME " : " POWER " ;
2017-08-14 13:10:16 +02:00
u32 n_opt = 0 ;
int poweroff = + + n_opt ;
int reboot = + + n_opt ;
int scripts = ( PathExist ( SCRIPT_PATH ) ) ? ( int ) + + n_opt : - 1 ;
int payloads = ( PathExist ( PAYLOAD_PATH ) ) ? ( int ) + + n_opt : - 1 ;
int more = + + n_opt ;
if ( poweroff > 0 ) optionstr [ poweroff - 1 ] = " Poweroff system " ;
if ( reboot > 0 ) optionstr [ reboot - 1 ] = " Reboot system " ;
if ( scripts > 0 ) optionstr [ scripts - 1 ] = " Scripts... " ;
if ( payloads > 0 ) optionstr [ payloads - 1 ] = " Payloads... " ;
if ( more > 0 ) optionstr [ more - 1 ] = " More... " ;
int user_select = 0 ;
while ( ( user_select = ShowSelectPrompt ( n_opt , optionstr , " %s button pressed. \n Select action: " , buttonstr ) ) & &
( user_select ! = poweroff ) & & ( user_select ! = reboot ) ) {
char loadpath [ 256 ] ;
if ( ( user_select = = more ) & & ( HomeMoreMenu ( current_path , current_dir , clipboard ) = = 0 ) ) break ; // more... menu
else if ( ( user_select = = scripts ) & & ( FileSelector ( loadpath , " HOME scripts... menu. \n Select script: " , SCRIPT_PATH , " *.gm9 " , true , false ) ) ) {
ExecuteGM9Script ( loadpath ) ;
2017-09-06 00:46:01 +02:00
GetDirContents ( current_dir , current_path ) ;
ClearScreenF ( true , true , COLOR_STD_BG ) ;
2017-08-14 13:10:16 +02:00
break ;
} else if ( ( user_select = = payloads ) & & ( FileSelector ( loadpath , " HOME payloads... menu. \n Select payload: " , PAYLOAD_PATH , " *.firm " , true , false ) ) ) {
2017-09-16 13:48:31 +02:00
BootFirmHandler ( loadpath , false , false ) ;
2017-08-14 13:10:16 +02:00
}
}
if ( user_select = = poweroff ) {
2016-10-22 18:07:20 +02:00
exit_mode = GODMODE_EXIT_POWEROFF ;
break ;
2017-08-14 13:10:16 +02:00
} else if ( user_select = = reboot ) {
2016-10-22 18:07:20 +02:00
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 ) ;
2017-05-31 23:26:45 +02:00
AutoEmuNandBase ( true ) ;
2017-03-01 17:03:27 +01:00
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
2017-09-13 19:40:06 +02: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 ;
}
2017-09-08 15:39:06 +02:00
# ifdef AUTORUN_SCRIPT
u32 ScriptRunner ( bool is_b9s ) {
// show splash and initialize
ClearScreenF ( true , true , COLOR_STD_BG ) ;
2017-09-13 19:40:06 +02:00
SplashInit ( " scriptrunner mode " ) ;
2017-09-08 15:39:06 +02:00
u64 timer = timer_start ( ) ;
InitSDCardFS ( ) ;
AutoEmuNandBase ( true ) ;
InitNandCrypto ( ! is_b9s ) ;
InitExtFS ( ) ;
while ( CheckButton ( BUTTON_A ) ) ; // don't continue while A is held
while ( timer_msec ( timer ) < 500 ) ; // show splash for at least 0.5 sec
ClearScreenF ( true , true , COLOR_STD_BG ) ; // clear splash
// copy script to script buffer and run it
memset ( SCRIPT_BUFFER , 0 , SCRIPT_BUFFER_SIZE ) ;
memcpy ( SCRIPT_BUFFER , autorun_gm9 , autorun_gm9_size ) ;
ExecuteGM9Script ( NULL ) ;
// deinit
DeinitExtFS ( ) ;
DeinitSDCardFS ( ) ;
return GODMODE_EXIT_REBOOT ;
}
# endif