2016-02-13 17:29:56 +01:00
# include "draw.h"
2016-02-25 16:57:01 +01:00
# include "fs.h"
2016-02-13 17:29:56 +01:00
# include "fatfs/ff.h"
2016-02-25 16:57:01 +01:00
// don't use this area for anything else!
2016-02-26 17:03:25 +01:00
static FATFS * fs = ( FATFS * ) 0x20316000 ;
2016-02-29 02:09:31 +01:00
2016-02-25 16:57:01 +01:00
// this is the main buffer
2016-02-26 19:43:30 +01:00
static u8 * main_buffer = ( u8 * ) 0x21100000 ;
2016-02-29 02:09:31 +01:00
// this is the main buffer size
2016-03-02 17:22:44 +01:00
static size_t main_buffer_size = 1 * 1024 * 1024 ;
2016-02-29 02:09:31 +01:00
2016-02-29 22:51:20 +01:00
// write permission level - careful with this
static u32 write_permission_level = 1 ;
2016-02-25 16:57:01 +01:00
// number of currently open file systems
static u32 numfs = 0 ;
2016-02-13 17:29:56 +01:00
2016-02-29 15:47:45 +01:00
bool InitFS ( ) {
2016-02-25 16:57:01 +01:00
# ifndef EXEC_GATEWAY
2016-02-13 17:29:56 +01:00
// TODO: Magic?
* ( u32 * ) 0x10000020 = 0 ;
* ( u32 * ) 0x10000020 = 0x340 ;
# endif
2016-02-27 19:58:41 +01:00
for ( numfs = 0 ; numfs < 7 ; numfs + + ) {
2016-02-25 16:57:01 +01:00
char fsname [ 8 ] ;
2016-03-02 17:22:44 +01:00
snprintf ( fsname , 7 , " %lu: " , numfs ) ;
2016-02-26 17:03:25 +01:00
int res = f_mount ( fs + numfs , fsname , 1 ) ;
if ( res ! = FR_OK ) {
if ( numfs > = 4 ) break ;
2016-02-27 19:58:41 +01:00
ShowPrompt ( false , " Initialising failed! (%lu/%s/%i) " , numfs , fsname , res ) ;
2016-02-25 16:57:01 +01:00
DeinitFS ( ) ;
return false ;
2016-02-13 17:29:56 +01:00
}
}
return true ;
}
2016-02-29 15:47:45 +01:00
void DeinitFS ( ) {
2016-02-25 16:57:01 +01:00
for ( u32 i = 0 ; i < numfs ; i + + ) {
char fsname [ 8 ] ;
snprintf ( fsname , 7 , " %lu: " , numfs ) ;
f_mount ( NULL , fsname , 1 ) ;
2016-02-13 17:29:56 +01:00
}
2016-02-25 16:57:01 +01:00
numfs = 0 ;
2016-02-13 17:29:56 +01:00
}
2016-02-29 22:51:20 +01:00
bool CheckWritePermissions ( const char * path ) {
2016-03-02 14:01:20 +01:00
u32 pdrv = ( * path ) - ' 0 ' ;
2016-02-29 22:51:20 +01:00
if ( ( pdrv > 6 ) | | ( * ( path + 1 ) ! = ' : ' ) ) {
ShowPrompt ( false , " Invalid path " ) ;
return false ;
}
if ( ( pdrv > = 1 ) & & ( pdrv < = 3 ) & & ( write_permission_level < 3 ) ) {
2016-03-01 02:00:48 +01:00
if ( ShowPrompt ( true , " Writing to the SysNAND is locked! \n Unlock it now? " ) )
return SetWritePermissions ( 3 ) ;
2016-02-29 22:51:20 +01:00
return false ;
} else if ( ( pdrv > = 4 ) & & ( pdrv < = 6 ) & & ( write_permission_level < 2 ) ) {
2016-03-01 02:00:48 +01:00
if ( ShowPrompt ( true , " Writing to the EmuNAND is locked! \n Unlock it now? " ) )
return SetWritePermissions ( 2 ) ;
2016-02-29 22:51:20 +01:00
return false ;
} else if ( ( pdrv = = 0 ) & & ( write_permission_level < 1 ) ) {
2016-03-01 02:00:48 +01:00
if ( ShowPrompt ( true , " Writing to the SD card is locked! \n Unlock it now? " ) )
return SetWritePermissions ( 1 ) ;
2016-02-29 22:51:20 +01:00
return false ;
}
return true ;
}
bool SetWritePermissions ( u32 level ) {
2016-03-01 02:00:48 +01:00
if ( write_permission_level > = level ) {
// no need to ask the user here
write_permission_level = level ;
return true ;
}
switch ( level ) {
case 1 :
if ( ! ShowUnlockSequence ( 1 , " You want to enable SD card \n writing permissions. " ) )
return false ;
break ;
case 2 :
if ( ! ShowUnlockSequence ( 2 , " You want to enable EmuNAND \n writing permissions. \n This is potentially dangerous! \n Keep a backup, just in case. " ) )
return false ;
break ;
case 3 :
if ( ! ShowUnlockSequence ( 3 , " !This is your only warning! \n \n You want to enable SysNAND \n writing permissions. \n This is potentially dangerous \n and can brick your 3DS! \n Having a SysNAND backup and \n NANDmod is recommended. " ) )
return false ;
break ;
default :
break ;
}
2016-02-29 22:51:20 +01:00
write_permission_level = level ;
return true ;
}
2016-03-01 02:00:48 +01:00
u32 GetWritePermissions ( ) {
return write_permission_level ;
}
2016-02-26 19:43:30 +01:00
bool FileCreate ( const char * path , u8 * data , u32 size ) {
FIL file ;
UINT bytes_written = 0 ;
2016-02-29 22:51:20 +01:00
if ( ! CheckWritePermissions ( path ) ) return false ;
2016-02-26 19:43:30 +01:00
if ( f_open ( & file , path , FA_READ | FA_WRITE | FA_CREATE_ALWAYS ) ! = FR_OK )
return false ;
f_write ( & file , data , size , & bytes_written ) ;
f_close ( & file ) ;
return ( bytes_written = = size ) ;
}
2016-03-02 17:32:19 +01:00
bool PathCopyWorker ( char * dest , char * orig , bool overwrite ) {
2016-03-02 17:22:44 +01:00
FILINFO fno = { . lfname = NULL } ;
2016-02-29 02:09:31 +01:00
bool ret = false ;
2016-03-02 17:22:44 +01:00
2016-02-29 02:09:31 +01:00
if ( f_stat ( dest , & fno ) ! = FR_OK ) return false ; // destination directory does not exist
if ( ! ( fno . fattrib & AM_DIR ) ) return false ; // destination is not a directory (must be at this point)
if ( f_stat ( orig , & fno ) ! = FR_OK ) return false ; // origin does not exist
2016-03-02 17:22:44 +01:00
// build full destination path (on top of destination directory)
2016-02-29 02:09:31 +01:00
char * oname = strrchr ( orig , ' / ' ) ;
2016-03-02 17:22:44 +01:00
char * dname = dest + strnlen ( dest , 255 ) ;
2016-02-29 02:09:31 +01:00
if ( oname = = NULL ) return false ; // not a proper origin path
oname + + ;
* ( dname + + ) = ' / ' ;
strncpy ( dname , oname , 256 - ( dname - dest ) ) ;
2016-03-02 17:22:44 +01:00
// check if destination is part of or equal origin
if ( strncmp ( dest , orig , strnlen ( orig , 255 ) ) = = 0 ) {
if ( ( dest [ strnlen ( orig , 255 ) ] = = ' / ' ) | | ( dest [ strnlen ( orig , 255 ) ] = = ' \0 ' ) )
return false ;
}
2016-02-29 02:09:31 +01:00
// check if destination exists
2016-03-02 17:32:19 +01:00
if ( ! overwrite & & ( f_stat ( dest , NULL ) = = FR_OK ) ) {
char namestr [ 36 + 1 ] ;
2016-03-02 17:22:44 +01:00
TruncateString ( namestr , dest , 36 , 8 ) ;
if ( ! ShowPrompt ( true , " Destination already exists: \n %s \n Overwrite existing file(s)? " , namestr ) )
2016-02-29 02:09:31 +01:00
return false ;
2016-03-02 17:32:19 +01:00
overwrite = true ;
2016-02-29 02:09:31 +01:00
}
// the copy process takes place here
2016-03-02 17:22:44 +01:00
if ( ! ShowProgress ( 0 , 0 , orig ) ) return false ;
2016-02-29 02:09:31 +01:00
if ( fno . fattrib & AM_DIR ) { // processing folders...
DIR pdir ;
char * fname = orig + strnlen ( orig , 256 ) ;
2016-03-02 17:22:44 +01:00
// create the destination folder if it does not already exist
2016-03-02 14:01:20 +01:00
if ( ( f_stat ( dest , NULL ) ! = FR_OK ) & & ( f_mkdir ( dest ) ! = FR_OK ) )
2016-02-29 02:09:31 +01:00
return false ;
if ( f_opendir ( & pdir , orig ) ! = FR_OK )
return false ;
2016-03-02 14:01:20 +01:00
* ( fname + + ) = ' / ' ;
fno . lfname = fname ;
fno . lfsize = 256 - ( fname - orig ) ;
2016-02-29 02:09:31 +01:00
while ( f_readdir ( & pdir , & fno ) = = FR_OK ) {
if ( ( strncmp ( fno . fname , " . " , 2 ) = = 0 ) | | ( strncmp ( fno . fname , " .. " , 3 ) = = 0 ) )
continue ; // filter out virtual entries
if ( fname [ 0 ] = = 0 )
strncpy ( fname , fno . fname , 256 - ( fname - orig ) ) ;
if ( fno . fname [ 0 ] = = 0 ) {
ret = true ;
break ;
2016-03-02 17:32:19 +01:00
} else if ( ! PathCopyWorker ( dest , orig , overwrite ) ) {
2016-02-29 02:09:31 +01:00
break ;
}
}
f_closedir ( & pdir ) ;
} else { // processing files...
FIL ofile ;
FIL dfile ;
size_t fsize ;
if ( f_open ( & ofile , orig , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
return false ;
if ( f_open ( & dfile , dest , FA_WRITE | FA_CREATE_ALWAYS ) ! = FR_OK ) {
f_close ( & ofile ) ;
return false ;
}
fsize = f_size ( & ofile ) ;
f_lseek ( & dfile , 0 ) ;
f_sync ( & dfile ) ;
f_lseek ( & ofile , 0 ) ;
f_sync ( & ofile ) ;
2016-03-02 14:01:20 +01:00
ret = true ;
2016-02-29 02:09:31 +01:00
for ( size_t pos = 0 ; pos < fsize ; pos + = main_buffer_size ) {
UINT bytes_read = 0 ;
2016-03-02 17:22:44 +01:00
UINT bytes_written = 0 ;
2016-02-29 02:09:31 +01:00
f_read ( & ofile , main_buffer , main_buffer_size , & bytes_read ) ;
2016-03-02 17:22:44 +01:00
if ( ! ShowProgress ( pos + ( bytes_read / 2 ) , fsize , orig ) ) {
ret = false ;
break ;
}
2016-02-29 02:09:31 +01:00
f_write ( & dfile , main_buffer , bytes_read , & bytes_written ) ;
if ( bytes_read ! = bytes_written ) {
ret = false ;
break ;
}
}
2016-03-02 17:22:44 +01:00
ShowProgress ( 1 , 1 , orig ) ;
2016-02-29 02:09:31 +01:00
f_close ( & ofile ) ;
f_close ( & dfile ) ;
}
2016-03-02 17:22:44 +01:00
* ( - - dname ) = ' \0 ' ;
2016-02-29 02:09:31 +01:00
return ret ;
}
2016-02-29 15:47:45 +01:00
bool PathCopy ( const char * destdir , const char * orig ) {
2016-02-29 02:09:31 +01:00
char fdpath [ 256 ] ; // 256 is the maximum length of a full path
char fopath [ 256 ] ;
2016-02-29 22:51:20 +01:00
if ( ! CheckWritePermissions ( destdir ) ) return false ;
2016-03-02 17:22:44 +01:00
strncpy ( fdpath , destdir , 255 ) ;
strncpy ( fopath , orig , 255 ) ;
2016-03-02 17:32:19 +01:00
return PathCopyWorker ( fdpath , fopath , false ) ;
2016-02-29 02:09:31 +01:00
}
2016-02-29 15:47:45 +01:00
bool PathDeleteWorker ( char * fpath ) {
2016-03-02 17:22:44 +01:00
FILINFO fno = { . lfname = NULL } ;
2016-02-29 02:09:31 +01:00
2016-03-02 14:15:29 +01:00
// this code handles directory content deletion
2016-02-29 02:09:31 +01:00
if ( f_stat ( fpath , & fno ) ! = FR_OK ) return false ; // fpath does not exist
2016-03-02 14:01:20 +01:00
if ( fno . fattrib & AM_DIR ) { // process folder contents
2016-02-29 02:09:31 +01:00
DIR pdir ;
2016-03-02 14:15:29 +01:00
char * fname = fpath + strnlen ( fpath , 255 ) ;
2016-02-29 02:09:31 +01:00
2016-03-02 14:01:20 +01:00
if ( f_opendir ( & pdir , fpath ) ! = FR_OK )
return false ;
2016-02-29 02:09:31 +01:00
* ( fname + + ) = ' / ' ;
fno . lfname = fname ;
2016-03-02 14:15:29 +01:00
fno . lfsize = fpath + 255 - fname ;
2016-02-29 02:09:31 +01:00
while ( f_readdir ( & pdir , & fno ) = = FR_OK ) {
if ( ( strncmp ( fno . fname , " . " , 2 ) = = 0 ) | | ( strncmp ( fno . fname , " .. " , 3 ) = = 0 ) )
continue ; // filter out virtual entries
if ( fname [ 0 ] = = 0 )
2016-03-02 14:15:29 +01:00
strncpy ( fname , fno . fname , fpath + 255 - fname ) ;
2016-02-29 02:09:31 +01:00
if ( fno . fname [ 0 ] = = 0 ) {
break ;
2016-03-02 14:15:29 +01:00
} else { // return value won't matter
PathDeleteWorker ( fpath ) ;
2016-02-29 02:09:31 +01:00
}
}
f_closedir ( & pdir ) ;
2016-03-02 14:01:20 +01:00
* ( - - fname ) = ' \0 ' ;
2016-02-29 02:09:31 +01:00
}
2016-03-02 14:15:29 +01:00
return ( f_unlink ( fpath ) = = FR_OK ) ;
2016-02-29 02:09:31 +01:00
}
2016-02-29 15:47:45 +01:00
bool PathDelete ( const char * path ) {
2016-02-29 02:09:31 +01:00
char fpath [ 256 ] ; // 256 is the maximum length of a full path
2016-02-29 22:51:20 +01:00
if ( ! CheckWritePermissions ( path ) ) return false ;
2016-02-29 02:09:31 +01:00
strncpy ( fpath , path , 256 ) ;
return PathDeleteWorker ( fpath ) ;
}
2016-02-29 15:47:45 +01:00
void CreateScreenshot ( ) {
2016-02-26 19:43:30 +01:00
const u8 bmp_header [ 54 ] = {
0x42 , 0x4D , 0x36 , 0xCA , 0x08 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x36 , 0x00 , 0x00 , 0x00 , 0x28 , 0x00 ,
0x00 , 0x00 , 0x90 , 0x01 , 0x00 , 0x00 , 0xE0 , 0x01 , 0x00 , 0x00 , 0x01 , 0x00 , 0x18 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0xCA , 0x08 , 0x00 , 0x12 , 0x0B , 0x00 , 0x00 , 0x12 , 0x0B , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
} ;
u8 * buffer = main_buffer + 54 ;
u8 * buffer_t = buffer + ( 400 * 240 * 3 ) ;
char filename [ 16 ] ;
static u32 n = 0 ;
for ( ; n < 1000 ; n + + ) {
snprintf ( filename , 16 , " 0:/snap%03i.bmp " , ( int ) n ) ;
2016-02-27 20:10:41 +01:00
if ( f_stat ( filename , NULL ) ! = FR_OK ) break ;
2016-02-26 19:43:30 +01:00
}
if ( n > = 1000 ) return ;
memcpy ( main_buffer , bmp_header , 54 ) ;
memset ( buffer , 0x1F , 400 * 240 * 3 * 2 ) ;
for ( u32 x = 0 ; x < 400 ; x + + )
for ( u32 y = 0 ; y < 240 ; y + + )
memcpy ( buffer_t + ( y * 400 + x ) * 3 , TOP_SCREEN0 + ( x * 240 + y ) * 3 , 3 ) ;
for ( u32 x = 0 ; x < 320 ; x + + )
for ( u32 y = 0 ; y < 240 ; y + + )
memcpy ( buffer + ( y * 400 + x + 40 ) * 3 , BOT_SCREEN0 + ( x * 240 + y ) * 3 , 3 ) ;
FileCreate ( filename , main_buffer , 54 + ( 400 * 240 * 3 * 2 ) ) ;
}
2016-02-29 22:27:04 +01:00
void DirEntryCpy ( DirEntry * dest , const DirEntry * orig ) {
memcpy ( dest , orig , sizeof ( DirEntry ) ) ;
dest - > name = dest - > path + ( orig - > name - orig - > path ) ;
}
2016-02-29 15:47:45 +01:00
void SortDirStruct ( DirStruct * contents ) {
for ( u32 s = 0 ; s < contents - > n_entries ; s + + ) {
DirEntry * cmp0 = & ( contents - > entry [ s ] ) ;
DirEntry * min0 = cmp0 ;
for ( u32 c = s + 1 ; c < contents - > n_entries ; c + + ) {
DirEntry * cmp1 = & ( contents - > entry [ c ] ) ;
if ( min0 - > type ! = cmp1 - > type ) {
if ( min0 - > type > cmp1 - > type )
min0 = cmp1 ;
continue ;
}
if ( strncasecmp ( min0 - > name , cmp1 - > name , 256 ) > 0 )
min0 = cmp1 ;
}
if ( min0 ! = cmp0 ) {
DirEntry swap ; // swap entries and fix names
2016-02-29 22:27:04 +01:00
DirEntryCpy ( & swap , cmp0 ) ;
DirEntryCpy ( cmp0 , min0 ) ;
DirEntryCpy ( min0 , & swap ) ;
2016-02-29 15:47:45 +01:00
}
}
}
2016-02-26 19:43:30 +01:00
bool GetRootDirContentsWorker ( DirStruct * contents ) {
2016-02-25 16:57:01 +01:00
static const char * drvname [ 16 ] = {
2016-02-26 17:03:25 +01:00
" SDCARD " ,
2016-02-27 19:58:41 +01:00
" SYSNAND CTRNAND " , " SYSNAND TWLN " , " SYSNAND TWLP " ,
" EMUNAND CTRNAND " , " EMUNAND TWLN " , " EMUNAND TWLP "
2016-02-25 16:57:01 +01:00
} ;
2016-02-13 17:29:56 +01:00
2016-02-25 16:57:01 +01:00
for ( u32 pdrv = 0 ; ( pdrv < numfs ) & & ( pdrv < MAX_ENTRIES ) ; pdrv + + ) {
memset ( contents - > entry [ pdrv ] . path , 0x00 , 16 ) ;
2016-02-26 17:03:25 +01:00
snprintf ( contents - > entry [ pdrv ] . path + 0 , 4 , " %lu: " , pdrv ) ;
2016-02-27 19:58:41 +01:00
snprintf ( contents - > entry [ pdrv ] . path + 4 , 32 , " [%lu:] %s " , pdrv , drvname [ pdrv ] ) ;
2016-02-25 16:57:01 +01:00
contents - > entry [ pdrv ] . name = contents - > entry [ pdrv ] . path + 4 ;
2016-02-27 19:58:41 +01:00
contents - > entry [ pdrv ] . size = GetTotalSpace ( contents - > entry [ pdrv ] . path ) ;
contents - > entry [ pdrv ] . type = T_FAT_ROOT ;
contents - > entry [ pdrv ] . marked = 0 ;
2016-02-13 17:29:56 +01:00
}
2016-02-26 17:03:25 +01:00
contents - > n_entries = numfs ;
2016-02-13 17:29:56 +01:00
2016-02-25 16:57:01 +01:00
return contents - > n_entries ;
2016-02-13 17:29:56 +01:00
}
2016-02-26 19:43:30 +01:00
bool GetDirContentsWorker ( DirStruct * contents , char * fpath , int fsize , bool recursive ) {
2016-02-13 17:29:56 +01:00
DIR pdir ;
FILINFO fno ;
char * fname = fpath + strnlen ( fpath , fsize - 1 ) ;
bool ret = false ;
if ( f_opendir ( & pdir , fpath ) ! = FR_OK )
return false ;
( fname + + ) [ 0 ] = ' / ' ;
fno . lfname = fname ;
fno . lfsize = fsize - ( fname - fpath ) ;
while ( f_readdir ( & pdir , & fno ) = = FR_OK ) {
if ( ( strncmp ( fno . fname , " . " , 2 ) = = 0 ) | | ( strncmp ( fno . fname , " .. " , 3 ) = = 0 ) )
continue ; // filter out virtual entries
if ( fname [ 0 ] = = 0 )
strncpy ( fname , fno . fname , ( fsize - 1 ) - ( fname - fpath ) ) ;
if ( fno . fname [ 0 ] = = 0 ) {
ret = true ;
break ;
2016-02-25 16:57:01 +01:00
} else {
DirEntry * entry = & ( contents - > entry [ contents - > n_entries ] ) ;
2016-02-26 18:52:30 +01:00
strncpy ( entry - > path , fpath , 256 ) ;
entry - > name = entry - > path + ( fname - fpath ) ;
2016-02-25 16:57:01 +01:00
if ( fno . fattrib & AM_DIR ) {
entry - > type = T_FAT_DIR ;
entry - > size = 0 ;
} else {
entry - > type = T_FAT_FILE ;
entry - > size = fno . fsize ;
}
2016-02-27 19:58:41 +01:00
entry - > marked = 0 ;
2016-02-25 16:57:01 +01:00
contents - > n_entries + + ;
if ( contents - > n_entries > = MAX_ENTRIES )
break ;
2016-02-13 17:29:56 +01:00
}
if ( recursive & & ( fno . fattrib & AM_DIR ) ) {
2016-02-25 16:57:01 +01:00
if ( ! GetDirContentsWorker ( contents , fpath , fsize , recursive ) )
2016-02-13 17:29:56 +01:00
break ;
}
}
f_closedir ( & pdir ) ;
return ret ;
}
2016-02-29 16:14:39 +01:00
void GetDirContents ( DirStruct * contents , const char * path ) {
contents - > n_entries = 0 ;
2016-02-25 16:57:01 +01:00
if ( strncmp ( path , " " , 256 ) = = 0 ) { // root directory
2016-02-29 16:14:39 +01:00
if ( ! GetRootDirContentsWorker ( contents ) )
contents - > n_entries = 0 ; // not required, but so what?
2016-02-25 16:57:01 +01:00
} else {
char fpath [ 256 ] ; // 256 is the maximum length of a full path
strncpy ( fpath , path , 256 ) ;
2016-02-29 16:14:39 +01:00
if ( ! GetDirContentsWorker ( contents , fpath , 256 , false ) )
contents - > n_entries = 0 ;
SortDirStruct ( contents ) ;
2016-02-13 17:29:56 +01:00
}
}
2016-02-27 19:58:41 +01:00
uint64_t GetFreeSpace ( const char * path )
2016-02-13 17:29:56 +01:00
{
DWORD free_clusters ;
2016-02-27 19:58:41 +01:00
FATFS * fs_ptr ;
char fsname [ 4 ] = { ' \0 ' } ;
int fsnum = - 1 ;
strncpy ( fsname , path , 2 ) ;
fsnum = * fsname - ( int ) ' 0 ' ;
if ( ( fsnum < 0 ) | | ( fsnum > = 7 ) | | ( fsname [ 1 ] ! = ' : ' ) )
return - 1 ;
if ( f_getfree ( fsname , & free_clusters , & fs_ptr ) ! = FR_OK )
2016-02-13 17:29:56 +01:00
return - 1 ;
2016-02-27 19:58:41 +01:00
return ( uint64_t ) free_clusters * fs [ fsnum ] . csize * _MAX_SS ;
2016-02-13 17:29:56 +01:00
}
2016-02-27 19:58:41 +01:00
uint64_t GetTotalSpace ( const char * path )
2016-02-13 17:29:56 +01:00
{
2016-02-27 19:58:41 +01:00
int fsnum = - 1 ;
fsnum = * path - ( int ) ' 0 ' ;
if ( ( fsnum < 0 ) | | ( fsnum > = 7 ) | | ( path [ 1 ] ! = ' : ' ) )
return - 1 ;
return ( uint64_t ) ( fs [ fsnum ] . n_fatent - 2 ) * fs [ fsnum ] . csize * _MAX_SS ;
}