Update to v1.0.6.

This commit is contained in:
Pablo Curiel 2019-04-23 01:14:57 -04:00
parent edd7eee294
commit d22add43bf
9 changed files with 651 additions and 769 deletions

View file

@ -4,18 +4,18 @@ Nintendo Switch Game Card Dump Tool
Main features
--------------
* Generates XCI cartridge dumps (with optional certificate removal and optional trimming).
* Generates XCI cartridge dumps with optional certificate removal and optional trimming.
* CRC32 checksum calculation for XCI dumps.
* Full XCI dump verification using XML database from nswdb.com (NSWreleases.xml).
* XML database update via libcurl.
* Precise HFS0 raw partition dumping (using the root HFS0 header from the game card).
* Full XCI dump verification using XML database from NSWDB.COM (NSWreleases.xml).
* XML database and in-app update via libcurl.
* Precise HFS0 raw partition dumping, using the root HFS0 header from the game card.
* Partition filesystem data dumping.
* Partition filesystem browser (with manual file dump support).
* Partition filesystem browser with manual file dump support.
* Manual game card certificate dump.
* Free SD card space checks in place.
* File splitting support for all operations, using 2 GiB parts.
* Game card Title ID and Control.nacp retrieval support using NCM and NS services.
* Dump speed and ETA calculation.
* Dump speed, ETA calculation and progress bar.
Thanks to
--------------
@ -31,6 +31,21 @@ Thanks to
Changelog
--------------
**v1.0.6:**
* Updated application codebase in order to make it compatible with the latest devkitA64 and libnx releases.
* Removed some fs-srv service functions from fsext.c/h that have been included in libnx (and fixed the ones that haven't).
* Revamped the GFX code to replace the 8x8 ASCII font with the shared system font, using the pl service and FreeType.
* Enabled (and fixed) the in-app update option. HTTPS compatibility is achieved through the mbedtls portlib.
* Disabled screen dimming and auto sleep.
* Added file counter to partition browser.
* Changed the naming convention for split gamecard dumps to *.xc[part number], in order to make them compatible with SX OS and other tools right away.
* Increased the delay after inserting a new gamecard by 1 second.
* Added a gamecard detection thread to monitor gamecard state changes in a better way. This thread is hooked to a gamecard detection kernel handle retrieved through an IEventNotifier object.
* Replaced partition filesystem mounting through fs-srv service calls with manual HFS0 partition header parsing. This should fix issues when browsing the Logo partition from type 0x02 gamecards.
* Blocked HOME button presses when running as a regular/system application instead of an applet. A warning message will be displayed whenever any operation is started if the application is running as an applet.
* Added detection for bundled FW versions 6.0.0 - 8.0.0.
**v1.0.5:**
* Fixed game card version reading (now using the ncm service instead of retrieving it from the cached Control.nacp).

View file

@ -13,6 +13,8 @@
#include "ui.h"
#include "util.h"
/* Extern variables */
extern u64 freeSpace;
extern int breaks;
@ -26,7 +28,8 @@ extern u64 hfs0_offset, hfs0_size;
extern u32 hfs0_partition_cnt;
extern char *partitionHfs0Header;
extern u64 partitionHfs0HeaderSize;
extern u64 partitionHfs0HeaderOffset, partitionHfs0HeaderSize;
extern u32 partitionHfs0FileCount, partitionHfs0StrTableSize;
extern u64 gameCardTitleID;
extern u32 gameCardVersion;
@ -36,6 +39,13 @@ extern u64 gameCardUpdateTitleID;
extern u32 gameCardUpdateVersion;
extern char gameCardUpdateVersionStr[128];
extern char *filenameBuffer;
extern int filenamesCount;
extern AppletType programAppletType;
/* Statically allocated variables */
static char strbuf[NAME_BUF_LEN * 2] = {'\0'};
void workaroundPartitionZeroAccess(FsDeviceOperator* fsOperator)
@ -279,181 +289,62 @@ bool getRootHfs0Header(FsDeviceOperator* fsOperator)
return true;
}
bool getHsf0PartitionDetails(u32 partition, u64 *out_offset, u64 *out_size)
bool getHfs0EntryDetails(char *hfs0Header, u64 hfs0HeaderOffset, u64 hfs0HeaderSize, u32 num_entries, u32 entry_idx, bool isRoot, u32 partitionIndex, u64 *out_offset, u64 *out_size)
{
if (hfs0_header == NULL) return false;
if (hfs0Header == NULL) return false;
if (partition > (hfs0_partition_cnt - 1)) return false;
if (entry_idx > (num_entries - 1)) return false;
hfs0_entry_table *entryTable = (hfs0_entry_table*)malloc(sizeof(hfs0_entry_table) * hfs0_partition_cnt);
hfs0_entry_table *entryTable = (hfs0_entry_table*)malloc(sizeof(hfs0_entry_table) * num_entries);
if (!entryTable) return false;
memcpy(entryTable, hfs0_header + HFS0_ENTRY_TABLE_ADDR, sizeof(hfs0_entry_table) * hfs0_partition_cnt);
memcpy(entryTable, hfs0Header + HFS0_ENTRY_TABLE_ADDR, sizeof(hfs0_entry_table) * num_entries);
switch(partition)
// 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);
switch(part_idx)
{
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)
*out_offset = (hfs0_offset + hfs0_size + entryTable[partition].file_offset);
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);
break;
case 2:
// Check if we're dealing with a type 0x01 gamecard
if (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT)
{
// secure (contained within IStorage instance with partition ID 1)
*out_offset = 0;
// 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));
} else {
// normal (contained within IStorage instance with partition ID 0)
*out_offset = (hfs0_offset + hfs0_size + entryTable[partition].file_offset);
// 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);
}
break;
case 3: // secure (gamecard type 0x02) (contained within IStorage instance with partition ID 1)
*out_offset = 0;
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));
break;
default:
break;
}
*out_size = entryTable[partition].file_size;
// Store the file size for the desired HFS0 entry
*out_size = entryTable[entry_idx].file_size;
free(entryTable);
return true;
}
bool getPartitionHfs0Header(FsDeviceOperator* fsOperator, u32 partition)
{
if (hfs0_header == NULL) return false;
if (partitionHfs0Header != NULL)
{
free(partitionHfs0Header);
partitionHfs0Header = NULL;
partitionHfs0HeaderSize = 0;
}
char *buf = NULL;
Result result;
FsGameCardHandle handle;
FsStorage gameCardStorage;
u64 partitionSize = 0, partitionOffset = 0, roundedHfs0HeaderSize = 0;
u32 hfs0FileCount = 0, hfs0StrTableSize = 0, magic = 0;
bool success = false;
if (getHsf0PartitionDetails(partition, &partitionOffset, &partitionSize))
{
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++;*/
// Same ugly hack from dumpRawPartition()
if (R_SUCCEEDED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? (partition < 2 ? 0 : 1) : (partition < 3 ? 0 : 1)))))
{
/*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, partitionOffset, 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(&hfs0FileCount, buf + HFS0_FILE_COUNT_ADDR, sizeof(u32));
memcpy(&hfs0StrTableSize, buf + HFS0_STR_TABLE_SIZE_ADDR, sizeof(u32));
partitionHfs0HeaderSize = (HFS0_ENTRY_TABLE_ADDR + (sizeof(hfs0_entry_table) * hfs0FileCount) + hfs0StrTableSize);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u file count: %u", partition, hfs0FileCount);
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, hfs0StrTableSize);
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++;
// Round up the partition HFS0 header size to a MEDIA_UNIT_SIZE bytes boundary
roundedHfs0HeaderSize = round_up(partitionHfs0HeaderSize, MEDIA_UNIT_SIZE);
partitionHfs0Header = (char*)malloc(roundedHfs0HeaderSize);
if (partitionHfs0Header)
{
// Check if we were dealing with the correct header size all along
if (roundedHfs0HeaderSize == 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, partitionOffset, partitionHfs0Header, roundedHfs0HeaderSize)))
{
success = true;
} else {
free(partitionHfs0Header);
partitionHfs0Header = NULL;
partitionHfs0HeaderSize = 0;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "StorageRead failed (0x%08X) at offset 0x%016lX", result, partitionOffset);
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 {
partitionHfs0HeaderSize = 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, partitionOffset);
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);
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result);
uiDrawString(strbuf, 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);
}
breaks += 2;
return success;
}
bool dumpGameCartridge(FsDeviceOperator* fsOperator, bool isFat32, bool dumpCert, bool trimDump, bool calcCrc)
{
u64 partitionOffset = 0, fileOffset = 0, xciDataSize = 0, totalSize = 0, n;
@ -499,7 +390,7 @@ bool dumpGameCartridge(FsDeviceOperator* fsOperator, bool isFat32, bool dumpCert
{
xciDataSize += partitionSizes[partition];
convertSize(partitionSizes[partition], partitionSizesStr[partition], sizeof(partitionSizesStr[partition]) / sizeof(partitionSizesStr[partition][0]));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u size: %s (%lu bytes)", partition, partitionSizesStr[partition], partitionSizes[partition]);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition #%u size: %s (%lu bytes).", partition, partitionSizesStr[partition], partitionSizes[partition]);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks += 2;
} else {
@ -526,7 +417,7 @@ bool dumpGameCartridge(FsDeviceOperator* fsOperator, bool isFat32, bool dumpCert
if (proceed)
{
convertSize(xciDataSize, xciDataSizeStr, sizeof(xciDataSizeStr) / sizeof(xciDataSizeStr[0]));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XCI data size: %s (%lu bytes)", xciDataSizeStr, xciDataSize);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "XCI data size: %s (%lu bytes).", xciDataSizeStr, xciDataSize);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks += 2;
@ -545,7 +436,7 @@ bool dumpGameCartridge(FsDeviceOperator* fsOperator, bool isFat32, bool dumpCert
snprintf(totalSizeStr, sizeof(totalSizeStr) / sizeof(totalSizeStr[0]), "%s", xciDataSizeStr);
}
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output dump size: %s (%lu bytes)", totalSizeStr, totalSize);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Output dump size: %s (%lu bytes).", totalSizeStr, totalSize);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks++;
@ -570,8 +461,11 @@ bool dumpGameCartridge(FsDeviceOperator* fsOperator, bool isFat32, bool dumpCert
uiDrawString(strbuf, 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;
}
timeGetCurrentTime(TimeType_LocalSystemClock, &start);
@ -885,16 +779,16 @@ bool dumpRawPartition(FsDeviceOperator* fsOperator, u32 partition, bool doSplitt
// * 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.
// Oddly enough, IFileSystem instances actually point to the specified partition ID filesystem. I don't understand why it doesn't work like that for IStorage, but whatever
// 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
// NOTE: Using partition == 2 returns error 0x149002, and using higher values probably do so, too
if (R_SUCCEEDED(result = fsOpenGameCardStorage(&gameCardStorage, &handle, (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? (partition < 2 ? 0 : 1) : (partition < 3 ? 0 : 1)))))
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.value);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks++;*/
if (getHsf0PartitionDetails(partition, &partitionOffset, &size))
if (getHfs0EntryDetails(hfs0_header, hfs0_offset, hfs0_size, hfs0_partition_cnt, partition, true, 0, &partitionOffset, &size))
{
convertSize(size, totalSizeStr, sizeof(totalSizeStr) / sizeof(totalSizeStr[0]));
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Partition size: %s (%lu bytes)", totalSizeStr, size);
@ -924,8 +818,11 @@ bool dumpRawPartition(FsDeviceOperator* fsOperator, u32 partition, bool doSplitt
uiDrawString(strbuf, 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();
@ -1099,50 +996,172 @@ bool dumpRawPartition(FsDeviceOperator* fsOperator, u32 partition, bool doSplitt
return success;
}
bool openPartitionFs(FsFileSystem* ret, FsDeviceOperator* fsOperator, u32 partition)
bool getPartitionHfs0Header(FsDeviceOperator* fsOperator, u32 partition)
{
FsGameCardHandle handle;
if (hfs0_header == NULL) return false;
if (partitionHfs0Header != NULL)
{
free(partitionHfs0Header);
partitionHfs0Header = NULL;
partitionHfs0HeaderOffset = 0;
partitionHfs0HeaderSize = 0;
partitionHfs0FileCount = 0;
partitionHfs0StrTableSize = 0;
}
char *buf = NULL;
Result result;
FsGameCardHandle handle;
FsStorage gameCardStorage;
u64 partitionSize = 0;
u32 magic = 0;
bool success = false;
if (getHfs0EntryDetails(hfs0_header, hfs0_offset, hfs0_size, hfs0_partition_cnt, partition, true, 0, &partitionHfs0HeaderOffset, &partitionSize))
{
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 = fsOpenGameCardFileSystem(ret, &handle, partition)))
// 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]), "OpenGameCardFileSystem succeeded: 0x%08X", result);
/*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 {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardFileSystem failed! (0x%08X)", result);
// 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);
breaks += 2;
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
breaks += 2;
}
} else {
uiDrawString("Error: unable to get partition details from the root HFS0 header!", 0, breaks * font_height, 255, 0, 0);
}
breaks += 2;
return success;
}
bool copyFile(const char* source, const char* dest, bool doSplitting, bool calcEta)
bool copyFileFromHfs0(FsDeviceOperator* fsOperator, u32 partition, const char* source, const char* dest, const u64 file_offset, const u64 size, bool doSplitting, bool calcEta)
{
Result result;
bool success = false;
char splitFilename[NAME_BUF_LEN] = {'\0'};
size_t destLen = strlen(dest);
FILE *inFile, *outFile;
FILE *outFile = NULL;
char *buf = NULL;
u64 size, off, n = DUMP_BUFFER_SIZE;
u64 off, n = DUMP_BUFFER_SIZE;
u8 splitIndex = 0;
u8 progress = 0;
char totalSizeStr[32] = {'\0'}, curSizeStr[32] = {'\0'};
FsGameCardHandle handle;
FsStorage gameCardStorage;
u64 start, now, remainingTime;
struct tm *timeinfo;
char etaInfo[32] = {'\0'};
@ -1152,15 +1171,22 @@ bool copyFile(const char* source, const char* dest, bool doSplitting, bool calcE
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Copying \"%s\"...", source);
uiDrawString(strbuf, 0, (breaks + 1) * font_height, 255, 255, 255);
uiRefreshDisplay();
if ((destLen + 1) < NAME_BUF_LEN)
{
inFile = fopen(source, "rb");
if (inFile)
if (R_SUCCEEDED(result = fsDeviceOperatorGetGameCardHandle(fsOperator, &handle)))
{
fseek(inFile, 0L, SEEK_END);
size = ftell(inFile);
rewind(inFile);
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle succeeded: 0x%08X", handle.value);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks++;*/
// 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.value);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks++;*/
convertSize(size, totalSizeStr, sizeof(totalSizeStr) / sizeof(totalSizeStr[0]));
@ -1178,9 +1204,9 @@ bool copyFile(const char* source, const char* dest, bool doSplitting, bool calcE
{
if (DUMP_BUFFER_SIZE > (size - off)) n = (size - off);
if (fread(buf, 1, n, inFile) != n)
if (R_FAILED(result = fsStorageRead(&gameCardStorage, file_offset + off, buf, n)))
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to read chunk from offset 0x%016lX", off);
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;
}
@ -1329,208 +1355,97 @@ bool copyFile(const char* source, const char* dest, bool doSplitting, bool calcE
uiDrawString("Failed to open output file!", 0, breaks * font_height, 255, 255, 255);
}
fclose(inFile);
fsStorageClose(&gameCardStorage);
} else {
breaks += 3;
uiDrawString("Failed to open input file!", 0, breaks * font_height, 255, 255, 255);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "OpenGameCardStorage failed! (0x%08X)", result);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
}
} else {
breaks += 3;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Destination path is too long! \"%s\" (%lu)", dest, destLen);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "GetGameCardHandle failed! (0x%08X)", result);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
}
} else {
breaks += 3;
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Destination path is too long! (%lu bytes)", destLen);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
}
return success;
}
bool _copyDirectory(char* sbuf, size_t source_len, char* dbuf, size_t dest_len, bool splitting)
bool copyHfs0Contents(FsDeviceOperator* fsOperator, u32 partition, hfs0_entry_table *partitionEntryTable, const char *dest, bool splitting)
{
struct dirent* ent;
bool success = true;
DIR *dir = opendir(sbuf);
if (dir)
if (!dest || !*dest)
{
sbuf[source_len] = '/';
dbuf[dest_len] = '/';
while ((ent = readdir(dir)) != NULL)
{
if ((strlen(ent->d_name) == 1 && !strcmp(ent->d_name, ".")) || (strlen(ent->d_name) == 2 && !strcmp(ent->d_name, ".."))) continue;
size_t d_name_len = strlen(ent->d_name);
if ((source_len + 1 + d_name_len + 1) >= NAME_BUF_LEN || (dest_len + 1 + d_name_len + 1) >= NAME_BUF_LEN)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Filename too long! \"%s\" (%lu)", ent->d_name, d_name_len);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
breaks++;
success = false;
break;
}
strcpy(sbuf + source_len + 1, ent->d_name);
strcpy(dbuf + dest_len + 1, ent->d_name);
if (ent->d_type == DT_DIR)
{
mkdir(dbuf, 0744);
if (!_copyDirectory(sbuf, source_len + 1 + d_name_len, dbuf, dest_len + 1 + d_name_len, splitting))
{
success = false;
break;
}
} else {
if (!copyFile(sbuf, dbuf, splitting, false))
{
success = false;
break;
}
}
}
closedir(dir);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error opening directory \"%s\"", dbuf);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
breaks++;
success = false;
}
return success;
}
bool copyDirectory(const char* source, const char* dest, bool splitting)
{
char sbuf[NAME_BUF_LEN], dbuf[NAME_BUF_LEN];
size_t source_len = strlen(source), dest_len = strlen(dest);
if (source_len + 1 >= NAME_BUF_LEN)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Source directory name too long! \"%s\" (%lu)", source, source_len);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
breaks++;
uiDrawString("Error: destination directory is empty.", 0, breaks * font_height, 255, 0, 0);
return false;
}
if (dest_len + 1 >= NAME_BUF_LEN)
if (!partitionHfs0Header || !partitionEntryTable)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Destination directory name too long! \"%s\" (%lu)", dest, dest_len);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
breaks++;
uiDrawString("HFS0 partition header information unavailable!", 0, breaks * font_height, 255, 0, 0);
return false;
}
char dbuf[NAME_BUF_LEN] = {'\0'};
size_t dest_len = strlen(dest);
if ((dest_len + 1) >= NAME_BUF_LEN)
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Destination directory name is too long! (%lu bytes)", dest_len);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
return false;
}
strcpy(sbuf, source);
strcpy(dbuf, dest);
mkdir(dbuf, 0744);
return _copyDirectory(sbuf, source_len, dbuf, dest_len, splitting);
}
dbuf[dest_len] = '/';
dest_len++;
void removeDirectory(const char *path)
u32 i;
bool success;
for(i = 0; i < partitionHfs0FileCount; i++)
{
struct dirent* ent;
char cur_path[NAME_BUF_LEN] = {'\0'};
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);
DIR *dir = opendir(path);
if (dir)
{
while ((ent = readdir(dir)) != NULL)
{
if ((strlen(ent->d_name) == 1 && !strcmp(ent->d_name, ".")) || (strlen(ent->d_name) == 2 && !strcmp(ent->d_name, ".."))) continue;
u64 file_offset = (partitionHfs0HeaderSize + partitionEntryTable[i].file_offset);
if (HFS0_TO_ISTORAGE_IDX(hfs0_partition_cnt, partition) == 0) file_offset += partitionHfs0HeaderOffset;
snprintf(cur_path, sizeof(cur_path) / sizeof(cur_path[0]), "%s/%s", path, ent->d_name);
if (ent->d_type == DT_DIR)
{
removeDirectory(cur_path);
} else {
remove(cur_path);
success = copyFileFromHfs0(fsOperator, partition, filename, dbuf, file_offset, partitionEntryTable[i].file_size, splitting, false);
if (!success) break;
}
}
closedir(dir);
rmdir(path);
}
}
bool getDirectorySize(const char *path, u64 *out_size)
{
struct dirent* ent;
char cur_path[NAME_BUF_LEN] = {'\0'};
bool success = true;
u64 total_size = 0, dir_size = 0;
FILE *file = NULL;
DIR *dir = opendir(path);
if (dir)
{
while ((ent = readdir(dir)) != NULL)
{
if ((strlen(ent->d_name) == 1 && !strcmp(ent->d_name, ".")) || (strlen(ent->d_name) == 2 && !strcmp(ent->d_name, ".."))) continue;
snprintf(cur_path, sizeof(cur_path) / sizeof(cur_path[0]), "%s/%s", path, ent->d_name);
if (ent->d_type == DT_DIR)
{
if (getDirectorySize(cur_path, &dir_size))
{
total_size += dir_size;
dir_size = 0;
} else {
success = false;
break;
}
} else {
file = fopen(cur_path, "rb");
if (file)
{
fseek(file, 0L, SEEK_END);
total_size += ftell(file);
fclose(file);
file = NULL;
} else {
success = false;
break;
}
}
}
closedir(dir);
} else {
success = false;
}
if (success) *out_size = total_size;
return success;
}
bool dumpPartitionData(FsDeviceOperator* fsOperator, u32 partition)
{
FsFileSystem fs;
int ret;
bool success = false;
u64 total_size;
u64 total_size = 0;
u32 i;
hfs0_entry_table *entryTable = NULL;
char dumpPath[NAME_BUF_LEN] = {'\0'}, totalSizeStr[32] = {'\0'};
workaroundPartitionZeroAccess(fsOperator);
if (openPartitionFs(&fs, fsOperator, partition))
if (getPartitionHfs0Header(fsOperator, partition))
{
ret = fsdevMountDevice("gamecard", fs);
if (ret != -1)
if (partitionHfs0FileCount)
{
/*snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "fsdevMountDevice succeeded: %d", ret);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks++;*/
entryTable = (hfs0_entry_table*)malloc(sizeof(hfs0_entry_table) * partitionHfs0FileCount);
if (entryTable)
{
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;
if (getDirectorySize("gamecard:/", &total_size))
{
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);
@ -1544,12 +1459,17 @@ bool dumpPartitionData(FsDeviceOperator* fsOperator, u32 partition)
uiDrawString(strbuf, 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;
}
if (copyDirectory("gamecard:/", dumpPath, true))
uiRefreshDisplay();
success = copyHfs0Contents(fsOperator, partition, entryTable, dumpPath, true);
if (success)
{
success = true;
breaks += 5;
uiDrawString("Process successfully completed!", 0, breaks * font_height, 0, 255, 0);
} else {
@ -1558,21 +1478,20 @@ bool dumpPartitionData(FsDeviceOperator* fsOperator, u32 partition)
} else {
uiDrawString("Error: not enough free space available in the SD card.", 0, breaks * font_height, 255, 0, 0);
}
free(entryTable);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to get total partition data size!");
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
uiDrawString("Unable to allocate memory for the HFS0 file entries!", 0, breaks * font_height, 255, 0, 0);
}
} else {
uiDrawString("The selected partition is empty!", 0, breaks * font_height, 255, 0, 0);
}
fsdevUnmountDevice("gamecard");
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "fsdevMountDevice failed! (%d)", ret);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
}
fsFsClose(&fs);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open partition #%u filesystem!", partition);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
free(partitionHfs0Header);
partitionHfs0Header = NULL;
partitionHfs0HeaderSize = 0;
partitionHfs0FileCount = 0;
partitionHfs0StrTableSize = 0;
}
breaks += 2;
@ -1580,32 +1499,110 @@ bool dumpPartitionData(FsDeviceOperator* fsOperator, u32 partition)
return success;
}
bool mountViewPartition(FsDeviceOperator *fsOperator, FsFileSystem *out, u32 partition)
bool getHfs0FileList(FsDeviceOperator* fsOperator, u32 partition)
{
int ret;
if (!getPartitionHfs0Header(fsOperator, partition)) return false;
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);
}
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)
{
uiDrawString("Filename unavailable!", 0, breaks * font_height, 255, 0, 0);
return false;
}
u64 file_offset = 0;
u64 file_size = 0;
bool success = false;
workaroundPartitionZeroAccess(fsOperator);
if (openPartitionFs(out, fsOperator, partition))
if (getHfs0EntryDetails(partitionHfs0Header, partitionHfs0HeaderOffset, partitionHfs0HeaderSize, partitionHfs0FileCount, file, false, partition, &file_offset, &file_size))
{
ret = fsdevMountDevice("view", *out);
if (ret != -1)
if (file_size <= freeSpace)
{
//snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "fsdevMountDevice succeeded: %d", ret);
//uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
//breaks++;
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));
success = true;
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 {
fsFsClose(out);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "fsdevMountDevice failed! (%d)", ret);
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);
}
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open partition #%u filesystem!", partition);
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: not enough free space available in the SD card (%lu bytes required).", file_size);
uiDrawString(strbuf, 0, breaks * font_height, 255, 0, 0);
}
} else {
uiDrawString("Error: unable to get file details from the partition HFS0 header!", 0, breaks * font_height, 255, 0, 0);
}
return success;
}
@ -1649,7 +1646,13 @@ bool dumpGameCertificate(FsDeviceOperator* fsOperator)
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Dumping game card certificate to file \"%s\"...", filename);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks++;
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();

View file

@ -33,6 +33,8 @@
#define GAMECARD_TYPE2_PART_NAMES(x) ((x) == 0 ? "Update" : ((x) == 1 ? "Logo" : ((x) == 2 ? "Normal" : ((x) == 3 ? "Secure" : "Unknown"))))
#define GAMECARD_PARTITION_NAME(x, y) ((x) == GAMECARD_TYPE1_PARTITION_CNT ? GAMECARD_TYPE1_PART_NAMES(y) : ((x) == GAMECARD_TYPE2_PARTITION_CNT ? GAMECARD_TYPE2_PART_NAMES(y) : "Unknown"))
#define HFS0_TO_ISTORAGE_IDX(x, y) ((x) == GAMECARD_TYPE1_PARTITION_CNT ? ((y) < 2 ? 0 : 1) : ((y) < 3 ? 0 : 1))
#define GAMECARD_SIZE_1GiB (u64)0x40000000
#define GAMECARD_SIZE_2GiB (u64)0x80000000
#define GAMECARD_SIZE_4GiB (u64)0x100000000
@ -81,18 +83,12 @@ typedef struct
u8 hashed_region_sha256[0x20];
} PACKED hfs0_entry_table;
void workaroundPartitionZeroAccess(FsDeviceOperator* fsOperator);
bool getRootHfs0Header(FsDeviceOperator* fsOperator);
bool getHsf0PartitionDetails(u32 partition, u64 *out_offset, u64 *out_size);
bool dumpGameCartridge(FsDeviceOperator* fsOperator, bool isFat32, bool dumpCert, bool trimDump, bool calcCrc);
bool dumpRawPartition(FsDeviceOperator* fsOperator, u32 partition, bool doSplitting);
bool openPartitionFs(FsFileSystem* ret, FsDeviceOperator* fsOperator, u32 partition);
bool copyFile(const char* source, const char* dest, bool doSplitting, bool calcEta);
bool copyDirectory(const char* source, const char* dest, bool doSplitting);
void removeDirectory(const char *path);
bool getDirectorySize(const char *path, u64 *out_size);
bool dumpPartitionData(FsDeviceOperator* fsOperator, u32 partition);
bool mountViewPartition(FsDeviceOperator *fsOperator, FsFileSystem *out, u32 partition);
bool getHfs0FileList(FsDeviceOperator* fsOperator, u32 partition);
bool dumpFileFromPartition(FsDeviceOperator* fsOperator, u32 partition, u32 file, char *filename);
bool dumpGameCertificate(FsDeviceOperator *fsOperator);
#endif

View file

@ -46,47 +46,6 @@ Result fsOpenGameCardStorage(FsStorage* out, const FsGameCardHandle* handle, u32
return rc;
}
Result fsOpenGameCardFileSystem(FsFileSystem* out, const FsGameCardHandle* handle, u32 partition)
{
IpcCommand c;
ipcInitialize(&c);
struct {
u64 magic;
u64 cmd_id;
u32 handle;
u32 partition;
} *raw;
raw = serviceIpcPrepareHeader(fsGetServiceSession(), &c, sizeof(*raw));
raw->magic = SFCI_MAGIC;
raw->cmd_id = 31;
raw->handle = handle->value;
raw->partition = partition;
Result rc = serviceIpcDispatch(fsGetServiceSession());
if (R_SUCCEEDED(rc))
{
IpcParsedCommand r;
struct {
u64 magic;
u64 result;
} *resp;
serviceIpcParse(fsGetServiceSession(), &r, sizeof(*resp));
resp = r.Raw;
rc = resp->result;
if (R_SUCCEEDED(rc)) serviceCreateSubservice(&out->s, fsGetServiceSession(), &r, 0);
}
return rc;
}
Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier* out)
{
IpcCommand c;

View file

@ -8,7 +8,6 @@
// IFileSystemProxy
Result fsOpenGameCardStorage(FsStorage* out, const FsGameCardHandle* handle, u32 partition);
Result fsOpenGameCardFileSystem(FsFileSystem* out, const FsGameCardHandle* handle, u32 partition);
Result fsOpenGameCardDetectionEventNotifier(FsEventNotifier* out);
// IDeviceOperator

View file

@ -57,7 +57,7 @@ int main(int argc, char *argv[])
result = fsOpenGameCardDetectionEventNotifier(&fsGameCardEventNotifier);
if (R_SUCCEEDED(result))
{
/* Retrieve kernel event handle */
/* Retrieve gamecard detection event handle */
result = fsEventNotifierGetEventHandle(&fsGameCardEventNotifier, &fsGameCardEventHandle);
if (R_SUCCEEDED(result))
{
@ -161,7 +161,7 @@ int main(int argc, char *argv[])
ret = -9;
}
/* Close kernel event */
/* Close gamecard detection kernel event */
eventClose(&fsGameCardKernelEvent);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to retrieve gamecard detection event handle! (0x%08X)", result);
@ -171,7 +171,7 @@ int main(int argc, char *argv[])
ret = -8;
}
/* Close gamecard event notifier */
/* Close gamecard detection event notifier */
fsEventNotifierClose(&fsGameCardEventNotifier);
} else {
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Failed to open gamecard detection event notifier! (0x%08X)", result);

View file

@ -19,6 +19,8 @@
extern FsDeviceOperator fsOperatorInstance;
extern AppletType programAppletType;
extern bool gameCardInserted;
extern char gameCardSizeStr[32], trimmedCardSizeStr[32];
@ -27,14 +29,16 @@ extern char *hfs0_header;
extern u64 hfs0_offset, hfs0_size;
extern u32 hfs0_partition_cnt;
extern char *partitionHfs0Header;
extern u64 partitionHfs0HeaderOffset, partitionHfs0HeaderSize;
extern u32 partitionHfs0FileCount, partitionHfs0StrTableSize;
extern u64 gameCardTitleID;
extern u32 gameCardVersion;
extern char gameCardName[0x201], fixedGameCardName[0x201], gameCardAuthor[0x101], gameCardVersionStr[64];
extern char gameCardUpdateVersionStr[128];
extern char currentDirectory[NAME_BUF_LEN];
extern char *filenameBuffer;
extern char *filenames[FILENAME_MAX_CNT];
extern int filenamesCount;
@ -54,19 +58,16 @@ int scroll = 0;
int breaks = 0;
int font_height = 0;
static u32 selectedPartitionIndex;
static u32 selectedFileIndex;
static bool highlight = false;
static bool isFat32 = false, dumpCert = false, trimDump = false, calcCrc = true;
static u32 selectedOption;
static FsFileSystem fs;
static char statusMessage[2048] = {'\0'};
static int statusMessageFadeout = 0;
static char fileCopyPath[NAME_BUF_LEN * 2] = {'\0'};
static int headlineCnt = 0;
u64 freeSpace = 0;
@ -272,19 +273,18 @@ void uiUpdateStatusMsg()
{
if (!strlen(statusMessage) || !statusMessageFadeout) return;
uiFill(0, FB_HEIGHT - (font_height * 2), FB_WIDTH, font_height * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB);
if ((statusMessageFadeout - 4) > BG_COLOR_RGB)
{
uiFill(0, FB_HEIGHT - (font_height * 2), FB_WIDTH, font_height * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB);
int fadeout = (statusMessageFadeout > 255 ? 255 : statusMessageFadeout);
uiDrawString(statusMessage, 0, FB_HEIGHT - (font_height * 2), fadeout, fadeout, fadeout);
uiRefreshDisplay();
statusMessageFadeout -= 4;
} else {
uiFill(0, FB_HEIGHT - (font_height * 2), FB_WIDTH, font_height * 2, BG_COLOR_RGB, BG_COLOR_RGB, BG_COLOR_RGB);
statusMessageFadeout = 0;
}
uiRefreshDisplay();
}
void uiPleaseWait()
@ -411,6 +411,12 @@ int uiInit()
/* Disable screen dimming and auto sleep */
appletSetMediaPlaybackState(true);
/* Get applet type */
programAppletType = appletGetAppletType();
/* Block HOME menu button presses if we're running as a regular application or a system application */
if (programAppletType == AppletType_Application || programAppletType == AppletType_SystemApplication) appletBeginBlockingHomeButton(0);
/* Clear screen */
uiClearScreen();
@ -419,6 +425,9 @@ int uiInit()
void uiDeinit()
{
/* Unblock HOME menu button presses if we're running as a regular application or a system application */
if (programAppletType == AppletType_Application || programAppletType == AppletType_SystemApplication) appletEndBlockingHomeButton();
/* Enable screen dimming and auto sleep */
appletSetMediaPlaybackState(false);
@ -459,6 +468,7 @@ UIResult uiProcess()
int menuItemsCount = 0;
u32 keysDown;
u32 keysHeld;
uiPrintHeadline();
loadGameCardInfo();
@ -588,11 +598,7 @@ UIResult uiProcess()
menu = (const char**)filenames;
menuItemsCount = filenamesCount;
uiDrawString((hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? viewGameCardFsType1MenuItems[selectedOption] : viewGameCardFsType2MenuItems[selectedOption]), 0, breaks * font_height, 115, 115, 255);
breaks += 2;
snprintf(titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]), "Current directory: %s", currentDirectory);
uiDrawString(titlebuf, 0, breaks * font_height, 255, 255, 255);
uiDrawString((hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? viewGameCardFsType1MenuItems[selectedPartitionIndex] : viewGameCardFsType2MenuItems[selectedPartitionIndex]), 0, breaks * font_height, 115, 115, 255);
breaks += 2;
snprintf(titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]), "File count: %d | Current file: %d", menuItemsCount, cursor + 1);
@ -677,18 +683,10 @@ UIResult uiProcess()
hidScanInput();
keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
keysHeld = hidKeysHeld(CONTROLLER_P1_AUTO);
// Exit
if (keysDown & KEY_PLUS)
{
if (uiState == stateViewGameCardFsBrowser)
{
fsdevUnmountDevice("view");
fsFsClose(&fs);
}
res = resultExit;
}
if (keysDown & KEY_PLUS) res = resultExit;
// Process key inputs only if the UI state hasn't been changed
if (res == resultNone)
@ -698,11 +696,7 @@ UIResult uiProcess()
if (uiState == stateXciDumpMenu)
{
// Select
if ((keysDown & KEY_A) && cursor == 0)
{
selectedOption = (u32)cursor;
res = resultDumpXci;
}
if ((keysDown & KEY_A) && cursor == 0) res = resultDumpXci;
// Back
if (keysDown & KEY_B) res = resultShowMainMenu;
@ -752,10 +746,10 @@ UIResult uiProcess()
}
// Go up
if (keysDown & KEY_UP) scrollAmount = -1;
if ((keysDown & KEY_DUP) || (keysHeld & KEY_LSTICK_UP) || (keysHeld & KEY_RSTICK_UP)) scrollAmount = -1;
// Go down
if (keysDown & KEY_DOWN) scrollAmount = 1;
if ((keysDown & KEY_DDOWN) || (keysHeld & KEY_LSTICK_DOWN) || (keysHeld & KEY_RSTICK_DOWN)) scrollAmount = 1;
} else {
// Select
if (keysDown & KEY_A)
@ -791,49 +785,28 @@ UIResult uiProcess()
} else
if (uiState == stateRawPartitionDumpMenu)
{
selectedOption = (u32)cursor;
// Save selected partition index
selectedPartitionIndex = (u32)cursor;
res = resultDumpRawPartition;
} else
if (uiState == statePartitionDataDumpMenu)
{
selectedOption = (u32)cursor;
// Save selected partition index
selectedPartitionIndex = (u32)cursor;
res = resultDumpPartitionData;
} else
if (uiState == stateViewGameCardFsMenu)
{
selectedOption = (u32)cursor;
// Save selected partition index
selectedPartitionIndex = (u32)cursor;
res = resultShowViewGameCardFsGetList;
} else
if (uiState == stateViewGameCardFsBrowser)
{
char *selectedPath = (char*)malloc(strlen(currentDirectory) + 1 + strlen(filenames[cursor]) + 2);
memset(selectedPath, 0, strlen(currentDirectory) + 1 + strlen(filenames[cursor]) + 2);
if (strlen(filenames[cursor]) == 2 && !strcmp(filenames[cursor], ".."))
{
for(i = (strlen(currentDirectory) - 1); i >= 0; i--)
{
if (currentDirectory[i] == '/')
{
strncpy(selectedPath, currentDirectory, i);
selectedPath[i] = '\0';
break;
}
}
} else {
snprintf(selectedPath, strlen(currentDirectory) + 1 + strlen(filenames[cursor]) + 2, "%s/%s", currentDirectory, filenames[cursor]);
}
if (isDirectory(selectedPath))
{
enterDirectory(selectedPath);
} else {
snprintf(fileCopyPath, sizeof(fileCopyPath) / sizeof(fileCopyPath[0]), "%s", selectedPath);
// Save selected file index
selectedFileIndex = (u32)cursor;
res = resultViewGameCardFsBrowserCopyFile;
}
free(selectedPath);
}
}
// Back
@ -845,40 +818,24 @@ UIResult uiProcess()
} else
if (uiState == stateViewGameCardFsBrowser)
{
if (!strcmp(currentDirectory, "view:/") && strlen(currentDirectory) == 6)
{
fsdevUnmountDevice("view");
fsFsClose(&fs);
free(partitionHfs0Header);
partitionHfs0Header = NULL;
partitionHfs0HeaderOffset = 0;
partitionHfs0HeaderSize = 0;
partitionHfs0FileCount = 0;
partitionHfs0StrTableSize = 0;
res = resultShowViewGameCardFsMenu;
} else {
char *selectedPath = (char*)malloc(strlen(currentDirectory) + 1);
memset(selectedPath, 0, strlen(currentDirectory) + 1);
for(i = (strlen(currentDirectory) - 1); i >= 0; i--)
{
if (currentDirectory[i] == '/')
{
strncpy(selectedPath, currentDirectory, i);
selectedPath[i] = '\0';
break;
}
}
if (isDirectory(selectedPath)) enterDirectory(selectedPath);
free(selectedPath);
}
}
}
// Go up
if (keysDown & KEY_UP) scrollAmount = -1;
if (keysDown & KEY_LEFT) scrollAmount = -5;
if ((keysDown & KEY_DUP) || (keysHeld & KEY_LSTICK_UP) || (keysHeld & KEY_RSTICK_UP)) scrollAmount = -1;
if ((keysDown & KEY_DLEFT) || (keysHeld & KEY_LSTICK_LEFT) || (keysHeld & KEY_RSTICK_LEFT)) scrollAmount = -5;
// Go down
if (keysDown & KEY_DOWN) scrollAmount = 1;
if (keysDown & KEY_RIGHT) scrollAmount = 5;
if ((keysDown & KEY_DDOWN) || (keysHeld & KEY_LSTICK_DOWN) || (keysHeld & KEY_RSTICK_DOWN)) scrollAmount = 1;
if ((keysDown & KEY_DRIGHT) || (keysHeld & KEY_LSTICK_RIGHT) || (keysHeld & KEY_RSTICK_RIGHT)) scrollAmount = 5;
}
// Calculate scroll only if the UI state hasn't been changed
@ -930,6 +887,8 @@ UIResult uiProcess()
uiDrawString(titlebuf, 0, breaks * font_height, 115, 115, 255);
breaks += 2;
uiRefreshDisplay();
dumpGameCartridge(&fsOperatorInstance, isFat32, dumpCert, trimDump, calcCrc);
waitForButtonPress();
@ -939,11 +898,13 @@ UIResult uiProcess()
} else
if (uiState == stateDumpRawPartition)
{
snprintf(titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]), "Raw %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? partitionDumpType1MenuItems[selectedOption] : partitionDumpType2MenuItems[selectedOption]));
snprintf(titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]), "Raw %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? partitionDumpType1MenuItems[selectedPartitionIndex] : partitionDumpType2MenuItems[selectedPartitionIndex]));
uiDrawString(titlebuf, 0, breaks * font_height, 115, 115, 255);
breaks += 2;
dumpRawPartition(&fsOperatorInstance, selectedOption, true);
uiRefreshDisplay();
dumpRawPartition(&fsOperatorInstance, selectedPartitionIndex, true);
waitForButtonPress();
@ -952,11 +913,13 @@ UIResult uiProcess()
} else
if (uiState == stateDumpPartitionData)
{
snprintf(titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]), "Data %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? partitionDumpType1MenuItems[selectedOption] : partitionDumpType2MenuItems[selectedOption]));
snprintf(titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]), "Data %s", (hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? partitionDumpType1MenuItems[selectedPartitionIndex] : partitionDumpType2MenuItems[selectedPartitionIndex]));
uiDrawString(titlebuf, 0, breaks * font_height, 115, 115, 255);
breaks += 2;
dumpPartitionData(&fsOperatorInstance, selectedOption);
uiRefreshDisplay();
dumpPartitionData(&fsOperatorInstance, selectedPartitionIndex);
waitForButtonPress();
@ -965,13 +928,15 @@ UIResult uiProcess()
} else
if (uiState == stateViewGameCardFsGetList)
{
uiDrawString((hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? viewGameCardFsType1MenuItems[selectedOption] : viewGameCardFsType1MenuItems[selectedOption]), 0, breaks * font_height, 115, 115, 255);
uiDrawString((hfs0_partition_cnt == GAMECARD_TYPE1_PARTITION_CNT ? viewGameCardFsType1MenuItems[selectedPartitionIndex] : viewGameCardFsType2MenuItems[selectedPartitionIndex]), 0, breaks * font_height, 115, 115, 255);
breaks += 2;
if (mountViewPartition(&fsOperatorInstance, &fs, selectedOption))
{
enterDirectory("view:/");
uiRefreshDisplay();
if (getHfs0FileList(&fsOperatorInstance, selectedPartitionIndex))
{
cursor = 0;
scroll = 0;
res = resultShowViewGameCardFsBrowser;
} else {
breaks += 2;
@ -981,45 +946,13 @@ UIResult uiProcess()
} else
if (uiState == stateViewGameCardFsBrowserCopyFile)
{
uiDrawString("Manual File Dump", 0, breaks * font_height, 115, 115, 255);
snprintf(titlebuf, sizeof(titlebuf) / sizeof(titlebuf[0]), "Manual File Dump: %s (Partition %u [%s])", filenames[selectedFileIndex], selectedPartitionIndex, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, selectedPartitionIndex));
uiDrawString(titlebuf, 0, breaks * font_height, 115, 115, 255);
breaks += 2;
FILE *inFile = fopen(fileCopyPath, "rb");
if (inFile)
{
fseek(inFile, 0L, SEEK_END);
u64 input_filesize = ftell(inFile);
fclose(inFile);
uiRefreshDisplay();
if (input_filesize <= freeSpace)
{
char destCopyPath[NAME_BUF_LEN] = {'\0'};
for(i = (strlen(fileCopyPath) - 1); i >= 0; i--)
{
if (fileCopyPath[i] == '/')
{
snprintf(destCopyPath, sizeof(destCopyPath) / sizeof(destCopyPath[0]), "sdmc:/%s v%u (%016lX) - Partition %u (%s)", fixedGameCardName, gameCardVersion, gameCardTitleID, selectedOption, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, selectedOption));
mkdir(destCopyPath, 0744);
snprintf(destCopyPath, sizeof(destCopyPath) / sizeof(destCopyPath[0]), "sdmc:/%s v%u (%016lX) - Partition %u (%s)/%.*s", fixedGameCardName, gameCardVersion, gameCardTitleID, selectedOption, GAMECARD_PARTITION_NAME(hfs0_partition_cnt, selectedOption), (int)(strlen(fileCopyPath) - i), fileCopyPath + i + 1);
break;
}
}
uiDrawString("Hold B to cancel.", 0, breaks * font_height, 255, 255, 255);
breaks += 2;
uiDrawString("Do not press the HOME button. Doing so could corrupt the SD card filesystem.", 0, breaks * font_height, 255, 0, 0);
breaks += 2;
copyFile(fileCopyPath, destCopyPath, true, true);
} 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 input file size.", 0, breaks * font_height, 255, 0, 0);
}
dumpFileFromPartition(&fsOperatorInstance, selectedPartitionIndex, selectedFileIndex, filenames[selectedFileIndex]);
breaks += 2;

View file

@ -4,6 +4,7 @@
#include <errno.h>
#include <math.h>
#include <dirent.h>
#include <unistd.h>
#include <ctype.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
@ -53,8 +54,6 @@ static char *result_buf = NULL;
static size_t result_sz = 0;
static size_t result_written = 0;
char currentDirectory[NAME_BUF_LEN] = {'\0'};
char *filenameBuffer = NULL;
char *filenames[FILENAME_MAX_CNT];
int filenamesCount = 0;
@ -65,6 +64,8 @@ Handle fsGameCardEventHandle;
Event fsGameCardKernelEvent;
UEvent exitEvent;
AppletType programAppletType;
bool gameCardInserted;
u64 gameCardSize = 0, trimmedCardSize = 0;
@ -75,7 +76,8 @@ u64 hfs0_offset = 0, hfs0_size = 0;
u32 hfs0_partition_cnt = 0;
char *partitionHfs0Header = NULL;
u64 partitionHfs0HeaderSize = 0;
u64 partitionHfs0HeaderOffset = 0, partitionHfs0HeaderSize = 0;
u32 partitionHfs0FileCount = 0, partitionHfs0StrTableSize = 0;
u64 gameCardTitleID = 0;
u32 gameCardVersion = 0;
@ -287,7 +289,10 @@ void loadGameCardInfo()
{
free(partitionHfs0Header);
partitionHfs0Header = NULL;
partitionHfs0HeaderOffset = 0;
partitionHfs0HeaderSize = 0;
partitionHfs0FileCount = 0;
partitionHfs0StrTableSize = 0;
}
gameCardTitleID = 0;
@ -379,76 +384,44 @@ void waitForButtonPress()
{
hidScanInput();
u32 keysDown = hidKeysDown(CONTROLLER_P1_AUTO);
if (keysDown && !(keysDown & KEY_TOUCH)) break;
if (keysDown && !((keysDown & KEY_TOUCH) || (keysDown & KEY_LSTICK_LEFT) || (keysDown & KEY_LSTICK_RIGHT) || (keysDown & KEY_LSTICK_UP) || (keysDown & KEY_LSTICK_DOWN) || \
(keysDown & KEY_RSTICK_LEFT) || (keysDown & KEY_RSTICK_RIGHT) || (keysDown & KEY_RSTICK_UP) || (keysDown & KEY_RSTICK_DOWN))) break;
}
}
bool isDirectory(char *path)
void addStringToFilenameBuffer(const char *string, char **nextFilename)
{
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;
filenames[filenamesCount++] = *nextFilename;
strcpy(*nextFilename, string);
*nextFilename += strlen(string) + 1;
*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)
void removeDirectory(const char *path)
{
struct dirent* ent;
int i, maxFilenamesCount = *filenamesCount;
char *nextFilename = filenameBuffer;
char cur_path[NAME_BUF_LEN] = {'\0'};
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);
DIR *dir = opendir(path);
if (dir)
{
for(i = 0; i < maxFilenamesCount; i++)
while ((ent = readdir(dir)) != NULL)
{
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);
snprintf(cur_path, sizeof(cur_path) / sizeof(cur_path[0]), "%s/%s", path, ent->d_name);
if (ent->d_type == DT_DIR)
{
removeDirectory(cur_path);
} else {
remove(cur_path);
}
}
closedir(dir);
rmdir(path);
}
free(slash);
// ".." should stay at the top
qsort(filenames + 1, (*filenamesCount) - 1, sizeof(char*), &sortAlpha);
}
void enterDirectory(const char *path)
{
snprintf(currentDirectory, sizeof(currentDirectory) / sizeof(currentDirectory[0]), "%s", path);
filenamesCount = FILENAME_MAX_CNT;
getDirectoryContents(filenameBuffer, &filenames[0], &filenamesCount, currentDirectory, (!strcmp(currentDirectory, "view:/") && strlen(currentDirectory) == 6));
cursor = 0;
scroll = 0;
}
bool parseNSWDBRelease(xmlDocPtr doc, xmlNodePtr cur, u32 crc)
@ -672,7 +645,13 @@ void updateNSWDBXml()
{
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Downloading XML database from \"%s\", please wait...", nswReleasesXmlUrl);
uiDrawString(strbuf, 0, breaks * font_height, 255, 255, 255);
breaks++;
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();
@ -930,6 +909,12 @@ void updateApplication()
uiDrawString("Please wait...", 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();
gcDumpToolNro = fopen(gcDumpToolTmpPath, "wb");

View file

@ -26,12 +26,8 @@ void fsGameCardDetectionThreadFunc(void *arg);
void delay(u8 seconds);
bool getGameCardTitleIDAndVersion(u64 *titleID, u32 *version);
void convertTitleVersionToDecimal(u32 version, char *versionBuf, int versionBufSize);
bool getGameCardControlNacp(u64 titleID, char *nameBuf, int nameBufSize, char *authorBuf, int authorBufSize);
void removeIllegalCharacters(char *name);
void strtrim(char *str);
@ -44,13 +40,9 @@ void convertSize(u64 size, char *out, int bufsize);
void waitForButtonPress();
bool isDirectory(char *path);
void addStringToFilenameBuffer(const char *string, char **nextFilename);
void addString(char **filenames, int *filenamesCount, char **nextFilename, const char *string);
void getDirectoryContents(char *filenameBuffer, char **filenames, int *filenamesCount, const char *directory, bool skipParent);
void enterDirectory(const char *path);
void removeDirectory(const char *path);
void gameCardDumpNSWDBCheck(u32 crc);