2018-06-21 07:42:46 +01:00
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <errno.h>
# include <math.h>
# include <time.h>
# include <dirent.h>
# include <sys/stat.h>
# include <sys/statvfs.h>
# include <switch/services/ns.h>
2018-06-26 02:11:18 +01:00
# include <libxml/globals.h>
# include <libxml/xpath.h>
2018-06-21 07:42:46 +01:00
2018-06-26 02:11:18 +01:00
# include "dumper.h"
2018-05-15 17:00:19 +01:00
# include "fsext.h"
2018-06-21 07:42:46 +01:00
# include "ncmext.h"
# include "ui.h"
# include "util.h"
extern int breaks ;
2018-05-15 17:00:19 +01:00
2018-06-26 02:11:18 +01:00
extern u64 gameCardSize ;
extern u64 gameCardTitleID ;
extern u32 hfs0_partition_cnt ;
const char * nswReleasesXmlPath = " sdmc:/NSWreleases.xml " ;
const char * nswReleasesRootElement = " releases " ;
const char * nswReleasesChildren = " release " ;
const char * nswReleasesChildrenImageSize = " imagesize " ;
const char * nswReleasesChildrenTitleID = " titleid " ;
const char * nswReleasesChildrenImgCrc = " imgcrc " ;
const char * nswReleasesChildrenReleaseName = " releasename " ;
const char * nswReleasesChildrenCard = " card " ;
2018-06-21 07:42:46 +01:00
bool isGameCardInserted ( FsDeviceOperator * o )
{
2018-05-15 17:00:19 +01:00
bool inserted ;
2018-06-21 07:42:46 +01:00
if ( R_FAILED ( fsDeviceOperatorIsGameCardInserted ( o , & inserted ) ) ) return false ;
2018-05-15 17:00:19 +01:00
return inserted ;
}
2018-06-21 07:42:46 +01:00
void syncDisplay ( )
{
2018-05-15 17:00:19 +01:00
gfxFlushBuffers ( ) ;
gfxSwapBuffers ( ) ;
gfxWaitForVsync ( ) ;
2018-06-21 07:42:46 +01:00
}
void delay ( u8 seconds )
{
if ( ! seconds ) return ;
time_t timer ;
time ( & timer ) ;
while ( time ( NULL ) < ( timer + seconds ) ) syncDisplay ( ) ;
}
bool getGameCardTitleID ( u64 * titleID )
{
bool success = false ;
Result result ;
ncmContentMetaDatabase ncmDb ;
ncmApplicationMetaKey * appList = ( ncmApplicationMetaKey * ) malloc ( sizeof ( ncmApplicationMetaKey ) ) ;
if ( appList )
{
memset ( appList , 0 , sizeof ( ncmApplicationMetaKey ) ) ;
if ( R_SUCCEEDED ( result = ncmOpenContentMetaDatabase ( FsStorageId_GameCard ) ) )
{
if ( R_SUCCEEDED ( result = ncmGetContentMetaDatabase ( & ncmDb , FsStorageId_GameCard ) ) )
{
if ( R_SUCCEEDED ( result = ncmMetaDatabaseListApplication ( & ncmDb , appList , sizeof ( ncmApplicationMetaKey ) , 0 ) ) )
{
* titleID = appList - > meta_record . titleID ;
success = true ;
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getGameCardTitleID: MetaDatabaseListApplication failed! (0x%08x) " , result ) ;
2018-06-21 07:42:46 +01:00
}
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getGameCardTitleID: GetContentMetaDatabase failed! (0x%08x) " , result ) ;
2018-06-21 07:42:46 +01:00
}
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getGameCardTitleID: OpenContentMetaDatabase failed! (0x%08x) " , result ) ;
2018-06-21 07:42:46 +01:00
}
// Seems to cause problems
2018-06-22 01:46:17 +01:00
//if (R_FAILED(result = ncmCloseContentMetaDatabase(FsStorageId_GameCard))) uiStatusMsg("getGameCardTitleID: CloseContentMetaDatabase failed! (0x%08x)", result);
2018-06-21 07:42:46 +01:00
free ( appList ) ;
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getGameCardTitleID: Unable to allocate memory for the NCM service operations. " ) ;
2018-06-21 07:42:46 +01:00
}
return success ;
}
bool getGameCardControlNacp ( u64 titleID , char * nameBuf , int nameBufSize , char * authorBuf , int authorBufSize , char * versionBuf , int versionBufSize )
{
if ( titleID = = 0 ) return false ;
bool success = false ;
Result result ;
size_t outsize = 0 ;
NsApplicationControlData * buf = NULL ;
NacpLanguageEntry * langentry = NULL ;
buf = ( NsApplicationControlData * ) malloc ( sizeof ( NsApplicationControlData ) ) ;
if ( buf )
{
memset ( buf , 0 , sizeof ( NsApplicationControlData ) ) ;
if ( R_SUCCEEDED ( result = nsGetApplicationControlData ( 1 , titleID , buf , sizeof ( NsApplicationControlData ) , & outsize ) ) )
{
if ( outsize > = sizeof ( buf - > nacp ) )
{
if ( R_SUCCEEDED ( result = nacpGetLanguageEntry ( & buf - > nacp , & langentry ) ) )
{
strncpy ( nameBuf , langentry - > name , nameBufSize - 1 ) ;
strncpy ( authorBuf , langentry - > author , authorBufSize - 1 ) ;
strncpy ( versionBuf , buf - > nacp . version , versionBufSize - 1 ) ;
success = true ;
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getGameCardControlNacp: GetLanguageEntry failed! (0x%08x) " , result ) ;
2018-06-21 07:42:46 +01:00
}
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getGameCardControlNacp: Control.nacp buffer size (%u bytes) is too small! Expected: %u bytes " , outsize , sizeof ( buf - > nacp ) ) ;
2018-06-21 07:42:46 +01:00
}
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getGameCardControlNacp: GetApplicationControlData failed! (0x%08x) " , result ) ;
2018-06-21 07:42:46 +01:00
}
free ( buf ) ;
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getGameCardControlNacp: Unable to allocate memory for the NS service operations. " ) ;
2018-06-21 07:42:46 +01:00
}
return success ;
}
int getSdCardFreeSpace ( u64 * out )
{
struct statvfs st ;
int rc ;
rc = statvfs ( " sdmc:/ " , & st ) ;
if ( rc ! = 0 )
{
2018-06-22 01:46:17 +01:00
uiStatusMsg ( " getSdCardFreeSpace: Unable to get SD card filesystem stats! statvfs: %d (%s). " , errno , strerror ( errno ) ) ;
2018-06-21 07:42:46 +01:00
} else {
* out = ( u64 ) ( st . f_bsize * st . f_bfree ) ;
}
return rc ;
}
# define KiB (1024.0)
# define MiB (1024.0 * KiB)
# define GiB (1024.0 * MiB)
void convertSize ( u64 size , char * out , int bufsize )
{
char buffer [ 16 ] ;
double bytes = ( double ) size ;
if ( bytes < 1000.0 )
{
snprintf ( buffer , sizeof ( buffer ) , " %.0lf B " , bytes ) ;
} else
if ( bytes < 10.0 * KiB )
{
snprintf ( buffer , sizeof ( buffer ) , " %.2lf KiB " , floor ( ( bytes * 100.0 ) / KiB ) / 100.0 ) ;
} else
if ( bytes < 100.0 * KiB )
{
snprintf ( buffer , sizeof ( buffer ) , " %.1lf KiB " , floor ( ( bytes * 10.0 ) / KiB ) / 10.0 ) ;
} else
if ( bytes < 1000.0 * KiB )
{
snprintf ( buffer , sizeof ( buffer ) , " %.0lf KiB " , floor ( bytes / KiB ) ) ;
} else
if ( bytes < 10.0 * MiB )
{
snprintf ( buffer , sizeof ( buffer ) , " %.2lf MiB " , floor ( ( bytes * 100.0 ) / MiB ) / 100.0 ) ;
} else
if ( bytes < 100.0 * MiB )
{
snprintf ( buffer , sizeof ( buffer ) , " %.1lf MiB " , floor ( ( bytes * 10.0 ) / MiB ) / 10.0 ) ;
} else
if ( bytes < 1000.0 * MiB )
{
snprintf ( buffer , sizeof ( buffer ) , " %.0lf MiB " , floor ( bytes / MiB ) ) ;
} else
if ( bytes < 10.0 * GiB )
{
snprintf ( buffer , sizeof ( buffer ) , " %.2lf GiB " , floor ( ( bytes * 100.0 ) / GiB ) / 100.0 ) ;
} else
if ( bytes < 100.0 * GiB )
{
snprintf ( buffer , sizeof ( buffer ) , " %.1lf GiB " , floor ( ( bytes * 10.0 ) / GiB ) / 10.0 ) ;
} else {
snprintf ( buffer , sizeof ( buffer ) , " %.0lf GiB " , floor ( bytes / GiB ) ) ;
}
snprintf ( out , bufsize , " %s " , buffer ) ;
}
void getCurrentTimestamp ( char * out , int bufsize )
{
2018-06-26 02:11:18 +01:00
time_t timer = time ( NULL ) ;
2018-06-21 07:42:46 +01:00
struct tm * timeinfo = localtime ( & timer ) ;
char buffer [ 32 ] = { ' \0 ' } ;
strftime ( buffer , sizeof ( buffer ) / sizeof ( buffer [ 0 ] ) , " %Y%m%d-%H%M%S " , timeinfo ) ;
snprintf ( out , bufsize , " %s " , buffer ) ;
}
void waitForButtonPress ( )
{
uiDrawString ( " Press any button to continue " , 0 , breaks * 8 , 255 , 255 , 255 ) ;
syncDisplay ( ) ;
while ( true )
{
hidScanInput ( ) ;
u32 keysDown = hidKeysDown ( CONTROLLER_P1_AUTO ) ;
if ( keysDown & & ! ( keysDown & KEY_TOUCH ) ) break ;
}
}
bool isDirectory ( char * path )
{
DIR * dir = opendir ( path ) ;
if ( ! dir ) return false ;
closedir ( dir ) ;
return true ;
}
void addString ( char * * filenames , int * filenamesCount , char * * nextFilename , const char * string )
{
filenames [ ( * filenamesCount ) + + ] = * nextFilename ;
strcpy ( * nextFilename , string ) ;
* nextFilename + = strlen ( string ) + 1 ;
}
static int sortAlpha ( const void * a , const void * b )
{
return strcasecmp ( * ( ( const char * * ) a ) , * ( ( const char * * ) b ) ) ;
}
void getDirectoryContents ( char * filenameBuffer , char * * filenames , int * filenamesCount , const char * directory , bool skipParent )
{
struct dirent * ent ;
int i , maxFilenamesCount = * filenamesCount ;
char * nextFilename = filenameBuffer ;
char * slash = ( char * ) malloc ( strlen ( directory ) + 2 ) ;
memset ( slash , 0 , strlen ( directory ) + 2 ) ;
snprintf ( slash , strlen ( directory ) + 2 , " %s/ " , directory ) ;
* filenamesCount = 0 ;
if ( ! skipParent ) addString ( filenames , filenamesCount , & nextFilename , " .. " ) ;
DIR * dir = opendir ( slash ) ;
if ( dir )
{
for ( i = 0 ; i < maxFilenamesCount ; i + + )
{
ent = readdir ( dir ) ;
if ( ! ent ) break ;
if ( ( strlen ( ent - > d_name ) = = 1 & & ! strcmp ( ent - > d_name , " . " ) ) | | ( strlen ( ent - > d_name ) = = 2 & & ! strcmp ( ent - > d_name , " .. " ) ) ) continue ;
addString ( filenames , filenamesCount , & nextFilename , ent - > d_name ) ;
}
closedir ( dir ) ;
}
free ( slash ) ;
// ".." should stay at the top
qsort ( filenames + 1 , ( * filenamesCount ) - 1 , sizeof ( char * ) , & sortAlpha ) ;
}
2018-06-26 02:11:18 +01:00
bool parseNSWDBRelease ( xmlDocPtr doc , xmlNodePtr cur , u32 crc , u32 cnt , char * releaseName , int bufsize )
{
xmlChar * key ;
xmlNodePtr node = cur ;
u8 imageSize = ( u8 ) ( gameCardSize / GAMECARD_SIZE_1GiB ) ;
u8 card = ( hfs0_partition_cnt = = GAMECARD_TYPE1_PARTITION_CNT ? 1 : 2 ) ;
u8 xmlImageSize = 0 ;
u64 xmlTitleID = 0 ;
u32 xmlCrc = 0 ;
u8 xmlCard = 0 ;
char xmlReleaseName [ 256 ] = { ' \0 ' } ;
bool found = false ;
char strbuf [ 512 ] = { ' \0 ' } ;
while ( node ! = NULL )
{
if ( ( ! xmlStrcmp ( node - > name , ( const xmlChar * ) nswReleasesChildrenImageSize ) ) )
{
key = xmlNodeListGetString ( doc , node - > xmlChildrenNode , 1 ) ;
xmlImageSize = ( u8 ) atoi ( ( const char * ) key ) ;
xmlFree ( key ) ;
} else
if ( ( ! xmlStrcmp ( node - > name , ( const xmlChar * ) nswReleasesChildrenTitleID ) ) )
{
key = xmlNodeListGetString ( doc , node - > xmlChildrenNode , 1 ) ;
xmlTitleID = strtoull ( ( const char * ) key , NULL , 16 ) ;
xmlFree ( key ) ;
} else
if ( ( ! xmlStrcmp ( node - > name , ( const xmlChar * ) nswReleasesChildrenImgCrc ) ) )
{
key = xmlNodeListGetString ( doc , node - > xmlChildrenNode , 1 ) ;
xmlCrc = strtoul ( ( const char * ) key , NULL , 16 ) ;
xmlFree ( key ) ;
} else
if ( ( ! xmlStrcmp ( node - > name , ( const xmlChar * ) nswReleasesChildrenReleaseName ) ) )
{
key = xmlNodeListGetString ( doc , node - > xmlChildrenNode , 1 ) ;
snprintf ( xmlReleaseName , sizeof ( xmlReleaseName ) / sizeof ( xmlReleaseName [ 0 ] ) , " %s " , ( char * ) key ) ;
xmlFree ( key ) ;
} else
if ( ( ! xmlStrcmp ( node - > name , ( const xmlChar * ) nswReleasesChildrenCard ) ) )
{
key = xmlNodeListGetString ( doc , node - > xmlChildrenNode , 1 ) ;
xmlCard = ( u8 ) atoi ( ( const char * ) key ) ;
xmlFree ( key ) ;
}
node = node - > next ;
}
//snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Cartridge Image Size: %u\nCartridge Title ID: %016lX\nCartridge Image CRC32: %08X\nCartridge Type: %u\n\nXML Image Size: %u\nXML Title ID: %016lX\nXML Image CRC32: %08X\nXML Release Name: %s\nXML Card Type: %u", imageSize, gameCardTitleID, crc, card, xmlImageSize, xmlTitleID, xmlCrc, xmlReleaseName, xmlCard);
//uiDrawString(strbuf, 0, 0, 255, 255, 255);
if ( xmlImageSize = = imageSize & & xmlTitleID = = gameCardTitleID & & xmlCrc = = crc & & xmlCard = = card )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Found matching Scene release: \" %s \" (CRC32: %08X). This is a good dump! " , xmlReleaseName , xmlCrc ) ;
uiDrawString ( strbuf , 0 , breaks * 8 , 0 , 255 , 0 ) ;
snprintf ( releaseName , bufsize , " %s " , xmlReleaseName ) ;
found = true ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Dump doesn't match Scene release: \" %s \" ! (CRC32: %08X) " , xmlReleaseName , xmlCrc ) ;
uiDrawString ( strbuf , 0 , breaks * 8 , 255 , 0 , 0 ) ;
}
breaks + + ;
return found ;
}
xmlXPathObjectPtr getNodeSet ( xmlDocPtr doc , xmlChar * xpath )
{
xmlXPathContextPtr context = NULL ;
xmlXPathObjectPtr result = NULL ;
context = xmlXPathNewContext ( doc ) ;
result = xmlXPathEvalExpression ( xpath , context ) ;
if ( xmlXPathNodeSetIsEmpty ( result - > nodesetval ) )
{
xmlXPathFreeObject ( result ) ;
return NULL ;
}
return result ;
}
bool gameCardDumpNSWDBCheck ( u32 crc , char * releaseName , int bufsize )
{
if ( ! gameCardTitleID | | ! hfs0_partition_cnt | | ! crc ) return false ;
xmlDocPtr doc = NULL ;
bool found = false ;
char strbuf [ 512 ] = { ' \0 ' } ;
doc = xmlParseFile ( nswReleasesXmlPath ) ;
if ( doc )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " //%s/%s[.//%s='%016lX'] " , nswReleasesRootElement , nswReleasesChildren , nswReleasesChildrenTitleID , gameCardTitleID ) ;
xmlXPathObjectPtr nodeSet = getNodeSet ( doc , ( xmlChar * ) strbuf ) ;
if ( nodeSet )
{
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Found %d %s with Title ID \" %016lX \" " , nodeSet - > nodesetval - > nodeNr , ( nodeSet - > nodesetval - > nodeNr > 1 ? " releases " : " release " ) , gameCardTitleID ) ;
uiDrawString ( strbuf , 0 , breaks * 8 , 255 , 255 , 255 ) ;
breaks + + ;
u32 i ;
for ( i = 0 ; i < nodeSet - > nodesetval - > nodeNr ; i + + )
{
xmlNodePtr node = nodeSet - > nodesetval - > nodeTab [ i ] - > xmlChildrenNode ;
found = parseNSWDBRelease ( doc , node , crc , i , releaseName , bufsize ) ;
if ( found ) break ;
}
if ( ! found )
{
uiDrawString ( " No matches found in XML document! " , 0 , breaks * 8 , 255 , 0 , 0 ) ;
} else {
breaks - - ;
}
xmlXPathFreeObject ( nodeSet ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Error: unable to find records with Title ID \" %016lX \" within the XML document! " , gameCardTitleID ) ;
uiDrawString ( strbuf , 0 , 0 , 255 , 0 , 0 ) ;
}
xmlFreeDoc ( doc ) ;
} else {
snprintf ( strbuf , sizeof ( strbuf ) / sizeof ( strbuf [ 0 ] ) , " Failed to open and/or parse \" %s \" ! " , nswReleasesXmlPath ) ;
uiDrawString ( strbuf , 0 , breaks * 8 , 255 , 255 , 255 ) ;
}
return found ;
}
char * RemoveIllegalCharacters ( char * name )
{
u32 i , len = strlen ( name ) ;
for ( i = 0 ; i < len ; i + + )
{
if ( memchr ( " ?[]/ \\ =+<>:; \" ,*|^ " , name [ i ] , sizeof ( " ?[]/ \\ =+<>:; \" ,*|^ " ) - 1 ) | | name [ i ] < 0x20 | | name [ i ] > 0x7E ) name [ i ] = ' _ ' ;
}
return name ;
}