1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-23 10:42:09 +00:00
nxdumptool/source/util.c

454 lines
12 KiB
C
Raw Normal View History

#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-26 02:11:18 +01:00
#include "dumper.h"
2018-05-15 17:00:19 +01:00
#include "fsext.h"
#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";
bool isGameCardInserted(FsDeviceOperator* o)
{
2018-05-15 17:00:19 +01:00
bool inserted;
if (R_FAILED(fsDeviceOperatorIsGameCardInserted(o, &inserted))) return false;
2018-05-15 17:00:19 +01:00
return inserted;
}
void syncDisplay()
{
2018-05-15 17:00:19 +01:00
gfxFlushBuffers();
gfxSwapBuffers();
gfxWaitForVsync();
}
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);
}
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg("getGameCardTitleID: GetContentMetaDatabase failed! (0x%08x)", result);
}
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg("getGameCardTitleID: OpenContentMetaDatabase failed! (0x%08x)", result);
}
// 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);
free(appList);
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg("getGameCardTitleID: Unable to allocate memory for the NCM service operations.");
}
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);
}
} 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));
}
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg("getGameCardControlNacp: GetApplicationControlData failed! (0x%08x)", result);
}
free(buf);
} else {
2018-06-22 01:46:17 +01:00
uiStatusMsg("getGameCardControlNacp: Unable to allocate memory for the NS service operations.");
}
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));
} 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);
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)
{
2018-06-26 02:42:25 +01:00
uiDrawString("No matches found in XML document! This could either be a bad dump or an undumped cartridge.", 0, breaks * 8, 255, 0, 0);
2018-06-26 02:11:18 +01:00
} 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 {
2018-06-26 02:42:25 +01:00
snprintf(strbuf, sizeof(strbuf) / sizeof(strbuf[0]), "Error: failed to open and/or parse \"%s\"!", nswReleasesXmlPath);
uiDrawString(strbuf, 0, breaks * 8, 255, 0, 0);
2018-06-26 02:11:18 +01:00
}
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;
}