2018-05-09 09:25:01 +00:00
/*
2019-05-06 20:51:09 +00:00
* This file is part of Checkpoint
* Copyright ( C ) 2017 - 2019 Bernardo Giordano , FlagBrew
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
* Additional Terms 7. b and 7. c of GPLv3 apply to this file :
* * Requiring preservation of specified reasonable legal notices or
* author attributions in that material or in the Appropriate Legal
* Notices displayed by works containing it .
* * Prohibiting misrepresentation of the origin of that material ,
* or requiring that modified versions of such material be marked in
* reasonable ways as different from the original version .
*/
2018-05-09 09:25:01 +00:00
# include "io.hpp"
bool io : : fileExists ( const std : : string & path )
{
2019-05-06 20:51:09 +00:00
struct stat buffer ;
return ( stat ( path . c_str ( ) , & buffer ) = = 0 ) ;
2018-05-09 09:25:01 +00:00
}
void io : : copyFile ( const std : : string & srcPath , const std : : string & dstPath )
{
2019-04-30 18:57:22 +00:00
FILE * src = fopen ( srcPath . c_str ( ) , " rb " ) ;
2019-05-18 20:51:58 +00:00
if ( src = = NULL ) {
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to open source file " + srcPath + " during copy. Skipping... " ) ;
2019-05-18 20:51:58 +00:00
return ;
}
2019-04-30 18:57:22 +00:00
FILE * dst = fopen ( dstPath . c_str ( ) , " wb " ) ;
2019-05-18 20:51:58 +00:00
if ( dst = = NULL ) {
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to open destination file " + dstPath + " during copy. Skipping... " ) ;
2019-05-18 20:51:58 +00:00
fclose ( src ) ;
2018-05-09 09:25:01 +00:00
return ;
}
2019-04-30 18:57:22 +00:00
fseek ( src , 0 , SEEK_END ) ;
u64 sz = ftell ( src ) ;
rewind ( src ) ;
2018-05-09 09:25:01 +00:00
2019-07-02 21:42:48 +00:00
u8 * buf = new u8 [ BUFFER_SIZE ] ;
u64 offset = 0 ;
2018-09-03 19:36:28 +00:00
while ( offset < sz ) {
2019-04-30 18:57:22 +00:00
u32 count = fread ( ( char * ) buf , 1 , BUFFER_SIZE , src ) ;
fwrite ( ( char * ) buf , 1 , count , dst ) ;
offset + = count ;
2018-05-09 09:25:01 +00:00
}
delete [ ] buf ;
2019-04-30 18:57:22 +00:00
fclose ( src ) ;
fclose ( dst ) ;
2018-05-11 20:20:54 +00:00
// commit each file to the save
2019-05-06 20:51:09 +00:00
if ( dstPath . rfind ( " save:/ " , 0 ) = = 0 ) {
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Committing file " + dstPath + " to the save archive. " ) ;
2018-05-12 19:07:24 +00:00
fsdevCommitDevice ( " save " ) ;
}
2018-05-09 09:25:01 +00:00
}
Result io : : copyDirectory ( const std : : string & srcPath , const std : : string & dstPath )
{
Result res = 0 ;
2019-05-06 20:51:09 +00:00
bool quit = false ;
2018-05-09 09:25:01 +00:00
Directory items ( srcPath ) ;
2019-05-06 20:51:09 +00:00
if ( ! items . good ( ) ) {
2018-05-09 09:25:01 +00:00
return items . error ( ) ;
}
2019-05-06 20:51:09 +00:00
for ( size_t i = 0 , sz = items . size ( ) ; i < sz & & ! quit ; i + + ) {
2018-05-09 09:25:01 +00:00
std : : string newsrc = srcPath + items . entry ( i ) ;
std : : string newdst = dstPath + items . entry ( i ) ;
2019-05-06 20:51:09 +00:00
if ( items . folder ( i ) ) {
2018-05-09 09:25:01 +00:00
res = io : : createDirectory ( newdst ) ;
2019-05-06 20:51:09 +00:00
if ( R_SUCCEEDED ( res ) ) {
2018-05-09 09:25:01 +00:00
newsrc + = " / " ;
newdst + = " / " ;
res = io : : copyDirectory ( newsrc , newdst ) ;
}
2019-05-06 20:51:09 +00:00
else {
2018-05-09 09:25:01 +00:00
quit = true ;
}
}
2019-05-06 20:51:09 +00:00
else {
2018-05-09 09:25:01 +00:00
io : : copyFile ( newsrc , newdst ) ;
}
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
return 0 ;
}
Result io : : createDirectory ( const std : : string & path )
{
2019-05-03 20:49:01 +00:00
mkdir ( path . c_str ( ) , 777 ) ;
2018-05-09 09:25:01 +00:00
return 0 ;
}
bool io : : directoryExists ( const std : : string & path )
{
struct stat sb ;
return ( stat ( path . c_str ( ) , & sb ) = = 0 & & S_ISDIR ( sb . st_mode ) ) ;
}
2018-09-03 19:36:28 +00:00
Result io : : deleteFolderRecursively ( const std : : string & path )
2018-05-09 09:25:01 +00:00
{
2018-09-03 19:36:28 +00:00
Directory dir ( path ) ;
2019-05-06 20:51:09 +00:00
if ( ! dir . good ( ) ) {
2018-09-03 19:36:28 +00:00
return dir . error ( ) ;
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
for ( size_t i = 0 , sz = dir . size ( ) ; i < sz ; i + + ) {
if ( dir . folder ( i ) ) {
2018-09-03 19:36:28 +00:00
std : : string newpath = path + " / " + dir . entry ( i ) + " / " ;
deleteFolderRecursively ( newpath ) ;
newpath = path + dir . entry ( i ) ;
rmdir ( newpath . c_str ( ) ) ;
}
2019-05-06 20:51:09 +00:00
else {
2018-09-03 19:36:28 +00:00
std : : string newpath = path + dir . entry ( i ) ;
std : : remove ( newpath . c_str ( ) ) ;
}
2018-05-09 09:25:01 +00:00
}
2018-09-03 19:36:28 +00:00
rmdir ( path . c_str ( ) ) ;
2018-05-09 09:25:01 +00:00
return 0 ;
}
2019-07-02 21:42:48 +00:00
std : : tuple < bool , Result , std : : string > io : : backup ( size_t index , u128 uid , size_t cellIndex )
2018-05-09 09:25:01 +00:00
{
2019-07-02 21:42:48 +00:00
const bool isNewFolder = cellIndex = = 0 ;
Result res = 0 ;
std : : tuple < bool , Result , std : : string > ret = std : : make_tuple ( false , - 1 , " " ) ;
2018-05-09 09:25:01 +00:00
Title title ;
2018-06-10 09:11:07 +00:00
getTitle ( title , uid , index ) ;
2019-05-06 20:51:09 +00:00
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : INFO , " Started backup of %s. Title id: 0x%016lX; User id: 0x%lX%lX. " , title . name ( ) . c_str ( ) , title . id ( ) , ( u64 ) ( title . userId ( ) > > 8 ) , ( u64 ) ( title . userId ( ) ) ) ;
2018-05-09 09:25:01 +00:00
FsFileSystem fileSystem ;
res = FileSystem : : mount ( & fileSystem , title . id ( ) , title . userId ( ) ) ;
2019-05-06 20:51:09 +00:00
if ( R_SUCCEEDED ( res ) ) {
2019-07-09 21:13:23 +00:00
int rc = FileSystem : : mount ( fileSystem ) ;
if ( rc = = - 1 ) {
2018-05-09 09:25:01 +00:00
FileSystem : : unmount ( ) ;
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to mount filesystem during backup. Title id: 0x%016lX; User id: 0x%lX%lX. " , title . id ( ) , ( u64 ) ( title . userId ( ) > > 8 ) , ( u64 ) ( title . userId ( ) ) ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , - 2 , " Failed to mount save. " ) ;
2018-05-09 09:25:01 +00:00
}
}
2019-05-06 20:51:09 +00:00
else {
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to mount filesystem during backup with result %X. Title id: 0x%016lX; User id: 0x%lX%lX. " , res , title . id ( ) , ( u64 ) ( title . userId ( ) > > 8 ) , ( u64 ) ( title . userId ( ) ) ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , res , " Failed to mount save. " ) ;
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
std : : string suggestion = DateTime : : dateTimeStr ( ) + " " +
( StringUtils : : containsInvalidChar ( Account : : username ( title . userId ( ) ) )
? " "
: StringUtils : : removeNotAscii ( StringUtils : : removeAccents ( Account : : username ( title . userId ( ) ) ) ) ) ;
2018-05-09 09:25:01 +00:00
std : : string customPath ;
2019-05-06 20:51:09 +00:00
if ( MS : : multipleSelectionEnabled ( ) ) {
2018-07-17 16:25:07 +00:00
customPath = isNewFolder ? suggestion : " " ;
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
else {
if ( isNewFolder ) {
2018-08-05 14:44:55 +00:00
std : : pair < bool , std : : string > keyboardResponse = KeyboardManager : : get ( ) . keyboard ( suggestion ) ;
2019-05-06 20:51:09 +00:00
if ( keyboardResponse . first ) {
2018-08-05 14:44:55 +00:00
customPath = StringUtils : : removeForbiddenCharacters ( keyboardResponse . second ) ;
}
2019-05-06 20:51:09 +00:00
else {
2018-08-05 14:44:55 +00:00
FileSystem : : unmount ( ) ;
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : INFO , " Copy operation aborted by the user through the system keyboard. " ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , 0 , " Operation aborted by the user. " ) ;
2018-08-05 14:44:55 +00:00
}
}
2019-05-06 20:51:09 +00:00
else {
2018-08-05 14:44:55 +00:00
customPath = " " ;
}
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-07-17 16:25:07 +00:00
std : : string dstPath ;
2019-05-06 20:51:09 +00:00
if ( ! isNewFolder ) {
2018-07-17 16:25:07 +00:00
// we're overriding an existing folder
dstPath = title . fullPath ( cellIndex ) ;
}
2019-05-06 20:51:09 +00:00
else {
2018-07-17 16:25:07 +00:00
dstPath = title . path ( ) + " / " + customPath ;
2019-05-06 20:51:09 +00:00
}
2018-07-17 16:25:07 +00:00
2019-05-06 20:51:09 +00:00
if ( ! isNewFolder | | io : : directoryExists ( dstPath ) ) {
2019-07-09 21:13:23 +00:00
int rc = io : : deleteFolderRecursively ( ( dstPath + " / " ) . c_str ( ) ) ;
if ( rc ! = 0 ) {
2018-05-09 09:25:01 +00:00
FileSystem : : unmount ( ) ;
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to recursively delete directory " + dstPath ) ;
2019-07-09 21:13:23 +00:00
return std : : make_tuple ( false , ( Result ) rc , " Failed to delete the existing backup \n directory recursively. " ) ;
2018-05-09 09:25:01 +00:00
}
}
2019-05-06 20:51:09 +00:00
2019-05-18 20:51:58 +00:00
io : : createDirectory ( dstPath ) ;
2018-05-09 09:25:01 +00:00
res = io : : copyDirectory ( " save:/ " , dstPath + " / " ) ;
2019-05-06 20:51:09 +00:00
if ( R_FAILED ( res ) ) {
2018-05-09 09:25:01 +00:00
FileSystem : : unmount ( ) ;
2018-09-03 19:36:28 +00:00
io : : deleteFolderRecursively ( ( dstPath + " / " ) . c_str ( ) ) ;
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to copy directory " + dstPath + " . Skipping... " ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , res , " Failed to backup save. " ) ;
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
refreshDirectories ( title . id ( ) ) ;
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
FileSystem : : unmount ( ) ;
2019-05-06 20:51:09 +00:00
if ( ! MS : : multipleSelectionEnabled ( ) ) {
2019-05-04 12:35:16 +00:00
blinkLed ( 4 ) ;
2019-07-02 21:42:48 +00:00
ret = std : : make_tuple ( true , 0 , " Progress correctly saved to disk. " ) ;
2019-04-12 21:39:55 +00:00
}
2019-07-24 19:45:22 +00:00
// TODO: figure out if this code can be accessed at all
2019-04-12 21:39:55 +00:00
auto systemKeyboardAvailable = KeyboardManager : : get ( ) . isSystemKeyboardAvailable ( ) ;
2019-05-06 20:51:09 +00:00
if ( ! systemKeyboardAvailable . first ) {
2019-07-02 21:42:48 +00:00
return std : : make_tuple (
false , systemKeyboardAvailable . second , " System keyboard applet not accessible. \n The suggested destination folder was used \n instead. " ) ;
2019-04-12 21:39:55 +00:00
}
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : INFO , " Backup succeeded. " ) ;
2019-07-02 21:42:48 +00:00
return ret ;
2018-05-09 09:25:01 +00:00
}
2019-07-02 21:42:48 +00:00
std : : tuple < bool , Result , std : : string > io : : restore ( size_t index , u128 uid , size_t cellIndex , const std : : string & nameFromCell )
2018-05-09 09:25:01 +00:00
{
2019-07-02 21:42:48 +00:00
Result res = 0 ;
std : : tuple < bool , Result , std : : string > ret = std : : make_tuple ( false , - 1 , " " ) ;
2018-05-09 09:25:01 +00:00
Title title ;
2018-06-10 09:11:07 +00:00
getTitle ( title , uid , index ) ;
2019-05-06 20:51:09 +00:00
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : INFO , " Started restore of %s. Title id: 0x%016lX; User id: 0x%lX%lX. " , title . name ( ) . c_str ( ) , title . id ( ) , ( u64 ) ( title . userId ( ) > > 8 ) , ( u64 ) ( title . userId ( ) ) ) ;
2018-05-09 09:25:01 +00:00
FsFileSystem fileSystem ;
2018-07-17 16:25:07 +00:00
res = title . systemSave ( ) ? FileSystem : : mount ( & fileSystem , title . id ( ) ) : FileSystem : : mount ( & fileSystem , title . id ( ) , title . userId ( ) ) ;
2019-05-06 20:51:09 +00:00
if ( R_SUCCEEDED ( res ) ) {
2019-07-09 21:13:23 +00:00
int rc = FileSystem : : mount ( fileSystem ) ;
if ( rc = = - 1 ) {
2018-05-09 09:25:01 +00:00
FileSystem : : unmount ( ) ;
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to mount filesystem during restore. Title id: 0x%016lX; User id: 0x%lX%lX. " , title . id ( ) , ( u64 ) ( title . userId ( ) > > 8 ) , ( u64 ) ( title . userId ( ) ) ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , - 2 , " Failed to mount save. " ) ;
2018-05-09 09:25:01 +00:00
}
}
2019-05-06 20:51:09 +00:00
else {
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to mount filesystem during restore with result %X. Title id: 0x%016lX; User id: 0x%lX%lX. " , res , title . id ( ) , ( u64 ) ( title . userId ( ) > > 8 ) , ( u64 ) ( title . userId ( ) ) ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , res , " Failed to mount save. " ) ;
2018-05-09 09:25:01 +00:00
}
2018-07-17 16:25:07 +00:00
std : : string srcPath = title . fullPath ( cellIndex ) + " / " ;
2018-05-09 09:25:01 +00:00
std : : string dstPath = " save:/ " ;
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
res = io : : deleteFolderRecursively ( dstPath . c_str ( ) ) ;
2019-05-06 20:51:09 +00:00
if ( R_FAILED ( res ) ) {
2018-05-09 09:25:01 +00:00
FileSystem : : unmount ( ) ;
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to recursively delete directory " + dstPath ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , res , " Failed to delete save. " ) ;
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
res = io : : copyDirectory ( srcPath , dstPath ) ;
2019-05-06 20:51:09 +00:00
if ( R_FAILED ( res ) ) {
2018-05-09 09:25:01 +00:00
FileSystem : : unmount ( ) ;
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to copy directory " + srcPath + " to " + dstPath + " . Skipping... " ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , res , " Failed to restore save. " ) ;
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
res = fsdevCommitDevice ( " save " ) ;
2019-05-06 20:51:09 +00:00
if ( R_FAILED ( res ) ) {
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : ERROR , " Failed to commit save with result %X. " , res ) ;
2019-07-02 21:42:48 +00:00
return std : : make_tuple ( false , res , " Failed to commit to save device. " ) ;
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
else {
2019-05-06 18:55:04 +00:00
blinkLed ( 4 ) ;
2019-07-02 21:42:48 +00:00
ret = std : : make_tuple ( true , 0 , nameFromCell + " \n has been restored successfully. " ) ;
2018-05-09 09:25:01 +00:00
}
2019-05-06 20:51:09 +00:00
2018-05-09 09:25:01 +00:00
FileSystem : : unmount ( ) ;
2019-07-24 19:45:22 +00:00
Logger : : getInstance ( ) . log ( Logger : : INFO , " Restore succeeded. " ) ;
2019-07-02 21:42:48 +00:00
return ret ;
2018-05-09 09:25:01 +00:00
}