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"
2016-04-25 02:46:32 +02:00
# include "sha.h"
2016-07-13 19:59:36 +02:00
# include "sdmmc.h"
2016-04-05 20:34:50 +02:00
# 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-07-22 04:54:35 +02:00
# define VIRT_FS 5
2016-03-11 01:29:14 +01:00
2016-07-27 00:19:12 +02:00
# define SKIP_CUR (1<<3)
# define OVERWRITE_CUR (1<<4)
2016-07-13 19:59:36 +02:00
// Volume2Partition resolution table
PARTITION VolToPart [ ] = {
{ 0 , 1 } , { 1 , 0 } , { 2 , 0 } , { 3 , 0 } , { 4 , 0 } ,
{ 5 , 0 } , { 6 , 0 } , { 7 , 0 } , { 8 , 0 } , { 9 , 0 }
} ;
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-05-29 14:45:12 +02:00
// write permissions - careful with this
static u32 write_permissions = PERM_BASE ;
2016-02-29 22:51:20 +01:00
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-07-22 18:32:44 +02:00
// last search pattern & path
static char search_pattern [ 256 ] = { 0 } ;
static char search_path [ 256 ] = { 0 } ;
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-05-01 15:41:11 +02:00
fs_mounted [ i ] = ( f_mount ( fs + i , fsname , 1 ) = = FR_OK ) ;
if ( ( i = = 7 ) & & ! fs_mounted [ 7 ] & & ( GetMountState ( ) = = IMG_RAMDRV ) ) {
2016-07-20 00:27:38 +02:00
f_mkfs ( " 7: " , FM_ANY , 0 , MAIN_BUFFER , MAIN_BUFFER_SIZE ) ; // format ramdrive if required
2016-05-01 15:41:11 +02:00
f_mount ( NULL , fsname , 1 ) ;
fs_mounted [ 7 ] = ( f_mount ( fs + 7 , " 7: " , 1 ) = = FR_OK ) ;
}
2016-02-13 17:29:56 +01:00
}
return true ;
}
2016-04-05 15:20:48 +02:00
void DeinitExtFS ( ) {
2016-04-10 14:50:58 +02:00
for ( u32 i = NORM_FS - 1 ; 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-07-22 18:32:44 +02:00
void SetFSSearch ( const char * pattern , const char * path ) {
if ( pattern & & path ) {
strncpy ( search_pattern , pattern , 256 ) ;
strncpy ( search_path , path , 256 ) ;
} else * search_pattern = * search_path = ' \0 ' ;
}
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-07-22 18:32:44 +02:00
if ( ! GetVirtualSource ( path ) & & ! IsSearchDrive ( 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-07-22 18:32:44 +02:00
bool IsSearchDrive ( const char * path ) {
return * search_pattern & & * search_path & & ( strncmp ( path , " Z: " , 3 ) = = 0 ) ;
}
2016-07-13 19:59:36 +02:00
uint64_t GetSDCardSize ( ) {
if ( sdmmc_sdcard_init ( ) ! = 0 ) return 0 ;
return ( u64 ) getMMCDevice ( 1 ) - > total_size * 512 ;
}
2016-07-26 20:58:41 +02:00
bool FormatSDCard ( u64 hidden_mb ) {
2016-07-13 19:59:36 +02:00
u8 mbr [ 0x200 ] = { 0 } ;
u8 mbrdata [ 0x42 ] = {
0x80 , 0x01 , 0x01 , 0x00 , 0x0C , 0xFE , 0xFF , 0xFF , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x80 , 0x01 , 0x01 , 0x00 , 0x1C , 0xFE , 0xFF , 0xFF , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x55 , 0xAA
} ;
u32 sd_size = getMMCDevice ( 1 ) - > total_size ;
2016-07-26 20:58:41 +02:00
u32 emu_sector = 1 ;
u32 emu_size = ( u32 ) ( ( hidden_mb * 1024 * 1024 ) / 512 ) ;
u32 fat_sector = align ( emu_sector + emu_size , 0x2000 ) ; // align to 4MB
u32 fat_size = ( fat_sector < sd_size ) ? sd_size - fat_sector : 0 ;
2016-07-13 19:59:36 +02:00
2016-07-26 20:58:41 +02:00
// FAT size check
if ( fat_size < 0x80000 ) { // minimum free space: 256MB
2016-07-13 19:59:36 +02:00
ShowPrompt ( false , " ERROR: SD card is too small " ) ;
return false ;
}
2016-07-26 20:58:41 +02:00
sd_size = fat_size ;
2016-07-13 19:59:36 +02:00
// build the MBR
2016-07-26 20:58:41 +02:00
memcpy ( mbrdata + 0x08 , & fat_sector , 4 ) ;
memcpy ( mbrdata + 0x0C , & fat_size , 4 ) ;
memcpy ( mbrdata + 0x18 , & emu_sector , 4 ) ;
memcpy ( mbrdata + 0x1C , & emu_size , 4 ) ;
2016-07-13 19:59:36 +02:00
memcpy ( mbr + 0x1BE , mbrdata , 0x42 ) ;
if ( hidden_mb ) memcpy ( mbr , " GATEWAYNAND " , 12 ) ;
else memset ( mbr + 0x1CE , 0 , 0x10 ) ;
// one last warning....
if ( ! ShowUnlockSequence ( 3 , " !WARNING! \n \n Proceeding will format this SD. \n This will irreversibly delete \n ALL data on it. \n " ) )
return false ;
2016-07-26 20:58:41 +02:00
ShowString ( " Formatting SD, please wait... " ) ;
2016-07-13 19:59:36 +02:00
// write the MBR to disk
// !this assumes a fully deinitialized file system!
if ( ( sdmmc_sdcard_init ( ) ! = 0 ) | | ( sdmmc_sdcard_writesectors ( 0 , 1 , mbr ) ! = 0 ) ) {
ShowPrompt ( false , " ERROR: SD card i/o failure " ) ;
return false ;
}
// format the SD card
// cluster size: auto (<= 4GB) / 32KiB (<= 8GB) / 64 KiB (> 8GB)
2016-07-13 20:58:14 +02:00
f_mount ( fs , " 0: " , 1 ) ;
2016-07-13 19:59:36 +02:00
UINT c_size = ( sd_size < 0x800000 ) ? 0 : ( sd_size < 0x1000000 ) ? 32768 : 65536 ;
2016-07-20 00:27:38 +02:00
bool ret = ( f_mkfs ( " 0: " , FM_FAT32 , c_size , MAIN_BUFFER , MAIN_BUFFER_SIZE ) = = FR_OK ) & & ( f_setlabel ( " 0:GM9SD " ) = = FR_OK ) ;
2016-07-13 20:58:14 +02:00
f_mount ( NULL , " 0: " , 1 ) ;
2016-07-13 19:59:36 +02:00
return ret ;
}
2016-03-16 18:46:05 +01:00
bool CheckWritePermissions ( const char * path ) {
2016-05-29 14:45:12 +02:00
char area_name [ 16 ] ;
2016-03-16 18:46:05 +01:00
int pdrv = PathToNumFS ( path ) ;
2016-05-29 14:45:12 +02:00
u32 perm ;
2016-03-16 18:46:05 +01:00
2016-05-29 14:45:12 +02:00
if ( pdrv = = 0 ) {
perm = PERM_SDCARD ;
snprintf ( area_name , 16 , " the SD card " ) ;
2016-05-30 16:17:09 +02:00
} else if ( ( pdrv = = 7 ) & & ( GetMountState ( ) = = IMG_RAMDRV ) ) {
2016-05-29 14:45:12 +02:00
perm = PERM_RAMDRIVE ;
snprintf ( area_name , 16 , " the RAM drive " ) ;
} else if ( ( ( pdrv > = 1 ) & & ( pdrv < = 3 ) ) | | ( GetVirtualSource ( path ) = = VRT_SYSNAND ) ) {
perm = PERM_SYSNAND ;
snprintf ( area_name , 16 , " the SysNAND " ) ;
2016-07-01 01:38:57 +02:00
// check virtual file flags (if any)
VirtualFile vfile ;
if ( FindVirtualFile ( & vfile , path , 0 ) & & ( vfile . flags & VFLAG_A9LH_AREA ) ) {
perm = PERM_A9LH ;
snprintf ( area_name , 16 , " A9LH regions " ) ;
}
2016-05-29 14:45:12 +02:00
} else if ( ( ( pdrv > = 4 ) & & ( pdrv < = 6 ) ) | | ( GetVirtualSource ( path ) = = VRT_EMUNAND ) ) {
perm = PERM_EMUNAND ;
snprintf ( area_name , 16 , " the EmuNAND " ) ;
2016-07-01 00:35:10 +02:00
} else if ( ( ( pdrv > = 7 ) & & ( pdrv < = 9 ) ) | | ( GetVirtualSource ( path ) = = VRT_IMGNAND ) ) {
2016-05-29 14:45:12 +02:00
perm = PERM_IMAGE ;
snprintf ( area_name , 16 , " images " ) ;
} else if ( GetVirtualSource ( path ) = = VRT_MEMORY ) {
perm = PERM_MEMORY ;
snprintf ( area_name , 16 , " memory areas " ) ;
} else {
2016-04-09 21:50:50 +02:00
return false ;
2016-02-29 22:51:20 +01:00
}
2016-05-29 14:45:12 +02:00
// check permission, return if already set
if ( ( write_permissions & perm ) = = perm )
return true ;
// ask the user
if ( ! ShowPrompt ( true , " Writing to %s is locked! \n Unlock it now? " , area_name ) )
return false ;
2016-02-29 22:51:20 +01:00
2016-05-29 14:45:12 +02:00
return SetWritePermissions ( perm , true ) ;
2016-02-29 22:51:20 +01:00
}
2016-05-29 14:45:12 +02:00
bool SetWritePermissions ( u32 perm , bool add_perm ) {
if ( ( write_permissions & perm ) = = perm ) { // write permissions already given
if ( ! add_perm ) write_permissions = perm ;
2016-03-01 02:00:48 +01:00
return true ;
}
2016-05-29 14:45:12 +02:00
switch ( perm ) {
2016-05-30 02:24:47 +02:00
case PERM_BASE :
if ( ! ShowUnlockSequence ( 1 , " You want to enable base \n writing permissions. " ) )
return false ;
break ;
2016-05-29 14:45:12 +02:00
case PERM_SDCARD :
2016-03-01 02:00:48 +01:00
if ( ! ShowUnlockSequence ( 1 , " You want to enable SD card \n writing permissions. " ) )
return false ;
break ;
2016-05-29 14:45:12 +02:00
case PERM_RAMDRIVE :
if ( ! ShowUnlockSequence ( 1 , " You want to enable RAM drive \n writing permissions. " ) )
return false ;
case PERM_EMUNAND :
2016-07-12 18:33:52 +02:00
if ( ! ShowUnlockSequence ( 2 , " You want to enable EmuNAND \n writing permissions. " ) )
2016-05-29 14:45:12 +02:00
return false ;
break ;
case PERM_IMAGE :
2016-07-01 01:38:57 +02:00
if ( ! ShowUnlockSequence ( 2 , " You want to enable image \n writing permissions. " ) )
2016-03-01 02:00:48 +01:00
return false ;
break ;
2016-05-30 02:24:47 +02:00
# ifndef SAFEMODE
2016-05-29 14:45:12 +02:00
case PERM_SYSNAND :
2016-07-01 01:38:57 +02:00
if ( ! ShowUnlockSequence ( 3 , " !Better be careful! \n \n You want to enable SysNAND \n writing permissions. \n This enables you to do some \n really dangerous stuff! " ) )
return false ;
break ;
case PERM_A9LH :
if ( ! ShowUnlockSequence ( 5 , " !THIS IS YOUR ONLY WARNING! \n \n You want to enable A9LH area \n writing permissions. \n This enables you to OVERWRITE \n your A9LH installation! " ) )
2016-03-01 02:00:48 +01:00
return false ;
break ;
2016-05-29 14:45:12 +02:00
case PERM_MEMORY :
if ( ! ShowUnlockSequence ( 4 , " !Better be careful! \n \n You want to enable memory \n writing permissions. \n Writing to certain areas may \n lead to unexpected results. " ) )
return false ;
break ;
2016-05-30 02:24:47 +02:00
case PERM_ALL :
2016-07-01 01:38:57 +02:00
if ( ! ShowUnlockSequence ( 3 , " !Better be careful! \n \n You want to enable ALL \n writing permissions. \n This enables you to do some \n really dangerous stuff! " ) )
2016-05-29 14:45:12 +02:00
return false ;
break ;
2016-05-30 02:24:47 +02:00
default :
return false ;
break ;
# else
2016-05-29 14:45:12 +02:00
case PERM_ALL :
2016-05-30 02:24:47 +02:00
perm & = ~ ( PERM_SYSNAND | PERM_MEMORY ) ;
if ( ! ShowUnlockSequence ( 2 , " You want to enable EmuNAND & \n image writing permissions. \n Keep backups, just in case. " ) )
2016-05-29 14:45:12 +02:00
return false ;
break ;
2016-03-01 02:00:48 +01:00
default :
2016-05-30 02:24:47 +02:00
ShowPrompt ( false , " Can't unlock write permission. \n Try GodMode9 instead! " ) ;
2016-05-29 14:45:12 +02:00
return false ;
2016-03-01 02:00:48 +01:00
break ;
2016-05-30 02:24:47 +02:00
# endif
2016-03-01 02:00:48 +01:00
}
2016-05-29 14:45:12 +02:00
write_permissions = add_perm ? write_permissions | perm : perm ;
2016-02-29 22:51:20 +01:00
return true ;
}
2016-03-01 02:00:48 +01:00
u32 GetWritePermissions ( ) {
2016-05-29 14:45:12 +02:00
return write_permissions ;
2016-03-01 02:00:48 +01:00
}
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-06-17 17:12:11 +02:00
bool FileSetData ( const char * path , const u8 * data , size_t size , size_t foffset , bool create ) {
2016-05-27 01:21:05 +02:00
if ( ! CheckWritePermissions ( path ) ) return false ;
2016-05-21 14:50:01 +02:00
if ( PathToNumFS ( path ) > = 0 ) {
UINT bytes_written = 0 ;
FIL file ;
if ( ! CheckWritePermissions ( path ) ) return false ;
2016-06-17 17:12:11 +02:00
if ( f_open ( & file , path , FA_WRITE | ( create ? FA_CREATE_ALWAYS : FA_OPEN_ALWAYS ) ) ! = FR_OK )
2016-05-21 14:50:01 +02:00
return false ;
f_lseek ( & file , foffset ) ;
f_write ( & file , data , size , & bytes_written ) ;
f_close ( & file ) ;
return ( bytes_written = = size ) ;
} else if ( GetVirtualSource ( path ) ) {
VirtualFile vfile ;
if ( ! FindVirtualFile ( & vfile , path , 0 ) )
return 0 ;
return ( WriteVirtualFile ( & vfile , data , foffset , size , NULL ) = = 0 ) ;
}
return false ;
2016-02-26 19:43:30 +01:00
}
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 ) ;
2016-07-12 18:33:52 +02:00
if ( f_read ( & file , data , size , & bytes_read ) ! = FR_OK ) {
f_close ( & file ) ;
2016-04-08 21:02:38 +02:00
return 0 ;
2016-07-12 18:33:52 +02:00
}
2016-04-08 21:02:38 +02:00
f_close ( & file ) ;
return bytes_read ;
2016-04-09 21:56:42 +02:00
} else if ( GetVirtualSource ( path ) ) {
2016-04-08 21:02:38 +02:00
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-04-10 15:55:39 +02:00
size_t FileGetSize ( const char * path ) {
if ( PathToNumFS ( path ) > = 0 ) {
FILINFO fno ;
if ( f_stat ( path , & fno ) ! = FR_OK )
return 0 ;
return fno . fsize ;
} else if ( GetVirtualSource ( path ) ) {
VirtualFile vfile ;
if ( ! FindVirtualFile ( & vfile , path , 0 ) )
return 0 ;
return vfile . size ;
}
return 0 ;
}
2016-04-25 02:46:32 +02:00
bool FileGetSha256 ( const char * path , u8 * sha256 ) {
bool ret = true ;
sha_init ( SHA256_MODE ) ;
ShowProgress ( 0 , 0 , path ) ;
if ( GetVirtualSource ( path ) ) { // for virtual files
VirtualFile vfile ;
u32 fsize ;
if ( ! FindVirtualFile ( & vfile , path , 0 ) )
return false ;
fsize = vfile . size ;
for ( size_t pos = 0 ; ( pos < fsize ) & & ret ; pos + = MAIN_BUFFER_SIZE ) {
UINT read_bytes = min ( MAIN_BUFFER_SIZE , fsize - pos ) ;
if ( ReadVirtualFile ( & vfile , MAIN_BUFFER , pos , read_bytes , NULL ) ! = 0 )
ret = false ;
if ( ! ShowProgress ( pos + read_bytes , fsize , path ) )
ret = false ;
sha_update ( MAIN_BUFFER , read_bytes ) ;
}
} else { // for regular FAT files
FIL file ;
size_t fsize ;
if ( f_open ( & file , path , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK )
return false ;
fsize = f_size ( & file ) ;
f_lseek ( & file , 0 ) ;
f_sync ( & file ) ;
for ( size_t pos = 0 ; ( pos < fsize ) & & ret ; pos + = MAIN_BUFFER_SIZE ) {
UINT bytes_read = 0 ;
if ( f_read ( & file , MAIN_BUFFER , MAIN_BUFFER_SIZE , & bytes_read ) ! = FR_OK )
ret = false ;
if ( ! ShowProgress ( pos + bytes_read , fsize , path ) )
ret = false ;
sha_update ( MAIN_BUFFER , bytes_read ) ;
}
f_close ( & file ) ;
}
ShowProgress ( 1 , 1 , path ) ;
sha_get ( sha256 ) ;
return ret ;
}
2016-07-12 18:33:52 +02:00
u32 FileFindData ( const char * path , u8 * data , u32 size , u32 offset ) {
u32 found = ( u32 ) - 1 ;
u32 fsize = FileGetSize ( path ) ;
for ( u32 pass = 0 ; pass < 2 ; pass + + ) {
bool show_progress = false ;
u32 pos = ( pass = = 0 ) ? offset : 0 ;
u32 search_end = ( pass = = 0 ) ? fsize : offset + size ;
search_end = ( search_end > fsize ) ? fsize : search_end ;
for ( ; ( pos < search_end ) & & ( found = = ( u32 ) - 1 ) ; pos + = MAIN_BUFFER_SIZE - ( size - 1 ) ) {
UINT read_bytes = min ( MAIN_BUFFER_SIZE , search_end - pos ) ;
if ( FileGetData ( path , MAIN_BUFFER , read_bytes , pos ) ! = read_bytes )
break ;
for ( u32 i = 0 ; i + size < = read_bytes ; i + + ) {
if ( memcmp ( MAIN_BUFFER + i , data , size ) = = 0 ) {
found = pos + i ;
break ;
}
}
if ( ! show_progress & & ( found = = ( u32 ) - 1 ) & & ( pos + read_bytes < fsize ) ) {
ShowProgress ( 0 , 0 , path ) ;
show_progress = true ;
}
if ( show_progress & & ( ! ShowProgress ( pos + read_bytes , fsize , path ) ) )
break ;
}
}
return found ;
}
2016-06-17 19:28:43 +02:00
bool FileInjectFile ( const char * dest , const char * orig , u32 offset ) {
VirtualFile dvfile ;
VirtualFile ovfile ;
FIL ofile ;
FIL dfile ;
size_t osize ;
size_t dsize ;
bool vdest ;
bool vorig ;
bool ret ;
if ( ! CheckWritePermissions ( dest ) ) return false ;
2016-06-18 15:23:21 +02:00
if ( strncmp ( dest , orig , 256 ) = = 0 ) {
ShowPrompt ( false , " Error: Can't inject file into itself " ) ;
return false ;
}
2016-06-17 19:28:43 +02:00
// open destination
if ( GetVirtualSource ( dest ) ) {
vdest = true ;
if ( ! FindVirtualFile ( & dvfile , dest , 0 ) )
return false ;
dsize = dvfile . size ;
} else {
vdest = false ;
if ( f_open ( & dfile , dest , FA_WRITE | FA_OPEN_EXISTING ) ! = FR_OK )
return false ;
dsize = f_size ( & dfile ) ;
f_lseek ( & dfile , offset ) ;
f_sync ( & dfile ) ;
}
// open origin
if ( GetVirtualSource ( orig ) ) {
vorig = true ;
if ( ! FindVirtualFile ( & ovfile , orig , 0 ) ) {
if ( ! vdest ) f_close ( & dfile ) ;
return false ;
}
osize = ovfile . size ;
} else {
vorig = false ;
if ( f_open ( & ofile , orig , FA_READ | FA_OPEN_EXISTING ) ! = FR_OK ) {
if ( ! vdest ) f_close ( & dfile ) ;
return false ;
}
osize = f_size ( & ofile ) ;
f_lseek ( & ofile , 0 ) ;
f_sync ( & ofile ) ;
}
// check file limits
if ( offset + osize > dsize ) {
ShowPrompt ( false , " Operation would write beyond end of file " ) ;
if ( ! vdest ) f_close ( & dfile ) ;
if ( ! vorig ) f_close ( & ofile ) ;
return false ;
}
ret = true ;
2016-06-17 19:49:19 +02:00
ShowProgress ( 0 , 0 , orig ) ;
2016-06-17 19:28:43 +02:00
for ( size_t pos = 0 ; ( pos < osize ) & & ret ; pos + = MAIN_BUFFER_SIZE ) {
UINT read_bytes = min ( MAIN_BUFFER_SIZE , osize - pos ) ;
UINT bytes_read = read_bytes ;
UINT bytes_written = read_bytes ;
if ( ( ! vorig & & ( f_read ( & ofile , MAIN_BUFFER , read_bytes , & bytes_read ) ! = FR_OK ) ) | |
( vorig & & ReadVirtualFile ( & ovfile , MAIN_BUFFER , pos , read_bytes , NULL ) ! = 0 ) )
ret = false ;
if ( ! ShowProgress ( pos + ( bytes_read / 2 ) , osize , orig ) )
ret = false ;
if ( ( ! vdest & & ( f_write ( & dfile , MAIN_BUFFER , read_bytes , & bytes_written ) ! = FR_OK ) ) | |
( vdest & & WriteVirtualFile ( & dvfile , MAIN_BUFFER , offset + pos , read_bytes , NULL ) ! = 0 ) )
ret = false ;
if ( bytes_read ! = bytes_written )
ret = false ;
}
ShowProgress ( 1 , 1 , orig ) ;
if ( ! vdest ) f_close ( & dfile ) ;
if ( ! vorig ) f_close ( & ofile ) ;
return ret ;
}
2016-07-27 00:19:12 +02:00
bool PathCopyVirtual ( const char * destdir , const char * orig , u32 * flags ) {
2016-03-21 23:19:23 +01:00
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 ) ;
2016-04-09 21:56:42 +02:00
if ( GetVirtualSource ( dest ) & & GetVirtualSource ( orig ) ) { // virtual to virtual
2016-03-21 23:19:23 +01:00
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 ;
}
2016-05-26 21:08:35 +02:00
if ( strncmp ( dest , orig , 256 ) = = 0 ) { // destination == origin
ShowPrompt ( false , " Origin equals destination: \n %s \n %s " , origstr , deststr ) ;
return false ;
}
2016-03-21 23:19:23 +01:00
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-04-09 21:56:42 +02:00
} else if ( GetVirtualSource ( dest ) ) { // SD card to virtual (other FAT not allowed!)
2016-03-21 23:19:23 +01:00
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 ( ) ;
2016-04-09 21:56:42 +02:00
} else if ( GetVirtualSource ( 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 ;
2016-05-26 21:08:35 +02:00
2016-03-21 23:19:23 +01:00
// check if destination exists
2016-07-27 00:19:12 +02:00
if ( flags & & ! ( * flags & OVERWRITE_ALL ) & & f_stat ( dest , NULL ) = = FR_OK ) {
2016-07-28 03:34:39 +02:00
if ( * flags & SKIP_ALL ) {
* flags | = SKIP_CUR ;
return true ;
}
2016-07-27 00:19:12 +02:00
const char * optionstr [ 5 ] =
{ " Choose new name " , " Overwrite file " , " Skip file " , " Overwrite all " , " Skip all " } ;
u32 user_select = ShowSelectPrompt ( ( * flags & ASK_ALL ) ? 5 : 3 , optionstr ,
" Destination already exists: \n %s " , deststr ) ;
2016-05-26 21:08:35 +02:00
if ( user_select = = 1 ) {
do {
char * dname = strrchr ( dest , ' / ' ) ;
if ( dname = = NULL ) return false ;
dname + + ;
2016-06-13 23:51:41 +02:00
if ( ! ShowStringPrompt ( dname , 255 - ( dname - dest ) , " Choose new destination name " ) )
2016-05-26 21:08:35 +02:00
return false ;
} while ( f_stat ( dest , NULL ) = = FR_OK ) ;
2016-07-27 00:19:12 +02:00
} else if ( user_select = = 3 ) {
* flags | = SKIP_CUR ;
return true ;
} else if ( user_select = = 4 ) {
* flags | = OVERWRITE_ALL ;
} else if ( user_select = = 5 ) {
2016-07-28 03:34:39 +02:00
* flags | = ( SKIP_CUR | SKIP_ALL ) ;
2016-07-27 00:19:12 +02:00
return true ;
} else if ( user_select ! = 2 ) {
return false ;
}
2016-03-21 23:19:23 +01:00
}
2016-05-26 21:08:35 +02:00
2016-03-21 23:19:23 +01:00
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 ) ;
2016-06-10 16:46:50 +02:00
if ( ! ret ) f_unlink ( dest ) ;
2016-03-21 23:19:23 +01:00
} else {
return false ;
}
return ret ;
}
2016-07-27 00:19:12 +02:00
bool PathCopyWorker ( char * dest , char * orig , u32 * flags , bool move ) {
2016-07-20 00:27:38 +02:00
FILINFO fno ;
2016-02-29 02:09:31 +01:00
bool ret = false ;
2016-07-27 00:19:12 +02: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
2016-05-26 21:08:35 +02:00
while ( strncmp ( dest , orig , 255 ) = = 0 ) {
2016-06-13 23:51:41 +02:00
if ( ! ShowStringPrompt ( dname , 255 - ( dname - dest ) , " Destination is equal to origin \n Choose another name? " ) )
2016-05-20 14:12:03 +02:00
return false ;
2016-05-26 21:08:35 +02:00
}
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-07-27 00:19:12 +02:00
if ( flags & & ! ( * flags & ( OVERWRITE_CUR | OVERWRITE_ALL ) ) & & ( f_stat ( dest , NULL ) = = FR_OK ) ) {
2016-07-28 03:34:39 +02:00
if ( * flags & SKIP_ALL ) {
* flags | = SKIP_CUR ;
return true ;
}
2016-07-27 00:19:12 +02:00
const char * optionstr [ 5 ] =
{ " Choose new name " , " Overwrite file(s) " , " Skip file(s) " , " Overwrite all " , " Skip all " } ;
2016-03-02 17:32:19 +01:00
char namestr [ 36 + 1 ] ;
2016-03-02 17:22:44 +01:00
TruncateString ( namestr , dest , 36 , 8 ) ;
2016-07-27 00:19:12 +02:00
u32 user_select = ShowSelectPrompt ( ( * flags & ASK_ALL ) ? 5 : 3 , optionstr ,
" Destination already exists: \n %s " , namestr ) ;
2016-05-26 21:08:35 +02:00
if ( user_select = = 1 ) {
do {
2016-06-13 23:51:41 +02:00
if ( ! ShowStringPrompt ( dname , 255 - ( dname - dest ) , " Choose new destination name " ) )
2016-05-26 21:08:35 +02:00
return false ;
} while ( f_stat ( dest , NULL ) = = FR_OK ) ;
2016-07-27 00:19:12 +02:00
} else if ( user_select = = 2 ) {
* flags | = OVERWRITE_CUR ;
} else if ( user_select = = 3 ) {
* flags | = SKIP_CUR ;
return true ;
} else if ( user_select = = 4 ) {
* flags | = OVERWRITE_ALL ;
} else if ( user_select = = 5 ) {
2016-07-28 03:34:39 +02:00
* flags | = ( SKIP_CUR | SKIP_ALL ) ;
2016-07-27 00:19:12 +02:00
return true ;
} else {
return false ;
}
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-04-29 02:21:36 +02:00
if ( move & & f_stat ( dest , NULL ) ! = FR_OK ) { // moving if dest not existing
ret = ( f_rename ( orig , dest ) = = FR_OK ) ;
} else if ( fno . fattrib & AM_DIR ) { // processing folders (same for move & copy)
2016-02-29 02:09:31 +01:00
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-04-27 22:03:25 +02:00
if ( ( f_opendir ( & pdir , dest ) ! = FR_OK ) & & ( f_mkdir ( dest ) ! = FR_OK ) ) {
2016-04-29 02:21:36 +02:00
ShowPrompt ( false , " Error: Overwriting file with dir " ) ;
2016-02-29 02:09:31 +01:00
return false ;
2016-04-27 22:03:25 +02:00
} else f_closedir ( & pdir ) ;
2016-02-29 02:09:31 +01:00
if ( f_opendir ( & pdir , orig ) ! = FR_OK )
return false ;
2016-03-02 14:01:20 +01:00
* ( 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
2016-07-20 00:27:38 +02:00
strncpy ( fname , fno . fname , 256 - ( fname - orig ) ) ;
2016-02-29 02:09:31 +01:00
if ( fno . fname [ 0 ] = = 0 ) {
ret = true ;
break ;
2016-07-27 00:19:12 +02:00
} else if ( ! PathCopyWorker ( dest , orig , flags , move ) ) {
2016-02-29 02:09:31 +01:00
break ;
}
}
f_closedir ( & pdir ) ;
2016-04-29 02:21:36 +02:00
} else if ( move ) { // moving if destination exists
if ( f_stat ( dest , & fno ) ! = FR_OK )
return false ;
if ( fno . fattrib & AM_DIR ) {
ShowPrompt ( false , " Error: Overwriting dir with file " ) ;
return false ;
}
if ( f_unlink ( dest ) ! = FR_OK )
return false ;
ret = ( f_rename ( orig , dest ) = = FR_OK ) ;
} else { // copying files
2016-02-29 02:09:31 +01:00
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 ) {
2016-04-29 02:21:36 +02:00
ShowPrompt ( false , " Error: Cannot create destination file " ) ;
2016-02-29 02:09:31 +01:00
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-06-10 16:46:50 +02:00
if ( ! ret ) f_unlink ( dest ) ;
2016-02-29 02:09:31 +01:00
}
2016-03-02 17:22:44 +01:00
* ( - - dname ) = ' \0 ' ;
2016-02-29 02:09:31 +01:00
return ret ;
}
2016-07-27 00:19:12 +02:00
bool PathCopy ( const char * destdir , const char * orig , u32 * flags ) {
2016-02-29 22:51:20 +01:00
if ( ! CheckWritePermissions ( destdir ) ) return false ;
2016-07-27 00:19:12 +02:00
if ( flags ) * flags = * flags & ~ ( SKIP_CUR | OVERWRITE_CUR ) ; // reset local flags
2016-04-09 21:56:42 +02:00
if ( GetVirtualSource ( destdir ) | | GetVirtualSource ( orig ) ) {
2016-03-21 23:19:23 +01:00
// users are inventive...
2016-04-09 21:56:42 +02:00
if ( ( PathToNumFS ( orig ) > 0 ) & & GetVirtualSource ( destdir ) ) {
2016-03-21 23:19:23 +01:00
ShowPrompt ( false , " Only files from SD card are accepted " ) ;
return false ;
}
2016-07-27 00:19:12 +02:00
return PathCopyVirtual ( destdir , orig , flags ) ;
2016-03-21 23:19:23 +01:00
} else {
char fdpath [ 256 ] ; // 256 is the maximum length of a full path
char fopath [ 256 ] ;
strncpy ( fdpath , destdir , 255 ) ;
strncpy ( fopath , orig , 255 ) ;
2016-07-27 00:19:12 +02:00
bool res = PathCopyWorker ( fdpath , fopath , flags , false ) ;
return res ;
2016-04-29 02:21:36 +02:00
}
}
2016-07-27 00:19:12 +02:00
bool PathMove ( const char * destdir , const char * orig , u32 * flags ) {
2016-04-29 02:21:36 +02:00
if ( ! CheckWritePermissions ( destdir ) ) return false ;
2016-05-26 21:07:18 +02:00
if ( ! CheckWritePermissions ( orig ) ) return false ;
2016-07-27 00:19:12 +02:00
if ( flags ) * flags = * flags & ~ ( SKIP_CUR | OVERWRITE_CUR ) ; // reset local flags
2016-04-29 02:21:36 +02:00
if ( GetVirtualSource ( destdir ) | | GetVirtualSource ( orig ) ) {
ShowPrompt ( false , " Error: Moving virtual files not possible " ) ;
return false ;
} else {
char fdpath [ 256 ] ; // 256 is the maximum length of a full path
char fopath [ 256 ] ;
strncpy ( fdpath , destdir , 255 ) ;
strncpy ( fopath , orig , 255 ) ;
bool same_drv = ( PathToNumFS ( orig ) = = PathToNumFS ( destdir ) ) ;
2016-07-27 00:19:12 +02:00
bool res = PathCopyWorker ( fdpath , fopath , flags , same_drv ) ;
2016-07-28 03:34:39 +02:00
if ( res & & ( ! flags | | ! ( * flags & SKIP_CUR ) ) ) PathDelete ( orig ) ;
2016-04-29 02:21:36 +02:00
return res ;
2016-03-21 23:19:23 +01:00
}
2016-02-29 02:09:31 +01:00
}
2016-02-29 15:47:45 +01:00
bool PathDeleteWorker ( char * fpath ) {
2016-07-20 00:27:38 +02:00
FILINFO fno ;
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 + + ) = ' / ' ;
while ( f_readdir ( & pdir , & fno ) = = FR_OK ) {
if ( ( strncmp ( fno . fname , " . " , 2 ) = = 0 ) | | ( strncmp ( fno . fname , " .. " , 3 ) = = 0 ) )
continue ; // filter out virtual entries
2016-07-20 00:27:38 +02: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 + + )
2016-05-18 23:46:22 +02:00
memcpy ( buffer_t + ( y * 400 + x ) * 3 , TOP_SCREEN + ( x * 240 + y ) * 3 , 3 ) ;
2016-02-26 19:43:30 +01:00
for ( u32 x = 0 ; x < 320 ; x + + )
for ( u32 y = 0 ; y < 240 ; y + + )
2016-05-18 23:46:22 +02:00
memcpy ( buffer + ( y * 400 + x + 40 ) * 3 , BOT_SCREEN + ( x * 240 + y ) * 3 , 3 ) ;
2016-06-17 17:12:11 +02:00
FileSetData ( filename , MAIN_BUFFER , 54 + ( 400 * 240 * 3 * 2 ) , 0 , true ) ;
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-07-21 00:29:48 +02:00
// inspired by http://www.geeksforgeeks.org/wildcard-character-matching/
bool MatchName ( const char * pattern , const char * path ) {
// handling non asterisk chars
for ( ; * pattern ! = ' * ' ; pattern + + , path + + ) {
if ( ( * pattern = = ' \0 ' ) & & ( * path = = ' \0 ' ) ) {
return true ; // end reached simultaneously, match found
} else if ( ( * pattern = = ' \0 ' ) | | ( * path = = ' \0 ' ) ) {
return false ; // end reached on only one, failure
} else if ( ( * pattern ! = ' ? ' ) & & ( tolower ( * pattern ) ! = tolower ( * path ) ) ) {
return false ; // chars don't match, failure
}
}
// handling the asterisk (matches one or more chars in path)
if ( ( * ( pattern + 1 ) = = ' ? ' ) | | ( * ( pattern + 1 ) = = ' * ' ) ) {
return false ; // stupid user shenanigans, failure
} else if ( * path = = ' \0 ' ) {
return false ; // asterisk, but end reached on path, failure
} else if ( * ( pattern + 1 ) = = ' \0 ' ) {
return true ; // nothing after the asterisk, match found
} else { // we couldn't really go without recursion here
for ( path + + ; * path ! = ' \0 ' ; path + + ) {
if ( MatchName ( pattern + 1 , path ) ) return true ;
}
}
return false ;
}
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-07-22 04:54:35 +02:00
" MEMORY VIRTUAL " ,
" LAST SEARCH "
2016-03-21 18:29:55 +01:00
} ;
static const char * drvnum [ ] = {
2016-07-22 04:54:35 +02:00
" 0: " , " 1: " , " 2: " , " 3: " , " 4: " , " 5: " , " 6: " , " 7: " , " 8: " , " 9: " , " S: " , " E: " , " I: " , " M: " , " Z: "
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 ;
2016-07-22 18:32:44 +02:00
else if ( ( pdrv > = NORM_FS ) & & ( ! CheckVirtualDrive ( drvnum [ pdrv ] ) ) & & ! ( IsSearchDrive ( 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-05-01 15:41:11 +02:00
else if ( ( GetMountState ( ) = = IMG_RAMDRV ) & & ( pdrv = = 7 ) ) // RAM drive special handling
snprintf ( entry - > path + 4 , 32 , " [7:] RAMDRIVE " ) ;
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-07-21 00:29:48 +02:00
bool GetVirtualDirContentsWorker ( DirStruct * contents , const char * path , const char * pattern ) {
2016-03-21 18:29:55 +01:00
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 ] ) ;
2016-07-21 00:29:48 +02:00
if ( pattern & & ! MatchName ( pattern , virtualFileList [ n ] ) ) continue ;
2016-03-21 18:29:55 +01:00
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-07-21 00:29:48 +02:00
bool GetDirContentsWorker ( DirStruct * contents , char * fpath , int fnsize , const char * pattern , bool recursive ) {
2016-02-13 17:29:56 +01:00
DIR pdir ;
FILINFO fno ;
2016-07-20 00:27:38 +02:00
char * fname = fpath + strnlen ( fpath , fnsize - 1 ) ;
2016-02-13 17:29:56 +01:00
bool ret = false ;
if ( f_opendir ( & pdir , fpath ) ! = FR_OK )
return false ;
( fname + + ) [ 0 ] = ' / ' ;
while ( f_readdir ( & pdir , & fno ) = = FR_OK ) {
if ( ( strncmp ( fno . fname , " . " , 2 ) = = 0 ) | | ( strncmp ( fno . fname , " .. " , 3 ) = = 0 ) )
continue ; // filter out virtual entries
2016-07-20 00:27:38 +02:00
strncpy ( fname , fno . fname , ( fnsize - 1 ) - ( fname - fpath ) ) ;
2016-02-13 17:29:56 +01:00
if ( fno . fname [ 0 ] = = 0 ) {
ret = true ;
break ;
2016-07-21 00:29:48 +02:00
} else if ( ! pattern | | MatchName ( pattern , fname ) ) {
2016-02-25 16:57:01 +01:00
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-07-22 18:32:44 +02:00
if ( contents - > n_entries > = MAX_ENTRIES ) {
ret = true ; // Too many entries, still okay
2016-02-25 16:57:01 +01:00
break ;
2016-07-22 18:32:44 +02:00
}
contents - > n_entries + + ;
2016-02-13 17:29:56 +01:00
}
if ( recursive & & ( fno . fattrib & AM_DIR ) ) {
2016-07-21 00:29:48 +02:00
if ( ! GetDirContentsWorker ( contents , fpath , fnsize , pattern , recursive ) )
2016-02-13 17:29:56 +01:00
break ;
}
}
f_closedir ( & pdir ) ;
return ret ;
}
2016-07-21 00:29:48 +02:00
void SearchDirContents ( DirStruct * contents , const char * path , const char * pattern , bool recursive ) {
2016-02-29 16:14:39 +01:00
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-04-09 21:56:42 +02:00
if ( GetVirtualSource ( path ) ) {
2016-07-21 00:29:48 +02:00
if ( ! GetVirtualDirContentsWorker ( contents , path , pattern ) )
2016-03-21 18:29:55 +01:00
contents - > n_entries = 0 ;
} else {
char fpath [ 256 ] ; // 256 is the maximum length of a full path
strncpy ( fpath , path , 256 ) ;
2016-07-21 00:29:48 +02:00
if ( ! GetDirContentsWorker ( contents , fpath , 256 , pattern , recursive ) )
2016-03-21 18:29:55 +01:00
contents - > n_entries = 0 ;
}
2016-02-29 16:14:39 +01:00
SortDirStruct ( contents ) ;
2016-02-13 17:29:56 +01:00
}
}
2016-07-21 00:29:48 +02:00
void GetDirContents ( DirStruct * contents , const char * path ) {
2016-07-22 18:32:44 +02:00
if ( * search_pattern & & * search_path & & IsSearchDrive ( path ) ) {
ShowString ( " Searching, please wait... " ) ;
SearchDirContents ( contents , search_path , search_pattern , true ) ;
ClearScreenF ( true , false , COLOR_STD_BG ) ;
} else SearchDirContents ( contents , path , NULL , false ) ;
2016-07-21 00:29:48 +02: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
}