2018-05-15 18:00:19 +02:00
# include <stdio.h>
# include <malloc.h>
2018-05-16 19:08:20 +02:00
# include <dirent.h>
# include <memory.h>
2018-05-16 23:29:43 +02:00
# include <limits.h>
2018-05-16 19:08:20 +02:00
# include <sys/stat.h>
2018-06-21 02:42:46 -04:00
# include <unistd.h>
2018-06-25 21:11:18 -04:00
# include <math.h>
2018-05-15 18:00:19 +02:00
2018-06-23 20:48:34 -04:00
# include "crc32_fast.h"
2018-06-21 02:42:46 -04:00
# include "dumper.h"
# include "fsext.h"
# include "ui.h"
# include "util.h"
2018-05-16 23:29:43 +02:00
2019-04-23 01:14:57 -04:00
/* Extern variables */
2018-06-21 02:42:46 -04:00
extern u64 freeSpace ;
2018-05-16 17:10:30 +02:00
2018-06-21 02:42:46 -04:00
extern int breaks ;
2019-04-21 12:27:33 -04:00
extern int font_height ;
2018-05-15 18:31:46 +02:00
2018-06-25 21:11:18 -04:00
extern u64 gameCardSize , trimmedCardSize ;
extern char gameCardSizeStr [ 32 ] , trimmedCardSizeStr [ 32 ] ;
2018-05-15 18:53:26 +02:00
2018-06-21 02:42:46 -04:00
extern char * hfs0_header ;
extern u64 hfs0_offset , hfs0_size ;
extern u32 hfs0_partition_cnt ;
2018-05-15 18:31:46 +02:00
2019-04-21 12:27:33 -04:00
extern char * partitionHfs0Header ;
2019-04-23 01:14:57 -04:00
extern u64 partitionHfs0HeaderOffset , partitionHfs0HeaderSize ;
extern u32 partitionHfs0FileCount , partitionHfs0StrTableSize ;
2018-06-30 02:37:42 -04:00
2018-06-21 02:42:46 -04:00
extern u64 gameCardTitleID ;
2018-06-30 02:37:42 -04:00
extern u32 gameCardVersion ;
extern char fixedGameCardName [ 0x201 ] ;
extern u64 gameCardUpdateTitleID ;
extern u32 gameCardUpdateVersion ;
extern char gameCardUpdateVersionStr [ 128 ] ;
2018-05-15 18:00:19 +02:00
2019-04-23 01:14:57 -04:00
extern char * filenameBuffer ;
extern int filenamesCount ;
extern AppletType programAppletType ;
/* Statically allocated variables */
2018-06-30 02:37:42 -04:00
static char strbuf [ NAME_BUF_LEN * 2 ] = { ' \0 ' } ;
2018-05-15 18:00:19 +02:00
2018-06-21 02:42:46 -04:00
void workaroundPartitionZeroAccess ( FsDeviceOperator * fsOperator )
{
2019-04-21 12:27:33 -04:00
FsGameCardHandle handle ;
if ( R_FAILED ( fsDeviceOperatorGetGameCardHandle ( fsOperator , & handle ) ) ) return ;
FsStorage gameCardStorage ;
if ( R_FAILED ( fsOpenGameCardStorage ( & gameCardStorage , & handle , 0 ) ) ) return ;
fsStorageClose ( & gameCardStorage ) ;
2018-06-21 02:42:46 -04:00
}
2018-05-15 18:00:19 +02:00
2018-06-21 02:42:46 -04:00
bool getRootHfs0Header ( FsDeviceOperator * fsOperator )
{
2019-04-21 12:27:33 -04:00
u32 magic ;
Result result ;
FsGameCardHandle handle ;
FsStorage gameCardStorage ;
hfs0_partition_cnt = 0 ;
workaroundPartitionZeroAccess ( fsOperator ) ;
if ( R_FAILED ( result = fsDeviceOperatorGetGameCardHandle ( fsOperator , & handle ) ) )
{
uiStatusMsg ( " getRootHfs0Header: GetGameCardHandle failed! (0x%08X) " , result ) ;
return false ;
}
// Get bundled FW version update
if ( R_SUCCEEDED ( fsDeviceOperatorUpdatePartitionInfo ( fsOperator , & handle , & gameCardUpdateVersion , & gameCardUpdateTitleID ) ) )
{
if ( gameCardUpdateTitleID = = GAMECARD_UPDATE_TITLEID )
{
char decimalVersion [ 64 ] = { ' \0 ' } ;
convertTitleVersionToDecimal ( gameCardUpdateVersion , decimalVersion , sizeof ( decimalVersion ) ) ;
switch ( gameCardUpdateVersion )
{
case SYSUPDATE_100 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 1.0.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_200 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 2.0.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_210 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 2.1.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_220 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 2.2.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_230 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 2.3.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_300 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 3.0.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_301 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 3.0.1 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_302 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 3.0.2 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_400 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 4.0.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_401 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 4.0.1 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_410 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 4.1.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_500 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 5.0.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_501 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 5.0.1 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_502 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 5.0.2 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_510 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 5.1.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_600 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 6.0.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_601 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 6.0.1 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_610 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 6.1.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_620 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 6.2.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_700 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 7.0.0 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_701 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 7.0.1 - v%s " , decimalVersion ) ;
break ;
case SYSUPDATE_800 :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " 8.0.0 - v%s " , decimalVersion ) ;
break ;
default :
snprintf ( gameCardUpdateVersionStr , sizeof ( gameCardUpdateVersionStr ) / sizeof ( gameCardUpdateVersionStr [ 0 ] ) , " UNKNOWN - v%s " , decimalVersion ) ;
break ;
}
} else {
gameCardUpdateTitleID = 0 ;
gameCardUpdateVersion = 0 ;
}
}
if ( R_FAILED ( result = fsOpenGameCardStorage ( & gameCardStorage , & handle , 0 ) ) )
{
uiStatusMsg ( " getRootHfs0Header: OpenGameCardStorage failed! (0x%08X) " , result ) ;
return false ;
}
char * gamecard_header = ( char * ) malloc ( GAMECARD_HEADER_SIZE ) ;
if ( ! gamecard_header )
{
uiStatusMsg ( " getRootHfs0Header: Unable to allocate memory for the gamecard header! " ) ;
fsStorageClose ( & gameCardStorage ) ;
return false ;
}
if ( R_FAILED ( result = fsStorageRead ( & gameCardStorage , 0 , gamecard_header , GAMECARD_HEADER_SIZE ) ) )
{
uiStatusMsg ( " getRootHfs0Header: StorageRead failed to read %u-byte chunk from offset 0x%016lX! (0x%08X) " , GAMECARD_HEADER_SIZE , 0 , result ) ;
free ( gamecard_header ) ;
fsStorageClose ( & gameCardStorage ) ;
return false ;
}
u8 cardSize = ( u8 ) gamecard_header [ GAMECARD_SIZE_ADDR ] ;
switch ( cardSize )
{
case 0xFA : // 1 GiB
gameCardSize = GAMECARD_SIZE_1GiB ;
break ;
case 0xF8 : // 2 GiB
gameCardSize = GAMECARD_SIZE_2GiB ;
break ;
case 0xF0 : // 4 GiB
gameCardSize = GAMECARD_SIZE_4GiB ;
break ;
case 0xE0 : // 8 GiB
gameCardSize = GAMECARD_SIZE_8GiB ;
break ;
case 0xE1 : // 16 GiB
gameCardSize = GAMECARD_SIZE_16GiB ;
break ;
case 0xE2 : // 32 GiB
gameCardSize = GAMECARD_SIZE_32GiB ;
break ;
default :
uiStatusMsg ( " getRootHfs0Header: Invalid game card size value: 0x%02X " , cardSize ) ;
free ( gamecard_header ) ;
fsStorageClose ( & gameCardStorage ) ;
return false ;
}
convertSize ( gameCardSize , gameCardSizeStr , sizeof ( gameCardSizeStr ) / sizeof ( gameCardSizeStr [ 0 ] ) ) ;
memcpy ( & trimmedCardSize , gamecard_header + GAMECARD_DATAEND_ADDR , sizeof ( u64 ) ) ;
trimmedCardSize = ( GAMECARD_HEADER_SIZE + ( trimmedCardSize * MEDIA_UNIT_SIZE ) ) ;
convertSize ( trimmedCardSize , trimmedCardSizeStr , sizeof ( trimmedCardSizeStr ) / sizeof ( trimmedCardSizeStr [ 0 ] ) ) ;
memcpy ( & hfs0_offset , gamecard_header + HFS0_OFFSET_ADDR , sizeof ( u64 ) ) ;
memcpy ( & hfs0_size , gamecard_header + HFS0_SIZE_ADDR , sizeof ( u64 ) ) ;
free ( gamecard_header ) ;
hfs0_header = ( char * ) malloc ( hfs0_size ) ;
if ( ! hfs0_header )
{
uiStatusMsg ( " getRootHfs0Header: Unable to allocate memory for the root HFS0 header! " ) ;
gameCardSize = 0 ;
memset ( gameCardSizeStr , 0 , sizeof ( gameCardSizeStr ) ) ;
trimmedCardSize = 0 ;
memset ( trimmedCardSizeStr , 0 , sizeof ( trimmedCardSizeStr ) ) ;
hfs0_offset = hfs0_size = 0 ;
fsStorageClose ( & gameCardStorage ) ;
return false ;
}
if ( R_FAILED ( result = fsStorageRead ( & gameCardStorage , hfs0_offset , hfs0_header , hfs0_size ) ) )
{
uiStatusMsg ( " getRootHfs0Header: StorageRead failed to read %u-byte chunk from offset 0x%016lX! (0x%08X) " , hfs0_size , hfs0_offset , result ) ;
gameCardSize = 0 ;
memset ( gameCardSizeStr , 0 , sizeof ( gameCardSizeStr ) ) ;
trimmedCardSize = 0 ;
memset ( trimmedCardSizeStr , 0 , sizeof ( trimmedCardSizeStr ) ) ;
free ( hfs0_header ) ;
hfs0_header = NULL ;
hfs0_offset = hfs0_size = 0 ;
fsStorageClose ( & gameCardStorage ) ;
return false ;
}
memcpy ( & magic , hfs0_header , sizeof ( u32 ) ) ;
magic = bswap_32 ( magic ) ;
if ( magic ! = HFS0_MAGIC )
{
uiStatusMsg ( " getRootHfs0Header: Magic word mismatch! 0x%08X != 0x%08X " , magic , HFS0_MAGIC ) ;
gameCardSize = 0 ;
memset ( gameCardSizeStr , 0 , sizeof ( gameCardSizeStr ) ) ;
trimmedCardSize = 0 ;
memset ( trimmedCardSizeStr , 0 , sizeof ( trimmedCardSizeStr ) ) ;
free ( hfs0_header ) ;
hfs0_header = NULL ;
hfs0_offset = hfs0_size = 0 ;
fsStorageClose ( & gameCardStorage ) ;
return false ;
}
memcpy ( & hfs0_partition_cnt , hfs0_header + HFS0_FILE_COUNT_ADDR , sizeof ( u32 ) ) ;
fsStorageClose ( & gameCardStorage ) ;
return true ;
2018-06-21 02:42:46 -04:00
}
2018-05-15 18:53:26 +02:00
2019-04-23 01:14:57 -04:00
bool getHfs0EntryDetails ( char * hfs0Header , u64 hfs0HeaderOffset , u64 hfs0HeaderSize , u32 num_entries , u32 entry_idx , bool isRoot , u32 partitionIndex , u64 * out_offset , u64 * out_size )
2018-06-21 02:42:46 -04:00
{
2019-04-23 01:14:57 -04:00
if ( hfs0Header = = NULL ) return false ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
if ( entry_idx > ( num_entries - 1 ) ) return false ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
hfs0_entry_table * entryTable = ( hfs0_entry_table * ) malloc ( sizeof ( hfs0_entry_table ) * num_entries ) ;
2019-04-21 12:27:33 -04:00
if ( ! entryTable ) return false ;
2019-04-23 01:14:57 -04:00
memcpy ( entryTable , hfs0Header + HFS0_ENTRY_TABLE_ADDR , sizeof ( hfs0_entry_table ) * num_entries ) ;
// Determine the partition index that's going to be used for offset calculation
// If we're dealing with a root HFS0 header, just use entry_idx
// Otherwise, partitionIndex must be used, because entry_idx represents the file entry we must look for in the provided HFS0 partition header
u32 part_idx = ( isRoot ? entry_idx : partitionIndex ) ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
switch ( part_idx )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
case 0 : // Update (contained within IStorage instance with partition ID 0)
case 1 : // Normal or Logo (depending on the gamecard type) (contained within IStorage instance with partition ID 0)
// Root HFS0: the header offset used to calculate the partition offset is relative to the true gamecard image start
// Partition HFS0: the header offset used to calculate the file offset is also relative to the true gamecard image start (but it was calculated in a previous call to this function)
* out_offset = ( hfs0HeaderOffset + hfs0HeaderSize + entryTable [ entry_idx ] . file_offset ) ;
2019-04-21 12:27:33 -04:00
break ;
case 2 :
2019-04-23 01:14:57 -04:00
// Check if we're dealing with a type 0x01 gamecard
2019-04-21 12:27:33 -04:00
if ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT )
{
2019-04-23 01:14:57 -04:00
// Secure (contained within IStorage instance with partition ID 1)
// Root HFS0: the resulting partition offset must be zero, because the secure partition is stored in a different IStorage instance
// Partition HFS0: the resulting file offset is relative to the start of the IStorage instance. Thus, it isn't necessary to use the header offset as part of the calculation
* out_offset = ( isRoot ? 0 : ( hfs0HeaderSize + entryTable [ entry_idx ] . file_offset ) ) ;
2019-04-21 12:27:33 -04:00
} else {
2019-04-23 01:14:57 -04:00
// Normal (contained within IStorage instance with partition ID 0)
// Root HFS0: the header offset used to calculate the partition offset is relative to the true gamecard image start
// Partition HFS0: the header offset used to calculate the file offset is also relative to the true gamecard image start (but it was calculated in a previous call to this function)
* out_offset = ( hfs0HeaderOffset + hfs0HeaderSize + entryTable [ entry_idx ] . file_offset ) ;
2019-04-21 12:27:33 -04:00
}
break ;
2019-04-23 01:14:57 -04:00
case 3 : // Secure (gamecard type 0x02) (contained within IStorage instance with partition ID 1)
// Root HFS0: the resulting partition offset must be zero, because the secure partition is stored in a different IStorage instance
// Partition HFS0: the resulting file offset is relative to the start of the IStorage instance. Thus, it isn't necessary to use the header offset as part of the calculation
* out_offset = ( isRoot ? 0 : ( hfs0HeaderSize + entryTable [ entry_idx ] . file_offset ) ) ;
2019-04-21 12:27:33 -04:00
break ;
default :
break ;
}
2019-04-23 01:14:57 -04:00
// Store the file size for the desired HFS0 entry
* out_size = entryTable [ entry_idx ] . file_size ;
2019-04-21 12:27:33 -04:00
free ( entryTable ) ;
return true ;
2018-06-21 02:42:46 -04:00
}
2018-05-15 18:00:19 +02:00
2018-06-25 21:11:18 -04:00
bool dumpGameCartridge ( FsDeviceOperator * fsOperator , bool isFat32 , bool dumpCert , bool trimDump , bool calcCrc )
2018-06-21 02:42:46 -04:00
{
2019-04-21 12:27:33 -04:00
u64 partitionOffset = 0 , fileOffset = 0 , xciDataSize = 0 , totalSize = 0 , n ;
u64 partitionSizes [ ISTORAGE_PARTITION_CNT ] ;
char partitionSizesStr [ ISTORAGE_PARTITION_CNT ] [ 32 ] = { ' \0 ' } , xciDataSizeStr [ 32 ] = { ' \0 ' } , curSizeStr [ 32 ] = { ' \0 ' } , totalSizeStr [ 32 ] = { ' \0 ' } , filename [ NAME_BUF_LEN ] = { ' \0 ' } ;
u32 partition ;
Result result ;
FsGameCardHandle handle ;
FsStorage gameCardStorage ;
bool proceed = true , success = false ;
FILE * outFile = NULL ;
char * buf = NULL ;
u8 splitIndex = 0 ;
u8 progress = 0 ;
u32 crc1 = 0 , crc2 = 0 ;
u64 start , now , remainingTime ;
struct tm * timeinfo ;
char etaInfo [ 32 ] = { ' \0 ' } ;
double lastSpeed = 0.0 , averageSpeed = 0.0 ;
for ( partition = 0 ; partition < ISTORAGE_PARTITION_CNT ; partition + + )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Getting partition #%u size... " , partition ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
workaroundPartitionZeroAccess ( fsOperator ) ;
if ( R_SUCCEEDED ( result = fsDeviceOperatorGetGameCardHandle ( fsOperator , & handle ) ) )
{
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
if ( R_SUCCEEDED ( result = fsOpenGameCardStorage ( & gameCardStorage , & handle , partition ) ) )
{
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle.value);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
if ( R_SUCCEEDED ( result = fsStorageGetSize ( & gameCardStorage , & ( partitionSizes [ partition ] ) ) ) )
{
xciDataSize + = partitionSizes [ partition ] ;
convertSize ( partitionSizes [ partition ] , partitionSizesStr [ partition ] , sizeof ( partitionSizesStr [ partition ] ) / sizeof ( partitionSizesStr [ partition ] [ 0 ] ) ) ;
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Partition #%u size: %s (%lu bytes). " , partition , partitionSizesStr [ partition ] , partitionSizes [ partition ] ) ;
2019-04-21 12:27:33 -04:00
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + = 2 ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " StorageGetSize failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
proceed = false ;
}
fsStorageClose ( & gameCardStorage ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " OpenGameCardStorage failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
proceed = false ;
}
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " GetGameCardHandle failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
proceed = false ;
}
uiRefreshDisplay ( ) ;
}
if ( proceed )
{
convertSize ( xciDataSize , xciDataSizeStr , sizeof ( xciDataSizeStr ) / sizeof ( xciDataSizeStr [ 0 ] ) ) ;
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " XCI data size: %s (%lu bytes). " , xciDataSizeStr , xciDataSize ) ;
2019-04-21 12:27:33 -04:00
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + = 2 ;
if ( trimDump )
{
totalSize = trimmedCardSize ;
snprintf ( totalSizeStr , sizeof ( totalSizeStr ) / sizeof ( totalSizeStr [ 0 ] ) , " %s " , trimmedCardSizeStr ) ;
// Change dump size for the last IStorage partition
u64 partitionSizesSum = 0 ;
for ( int i = 0 ; i < ( ISTORAGE_PARTITION_CNT - 1 ) ; i + + ) partitionSizesSum + = partitionSizes [ i ] ;
partitionSizes [ ISTORAGE_PARTITION_CNT - 1 ] = ( trimmedCardSize - partitionSizesSum ) ;
} else {
totalSize = xciDataSize ;
snprintf ( totalSizeStr , sizeof ( totalSizeStr ) / sizeof ( totalSizeStr [ 0 ] ) , " %s " , xciDataSizeStr ) ;
}
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Output dump size: %s (%lu bytes). " , totalSizeStr , totalSize ) ;
2019-04-21 12:27:33 -04:00
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
if ( totalSize < = freeSpace )
{
breaks + + ;
if ( totalSize > SPLIT_FILE_MIN & & isFat32 )
{
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX).xc%u " , fixedGameCardName , gameCardVersion , gameCardTitleID , splitIndex ) ;
} else {
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX).xci " , fixedGameCardName , gameCardVersion , gameCardTitleID ) ;
}
outFile = fopen ( filename , " wb " ) ;
if ( outFile )
{
buf = ( char * ) malloc ( DUMP_BUFFER_SIZE ) ;
if ( buf )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Dump procedure started. Writing output to \" %s \" . Hold B to cancel. " , filename ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + = 2 ;
2019-04-23 01:14:57 -04:00
if ( programAppletType ! = AppletType_Application & & programAppletType ! = AppletType_SystemApplication )
{
uiDrawString ( " Do not press the HOME button. Doing so could corrupt the SD card filesystem. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
breaks + = 2 ;
}
2019-04-21 12:27:33 -04:00
timeGetCurrentTime ( TimeType_LocalSystemClock , & start ) ;
for ( partition = 0 ; partition < ISTORAGE_PARTITION_CNT ; partition + + )
{
n = DUMP_BUFFER_SIZE ;
uiFill ( 0 , breaks * font_height , FB_WIDTH , font_height * 4 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Dumping partition #%u... " , partition ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
workaroundPartitionZeroAccess ( fsOperator ) ;
if ( R_SUCCEEDED ( result = fsDeviceOperatorGetGameCardHandle ( fsOperator , & handle ) ) )
{
if ( R_SUCCEEDED ( result = fsOpenGameCardStorage ( & gameCardStorage , & handle , partition ) ) )
{
for ( partitionOffset = 0 ; partitionOffset < partitionSizes [ partition ] ; partitionOffset + = n , fileOffset + = n )
{
if ( DUMP_BUFFER_SIZE > ( partitionSizes [ partition ] - partitionOffset ) ) n = ( partitionSizes [ partition ] - partitionOffset ) ;
if ( R_FAILED ( result = fsStorageRead ( & gameCardStorage , partitionOffset , buf , n ) ) )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " StorageRead failed (0x%08X) at offset 0x%016lX for partition #%u " , result , partitionOffset , partition ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
proceed = false ;
break ;
}
// Remove game card certificate
if ( fileOffset = = 0 & & ! dumpCert ) memset ( buf + CERT_OFFSET , 0xFF , CERT_SIZE ) ;
if ( calcCrc )
{
if ( ! trimDump )
{
if ( dumpCert )
{
if ( fileOffset = = 0 )
{
// Update CRC32 (with gamecard certificate)
crc32 ( buf , n , & crc1 ) ;
// Backup gamecard certificate to an array
char tmpCert [ CERT_SIZE ] = { ' \0 ' } ;
memcpy ( tmpCert , buf + CERT_OFFSET , CERT_SIZE ) ;
// Remove gamecard certificate from buffer
memset ( buf + CERT_OFFSET , 0xFF , CERT_SIZE ) ;
// Update CRC32 (without gamecard certificate)
crc32 ( buf , n , & crc2 ) ;
// Restore gamecard certificate to buffer
memcpy ( buf + CERT_OFFSET , tmpCert , CERT_SIZE ) ;
} else {
// Update CRC32 (with gamecard certificate)
crc32 ( buf , n , & crc1 ) ;
// Update CRC32 (without gamecard certificate)
crc32 ( buf , n , & crc2 ) ;
}
} else {
// Update CRC32
crc32 ( buf , n , & crc2 ) ;
}
} else {
// Update CRC32
crc32 ( buf , n , & crc1 ) ;
}
}
if ( totalSize > SPLIT_FILE_MIN & & isFat32 & & ( fileOffset + n ) < totalSize & & ( fileOffset + n ) > = ( ( splitIndex + 1 ) * SPLIT_FILE_2GiB ) )
{
u64 new_file_chunk_size = ( ( fileOffset + n ) - ( ( splitIndex + 1 ) * SPLIT_FILE_2GiB ) ) ;
u64 old_file_chunk_size = ( n - new_file_chunk_size ) ;
if ( old_file_chunk_size > 0 )
{
if ( fwrite ( buf , 1 , old_file_chunk_size , outFile ) ! = old_file_chunk_size )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , fileOffset ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
proceed = false ;
break ;
}
}
fclose ( outFile ) ;
splitIndex + + ;
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX).xc%u " , fixedGameCardName , gameCardVersion , gameCardTitleID , splitIndex ) ;
outFile = fopen ( filename , " wb " ) ;
if ( ! outFile )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to open output file for part #%u! " , splitIndex ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
proceed = false ;
break ;
}
if ( new_file_chunk_size > 0 )
{
if ( fwrite ( buf + old_file_chunk_size , 1 , new_file_chunk_size , outFile ) ! = new_file_chunk_size )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , fileOffset + old_file_chunk_size ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
proceed = false ;
break ;
}
}
} else {
if ( fwrite ( buf , 1 , n , outFile ) ! = n )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , fileOffset ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
proceed = false ;
break ;
}
}
timeGetCurrentTime ( TimeType_LocalSystemClock , & now ) ;
lastSpeed = ( ( ( double ) ( fileOffset + n ) / ( double ) DUMP_BUFFER_SIZE ) / difftime ( ( time_t ) now , ( time_t ) start ) ) ;
averageSpeed = ( ( SMOOTHING_FACTOR * lastSpeed ) + ( ( 1 - SMOOTHING_FACTOR ) * averageSpeed ) ) ;
if ( ! isnormal ( averageSpeed ) ) averageSpeed = 0.00 ; // Very low values
remainingTime = ( u64 ) ( ( ( double ) ( totalSize - ( fileOffset + n ) ) / ( double ) DUMP_BUFFER_SIZE ) / averageSpeed ) ;
timeinfo = localtime ( ( time_t * ) & remainingTime ) ;
strftime ( etaInfo , sizeof ( etaInfo ) / sizeof ( etaInfo [ 0 ] ) , " %HH%MM%SS " , timeinfo ) ;
progress = ( u8 ) ( ( ( fileOffset + n ) * 100 ) / totalSize ) ;
uiFill ( 0 , ( ( breaks + 2 ) * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " %.2lf MiB/s [ETA: %s] " , averageSpeed , etaInfo ) ;
uiDrawString ( strbuf , font_height * 2 , ( breaks + 2 ) * font_height , 255 , 255 , 255 ) ;
uiFill ( FB_WIDTH / 4 , ( ( breaks + 2 ) * font_height ) + 2 , FB_WIDTH / 2 , font_height , 0 , 0 , 0 ) ;
uiFill ( FB_WIDTH / 4 , ( ( breaks + 2 ) * font_height ) + 2 , ( ( ( fileOffset + n ) * ( FB_WIDTH / 2 ) ) / totalSize ) , font_height , 0 , 255 , 0 ) ;
uiFill ( FB_WIDTH - ( FB_WIDTH / 4 ) , ( ( breaks + 2 ) * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
convertSize ( fileOffset + n , curSizeStr , sizeof ( curSizeStr ) / sizeof ( curSizeStr [ 0 ] ) ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " %u%% [%s / %s] " , progress , curSizeStr , totalSizeStr ) ;
uiDrawString ( strbuf , FB_WIDTH - ( FB_WIDTH / 4 ) + ( font_height * 2 ) , ( breaks + 2 ) * font_height , 255 , 255 , 255 ) ;
uiRefreshDisplay ( ) ;
if ( ( fileOffset + n ) < totalSize & & ( ( fileOffset / DUMP_BUFFER_SIZE ) % 10 ) = = 0 )
{
hidScanInput ( ) ;
u32 keysDown = hidKeysDown ( CONTROLLER_P1_AUTO ) ;
if ( keysDown & KEY_B )
{
uiDrawString ( " Process canceled " , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
proceed = false ;
break ;
}
}
}
if ( fileOffset > = totalSize ) success = true ;
// Support empty files
if ( ! partitionSizes [ partition ] )
{
uiFill ( FB_WIDTH / 4 , ( ( breaks + 2 ) * font_height ) + 2 , ( ( fileOffset * ( FB_WIDTH / 2 ) ) / totalSize ) , font_height , 0 , 255 , 0 ) ;
uiFill ( FB_WIDTH - ( FB_WIDTH / 4 ) , ( ( breaks + 2 ) * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
convertSize ( fileOffset , curSizeStr , sizeof ( curSizeStr ) / sizeof ( curSizeStr [ 0 ] ) ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " %u%% [%s / %s] " , progress , curSizeStr , totalSizeStr ) ;
uiDrawString ( strbuf , FB_WIDTH - ( FB_WIDTH / 4 ) + ( font_height * 2 ) , ( breaks + 2 ) * font_height , 255 , 255 , 255 ) ;
uiRefreshDisplay ( ) ;
}
if ( ! proceed )
{
uiFill ( FB_WIDTH / 4 , ( ( breaks + 2 ) * font_height ) + 2 , ( ( ( fileOffset + n ) * ( FB_WIDTH / 2 ) ) / totalSize ) , font_height , 255 , 0 , 0 ) ;
breaks + = 5 ;
}
fsStorageClose ( & gameCardStorage ) ;
} else {
breaks + + ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " OpenGameCardStorage failed for partition #%u! (0x%08X) " , partition , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
proceed = false ;
}
} else {
breaks + + ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " GetGameCardHandle failed for partition #%u! (0x%08X) " , partition , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
proceed = false ;
}
if ( ! proceed ) break ;
}
free ( buf ) ;
} else {
uiDrawString ( " Failed to allocate memory for the dump process! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
if ( success )
{
fclose ( outFile ) ;
breaks + = 5 ;
now - = start ;
timeinfo = localtime ( ( time_t * ) & now ) ;
strftime ( etaInfo , sizeof ( etaInfo ) / sizeof ( etaInfo [ 0 ] ) , " %HH%MM%SS " , timeinfo ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Process successfully completed after %s! " , etaInfo ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 0 , 255 , 0 ) ;
if ( calcCrc )
{
breaks + + ;
if ( ! trimDump )
{
if ( dumpCert )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " XCI dump CRC32 checksum (with certificate): %08X " , crc1 ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 0 , 255 , 0 ) ;
breaks + + ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " XCI dump CRC32 checksum (without certificate): %08X " , crc2 ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 0 , 255 , 0 ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " XCI dump CRC32 checksum: %08X " , crc2 ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 0 , 255 , 0 ) ;
}
breaks + = 2 ;
uiDrawString ( " Starting verification process using XML database from NSWDB.COM... " , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
uiRefreshDisplay ( ) ;
gameCardDumpNSWDBCheck ( crc2 ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " XCI dump CRC32 checksum: %08X " , crc1 ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 0 , 255 , 0 ) ;
breaks + + ;
uiDrawString ( " Dump verification disabled (not compatible with trimmed dumps). " , 0 , breaks * font_height , 255 , 255 , 255 ) ;
}
}
} else {
if ( outFile ) fclose ( outFile ) ;
if ( totalSize > SPLIT_FILE_MIN & & isFat32 )
{
for ( u8 i = 0 ; i < = splitIndex ; i + + )
{
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX).xc%u " , fixedGameCardName , gameCardVersion , gameCardTitleID , i ) ;
remove ( filename ) ;
}
} else {
remove ( filename ) ;
}
}
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to open output file \" %s \" ! " , filename ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
uiDrawString ( " Error: not enough free space available in the SD card. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
}
breaks + = 2 ;
return success ;
2018-06-21 02:42:46 -04:00
}
2018-05-15 18:00:19 +02:00
2018-06-21 02:42:46 -04:00
bool dumpRawPartition ( FsDeviceOperator * fsOperator , u32 partition , bool doSplitting )
{
2019-04-21 12:27:33 -04:00
Result result ;
u64 size , partitionOffset ;
bool success = false ;
char * buf ;
u64 off , n = DUMP_BUFFER_SIZE ;
FsGameCardHandle handle ;
FsStorage gameCardStorage ;
char totalSizeStr [ 32 ] = { ' \0 ' } , curSizeStr [ 32 ] = { ' \0 ' } , filename [ NAME_BUF_LEN ] = { ' \0 ' } ;
u8 progress = 0 ;
FILE * outFile = NULL ;
u8 splitIndex = 0 ;
u64 start , now , remainingTime ;
struct tm * timeinfo ;
char etaInfo [ 32 ] = { ' \0 ' } ;
double lastSpeed = 0.0 , averageSpeed = 0.0 ;
workaroundPartitionZeroAccess ( fsOperator ) ;
if ( R_SUCCEEDED ( result = fsDeviceOperatorGetGameCardHandle ( fsOperator , & handle ) ) )
{
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
// Ugly hack
// The IStorage instance returned for partition == 0 contains the gamecard header, the gamecard certificate, the root HFS0 header and:
// * The "update" (0) partition and the "normal" (1) partition (for gamecard type 0x01)
// * The "update" (0) partition, the "logo" (1) partition and the "normal" (2) partition (for gamecard type 0x02)
// The IStorage instance returned for partition == 1 contains the "secure" partition (which can either be 2 or 3 depending on the gamecard type)
// This ugly hack makes sure we just dump the *actual* raw HFS0 partition, without preceding data, padding, etc.
2019-04-23 01:14:57 -04:00
// Oddly enough, IFileSystem instances actually points to the specified partition ID filesystem. I don't understand why it doesn't work like that for IStorage, but whatever
2019-04-21 12:27:33 -04:00
// NOTE: Using partition == 2 returns error 0x149002, and using higher values probably do so, too
2019-04-23 01:14:57 -04:00
if ( R_SUCCEEDED ( result = fsOpenGameCardStorage ( & gameCardStorage , & handle , HFS0_TO_ISTORAGE_IDX ( hfs0_partition_cnt , partition ) ) ) )
2019-04-21 12:27:33 -04:00
{
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle.value);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
2019-04-23 01:14:57 -04:00
if ( getHfs0EntryDetails ( hfs0_header , hfs0_offset , hfs0_size , hfs0_partition_cnt , partition , true , 0 , & partitionOffset , & size ) )
2019-04-21 12:27:33 -04:00
{
convertSize ( size , totalSizeStr , sizeof ( totalSizeStr ) / sizeof ( totalSizeStr [ 0 ] ) ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Partition size: %s (%lu bytes) " , totalSizeStr , size ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Partition offset (relative to IStorage instance): 0x%016lX " , partitionOffset ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
if ( size < = freeSpace )
{
if ( size > SPLIT_FILE_MIN & & doSplitting )
{
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s).hfs0.%02u " , fixedGameCardName , gameCardVersion , gameCardTitleID , partition , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , partition ) , splitIndex ) ;
} else {
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s).hfs0 " , fixedGameCardName , gameCardVersion , gameCardTitleID , partition , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , partition ) ) ;
}
outFile = fopen ( filename , " wb " ) ;
if ( outFile )
{
buf = ( char * ) malloc ( DUMP_BUFFER_SIZE ) ;
if ( buf )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Dumping raw HFS0 partition #%u to \" %.*s \" . Hold B to cancel. " , partition , ( int ) ( ( size > SPLIT_FILE_MIN & & doSplitting ) ? ( strlen ( filename ) - 3 ) : strlen ( filename ) ) , filename ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + = 2 ;
2019-04-23 01:14:57 -04:00
if ( programAppletType ! = AppletType_Application & & programAppletType ! = AppletType_SystemApplication )
{
uiDrawString ( " Do not press the HOME button. Doing so could corrupt the SD card filesystem. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
breaks + = 2 ;
}
2019-04-21 12:27:33 -04:00
uiRefreshDisplay ( ) ;
timeGetCurrentTime ( TimeType_LocalSystemClock , & start ) ;
for ( off = 0 ; off < size ; off + = n )
{
if ( DUMP_BUFFER_SIZE > ( size - off ) ) n = ( size - off ) ;
if ( R_FAILED ( result = fsStorageRead ( & gameCardStorage , partitionOffset + off , buf , n ) ) )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " StorageRead failed (0x%08X) at offset 0x%016lX " , result , partitionOffset + off ) ;
uiDrawString ( strbuf , 0 , ( breaks + 3 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
if ( size > SPLIT_FILE_MIN & & doSplitting & & ( off + n ) < size & & ( off + n ) > = ( ( splitIndex + 1 ) * SPLIT_FILE_2GiB ) )
{
u64 new_file_chunk_size = ( ( off + n ) - ( ( splitIndex + 1 ) * SPLIT_FILE_2GiB ) ) ;
u64 old_file_chunk_size = ( n - new_file_chunk_size ) ;
if ( old_file_chunk_size > 0 )
{
if ( fwrite ( buf , 1 , old_file_chunk_size , outFile ) ! = old_file_chunk_size )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , off ) ;
uiDrawString ( strbuf , 0 , ( breaks + 3 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
}
fclose ( outFile ) ;
splitIndex + + ;
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s).hfs0.%02u " , fixedGameCardName , gameCardVersion , gameCardTitleID , partition , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , partition ) , splitIndex ) ;
outFile = fopen ( filename , " wb " ) ;
if ( ! outFile )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to open output file for part #%u! " , splitIndex ) ;
uiDrawString ( strbuf , 0 , ( breaks + 3 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
if ( new_file_chunk_size > 0 )
{
if ( fwrite ( buf + old_file_chunk_size , 1 , new_file_chunk_size , outFile ) ! = new_file_chunk_size )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , off + old_file_chunk_size ) ;
uiDrawString ( strbuf , 0 , ( breaks + 3 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
}
} else {
if ( fwrite ( buf , 1 , n , outFile ) ! = n )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , off ) ;
uiDrawString ( strbuf , 0 , ( breaks + 3 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
}
timeGetCurrentTime ( TimeType_LocalSystemClock , & now ) ;
lastSpeed = ( ( ( double ) ( off + n ) / ( double ) DUMP_BUFFER_SIZE ) / difftime ( ( time_t ) now , ( time_t ) start ) ) ;
averageSpeed = ( ( SMOOTHING_FACTOR * lastSpeed ) + ( ( 1 - SMOOTHING_FACTOR ) * averageSpeed ) ) ;
if ( ! isnormal ( averageSpeed ) ) averageSpeed = 0.00 ; // Very low values
remainingTime = ( u64 ) ( ( ( double ) ( size - ( off + n ) ) / ( double ) DUMP_BUFFER_SIZE ) / averageSpeed ) ;
timeinfo = localtime ( ( time_t * ) & remainingTime ) ;
strftime ( etaInfo , sizeof ( etaInfo ) / sizeof ( etaInfo [ 0 ] ) , " %HH%MM%SS " , timeinfo ) ;
progress = ( u8 ) ( ( ( off + n ) * 100 ) / size ) ;
uiFill ( 0 , ( breaks * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " %.2lf MiB/s [ETA: %s] " , averageSpeed , etaInfo ) ;
uiDrawString ( strbuf , font_height * 2 , breaks * font_height , 255 , 255 , 255 ) ;
uiFill ( FB_WIDTH / 4 , ( breaks * font_height ) + 2 , FB_WIDTH / 2 , font_height , 0 , 0 , 0 ) ;
uiFill ( FB_WIDTH / 4 , ( breaks * font_height ) + 2 , ( ( ( off + n ) * ( FB_WIDTH / 2 ) ) / size ) , font_height , 0 , 255 , 0 ) ;
uiFill ( FB_WIDTH - ( FB_WIDTH / 4 ) , ( breaks * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
convertSize ( off + n , curSizeStr , sizeof ( curSizeStr ) / sizeof ( curSizeStr [ 0 ] ) ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " %u%% [%s / %s] " , progress , curSizeStr , totalSizeStr ) ;
uiDrawString ( strbuf , FB_WIDTH - ( FB_WIDTH / 4 ) + ( font_height * 2 ) , breaks * font_height , 255 , 255 , 255 ) ;
uiRefreshDisplay ( ) ;
if ( ( off + n ) < size & & ( ( off / DUMP_BUFFER_SIZE ) % 10 ) = = 0 )
{
hidScanInput ( ) ;
u32 keysDown = hidKeysDown ( CONTROLLER_P1_AUTO ) ;
if ( keysDown & KEY_B )
{
uiDrawString ( " Process canceled " , 0 , ( breaks + 3 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
}
}
if ( off > = size ) success = true ;
// Support empty files
if ( ! size )
{
uiFill ( FB_WIDTH / 4 , ( breaks * font_height ) + 2 , FB_WIDTH / 2 , font_height , 0 , 255 , 0 ) ;
uiFill ( FB_WIDTH - ( FB_WIDTH / 4 ) , ( breaks * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
uiDrawString ( " 100%% [0 B / 0 B] " , FB_WIDTH - ( FB_WIDTH / 4 ) + ( font_height * 2 ) , breaks * font_height , 255 , 255 , 255 ) ;
uiRefreshDisplay ( ) ;
}
if ( success )
{
now - = start ;
timeinfo = localtime ( ( time_t * ) & now ) ;
strftime ( etaInfo , sizeof ( etaInfo ) / sizeof ( etaInfo [ 0 ] ) , " %HH%MM%SS " , timeinfo ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Process successfully completed after %s! " , etaInfo ) ;
uiDrawString ( strbuf , 0 , ( breaks + 3 ) * font_height , 0 , 255 , 0 ) ;
} else {
uiFill ( FB_WIDTH / 4 , ( breaks * font_height ) + 2 , ( ( ( off + n ) * ( FB_WIDTH / 2 ) ) / size ) , font_height , 255 , 0 , 0 ) ;
}
breaks + = 3 ;
free ( buf ) ;
} else {
uiDrawString ( " Failed to allocate memory for the dump process! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
if ( outFile ) fclose ( outFile ) ;
if ( ! success )
{
if ( size > SPLIT_FILE_MIN & & doSplitting )
{
for ( u8 i = 0 ; i < = splitIndex ; i + + )
{
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s).hfs0.%02u " , fixedGameCardName , gameCardVersion , gameCardTitleID , partition , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , partition ) , i ) ;
remove ( filename ) ;
}
} else {
remove ( filename ) ;
}
}
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to open output file \" %s \" ! " , filename ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
uiDrawString ( " Error: not enough free space available in the SD card. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
uiDrawString ( " Error: unable to get partition details from the root HFS0 header! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
fsStorageClose ( & gameCardStorage ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " OpenGameCardStorage failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " GetGameCardHandle failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
breaks + = 2 ;
return success ;
2018-05-16 19:08:20 +02:00
}
2019-04-23 01:14:57 -04:00
bool getPartitionHfs0Header ( FsDeviceOperator * fsOperator , u32 partition )
2018-06-21 02:42:46 -04:00
{
2019-04-23 01:14:57 -04:00
if ( hfs0_header = = NULL ) return false ;
if ( partitionHfs0Header ! = NULL )
{
free ( partitionHfs0Header ) ;
partitionHfs0Header = NULL ;
partitionHfs0HeaderOffset = 0 ;
partitionHfs0HeaderSize = 0 ;
partitionHfs0FileCount = 0 ;
partitionHfs0StrTableSize = 0 ;
}
char * buf = NULL ;
2019-04-21 12:27:33 -04:00
Result result ;
2019-04-23 01:14:57 -04:00
FsGameCardHandle handle ;
FsStorage gameCardStorage ;
u64 partitionSize = 0 ;
u32 magic = 0 ;
2019-04-21 12:27:33 -04:00
bool success = false ;
2019-04-23 01:14:57 -04:00
if ( getHfs0EntryDetails ( hfs0_header , hfs0_offset , hfs0_size , hfs0_partition_cnt , partition , true , 0 , & partitionHfs0HeaderOffset , & partitionSize ) )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
workaroundPartitionZeroAccess ( fsOperator ) ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
if ( R_SUCCEEDED ( result = fsDeviceOperatorGetGameCardHandle ( fsOperator , & handle ) ) )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value);
2019-04-21 12:27:33 -04:00
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
2019-04-23 01:14:57 -04:00
// Same ugly hack from dumpRawPartition()
if ( R_SUCCEEDED ( result = fsOpenGameCardStorage ( & gameCardStorage , & handle , HFS0_TO_ISTORAGE_IDX ( hfs0_partition_cnt , partition ) ) ) )
{
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
buf = ( char * ) malloc ( MEDIA_UNIT_SIZE ) ;
if ( buf )
{
// First read MEDIA_UNIT_SIZE bytes
if ( R_SUCCEEDED ( result = fsStorageRead ( & gameCardStorage , partitionHfs0HeaderOffset , buf , MEDIA_UNIT_SIZE ) ) )
{
// Check the HFS0 magic word
memcpy ( & magic , buf , sizeof ( u32 ) ) ;
magic = bswap_32 ( magic ) ;
if ( magic = = HFS0_MAGIC )
{
// Calculate the size for the partition HFS0 header
memcpy ( & partitionHfs0FileCount , buf + HFS0_FILE_COUNT_ADDR , sizeof ( u32 ) ) ;
memcpy ( & partitionHfs0StrTableSize , buf + HFS0_STR_TABLE_SIZE_ADDR , sizeof ( u32 ) ) ;
partitionHfs0HeaderSize = ( HFS0_ENTRY_TABLE_ADDR + ( sizeof ( hfs0_entry_table ) * partitionHfs0FileCount ) + partitionHfs0StrTableSize ) ;
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u HFS0 header offset (relative to IStorage instance): 0x%016lX", partition, partitionHfs0HeaderOffset);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Partition #%u HFS0 header size: %lu bytes " , partition , partitionHfs0HeaderSize ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Partition #%u file count: %u " , partition , partitionHfs0FileCount ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Partition #%u string table size: %u bytes " , partition , partitionHfs0StrTableSize ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
uiRefreshDisplay ( ) ; */
// Round up the partition HFS0 header size to a MEDIA_UNIT_SIZE bytes boundary
partitionHfs0HeaderSize = round_up ( partitionHfs0HeaderSize , MEDIA_UNIT_SIZE ) ;
partitionHfs0Header = ( char * ) malloc ( partitionHfs0HeaderSize ) ;
if ( partitionHfs0Header )
{
// Check if we were dealing with the correct header size all along
if ( partitionHfs0HeaderSize = = MEDIA_UNIT_SIZE )
{
// Just copy what we already have
memcpy ( partitionHfs0Header , buf , MEDIA_UNIT_SIZE ) ;
success = true ;
} else {
// Read the whole HFS0 header
if ( R_SUCCEEDED ( result = fsStorageRead ( & gameCardStorage , partitionHfs0HeaderOffset , partitionHfs0Header , partitionHfs0HeaderSize ) ) )
{
success = true ;
} else {
free ( partitionHfs0Header ) ;
partitionHfs0Header = NULL ;
partitionHfs0HeaderOffset = 0 ;
partitionHfs0HeaderSize = 0 ;
partitionHfs0FileCount = 0 ;
partitionHfs0StrTableSize = 0 ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " StorageRead failed (0x%08X) at offset 0x%016lX " , result , partitionHfs0HeaderOffset ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
}
if ( success )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Partition #%u HFS0 header successfully retrieved! " , partition ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
}
} else {
partitionHfs0HeaderOffset = 0 ;
partitionHfs0HeaderSize = 0 ;
partitionHfs0FileCount = 0 ;
partitionHfs0StrTableSize = 0 ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to allocate memory for the HFS0 header from partition #%u! " , partition ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Magic word mismatch! 0x%08X != 0x%08X " , magic , HFS0_MAGIC ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " StorageRead failed (0x%08X) at offset 0x%016lX " , result , partitionHfs0HeaderOffset ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
free ( buf ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to allocate memory for the HFS0 header from partition #%u! " , partition ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
fsStorageClose ( & gameCardStorage ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " OpenGameCardStorage failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
2019-04-21 12:27:33 -04:00
} else {
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " GetGameCardHandle failed! (0x%08X) " , result ) ;
2019-04-21 12:27:33 -04:00
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
2019-04-23 01:14:57 -04:00
uiDrawString ( " Error: unable to get partition details from the root HFS0 header! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
breaks + = 2 ;
2019-04-21 12:27:33 -04:00
return success ;
2018-06-21 02:42:46 -04:00
}
2018-05-16 23:29:43 +02:00
2019-04-23 01:14:57 -04:00
bool copyFileFromHfs0 ( FsDeviceOperator * fsOperator , u32 partition , const char * source , const char * dest , const u64 file_offset , const u64 size , bool doSplitting , bool calcEta )
2018-06-21 02:42:46 -04:00
{
2019-04-23 01:14:57 -04:00
Result result ;
2019-04-21 12:27:33 -04:00
bool success = false ;
char splitFilename [ NAME_BUF_LEN ] = { ' \0 ' } ;
size_t destLen = strlen ( dest ) ;
2019-04-23 01:14:57 -04:00
FILE * outFile = NULL ;
2019-04-21 12:27:33 -04:00
char * buf = NULL ;
2019-04-23 01:14:57 -04:00
u64 off , n = DUMP_BUFFER_SIZE ;
2019-04-21 12:27:33 -04:00
u8 splitIndex = 0 ;
u8 progress = 0 ;
char totalSizeStr [ 32 ] = { ' \0 ' } , curSizeStr [ 32 ] = { ' \0 ' } ;
2019-04-23 01:14:57 -04:00
FsGameCardHandle handle ;
FsStorage gameCardStorage ;
2019-04-21 12:27:33 -04:00
u64 start , now , remainingTime ;
struct tm * timeinfo ;
char etaInfo [ 32 ] = { ' \0 ' } ;
double lastSpeed = 0.0 , averageSpeed = 0.0 ;
uiFill ( 0 , breaks * font_height , FB_WIDTH , font_height * 4 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Copying \" %s \" ... " , source ) ;
uiDrawString ( strbuf , 0 , ( breaks + 1 ) * font_height , 255 , 255 , 255 ) ;
2019-04-23 01:14:57 -04:00
uiRefreshDisplay ( ) ;
2019-04-21 12:27:33 -04:00
if ( ( destLen + 1 ) < NAME_BUF_LEN )
{
2019-04-23 01:14:57 -04:00
if ( R_SUCCEEDED ( result = fsDeviceOperatorGetGameCardHandle ( fsOperator , & handle ) ) )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
// Same ugly hack from dumpRawPartition()
if ( R_SUCCEEDED ( result = fsOpenGameCardStorage ( & gameCardStorage , & handle , HFS0_TO_ISTORAGE_IDX ( hfs0_partition_cnt , partition ) ) ) )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle.value);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
convertSize ( size , totalSizeStr , sizeof ( totalSizeStr ) / sizeof ( totalSizeStr [ 0 ] ) ) ;
if ( size > SPLIT_FILE_MIN & & doSplitting ) snprintf ( splitFilename , sizeof ( splitFilename ) / sizeof ( splitFilename [ 0 ] ) , " %s.%02u " , dest , splitIndex ) ;
outFile = fopen ( ( ( size > SPLIT_FILE_MIN & & doSplitting ) ? splitFilename : dest ) , " wb " ) ;
if ( outFile )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
buf = ( char * ) malloc ( DUMP_BUFFER_SIZE ) ;
if ( buf )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
if ( calcEta ) timeGetCurrentTime ( TimeType_LocalSystemClock , & start ) ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
for ( off = 0 ; off < size ; off + = n )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
if ( DUMP_BUFFER_SIZE > ( size - off ) ) n = ( size - off ) ;
if ( R_FAILED ( result = fsStorageRead ( & gameCardStorage , file_offset + off , buf , n ) ) )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " StorageRead failed (0x%08X) at offset 0x%016lX " , result , file_offset + off ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
if ( size > SPLIT_FILE_MIN & & doSplitting & & ( off + n ) < size & & ( off + n ) > = ( ( splitIndex + 1 ) * SPLIT_FILE_2GiB ) )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
u64 new_file_chunk_size = ( ( off + n ) - ( ( splitIndex + 1 ) * SPLIT_FILE_2GiB ) ) ;
u64 old_file_chunk_size = ( n - new_file_chunk_size ) ;
if ( old_file_chunk_size > 0 )
{
if ( fwrite ( buf , 1 , old_file_chunk_size , outFile ) ! = old_file_chunk_size )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , off ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
}
fclose ( outFile ) ;
splitIndex + + ;
snprintf ( splitFilename , sizeof ( splitFilename ) / sizeof ( splitFilename [ 0 ] ) , " %s.%02u " , dest , splitIndex ) ;
outFile = fopen ( splitFilename , " wb " ) ;
if ( ! outFile )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to open output file for part #%u! " , splitIndex ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
if ( new_file_chunk_size > 0 )
{
if ( fwrite ( buf + old_file_chunk_size , 1 , new_file_chunk_size , outFile ) ! = new_file_chunk_size )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , off + old_file_chunk_size ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
}
} else {
if ( fwrite ( buf , 1 , n , outFile ) ! = n )
2019-04-21 12:27:33 -04:00
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to write chunk to offset 0x%016lX " , off ) ;
uiDrawString ( strbuf , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
break ;
}
}
2019-04-23 01:14:57 -04:00
if ( calcEta )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
timeGetCurrentTime ( TimeType_LocalSystemClock , & now ) ;
lastSpeed = ( ( ( double ) ( off + n ) / ( double ) DUMP_BUFFER_SIZE ) / difftime ( ( time_t ) now , ( time_t ) start ) ) ;
averageSpeed = ( ( SMOOTHING_FACTOR * lastSpeed ) + ( ( 1 - SMOOTHING_FACTOR ) * averageSpeed ) ) ;
if ( ! isnormal ( averageSpeed ) ) averageSpeed = 0.00 ; // Very low values
remainingTime = ( u64 ) ( ( ( double ) ( size - ( off + n ) ) / ( double ) DUMP_BUFFER_SIZE ) / averageSpeed ) ;
timeinfo = localtime ( ( time_t * ) & remainingTime ) ;
strftime ( etaInfo , sizeof ( etaInfo ) / sizeof ( etaInfo [ 0 ] ) , " %HH%MM%SS " , timeinfo ) ;
uiFill ( 0 , ( ( breaks + 3 ) * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " %.2lf MiB/s [ETA: %s] " , averageSpeed , etaInfo ) ;
uiDrawString ( strbuf , font_height * 2 , ( breaks + 3 ) * font_height , 255 , 255 , 255 ) ;
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
progress = ( u8 ) ( ( ( off + n ) * 100 ) / size ) ;
uiFill ( FB_WIDTH / 4 , ( ( breaks + 3 ) * font_height ) + 2 , FB_WIDTH / 2 , font_height , 0 , 0 , 0 ) ;
uiFill ( FB_WIDTH / 4 , ( ( breaks + 3 ) * font_height ) + 2 , ( ( ( off + n ) * ( FB_WIDTH / 2 ) ) / size ) , font_height , 0 , 255 , 0 ) ;
uiFill ( FB_WIDTH - ( FB_WIDTH / 4 ) , ( ( breaks + 3 ) * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
convertSize ( off + n , curSizeStr , sizeof ( curSizeStr ) / sizeof ( curSizeStr [ 0 ] ) ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " %u%% [%s / %s] " , progress , curSizeStr , totalSizeStr ) ;
uiDrawString ( strbuf , FB_WIDTH - ( FB_WIDTH / 4 ) + ( font_height * 2 ) , ( breaks + 3 ) * font_height , 255 , 255 , 255 ) ;
uiRefreshDisplay ( ) ;
if ( ( off + n ) < size & & ( ( off / DUMP_BUFFER_SIZE ) % 10 ) = = 0 )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
hidScanInput ( ) ;
u32 keysDown = hidKeysDown ( CONTROLLER_P1_AUTO ) ;
if ( keysDown & KEY_B )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
uiDrawString ( " Process canceled " , 0 , ( breaks + 5 ) * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
break ;
}
}
}
2019-04-23 01:14:57 -04:00
if ( off > = size ) success = true ;
// Support empty files
if ( ! size )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
uiFill ( FB_WIDTH / 4 , ( ( breaks + 3 ) * font_height ) + 2 , FB_WIDTH / 2 , font_height , 0 , 255 , 0 ) ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
uiFill ( FB_WIDTH - ( FB_WIDTH / 4 ) , ( ( breaks + 3 ) * font_height ) - 4 , FB_WIDTH / 4 , font_height + 8 , BG_COLOR_RGB , BG_COLOR_RGB , BG_COLOR_RGB ) ;
uiDrawString ( " 100%% [0 B / 0 B] " , FB_WIDTH - ( FB_WIDTH / 4 ) + ( font_height * 2 ) , ( breaks + 3 ) * font_height , 255 , 255 , 255 ) ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
uiRefreshDisplay ( ) ;
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
if ( ! success )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
uiFill ( FB_WIDTH / 4 , ( ( breaks + 3 ) * font_height ) + 2 , ( ( ( off + n ) * ( FB_WIDTH / 2 ) ) / size ) , font_height , 255 , 0 , 0 ) ;
breaks + = 5 ;
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
free ( buf ) ;
} else {
breaks + = 3 ;
uiDrawString ( " Failed to allocate memory for the dump process! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
if ( outFile ) fclose ( outFile ) ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
if ( success )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
if ( calcEta )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
breaks + = 5 ;
now - = start ;
timeinfo = localtime ( ( time_t * ) & now ) ;
strftime ( etaInfo , sizeof ( etaInfo ) / sizeof ( etaInfo [ 0 ] ) , " %HH%MM%SS " , timeinfo ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Process successfully completed after %s! " , etaInfo ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 0 , 255 , 0 ) ;
2019-04-21 12:27:33 -04:00
}
} else {
2019-04-23 01:14:57 -04:00
if ( size > SPLIT_FILE_MIN & & doSplitting )
{
for ( u8 i = 0 ; i < = splitIndex ; i + + )
{
snprintf ( splitFilename , sizeof ( splitFilename ) / sizeof ( splitFilename [ 0 ] ) , " %s.%02u " , dest , i ) ;
remove ( splitFilename ) ;
}
} else {
remove ( dest ) ;
}
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
} else {
breaks + = 3 ;
uiDrawString ( " Failed to open output file! " , 0 , breaks * font_height , 255 , 255 , 255 ) ;
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
fsStorageClose ( & gameCardStorage ) ;
2019-04-21 12:27:33 -04:00
} else {
breaks + = 3 ;
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " OpenGameCardStorage failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
}
} else {
breaks + = 3 ;
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " GetGameCardHandle failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
}
} else {
breaks + = 3 ;
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Destination path is too long! (%lu bytes) " , destLen ) ;
2019-04-21 12:27:33 -04:00
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
return success ;
2018-06-21 02:42:46 -04:00
}
2018-05-16 23:29:43 +02:00
2019-04-23 01:14:57 -04:00
bool copyHfs0Contents ( FsDeviceOperator * fsOperator , u32 partition , hfs0_entry_table * partitionEntryTable , const char * dest , bool splitting )
2018-06-21 02:42:46 -04:00
{
2019-04-23 01:14:57 -04:00
if ( ! dest | | ! * dest )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
uiDrawString ( " Error: destination directory is empty. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
return false ;
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
if ( ! partitionHfs0Header | | ! partitionEntryTable )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
uiDrawString ( " HFS0 partition header information unavailable! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
return false ;
}
2019-04-23 01:14:57 -04:00
char dbuf [ NAME_BUF_LEN ] = { ' \0 ' } ;
size_t dest_len = strlen ( dest ) ;
if ( ( dest_len + 1 ) > = NAME_BUF_LEN )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Destination directory name is too long! (%lu bytes) " , dest_len ) ;
2019-04-21 12:27:33 -04:00
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
return false ;
}
strcpy ( dbuf , dest ) ;
mkdir ( dbuf , 0744 ) ;
2019-04-23 01:14:57 -04:00
dbuf [ dest_len ] = ' / ' ;
dest_len + + ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
u32 i ;
bool success ;
for ( i = 0 ; i < partitionHfs0FileCount ; i + + )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
u32 filename_offset = ( HFS0_ENTRY_TABLE_ADDR + ( sizeof ( hfs0_entry_table ) * partitionHfs0FileCount ) + partitionEntryTable [ i ] . filename_offset ) ;
char * filename = ( partitionHfs0Header + filename_offset ) ;
strcpy ( dbuf + dest_len , filename ) ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
u64 file_offset = ( partitionHfs0HeaderSize + partitionEntryTable [ i ] . file_offset ) ;
if ( HFS0_TO_ISTORAGE_IDX ( hfs0_partition_cnt , partition ) = = 0 ) file_offset + = partitionHfs0HeaderOffset ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
success = copyFileFromHfs0 ( fsOperator , partition , filename , dbuf , file_offset , partitionEntryTable [ i ] . file_size , splitting , false ) ;
if ( ! success ) break ;
2019-04-21 12:27:33 -04:00
}
return success ;
2018-06-21 02:42:46 -04:00
}
2018-05-16 19:08:20 +02:00
2018-06-21 02:42:46 -04:00
bool dumpPartitionData ( FsDeviceOperator * fsOperator , u32 partition )
{
2019-04-21 12:27:33 -04:00
bool success = false ;
2019-04-23 01:14:57 -04:00
u64 total_size = 0 ;
u32 i ;
hfs0_entry_table * entryTable = NULL ;
2019-04-21 12:27:33 -04:00
char dumpPath [ NAME_BUF_LEN ] = { ' \0 ' } , totalSizeStr [ 32 ] = { ' \0 ' } ;
workaroundPartitionZeroAccess ( fsOperator ) ;
2019-04-23 01:14:57 -04:00
if ( getPartitionHfs0Header ( fsOperator , partition ) )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
if ( partitionHfs0FileCount )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
entryTable = ( hfs0_entry_table * ) malloc ( sizeof ( hfs0_entry_table ) * partitionHfs0FileCount ) ;
if ( entryTable )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
memcpy ( entryTable , partitionHfs0Header + HFS0_ENTRY_TABLE_ADDR , sizeof ( hfs0_entry_table ) * partitionHfs0FileCount ) ;
// Calculate total size
for ( i = 0 ; i < partitionHfs0FileCount ; i + + ) total_size + = entryTable [ i ] . file_size ;
2019-04-21 12:27:33 -04:00
convertSize ( total_size , totalSizeStr , sizeof ( totalSizeStr ) / sizeof ( totalSizeStr [ 0 ] ) ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Total partition data size: %s (%lu bytes) " , totalSizeStr , total_size ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ;
if ( total_size < = freeSpace )
{
snprintf ( dumpPath , sizeof ( dumpPath ) / sizeof ( dumpPath [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s) " , fixedGameCardName , gameCardVersion , gameCardTitleID , partition , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , partition ) ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Copying partition #%u data to \" %s/ \" . Hold B to cancel. " , partition , dumpPath ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + = 2 ;
2019-04-23 01:14:57 -04:00
if ( programAppletType ! = AppletType_Application & & programAppletType ! = AppletType_SystemApplication )
{
uiDrawString ( " Do not press the HOME button. Doing so could corrupt the SD card filesystem. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
breaks + = 2 ;
}
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
uiRefreshDisplay ( ) ;
success = copyHfs0Contents ( fsOperator , partition , entryTable , dumpPath , true ) ;
if ( success )
2019-04-21 12:27:33 -04:00
{
breaks + = 5 ;
uiDrawString ( " Process successfully completed! " , 0 , breaks * font_height , 0 , 255 , 0 ) ;
} else {
removeDirectory ( dumpPath ) ;
}
} else {
uiDrawString ( " Error: not enough free space available in the SD card. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
2019-04-23 01:14:57 -04:00
free ( entryTable ) ;
2019-04-21 12:27:33 -04:00
} else {
2019-04-23 01:14:57 -04:00
uiDrawString ( " Unable to allocate memory for the HFS0 file entries! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
}
} else {
2019-04-23 01:14:57 -04:00
uiDrawString ( " The selected partition is empty! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
}
2019-04-23 01:14:57 -04:00
free ( partitionHfs0Header ) ;
partitionHfs0Header = NULL ;
partitionHfs0HeaderSize = 0 ;
partitionHfs0FileCount = 0 ;
partitionHfs0StrTableSize = 0 ;
2019-04-21 12:27:33 -04:00
}
breaks + = 2 ;
return success ;
2018-05-16 19:08:20 +02:00
}
2019-04-23 01:14:57 -04:00
bool getHfs0FileList ( FsDeviceOperator * fsOperator , u32 partition )
2018-06-21 02:42:46 -04:00
{
2019-04-23 01:14:57 -04:00
if ( ! getPartitionHfs0Header ( fsOperator , partition ) ) return false ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
if ( ! partitionHfs0Header )
{
uiDrawString ( " HFS0 partition header information unavailable! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
return false ;
}
if ( ! partitionHfs0FileCount )
{
uiDrawString ( " The selected partition is empty! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
return false ;
}
if ( partitionHfs0FileCount > FILENAME_MAX_CNT )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " HFS0 partition contains more than %u files! (%u entries) " , FILENAME_MAX_CNT , partitionHfs0FileCount ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
return false ;
}
hfs0_entry_table * entryTable = ( hfs0_entry_table * ) malloc ( sizeof ( hfs0_entry_table ) * partitionHfs0FileCount ) ;
if ( ! entryTable )
{
uiDrawString ( " Unable to allocate memory for the HFS0 file entries! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
return false ;
}
memcpy ( entryTable , partitionHfs0Header + HFS0_ENTRY_TABLE_ADDR , sizeof ( hfs0_entry_table ) * partitionHfs0FileCount ) ;
memset ( filenameBuffer , 0 , FILENAME_BUFFER_SIZE ) ;
int i ;
int max_elements = ( int ) partitionHfs0FileCount ;
char * nextFilename = filenameBuffer ;
filenamesCount = 0 ;
for ( i = 0 ; i < max_elements ; i + + )
{
u32 filename_offset = ( HFS0_ENTRY_TABLE_ADDR + ( sizeof ( hfs0_entry_table ) * partitionHfs0FileCount ) + entryTable [ i ] . filename_offset ) ;
addStringToFilenameBuffer ( partitionHfs0Header + filename_offset , & nextFilename ) ;
}
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
free ( entryTable ) ;
return true ;
}
bool dumpFileFromPartition ( FsDeviceOperator * fsOperator , u32 partition , u32 file , char * filename )
{
if ( ! partitionHfs0Header )
{
uiDrawString ( " HFS0 partition header information unavailable! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
return false ;
}
if ( ! filename | | ! * filename )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
uiDrawString ( " Filename unavailable! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
return false ;
}
u64 file_offset = 0 ;
u64 file_size = 0 ;
bool success = false ;
if ( getHfs0EntryDetails ( partitionHfs0Header , partitionHfs0HeaderOffset , partitionHfs0HeaderSize , partitionHfs0FileCount , file , false , partition , & file_offset , & file_size ) )
{
if ( file_size < = freeSpace )
2019-04-21 12:27:33 -04:00
{
2019-04-23 01:14:57 -04:00
char destCopyPath [ NAME_BUF_LEN ] = { ' \0 ' } ;
snprintf ( destCopyPath , sizeof ( destCopyPath ) / sizeof ( destCopyPath [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s) " , fixedGameCardName , gameCardVersion , gameCardTitleID , partition , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , partition ) ) ;
2019-04-21 12:27:33 -04:00
2019-04-23 01:14:57 -04:00
if ( ( strlen ( destCopyPath ) + 1 + strlen ( filename ) ) < NAME_BUF_LEN )
{
mkdir ( destCopyPath , 0744 ) ;
snprintf ( destCopyPath , sizeof ( destCopyPath ) / sizeof ( destCopyPath [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Partition %u (%s)/%s " , fixedGameCardName , gameCardVersion , gameCardTitleID , partition , GAMECARD_PARTITION_NAME ( hfs0_partition_cnt , partition ) , filename ) ;
uiDrawString ( " Hold B to cancel. " , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + = 2 ;
if ( programAppletType ! = AppletType_Application & & programAppletType ! = AppletType_SystemApplication )
{
uiDrawString ( " Do not press the HOME button. Doing so could corrupt the SD card filesystem. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
breaks + = 2 ;
}
uiRefreshDisplay ( ) ;
success = copyFileFromHfs0 ( fsOperator , partition , filename , destCopyPath , file_offset , file_size , true , true ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Destination path is too long! (%lu bytes) " , strlen ( destCopyPath ) + 1 + strlen ( filename ) ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
2019-04-21 12:27:33 -04:00
} else {
2019-04-23 01:14:57 -04:00
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Error: not enough free space available in the SD card (%lu bytes required). " , file_size ) ;
2019-04-21 12:27:33 -04:00
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
2019-04-23 01:14:57 -04:00
uiDrawString ( " Error: unable to get file details from the partition HFS0 header! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
2019-04-21 12:27:33 -04:00
}
return success ;
2018-05-16 19:08:20 +02:00
}
2018-06-21 02:42:46 -04:00
bool dumpGameCertificate ( FsDeviceOperator * fsOperator )
{
2019-04-21 12:27:33 -04:00
u32 crc ;
Result result ;
FsGameCardHandle handle ;
FsStorage gameCardStorage ;
bool success = false ;
FILE * outFile = NULL ;
char filename [ NAME_BUF_LEN ] = { ' \0 ' } ;
char * buf = NULL ;
workaroundPartitionZeroAccess ( fsOperator ) ;
if ( R_SUCCEEDED ( result = fsDeviceOperatorGetGameCardHandle ( fsOperator , & handle ) ) )
{
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
if ( R_SUCCEEDED ( result = fsOpenGameCardStorage ( & gameCardStorage , & handle , 0 ) ) )
{
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage succeeded: 0x%08X", handle.value);
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
breaks + + ; */
if ( CERT_SIZE < = freeSpace )
{
buf = ( char * ) malloc ( DUMP_BUFFER_SIZE ) ;
if ( buf )
{
if ( R_SUCCEEDED ( result = fsStorageRead ( & gameCardStorage , CERT_OFFSET , buf , CERT_SIZE ) ) )
{
// Calculate CRC32
crc32 ( buf , CERT_SIZE , & crc ) ;
snprintf ( filename , sizeof ( filename ) / sizeof ( filename [ 0 ] ) , " sdmc:/%s v%u (%016lX) - Certificate (%08X).bin " , fixedGameCardName , gameCardVersion , gameCardTitleID , crc ) ;
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Dumping game card certificate to file \" %s \" ... " , filename ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 255 , 255 ) ;
2019-04-23 01:14:57 -04:00
breaks + = 2 ;
if ( programAppletType ! = AppletType_Application & & programAppletType ! = AppletType_SystemApplication )
{
uiDrawString ( " Do not press the HOME button. Doing so could corrupt the SD card filesystem. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
breaks + = 2 ;
}
2019-04-21 12:27:33 -04:00
uiRefreshDisplay ( ) ;
outFile = fopen ( filename , " wb " ) ;
if ( outFile )
{
if ( fwrite ( buf , 1 , CERT_SIZE , outFile ) = = CERT_SIZE )
{
success = true ;
uiDrawString ( " Process successfully completed! " , 0 , breaks * font_height , 0 , 255 , 0 ) ;
} else {
uiDrawString ( " Failed to write certificate data! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
fclose ( outFile ) ;
if ( ! success ) remove ( filename ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to open output file \" %s \" ! " , filename ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " StorageRead failed (0x%08X) at offset 0x%08X " , result , CERT_OFFSET ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
free ( buf ) ;
} else {
uiDrawString ( " Failed to allocate memory for the dump process! " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
uiDrawString ( " Error: not enough free space available in the SD card. " , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
fsStorageClose ( & gameCardStorage ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " OpenGameCardStorage failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " GetGameCardHandle failed! (0x%08X) " , result ) ;
uiDrawString ( strbuf , 0 , breaks * font_height , 255 , 0 , 0 ) ;
}
breaks + = 2 ;
return success ;
2018-06-21 02:42:46 -04:00
}