2016-03-28 17:32:29 +02:00
# include "ui.h"
2016-02-25 16:57:01 +01:00
# include "fs.h"
2016-03-21 18:29:55 +01:00
# include "virtual.h"
2016-04-05 20:34:50 +02:00
# include "image.h"
# include "ff.h"
2016-02-13 17:29:56 +01:00
2016-03-21 19:24:06 +01:00
# define MAIN_BUFFER ((u8*)0x21200000)
# define MAIN_BUFFER_SIZE (0x100000) // must be multiple of 0x200
2016-03-11 01:29:14 +01:00
2016-04-04 22:45:49 +02:00
# define NORM_FS 10
2016-04-09 21:50:50 +02:00
# define VIRT_FS 4
2016-03-11 01:29:14 +01:00
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-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
2016-04-04 22:45:49 +02:00
static bool fs_mounted [ NORM_FS ] = { false } ;
2016-02-13 17:29:56 +01:00
2016-03-11 01:29:14 +01:00
bool InitSDCardFS ( ) {
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-03-11 01:29:14 +01:00
fs_mounted [ 0 ] = ( f_mount ( fs , " 0: " , 1 ) = = FR_OK ) ;
return fs_mounted [ 0 ] ;
}
2016-04-05 15:20:48 +02:00
bool InitExtFS ( ) {
2016-03-16 15:45:24 +01:00
if ( ! fs_mounted [ 0 ] )
return false ;
2016-04-04 22:45:49 +02:00
for ( u32 i = 1 ; i < NORM_FS ; i + + ) {
2016-02-25 16:57:01 +01:00
char fsname [ 8 ] ;
2016-03-11 01:29:14 +01:00
snprintf ( fsname , 7 , " %lu: " , i ) ;
2016-04-07 12:00:41 +02:00
if ( f_mount ( fs + i , fsname , 1 ) = = FR_OK )
fs_mounted [ i ] = true ;
2016-02-13 17:29:56 +01:00
}
return true ;
}
2016-04-05 15:20:48 +02:00
void DeinitExtFS ( ) {
2016-04-06 16:42:08 +02:00
MountImage ( NULL ) ;
2016-04-04 22:45:49 +02:00
for ( u32 i = NORM_FS ; i > 0 ; i - - ) {
2016-03-11 01:29:14 +01:00
if ( fs_mounted [ i ] ) {
char fsname [ 8 ] ;
snprintf ( fsname , 7 , " %lu: " , i ) ;
f_mount ( NULL , fsname , 1 ) ;
fs_mounted [ i ] = false ;
}
2016-02-13 17:29:56 +01:00
}
}
2016-03-21 23:18:33 +01:00
void DeinitSDCardFS ( ) {
if ( fs_mounted [ 0 ] ) {
f_mount ( NULL , " 0: " , 1 ) ;
fs_mounted [ 0 ] = false ;
}
}
2016-03-16 18:46:05 +01:00
int PathToNumFS ( const char * path ) {
int fsnum = * path - ( int ) ' 0 ' ;
2016-04-04 22:45:49 +02:00
if ( ( fsnum < 0 ) | | ( fsnum > = NORM_FS ) | | ( path [ 1 ] ! = ' : ' ) ) {
2016-03-21 18:29:55 +01:00
if ( ! IsVirtualPath ( path ) ) ShowPrompt ( false , " Invalid path (%s) " , path ) ;
2016-03-16 18:46:05 +01:00
return - 1 ;
2016-02-29 22:51:20 +01:00
}
2016-03-16 18:46:05 +01:00
return fsnum ;
}
2016-04-05 15:20:48 +02:00
bool IsMountedFS ( const char * path ) {
int fsnum = PathToNumFS ( path ) ;
return ( ( fsnum > = 0 ) & & ( fsnum < NORM_FS ) ) ? fs_mounted [ fsnum ] : false ;
}
2016-03-16 18:46:05 +01:00
bool CheckWritePermissions ( const char * path ) {
int pdrv = PathToNumFS ( path ) ;
2016-03-21 18:29:55 +01:00
if ( pdrv < 0 ) {
if ( IsVirtualPath ( path ) ) // this is a hack, but okay for now
2016-04-09 21:50:50 +02:00
pdrv = ( IsVirtualPath ( path ) = = VRT_MEMORY ) ? 10 :
( IsVirtualPath ( path ) = = VRT_SYSNAND ) ? 1 : 4 ;
2016-03-21 18:29:55 +01:00
else return false ;
}
2016-03-16 18:46:05 +01:00
2016-02-29 22:51:20 +01:00
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 ;
2016-04-04 22:45:49 +02:00
} else if ( ( pdrv > = 7 ) & & ( pdrv < = 9 ) & & ( write_permission_level < 2 ) ) {
2016-04-05 13:10:23 +02:00
if ( ShowPrompt ( true , " Writing to images is locked! \n Unlock it now? " ) )
2016-04-04 22:45:49 +02:00
return SetWritePermissions ( 2 ) ;
return false ;
2016-02-29 22:51:20 +01:00
} 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 ;
2016-04-09 21:50:50 +02:00
} else if ( pdrv > = 10 ) {
ShowPrompt ( false , " Writing to memory is forbidden! " ) ;
return false ;
2016-02-29 22:51:20 +01:00
}
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 :
2016-04-05 13:10:23 +02:00
if ( ! ShowUnlockSequence ( 2 , " You want to enable EmuNAND & \n image writing permissions. \n Keep backups, just in case. " ) )
2016-03-01 02:00:48 +01:00
return false ;
break ;
case 3 :
2016-04-05 13:10:23 +02:00
if ( ! ShowUnlockSequence ( 3 , " !This is your only warning! \n \n You want to enable SysNAND \n writing permissions. \n This enables you to do some \n really dangerous stuff! \n Having a SysNAND backup and \n NANDmod is recommended. " ) )
2016-03-01 02:00:48 +01:00
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-03-28 20:10:43 +02:00
bool GetTempFileName ( char * path ) {
// this assumes path is initialized with enough room
char * tempname = strrchr ( path , ' / ' ) ;
if ( ! tempname ) return false ;
tempname + + ;
strncpy ( tempname , " AAAAAAAA.TMP " , 255 - ( tempname - path ) ) ;
char * cc = tempname ;
// this does not try all permutations
for ( ; ( * cc < = ' Z ' ) & & ( cc - tempname < 8 ) ; ( * cc ) + + ) {
if ( f_stat ( path , NULL ) ! = FR_OK ) break ;
if ( * cc = = ' Z ' ) cc + + ;
}
return ( cc - tempname < 8 ) ? true : false ;
}
2016-03-11 01:29:14 +01:00
bool FileCreateData ( const char * path , u8 * data , size_t size ) {
2016-02-26 19:43:30 +01:00
FIL file ;
UINT bytes_written = 0 ;
2016-02-29 22:51:20 +01:00
if ( ! CheckWritePermissions ( path ) ) return false ;
2016-03-11 01:29:14 +01:00
if ( f_open ( & file , path , FA_WRITE | FA_CREATE_ALWAYS ) ! = FR_OK )
2016-02-26 19:43:30 +01:00
return false ;
f_write ( & file , data , size , & bytes_written ) ;
f_close ( & file ) ;
return ( bytes_written = = size ) ;
}
2016-04-08 21:02:38 +02:00
size_t FileGetData ( const char * path , u8 * data , size_t size , size_t foffset )
2016-03-11 01:29:14 +01:00
{
2016-04-08 21:02:38 +02:00
if ( PathToNumFS ( path ) > = 0 ) {
UINT bytes_read = 0 ;
FIL file ;
if ( f_open ( & file , path , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
return 0 ;
f_lseek ( & file , foffset ) ;
if ( f_read ( & file , data , size , & bytes_read ) ! = FR_OK )
return 0 ;
f_close ( & file ) ;
return bytes_read ;
} else if ( IsVirtualPath ( path ) ) {
u32 bytes_read = 0 ;
VirtualFile vfile ;
if ( ! FindVirtualFile ( & vfile , path , 0 ) )
return 0 ;
return ( ReadVirtualFile ( & vfile , data , foffset , size , & bytes_read ) = = 0 ) ? bytes_read : 0 ;
}
return 0 ;
2016-03-11 01:29:14 +01:00
}
2016-03-21 23:19:23 +01:00
bool PathCopyVirtual ( const char * destdir , const char * orig ) {
char dest [ 256 ] ; // maximum path name length in FAT
char * oname = strrchr ( orig , ' / ' ) ;
char deststr [ 36 + 1 ] ;
char origstr [ 36 + 1 ] ;
bool ret = true ;
if ( oname = = NULL ) return false ; // not a proper origin path
oname + + ;
2016-03-22 19:44:21 +01:00
snprintf ( dest , 255 , " %s/%s " , destdir , oname ) ;
2016-03-21 23:19:23 +01:00
TruncateString ( deststr , dest , 36 , 8 ) ;
TruncateString ( origstr , orig , 36 , 8 ) ;
if ( IsVirtualPath ( dest ) & & IsVirtualPath ( orig ) ) { // virtual to virtual
VirtualFile dvfile ;
VirtualFile ovfile ;
u32 osize ;
2016-03-22 19:44:21 +01:00
if ( ! FindVirtualFile ( & dvfile , dest , 0 ) )
2016-03-21 23:19:23 +01:00
return false ;
2016-03-22 19:44:21 +01:00
if ( ! FindVirtualFile ( & ovfile , orig , 0 ) )
2016-03-21 23:19:23 +01:00
return false ;
osize = ovfile . size ;
if ( dvfile . size ! = osize ) { // almost impossible, but so what...
ShowPrompt ( false , " Virtual file size mismatch: \n %s \n %s " , origstr , deststr ) ;
return false ;
}
if ( ( dvfile . keyslot = = ovfile . keyslot ) & & ( dvfile . offset = = ovfile . offset ) ) // this improves copy times
dvfile . keyslot = ovfile . keyslot = 0xFF ;
2016-04-05 15:20:48 +02:00
DeinitExtFS ( ) ;
2016-03-21 23:19:23 +01:00
if ( ! ShowProgress ( 0 , 0 , orig ) ) ret = false ;
for ( size_t pos = 0 ; ( pos < osize ) & & ret ; pos + = MAIN_BUFFER_SIZE ) {
UINT read_bytes = min ( MAIN_BUFFER_SIZE , osize - pos ) ;
2016-04-08 21:02:38 +02:00
if ( ReadVirtualFile ( & ovfile , MAIN_BUFFER , pos , read_bytes , NULL ) ! = 0 )
2016-03-21 23:19:23 +01:00
ret = false ;
if ( ! ShowProgress ( pos + ( read_bytes / 2 ) , osize , orig ) )
ret = false ;
2016-04-08 21:02:38 +02:00
if ( WriteVirtualFile ( & dvfile , MAIN_BUFFER , pos , read_bytes , NULL ) ! = 0 )
2016-03-21 23:19:23 +01:00
ret = false ;
}
ShowProgress ( 1 , 1 , orig ) ;
2016-04-05 15:20:48 +02:00
InitExtFS ( ) ;
2016-03-21 23:19:23 +01:00
} else if ( IsVirtualPath ( dest ) ) { // SD card to virtual (other FAT not allowed!)
VirtualFile dvfile ;
FIL ofile ;
u32 osize ;
if ( f_open ( & ofile , orig , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
return false ;
f_lseek ( & ofile , 0 ) ;
f_sync ( & ofile ) ;
osize = f_size ( & ofile ) ;
2016-03-22 19:44:21 +01:00
if ( ! FindVirtualFile ( & dvfile , dest , 0 ) ) {
if ( ! FindVirtualFile ( & dvfile , dest , osize ) ) {
f_close ( & ofile ) ;
return false ;
}
snprintf ( dest , 255 , " %s/%s " , destdir , dvfile . name ) ;
if ( ! ShowPrompt ( true , " Entry not found: %s \n Inject into %s instead? " , deststr , dest ) ) {
f_close ( & ofile ) ;
return false ;
}
TruncateString ( deststr , dest , 36 , 8 ) ;
}
2016-03-21 23:19:23 +01:00
if ( dvfile . size ! = osize ) {
char osizestr [ 32 ] ;
char dsizestr [ 32 ] ;
FormatBytes ( osizestr , osize ) ;
FormatBytes ( dsizestr , dvfile . size ) ;
2016-03-22 14:30:11 +01:00
if ( dvfile . size > osize ) {
if ( ! ShowPrompt ( true , " File smaller than available space: \n %s (%s) \n %s (%s) \n Continue? " , origstr , osizestr , deststr , dsizestr ) ) {
f_close ( & ofile ) ;
return false ;
}
} else {
ShowPrompt ( false , " File bigger than available space: \n %s (%s) \n %s (%s) " , origstr , osizestr , deststr , dsizestr ) ;
f_close ( & ofile ) ;
return false ;
}
2016-03-21 23:19:23 +01:00
}
2016-04-05 15:20:48 +02:00
DeinitExtFS ( ) ;
2016-03-21 23:19:23 +01:00
if ( ! ShowProgress ( 0 , 0 , orig ) ) ret = false ;
for ( size_t pos = 0 ; ( pos < osize ) & & ret ; pos + = MAIN_BUFFER_SIZE ) {
UINT bytes_read = 0 ;
if ( f_read ( & ofile , MAIN_BUFFER , MAIN_BUFFER_SIZE , & bytes_read ) ! = FR_OK )
ret = false ;
if ( ! ShowProgress ( pos + ( bytes_read / 2 ) , osize , orig ) )
ret = false ;
2016-04-08 21:02:38 +02:00
if ( WriteVirtualFile ( & dvfile , MAIN_BUFFER , pos , bytes_read , NULL ) ! = 0 )
2016-03-21 23:19:23 +01:00
ret = false ;
}
ShowProgress ( 1 , 1 , orig ) ;
f_close ( & ofile ) ;
2016-04-05 15:20:48 +02:00
InitExtFS ( ) ;
} else if ( IsVirtualPath ( orig ) ) { // virtual to any file system
2016-03-21 23:19:23 +01:00
VirtualFile ovfile ;
FIL dfile ;
u32 osize ;
2016-03-22 19:44:21 +01:00
if ( ! FindVirtualFile ( & ovfile , orig , 0 ) )
2016-03-21 23:19:23 +01:00
return false ;
// check if destination exists
if ( f_stat ( dest , NULL ) = = FR_OK ) {
if ( ! ShowPrompt ( true , " Destination already exists: \n %s \n Overwrite existing file(s)? " , deststr ) )
return false ;
}
if ( f_open ( & dfile , dest , FA_WRITE | FA_CREATE_ALWAYS ) ! = FR_OK )
return false ;
f_lseek ( & dfile , 0 ) ;
f_sync ( & dfile ) ;
osize = ovfile . size ;
if ( GetFreeSpace ( dest ) < osize ) {
ShowPrompt ( false , " Error: File is too big for destination " ) ;
f_close ( & dfile ) ;
return false ;
}
if ( ! ShowProgress ( 0 , 0 , orig ) ) ret = false ;
for ( size_t pos = 0 ; ( pos < osize ) & & ret ; pos + = MAIN_BUFFER_SIZE ) {
UINT read_bytes = min ( MAIN_BUFFER_SIZE , osize - pos ) ;
UINT bytes_written = 0 ;
2016-04-08 21:02:38 +02:00
if ( ReadVirtualFile ( & ovfile , MAIN_BUFFER , pos , read_bytes , NULL ) ! = 0 )
2016-03-21 23:19:23 +01:00
ret = false ;
if ( ! ShowProgress ( pos + ( read_bytes / 2 ) , osize , orig ) )
ret = false ;
if ( f_write ( & dfile , MAIN_BUFFER , read_bytes , & bytes_written ) ! = FR_OK )
ret = false ;
if ( read_bytes ! = bytes_written )
ret = false ;
}
ShowProgress ( 1 , 1 , orig ) ;
f_close ( & dfile ) ;
} else {
return false ;
}
return ret ;
}
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-03-14 17:22:53 +01:00
if ( f_stat ( dest , & fno ) ! = FR_OK ) { // is root or destination does not exist
DIR tmp_dir ; // check if root
if ( f_opendir ( & tmp_dir , dest ) ! = FR_OK ) return false ;
f_closedir ( & tmp_dir ) ;
} else if ( ! ( fno . fattrib & AM_DIR ) ) return false ; // destination is not a directory (must be at this point)
2016-02-29 02:09:31 +01:00
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 ) {
2016-03-14 17:22:53 +01:00
if ( ( dest [ strnlen ( orig , 255 ) ] = = ' / ' ) | | ( dest [ strnlen ( orig , 255 ) ] = = ' \0 ' ) ) {
ShowPrompt ( false , " Error: Destination is part of origin " ) ;
2016-03-02 17:22:44 +01:00
return false ;
2016-03-14 17:22:53 +01:00
}
2016-03-02 17:22:44 +01:00
}
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 ;
2016-03-15 22:17:59 +01:00
fsize = f_size ( & ofile ) ;
if ( GetFreeSpace ( dest ) < fsize ) {
ShowPrompt ( false , " Error: File is too big for destination " ) ;
f_close ( & ofile ) ;
return false ;
}
2016-02-29 02:09:31 +01:00
if ( f_open ( & dfile , dest , FA_WRITE | FA_CREATE_ALWAYS ) ! = FR_OK ) {
f_close ( & ofile ) ;
return false ;
}
2016-03-15 22:17:59 +01:00
2016-02-29 02:09:31 +01:00
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-03-21 19:24:06 +01:00
for ( size_t pos = 0 ; ( pos < fsize ) & & ret ; pos + = MAIN_BUFFER_SIZE ) {
2016-02-29 02:09:31 +01:00
UINT bytes_read = 0 ;
2016-03-02 17:22:44 +01:00
UINT bytes_written = 0 ;
2016-03-21 23:18:33 +01:00
if ( f_read ( & ofile , MAIN_BUFFER , MAIN_BUFFER_SIZE , & bytes_read ) ! = FR_OK )
ret = false ;
2016-03-15 22:17:59 +01:00
if ( ! ShowProgress ( pos + ( bytes_read / 2 ) , fsize , orig ) )
2016-03-02 17:22:44 +01:00
ret = false ;
2016-03-21 23:18:33 +01:00
if ( f_write ( & dfile , MAIN_BUFFER , bytes_read , & bytes_written ) ! = FR_OK )
ret = false ;
2016-03-15 22:17:59 +01:00
if ( bytes_read ! = bytes_written )
2016-02-29 02:09:31 +01:00
ret = false ;
}
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 22:51:20 +01:00
if ( ! CheckWritePermissions ( destdir ) ) return false ;
2016-03-21 23:19:23 +01:00
if ( IsVirtualPath ( destdir ) | | IsVirtualPath ( orig ) ) {
// users are inventive...
if ( ( PathToNumFS ( orig ) > 0 ) & & IsVirtualPath ( destdir ) ) {
ShowPrompt ( false , " Only files from SD card are accepted " ) ;
return false ;
}
return PathCopyVirtual ( destdir , orig ) ;
} else {
char fdpath [ 256 ] ; // 256 is the maximum length of a full path
char fopath [ 256 ] ;
strncpy ( fdpath , destdir , 255 ) ;
strncpy ( fopath , orig , 255 ) ;
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-03-14 23:38:43 +01:00
bool PathRename ( const char * path , const char * newname ) {
char npath [ 256 ] ; // 256 is the maximum length of a full path
char * oldname = strrchr ( path , ' / ' ) ;
2016-03-28 20:10:43 +02:00
2016-03-15 16:25:48 +01:00
if ( ! CheckWritePermissions ( path ) ) return false ;
2016-03-14 23:38:43 +01:00
if ( ! oldname ) return false ;
oldname + + ;
strncpy ( npath , path , oldname - path ) ;
2016-03-28 20:10:43 +02:00
strncpy ( npath + ( oldname - path ) , newname , 255 - ( oldname - path ) ) ;
FRESULT res = f_rename ( path , npath ) ;
if ( res = = FR_EXIST ) { // new path already exists (possible LFN/case issue)
char temp [ 256 ] ;
strncpy ( temp , path , oldname - path ) ;
if ( ! GetTempFileName ( temp ) ) return false ;
if ( f_rename ( path , temp ) = = FR_OK ) {
if ( ( f_stat ( npath , NULL ) = = FR_OK ) | | ( f_rename ( temp , npath ) ! = FR_OK ) ) {
ShowPrompt ( false , " Destination exists in folder " ) ;
f_rename ( temp , path ) ; // something went wrong - try renaming back
return false ;
} else return true ;
} else return false ;
} else return ( res = = FR_OK ) ? true : false ;
2016-03-14 23:38:43 +01:00
}
2016-03-14 23:58:25 +01:00
bool DirCreate ( const char * cpath , const char * dirname ) {
char npath [ 256 ] ; // 256 is the maximum length of a full path
2016-03-15 16:25:48 +01:00
if ( ! CheckWritePermissions ( cpath ) ) return false ;
2016-03-14 23:58:25 +01:00
snprintf ( npath , 255 , " %s/%s " , cpath , dirname ) ;
return ( f_mkdir ( npath ) = = FR_OK ) ;
}
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
} ;
2016-03-21 19:24:06 +01:00
u8 * buffer = MAIN_BUFFER + 54 ;
2016-02-26 19:43:30 +01:00
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 ;
2016-03-21 19:24:06 +01:00
memcpy ( MAIN_BUFFER , bmp_header , 54 ) ;
2016-02-26 19:43:30 +01:00
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 ) ;
2016-03-21 19:24:06 +01:00
FileCreateData ( filename , MAIN_BUFFER , 54 + ( 400 * 240 * 3 * 2 ) ) ;
2016-02-26 19:43:30 +01:00
}
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 ;
2016-03-21 16:58:35 +01:00
if ( cmp0 - > type = = T_DOTDOT ) continue ;
2016-02-29 15:47:45 +01:00
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-03-21 18:29:55 +01:00
static const char * drvname [ ] = {
2016-02-26 17:03:25 +01:00
" SDCARD " ,
2016-02-27 19:58:41 +01:00
" SYSNAND CTRNAND " , " SYSNAND TWLN " , " SYSNAND TWLP " ,
2016-03-21 18:29:55 +01:00
" EMUNAND CTRNAND " , " EMUNAND TWLN " , " EMUNAND TWLP " ,
2016-04-04 22:45:49 +02:00
" IMGNAND CTRNAND " , " IMGNAND TWLN " , " IMGNAND TWLP " ,
" SYSNAND VIRTUAL " , " EMUNAND VIRTUAL " , " IMGNAND VIRTUAL " ,
2016-04-09 21:50:50 +02:00
" MEMORY VIRTUAL "
2016-03-21 18:29:55 +01:00
} ;
static const char * drvnum [ ] = {
2016-04-09 21:50:50 +02:00
" 0: " , " 1: " , " 2: " , " 3: " , " 4: " , " 5: " , " 6: " , " 7: " , " 8: " , " 9: " , " S: " , " E: " , " I: " , " M: "
2016-02-25 16:57:01 +01:00
} ;
2016-03-11 01:29:14 +01:00
u32 n_entries = 0 ;
2016-02-13 17:29:56 +01:00
2016-03-21 18:29:55 +01:00
// virtual root objects hacked in
2016-04-04 22:45:49 +02:00
for ( u32 pdrv = 0 ; ( pdrv < NORM_FS + VIRT_FS ) & & ( n_entries < MAX_ENTRIES ) ; pdrv + + ) {
2016-03-21 18:29:55 +01:00
DirEntry * entry = & ( contents - > entry [ n_entries ] ) ;
2016-04-04 22:45:49 +02:00
if ( ( pdrv < NORM_FS ) & & ! fs_mounted [ pdrv ] ) continue ;
else if ( ( pdrv > = NORM_FS ) & & ( ! CheckVirtualPath ( drvnum [ pdrv ] ) ) ) continue ;
2016-03-21 18:29:55 +01:00
memset ( entry - > path , 0x00 , 64 ) ;
2016-03-22 19:24:21 +01:00
snprintf ( entry - > path + 0 , 4 , drvnum [ pdrv ] ) ;
snprintf ( entry - > path + 4 , 32 , " [%s] %s " , drvnum [ pdrv ] , drvname [ pdrv ] ) ;
2016-04-05 20:34:50 +02:00
if ( ( GetMountState ( ) = = IMG_FAT ) & & ( pdrv = = 7 ) ) // FAT image special handling
snprintf ( entry - > path + 4 , 32 , " [7:] FAT IMAGE " ) ;
2016-03-21 18:29:55 +01:00
entry - > name = entry - > path + 4 ;
entry - > size = GetTotalSpace ( entry - > path ) ;
entry - > type = T_ROOT ;
entry - > marked = 0 ;
2016-03-11 01:29:14 +01:00
n_entries + + ;
2016-02-13 17:29:56 +01:00
}
2016-03-11 01:29:14 +01:00
contents - > n_entries = n_entries ;
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-03-21 18:29:55 +01:00
bool GetVirtualDirContentsWorker ( DirStruct * contents , const char * path ) {
if ( strchr ( path , ' / ' ) ) return false ; // only top level paths
for ( u32 n = 0 ; ( n < virtualFileList_size ) & & ( contents - > n_entries < MAX_ENTRIES ) ; n + + ) {
VirtualFile vfile ;
DirEntry * entry = & ( contents - > entry [ contents - > n_entries ] ) ;
snprintf ( entry - > path , 256 , " %s/%s " , path , virtualFileList [ n ] ) ;
2016-03-22 19:44:21 +01:00
if ( ! FindVirtualFile ( & vfile , entry - > path , 0 ) ) continue ;
2016-03-21 18:29:55 +01:00
entry - > name = entry - > path + strnlen ( path , 256 ) + 1 ;
entry - > size = vfile . size ;
entry - > type = T_FILE ;
entry - > marked = 0 ;
contents - > n_entries + + ;
}
return true ; // not much we can check here
}
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 ) {
2016-03-21 16:58:35 +01:00
entry - > type = T_DIR ;
2016-02-25 16:57:01 +01:00
entry - > size = 0 ;
} else {
2016-03-21 16:58:35 +01:00
entry - > type = T_FILE ;
2016-02-25 16:57:01 +01:00
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-03-02 19:36:20 +01:00
if ( ! ( * path ) ) { // 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 {
2016-03-02 19:36:20 +01:00
// create virtual '..' entry
contents - > entry - > name = contents - > entry - > path + 8 ;
strncpy ( contents - > entry - > path , " *?*?* " , 8 ) ;
strncpy ( contents - > entry - > name , " .. " , 4 ) ;
2016-03-21 16:58:35 +01:00
contents - > entry - > type = T_DOTDOT ;
2016-03-02 19:36:20 +01:00
contents - > entry - > size = 0 ;
contents - > n_entries = 1 ;
2016-03-21 18:29:55 +01:00
if ( IsVirtualPath ( path ) ) {
if ( ! GetVirtualDirContentsWorker ( contents , path ) )
contents - > n_entries = 0 ;
} else {
char fpath [ 256 ] ; // 256 is the maximum length of a full path
strncpy ( fpath , path , 256 ) ;
if ( ! GetDirContentsWorker ( contents , fpath , 256 , false ) )
contents - > n_entries = 0 ;
}
2016-02-29 16:14:39 +01:00
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 ' } ;
2016-03-16 18:46:05 +01:00
int pdrv = PathToNumFS ( path ) ;
2016-03-21 18:29:55 +01:00
if ( pdrv < 0 ) return 0 ;
2016-02-27 19:58:41 +01:00
2016-03-16 18:46:05 +01:00
snprintf ( fsname , 3 , " %i: " , pdrv ) ;
2016-02-27 19:58:41 +01:00
if ( f_getfree ( fsname , & free_clusters , & fs_ptr ) ! = FR_OK )
2016-03-21 18:29:55 +01:00
return 0 ;
2016-02-13 17:29:56 +01:00
2016-03-16 18:46:05 +01:00
return ( uint64_t ) free_clusters * fs [ pdrv ] . 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-03-16 18:46:05 +01:00
int pdrv = PathToNumFS ( path ) ;
2016-03-21 18:29:55 +01:00
if ( pdrv < 0 ) return 0 ;
2016-02-27 19:58:41 +01:00
2016-03-16 18:46:05 +01:00
return ( uint64_t ) ( fs [ pdrv ] . n_fatent - 2 ) * fs [ pdrv ] . csize * _MAX_SS ;
}
uint64_t GetPartitionOffsetSector ( const char * path )
{
int pdrv = PathToNumFS ( path ) ;
if ( pdrv < 0 ) return - 1 ;
2016-02-27 19:58:41 +01:00
2016-03-28 13:09:36 +02:00
return ( uint64_t ) fs [ pdrv ] . volbase ;
2016-02-27 19:58:41 +01:00
}