#include #include #include #include #include #include #include #include #include #include #include "fsext.h" #include "ncmext.h" #include "ui.h" #include "util.h" extern int breaks; bool isGameCardInserted(FsDeviceOperator* o) { bool inserted; if (R_FAILED(fsDeviceOperatorIsGameCardInserted(o, &inserted))) return false; return inserted; } void syncDisplay() { 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 { uiStatusMsg("getGameCardTitleID: MetaDatabaseListApplication failed! (0x%08x)", result); } } else { uiStatusMsg("getGameCardTitleID: GetContentMetaDatabase failed! (0x%08x)", result); } } else { uiStatusMsg("getGameCardTitleID: OpenContentMetaDatabase failed! (0x%08x)", result); } // Seems to cause problems //if (R_FAILED(result = ncmCloseContentMetaDatabase(FsStorageId_GameCard))) uiStatusMsg("getGameCardTitleID: CloseContentMetaDatabase failed! (0x%08x)", result); free(appList); } else { 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 { uiStatusMsg("getGameCardControlNacp: GetLanguageEntry failed! (0x%08x)", result); } } else { uiStatusMsg("getGameCardControlNacp: Control.nacp buffer size (%u bytes) is too small! Expected: %u bytes", outsize, sizeof(buf->nacp)); } } else { uiStatusMsg("getGameCardControlNacp: GetApplicationControlData failed! (0x%08x)", result); } free(buf); } else { 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) { 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) { time_t timer; time(&timer); 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); }