diff --git a/icon.bmp b/icon.bmp index f4300c5..5b106aa 100644 Binary files a/icon.bmp and b/icon.bmp differ diff --git a/src/installmenu.c b/src/installmenu.c index 126ee51..131da3a 100644 --- a/src/installmenu.c +++ b/src/installmenu.c @@ -1,280 +1,10 @@ -#include "menus.h" +#include "menu.h" +#include "main.h" #include "storage.h" +#include "titles.h" #include "maketmd.h" #include -static int cursor = 0; -static int scrolly = 0; -static int numberOfTitles = 0; - -static void moveCursor(int dir); - -static void printList(); -static void printFileInfoNum(int num); - -static int getNumberOfTitles(); -//static void getGameTitle(char* title, FILE* f); -static int getFile(char* dest, int num, int fullpath); - -static void subMenu(); -static void install(char* fpath); - -void installMenu() -{ - cursor = 0; - scrolly = 0; - numberOfTitles = getNumberOfTitles(); - - consoleSelect(&topScreen); - consoleClear(); - - consoleSelect(&bottomScreen); - consoleClear(); - - //No titles error - if (numberOfTitles == 0) - { - iprintf("No files found.\n"); - iprintf("Place .nds(dsi) or .app files in %s\n", ROM_PATH); - iprintf("\nBack - B\n"); - - keyWait(KEY_B | KEY_A | KEY_START); - return; - } - - //Print data - consoleSelect(&topScreen); - printFileInfoNum(cursor); - - consoleSelect(&bottomScreen); - printList(); - - while (1) - { - swiWaitForVBlank(); - scanKeys(); - - int thisCursor = cursor; - int thisscrolly = scrolly; - - //Clear cursor - consoleSelect(&bottomScreen); - iprintf("\x1b[%d;0H ", cursor - scrolly); - - //Move cursor - if (keysDown() & KEY_DOWN) - moveCursor(1); - - if (keysDown() & KEY_UP) - moveCursor(-1); - - if (keysDown() & KEY_RIGHT) - { - repeat (10) - moveCursor(1); - } - - if (keysDown() & KEY_LEFT) - { - repeat (10) - moveCursor(-1); - } - - //Refresh screens - if (thisCursor != cursor) - { - consoleSelect(&topScreen); - consoleClear(); - - printFileInfoNum(cursor); - } - - if (thisscrolly != scrolly) - { - consoleSelect(&bottomScreen); - consoleClear(); - - printList(); - } - - //Print cursor - consoleSelect(&bottomScreen); - iprintf("\x1b[%d;0H>", cursor - scrolly); - - // - if (keysDown() & KEY_B) - break; - else if (keysDown() & KEY_A) - { - subMenu(); - - consoleSelect(&topScreen); - printFileInfoNum(cursor); - - consoleSelect(&bottomScreen); - printList(); - } - } -} - -void moveCursor(int dir) -{ - cursor += sign(dir); - - if (cursor < 0) - cursor = 0; - - if (cursor >= numberOfTitles - 1) - cursor = numberOfTitles - 1; - - if (cursor - scrolly >= 23) - scrolly += 1; - - if (cursor - scrolly < 0) - scrolly -= 1; -} - -void printList() -{ - consoleClear(); - - for (int i = scrolly; i < scrolly + 23; i++) - { - char str[256]; - if (getFile(str, i, 0) == 1) - iprintf(" %.30s\n", str); - } - - //Scroll arrows - if (scrolly > 0) - iprintf("\x1b[0;31H^"); - - if (scrolly < numberOfTitles - 23) - iprintf("\x1b[22;31Hv"); -} - -void printFileInfoNum(int num) -{ - consoleClear(); - - char path[256]; - if (getFile(path, num, 1) == 1) - printFileInfo(path); -} - -int getNumberOfTitles() -{ - DIR* dir; - struct dirent* ent; - int count = 0; - - dir = opendir(ROM_PATH); - - if (dir) - { - while ( (ent = readdir(dir)) != NULL ) - { - if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0) - continue; - - if (ent->d_type != DT_DIR) - { - if (strstr(ent->d_name, ".nds") != NULL || strstr(ent->d_name, ".app") != NULL) - count++; - } - } - } - - closedir(dir); - return count; -} -/* -void getGameTitle(char* title, FILE* f) -{ - tDSiHeader header; - tNDSBanner banner; - - fseek(f, 0, SEEK_SET); - fread(&header, sizeof(tDSiHeader), 1, f); - fseek(f, header.ndshdr.bannerOffset, SEEK_SET); - fread(&banner, sizeof(tNDSBanner), 1, f); - -// iprintf("\t%s\n", header.ndshdr.gameTitle); -// iprintf("\t%s\n", (char*)banner.titles[0]); - - int line = 0; - for (int i = 0; i < 64; i++) - { - char c = banner.titles[0][i]; - - if (c == '\n') - { - if (line == 0) - { - title[i] = ' '; - line = 1; - } - else - { - title[i] = '\0'; - break; - } - } - else - title[i] = c; - } - - title[64] = '\0'; -} -*/ - -int getFile(char* dest, int num, int fullpath) -{ - DIR* dir; - struct dirent* ent; - int result = 0; - - dir = opendir(ROM_PATH); - - if (dir) - { - int count = 0; - - while ( (ent = readdir(dir)) != NULL ) - { - if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0) - continue; - - if (ent->d_type != DT_DIR) - { - if(strstr(ent->d_name, ".nds") != NULL || strstr(ent->d_name, ".app") != NULL) - { - if (count < num) - { - count++; - continue; - } - else - { - if (fullpath == 0) - sprintf(dest, "%s", ent->d_name); - else - sprintf(dest, "%s%s", ROM_PATH, ent->d_name); - - result = 1; - break; - } - } - } - } - } - - closedir(dir); - return result; -} - -// -static int subCursor = 0; enum { INSTALL_MENU_INSTALL, @@ -282,154 +12,305 @@ enum { INSTALL_MENU_BACK }; -void subMenu() +static void generateList(Menu* m); +static void printItem(Menu* m); + +static int subMenu(); +static void install(Menu* m); +static void delete(Menu* m); + + +void installMenu() { - bool printMenu = true; - subCursor = 0; + Menu* m = (Menu*)malloc(sizeof(Menu)); + + clearMenu(m); + generateList(m); + + //No files found + if (getNumberOfMenuItems(m) <= 0) + { + consoleSelect(&bottomScreen); + consoleClear(); + + iprintf("No files found.\n"); + iprintf("Place .nds, .app, or .dsi files in %s\n", ROM_PATH); + iprintf("\nBack - B\n"); + + keyWait(KEY_B | KEY_A | KEY_START); + return; + } + + //Print data + printItem(m); + printMenu(m); while (1) { swiWaitForVBlank(); scanKeys(); - if (printMenu == true) + if (moveCursor(m)) { - printMenu = false; + printItem(m); + printMenu(m); + } - consoleSelect(&bottomScreen); - consoleClear(); + if (keysDown() & KEY_B) + break; - iprintf("\tInstall\n"); - iprintf("\tDelete\n"); - iprintf("\tBack - B\n"); + //Selection + else if (keysDown() & KEY_A) + { + switch (subMenu()) + { + case INSTALL_MENU_INSTALL: + install(m); + break; + + case INSTALL_MENU_DELETE: + delete(m); + break; + + case INSTALL_MENU_BACK: + break; + } + + printMenu(m); } + } - //Clear cursor - iprintf("\x1b[%d;0H ", subCursor); + free(m); +} - //Move cursor - if (keysDown() & KEY_DOWN) +//mode = 0: add items to menu +//mode = 1: return full path of item num to out +static bool _walkList(Menu* m, int mode, char* out) +{ + //Skip if no menu + if (m == NULL) + return false; + + //Reset menu + if (mode == 0) + clearMenu(m); + + // + DIR* dir; + struct dirent* ent; + bool result = false; + + dir = opendir(ROM_PATH); + + if (dir) + { + int count = 0; + + //Scan /dsi/ + while ( (ent = readdir(dir)) != NULL ) { - if (subCursor < INSTALL_MENU_BACK) - subCursor++; + if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0) + continue; + + if (ent->d_type != DT_DIR) + { + if (strstr(ent->d_name, ".nds") != NULL || strstr(ent->d_name, ".app") != NULL || strstr(ent->d_name, ".dsi") != NULL || + strstr(ent->d_name, ".NDS") != NULL || strstr(ent->d_name, ".APP") != NULL || strstr(ent->d_name, ".DSI") != NULL) + { + //Generate list + if (mode == 0) + { + char name[128]; + sprintf(name, "%s", ent->d_name); + + addMenuItem(m, name); + } + + //Get item file name + else if (mode == 1) + { + if (count < m->cursor) + count++; + else + { + sprintf(out, "%s%s", ROM_PATH, ent->d_name); + + result = true; + goto endloop; + } + } + } + } } + } - if (keysDown() & KEY_UP) - { - if (subCursor > 0) - subCursor--; - } +endloop: + closedir(dir); + return result; +} - //Reprint cursor - iprintf("\x1b[%d;0H>", subCursor); +void generateList(Menu* m) +{ + if (m == NULL) return; + + consoleSelect(&bottomScreen); + consoleClear(); + + iprintf("Gathering files...\n"); + + _walkList(m, 0, NULL); +} + +static void printItem(Menu* m) +{ + if (m == NULL) return; + + char path[256]; + if (_walkList(m, 1, path) == true) + printFileInfo(path); +} + +// +int subMenu() +{ + int result = -1; + + Menu* m = (Menu*)malloc(sizeof(Menu)); + clearMenu(m); + + addMenuItem(m, "Install"); + addMenuItem(m, "Delete"); + addMenuItem(m, "Back - B"); + + printMenu(m); + + while (1) + { + swiWaitForVBlank(); + scanKeys(); + + if (moveCursor(m)) + printMenu(m); - // if (keysDown() & KEY_B) break; else if (keysDown() & KEY_A) { - if (subCursor == INSTALL_MENU_INSTALL) - { - char fpath[256]; - getFile(fpath, cursor, 1); - - char msg[512+1]; - msg[512] = '\0'; - sprintf(msg, "Are you sure you want to install\n%s\n", fpath); - - if (choiceBox(msg) == YES) - install(fpath); - - break; - } - - else if (subCursor == INSTALL_MENU_DELETE) - { - char fpath[256]; - getFile(fpath, cursor, 1); - - char msg[512+1]; - msg[512] = '\0'; - sprintf(msg, "Are you sure you want to delete\n%s\n", fpath); - - if (choiceBox(msg) == YES) - { - if (remove(fpath) != 0) - messageBox("File could not be deleted."); - - else - messageBox("File deleted."); - - //Reset - cursor = 0; - scrolly = 0; - numberOfTitles = getNumberOfTitles(); - - break; - } - else - { - printMenu = true; - } - } - - else if (subCursor == INSTALL_MENU_BACK) - { - break; - } + result = m->cursor; + break; } - } + } + + free(m); + + return result; } -void install(char* fpath) +void install(Menu* m) { + char fpath[256]; + + //Confirmation message + { + int choice = NO; + + if (_walkList(m, 1, fpath) == true) + { + char msg[512]; + sprintf(msg, "Are you sure you want to install\n%s\n", fpath); + choice = choiceBox(msg); + } + + if (choice == NO) + return; + } + + //Start installation consoleSelect(&bottomScreen); consoleClear(); iprintf("Installing %s\n", fpath); swiWaitForVBlank(); + tDSiHeader* header = (tDSiHeader*)malloc(sizeof(tDSiHeader)); + tNDSBanner* banner = (tNDSBanner*)malloc(sizeof(tNDSBanner)); + FILE* f = fopen(fpath, "rb"); if (!f) { - iprintf("Error: could not open file.\n\nPress B to exit.\n"); - keyWait(KEY_A | KEY_B); + iprintf("Error: could not open file.\n"); + goto error; } else { - //Load header - tDSiHeader header; - tNDSBanner banner; + bool patchHeader = false; + //Read header and banner { fseek(f, 0, SEEK_SET); - fread(&header, sizeof(tDSiHeader), 1, f); - fseek(f, header.ndshdr.bannerOffset, SEEK_SET); - fread(&banner, sizeof(tNDSBanner), 1, f); + fread(header, sizeof(tDSiHeader), 1, f); + + fseek(f, header->ndshdr.bannerOffset, SEEK_SET); + fread(banner, sizeof(tNDSBanner), 1, f); } -/* - //Check header size - if (header.ndshdr.headerSize != 0x4000) + + //Patch homebrew roms if gameCode is #### or null + if ((strcmp(header->ndshdr.gameCode, "####") == 0 && header->tid_low == 0x23232323) || + (!*header->ndshdr.gameCode && header->tid_low == 0)) { - iprintf("Error: no DSi header."); - goto error; + iprintf("Patching header..."); + + patchHeader = true; + + //Set as standard app + header->tid_high = 0x00030004; + + //Give it a random game code + do + { + //First letter shouldn't be A + do + { + for (int i = 0; i < 4; i++) + header->ndshdr.gameCode[i] = 0x41 + (rand() % (26)); + } + while (header->ndshdr.gameCode[0] == 'A'); + + //Correct title id + header->tid_low = gameCodeToTitleID(header->ndshdr.gameCode); + } + while (titleIsUsed(header->tid_low, header->tid_high) == true); + + //Fix header checksum + header->ndshdr.headerCRC16 = swiCRC16(0xFFFF, header, 0x15E); + + //Fix RSA signature + u8 buffer[20]; + swiSHA1Calc(&buffer, header, 0xE00); + memcpy(&(header->rsa_signature[0x6C]), buffer, 20); + + iprintf("Done\n"); } -*/ - //Check high title id - if (header.tid_high != 0x00030004 && - header.tid_high != 0x00030005 && - header.tid_high != 0x00030015) + + //Must be DSi rom + //High title id must be one of three { - iprintf("This file cannot be installed.\nInvalid title ID."); - goto error; + if (header->tid_high != 0x00030004 && + header->tid_high != 0x00030005 && + header->tid_high != 0x00030015 && + header->tid_high != 0x00030017) + { + iprintf("Error: This is not a DSi rom.\n"); + goto error; + } } //Print file size - int fileSize = -1; + int fileSize = -1; { - iprintf("File Size: "); swiWaitForVBlank(); + iprintf("File Size: "); fileSize = getFileSize(f); @@ -437,12 +318,12 @@ void install(char* fpath) printf("\n"); } - //Do not want file opened anymore + //Do not need file opened anymore fclose(f); //SD card check { - iprintf("Enough room on SD card?..."); swiWaitForVBlank(); + iprintf("Enough room on SD card?..."); if (getSDCardFree() < fileSize) { @@ -455,28 +336,28 @@ void install(char* fpath) //DSi storage check { - iprintf("Enough room on DSi?..."); swiWaitForVBlank(); + iprintf("Enough room on DSi?..."); if (getDsiFree() < fileSize) { - iprintf("No\n"); swiWaitForVBlank(); + iprintf("No\n"); goto error; } - iprintf("Yes\n"); swiWaitForVBlank(); + iprintf("Yes\n"); } //Menu slot check { - iprintf("Open DSi menu slot?..."); swiWaitForVBlank(); + iprintf("Open DSi menu slot?..."); if (getMenuSlotsFree() <= 0) { - iprintf("No\n"); swiWaitForVBlank(); + iprintf("No\n"); goto error; } - iprintf("Yes\n"); swiWaitForVBlank(); + iprintf("Yes\n"); } //Create title directory @@ -484,10 +365,8 @@ void install(char* fpath) char dirPath[256]; { - sprintf(titleID, "%08x", (unsigned int)header.tid_low); - sprintf(dirPath, "/title/%08x/%s", (unsigned int)header.tid_high, titleID); - - //iprintf("Creating dir\n%s\n", dirPath); swiWaitForVBlank(); + sprintf(titleID, "%08x", (unsigned int)header->tid_low); + sprintf(dirPath, "/title/%08x/%s", (unsigned int)header->tid_high, titleID); } //Check if title is already installed @@ -523,24 +402,46 @@ void install(char* fpath) char contentPath[256]; sprintf(contentPath, "%s/content", dirPath); - //iprintf("Creating dir\n%s\n", contentPath); swiWaitForVBlank(); mkdir(contentPath, 0777); //Create 0000000.app - //Does 00000000 always work? { char appPath[256]; sprintf(appPath, "%s/00000000.app", contentPath); - iprintf("Creating 00000000.app..."); swiWaitForVBlank(); - - if (copyFile(fpath, appPath) == 0) + //Copy nds file to app { - iprintf("Failed\n"); - goto error; + iprintf("Creating 00000000.app..."); + + if (copyFile(fpath, appPath) == 0) + { + iprintf("Failed\n"); + goto error; + } + + iprintf("Done\n"); } - iprintf("Done\n"); + //Write new patched header + if (patchHeader == true) + { + iprintf("Writing header..."); + + FILE* f = fopen(appPath, "r+"); + + if (!f) + iprintf("Failed\n"); + + else + { + fseek(f, 0, SEEK_SET); + fwrite(header, sizeof(tDSiHeader), 1, f); + + iprintf("Done\n"); + } + + fclose(f); + } //Make TMD { @@ -558,101 +459,140 @@ void install(char* fpath) char dataPath[256]; sprintf(dataPath, "%s/data", dirPath); - //iprintf("Creating dir\n%s\n", dataPath); swiWaitForVBlank(); mkdir(dataPath, 0777); //If needed, create public.sav - if (header.public_sav_size > 0) + if (header->public_sav_size > 0) { char publicPath[512]; sprintf(publicPath, "%s/public.sav", dataPath); - iprintf("Creating public.sav..."); swiWaitForVBlank(); - - FILE* file = fopen(publicPath, "wb"); - - if (!file) - iprintf("Failed\n"); - - else { - char num = 0; + iprintf("Creating public.sav..."); - repeat (header.public_sav_size) - fwrite(&num, 1, 1, f); + FILE* file = fopen(publicPath, "wb"); - iprintf("Done\n"); + if (!file) + iprintf("Failed\n"); + + else + { + char num = 0; + + repeat (header->public_sav_size) + fwrite(&num, 1, 1, f); + + iprintf("Done\n"); + } + + fclose(file); } - - fclose(file); } //If needed, create private.sav - if (header.private_sav_size > 0) + if (header->private_sav_size > 0) { char privatePath[512]; sprintf(privatePath, "%s/private.sav", dataPath); - iprintf("Creating private.sav..."); swiWaitForVBlank(); - - FILE* file = fopen(privatePath, "wb"); - - if (!file) - iprintf("Failed\n"); - - else { - char num = 0; + iprintf("Creating private.sav..."); - repeat (header.private_sav_size) - fwrite(&num, 1, 1, f); + FILE* file = fopen(privatePath, "wb"); - iprintf("Done\n"); + if (!file) + iprintf("Failed\n"); + + else + { + char num = 0; + + repeat (header->private_sav_size) + fwrite(&num, 1, 1, f); + + iprintf("Done\n"); + } + + fclose(file); } - - fclose(file); } //If needed, create banner.sav - if (header.appflags & 0x4) + if (header->appflags & 0x4) { char bannerPath[512]; sprintf(bannerPath, "%s/banner.sav", dataPath); - iprintf("Creating banner.sav..."); swiWaitForVBlank(); - - FILE* file = fopen(bannerPath, "wb"); - - if (!file) - iprintf("Failed\n"); - - else { - char num = 0; + iprintf("Creating banner.sav..."); - repeat (1024*16) //Is banner.sav always 16kb? - fwrite(&num, 1, 1, f); + FILE* file = fopen(bannerPath, "wb"); - iprintf("Done\n"); + if (!file) + iprintf("Failed\n"); + + else + { + char num = 0; + + repeat (0x4000) + fwrite(&num, 1, 1, f); + + iprintf("Done\n"); + } + + fclose(file); } - - fclose(file); } - } + } + // iprintf("\nInstallation complete.\nPress B to exit.\n"); - keyWait(KEY_A | KEY_B); + keyWait(KEY_A | KEY_B); } -complete: - fclose(f); - return; + goto complete; error: - fclose(f); - iprintf("\nInstallation failed.\n\nPress B to exit.\n"); keyWait(KEY_A | KEY_B); +complete: + free(banner); + free(header); + + fclose(f); return; +} + +static void delete(Menu* m) +{ + char path[256]; + int choice = NO; + + if (_walkList(m, 1, path) == true) + { + { + char msg[512]; + sprintf(msg, "Are you sure you want to delete\n%s\n", path); + choice = choiceBox(msg); + } + + if (choice == YES) + { + if (remove(path) != 0) + { + messageBox("File could not be deleted."); + } + else + { + messageBox("File deleted."); + + generateList(m); + printItem(m); + } + } + } + + printMenu(m); } \ No newline at end of file diff --git a/src/main.c b/src/main.c index 517304b..6d96424 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,8 @@ #include "main.h" -#include "menus.h" +#include "menu.h" +#include -#define VERSION "0.4" +#define VERSION "0.5" enum { MAIN_MENU_INSTALL, @@ -15,6 +16,8 @@ static int mainMenu(); int main(int argc, char **argv) { + srand(time(0)); + videoSetMode(MODE_0_2D); videoSetModeSub(MODE_0_2D); @@ -24,6 +27,9 @@ int main(int argc, char **argv) consoleInit(&topScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, true, true); consoleInit(&bottomScreen, 3, BgType_Text4bpp, BgSize_T_256x256, 31, 0, false, true); + consoleSelect(&bottomScreen); + consoleClear(); + if (!fatInitDefault()) { consoleSelect(&bottomScreen); @@ -64,9 +70,7 @@ int main(int argc, char **argv) return 0; } -static int cursor = 0; - -int mainMenu() +static int mainMenu() { consoleSelect(&topScreen); consoleClear(); @@ -75,59 +79,30 @@ int mainMenu() iprintf("\nversion %s\n", VERSION); iprintf("\x1b[23;0HJeff - 2018"); - consoleSelect(&bottomScreen); - consoleClear(); + Menu* m = (Menu*)malloc(sizeof(Menu)); + clearMenu(m); + + addMenuItem(m, "Install"); + addMenuItem(m, "Titles"); + addMenuItem(m, "Test"); + addMenuItem(m, "Exit"); + + printMenu(m); - iprintf("\tInstall\n"); - iprintf("\tTitles\n"); -// iprintf("\tRestore\n"); - iprintf("\tTest\n"); - iprintf("\tExit"); - while (1) { swiWaitForVBlank(); scanKeys(); - - //Clear cursor - iprintf("\x1b[%d;0H ", cursor); - - if (keysDown() & KEY_DOWN) - { - if ( (cursor += 1) > MAIN_MENU_EXIT ) - cursor = 0; - } - - if (keysDown() & KEY_RIGHT) - { - repeat (10) - { - if ( (cursor += 1) > MAIN_MENU_EXIT ) - cursor = 0; - } - } - - if (keysDown() & KEY_UP) - { - if ( (cursor -= 1) < 0 ) - cursor = MAIN_MENU_EXIT; - } - - if (keysDown() & KEY_LEFT) - { - repeat (10) - { - if ( (cursor -= 1) < 0 ) - cursor = MAIN_MENU_EXIT; - } - } - - //print cursor - iprintf("\x1b[%d;0H>", cursor); - + + if (moveCursor(m) == 1) + printMenu(m); + if (keysDown() & KEY_A) break; } - + + int cursor = m->cursor; + free(m); + return cursor; } \ No newline at end of file diff --git a/src/menu.c b/src/menu.c new file mode 100644 index 0000000..9cd306b --- /dev/null +++ b/src/menu.c @@ -0,0 +1,191 @@ +#include "menu.h" +#include "main.h" +#include + +#define SCREEN_ROWS 23 + +void clearMenu(Menu* m) +{ + if (m == NULL) return; + + m->numberOfItems = 0; + m->cursor = 0; + m->scrolly = 0; +} + +static void _printMenuItem(Menu* m, int item) +{ + if (item < 0 || item > m->numberOfItems) return; + + iprintf(" %.30s", m->items[item].label); +} + +void printMenu(Menu* m) +{ + if (m == NULL) return; + + swiWaitForVBlank(); + consoleSelect(&bottomScreen); + consoleClear(); + + int i = m->scrolly; + while (i < m->scrolly + SCREEN_ROWS && i < m->numberOfItems) + { + _printMenuItem(m, i); + iprintf("\n"); + + i++; + } + + //Cursor + iprintf("\x1b[%d;0H>", m->cursor - m->scrolly); + + //Scroll arrows + if (m->scrolly > 0) + iprintf("\x1b[0;31H^"); + + if (m->scrolly < m->numberOfItems - SCREEN_ROWS) + iprintf("\x1b[22;31Hv"); +} + +int getMenuCursor(Menu* m) +{ + if (m == NULL) return -1; + return m->cursor; +} + +int getNumberOfMenuItems(Menu* m) +{ + if (m == NULL) return -1; + return m->numberOfItems; +} + +static void _moveCursor(Menu* m, int dir) +{ + m->cursor += sign(dir); + + if (m->cursor < 0) + m->cursor = 0; + + if (m->cursor >= m->numberOfItems - 1) + m->cursor = m->numberOfItems - 1; + + if (m->cursor - m->scrolly >= SCREEN_ROWS) + m->scrolly += 1; + + if (m->cursor - m->scrolly < 0) + m->scrolly -= 1; +} + +int moveCursor(Menu* m) +{ + if (m == NULL) + return 0; + + int lastCursor = m->cursor; + + if (keysDown() & KEY_DOWN) + _moveCursor(m, 1); + + if (keysDown() & KEY_UP) + _moveCursor(m, -1); + + if (keysDown() & KEY_RIGHT) + { + repeat (10) + _moveCursor(m, 1); + } + + if (keysDown() & KEY_LEFT) + { + repeat (10) + _moveCursor(m, -1); + } + + return !(lastCursor == m->cursor); +} + +void addMenuItem(Menu* m, char* label) +{ + if (m == NULL) return; + + if (label != NULL) + sprintf(m->items[m->numberOfItems].label, "%.32s", label); + + m->numberOfItems += 1; + + m->cursor = 0; + m->scrolly = 0; +} + +// +void keyWait(u32 key) +{ + while (1) + { + swiWaitForVBlank(); + scanKeys(); + + if (keysDown() & key) + break; + } +} + +int choiceBox(char* message) +{ + const int choiceRow = 10; + int cursor = 0; + + consoleSelect(&bottomScreen); + consoleClear(); + + iprintf("%s\n", message); + iprintf("\x1b[%d;0H\tYes\n\tNo\n", choiceRow); + + while (1) + { + swiWaitForVBlank(); + scanKeys(); + + //Clear cursor + iprintf("\x1b[%d;0H ", choiceRow + cursor); + + if (keysDown() & (KEY_UP | KEY_DOWN)) + cursor = !cursor; + + //Print cursor + iprintf("\x1b[%d;0H>", choiceRow + cursor); + + if (keysDown() & (KEY_A | KEY_START)) + break; + + if (keysDown() & KEY_B) + { + cursor = 1; + break; + } + } + + scanKeys(); + return (cursor == 0)? YES: NO; +} + +void messageBox(char* message) +{ + consoleSelect(&bottomScreen); + consoleClear(); + + iprintf("%s\n", message); + iprintf("\nOkay - A\n"); + + while (1) + { + swiWaitForVBlank(); + scanKeys(); + + if (keysDown() & (KEY_A | KEY_START)) + break; + } + + scanKeys(); +} \ No newline at end of file diff --git a/src/menu.h b/src/menu.h new file mode 100644 index 0000000..309fdb6 --- /dev/null +++ b/src/menu.h @@ -0,0 +1,40 @@ +#ifndef MENU_H +#define MENU_H + +#include + +void titleMenu(); +void installMenu(); +void testMenu(); + +#define NUM_OF_ITEMS 100 + +typedef struct { + char label[32+1]; +} MenuItem; + +typedef struct { + int cursor; + int scrolly; + int numberOfItems; + MenuItem items[NUM_OF_ITEMS]; +} Menu; + +void clearMenu(Menu* m); +void printMenu(Menu* m); + +int getMenuCursor(Menu* m); +int getNumberOfMenuItems(Menu* m); +int moveCursor(Menu* m); + +void addMenuItem(Menu* m, char* label); + + +#define YES 1 +#define NO 0 + +void keyWait(u32 key); +int choiceBox(char* message); +void messageBox(char* message); + +#endif \ No newline at end of file diff --git a/src/storage.c b/src/storage.c index 63d66a6..1cccb11 100644 --- a/src/storage.c +++ b/src/storage.c @@ -25,21 +25,26 @@ void printBytes(int bytes) void printFileInfo(const char* path) { - tDSiHeader header; - tNDSBanner banner; + if (path == NULL) return; + + consoleSelect(&topScreen); + consoleClear(); + + tDSiHeader* header = (tDSiHeader*)malloc(sizeof(tDSiHeader)); + tNDSBanner* banner = (tNDSBanner*)malloc(sizeof(tNDSBanner)); FILE* f = fopen(path, "rb"); if (f) { - if (fread(&header, sizeof(tDSiHeader), 1, f) != 1) + if (fread(header, sizeof(tDSiHeader), 1, f) != 1) iprintf("Could not read dsi header.\n"); else { - fseek(f, header.ndshdr.bannerOffset, SEEK_SET); + fseek(f, header->ndshdr.bannerOffset, SEEK_SET); - if (fread(&banner, sizeof(tNDSBanner), 1, f) != 1) + if (fread(banner, sizeof(tNDSBanner), 1, f) != 1) iprintf("Could not read banner.\n"); else @@ -51,7 +56,7 @@ void printFileInfo(const char* path) //Convert 2 byte characters to 1 byte for (int i = 0; i < 128; i++) - gameTitle[i] = (char)banner.titles[1][i]; + gameTitle[i] = (char)banner->titles[1][i]; iprintf("%s\n\n", gameTitle); } @@ -63,14 +68,14 @@ void printFileInfo(const char* path) iprintf("\n"); } - iprintf("Label: %.12s\n", header.ndshdr.gameTitle); - iprintf("Game Code: %.4s\n", header.ndshdr.gameCode); + iprintf("Label: %.12s\n", header->ndshdr.gameTitle); + iprintf("Game Code: %.4s\n", header->ndshdr.gameCode); //System type { iprintf("Unit Code: "); - switch (header.ndshdr.unitCode) + switch (header->ndshdr.unitCode) { case 0: iprintf("NDS"); break; case 2: iprintf("NDS+DSi"); break; @@ -85,19 +90,27 @@ void printFileInfo(const char* path) { iprintf("Program Type: "); - switch (header.ndshdr.reserved1[7]) + switch (header->ndshdr.reserved1[7]) { case 0x3: iprintf("Normal"); break; case 0xB: iprintf("Sys"); break; case 0xF: iprintf("Debug/Sys"); break; - default: iprintf("unknown"); + default: iprintf("unknown"); } iprintf("\n"); } - iprintf("Title ID: %08x %08x\n", (unsigned int)header.tid_high, - (unsigned int)header.tid_low); + //DSi data + if (header->tid_high == 0x00030004 || + header->tid_high == 0x00030005 || + header->tid_high == 0x00030015 || + header->tid_high == 0x00030017 || + header->tid_high == 0x00030000) + { + iprintf("Title ID: %08x %08x\n", (unsigned int)header->tid_high, + (unsigned int)header->tid_low); + } //Print full file path iprintf("\n%s\n", path); @@ -106,6 +119,9 @@ void printFileInfo(const char* path) } fclose(f); + + free(banner); + free(header); } //Progress bar diff --git a/src/testmenu.c b/src/testmenu.c index d808136..3983690 100644 --- a/src/testmenu.c +++ b/src/testmenu.c @@ -1,4 +1,5 @@ -#include "menus.h" +#include "main.h" +#include "menu.h" #include "storage.h" void testMenu() diff --git a/src/titlemenu.c b/src/titlemenu.c index ffdd204..9208925 100644 --- a/src/titlemenu.c +++ b/src/titlemenu.c @@ -1,43 +1,43 @@ -#include "menus.h" +#include "menu.h" +#include "main.h" #include "storage.h" #include -#define NUM_OF_DIRECTORIES 3 -static const char* directories[] = { - "00030004", - "00030005", - "00030015" +static void generateList(Menu* m); +static void printItem(Menu* m); + +static int subMenu(); +static void dump(Menu* m); +static void delete(Menu* m); +//static void backupData(Menu* m); +//static void restoreData(Menu* m); + +enum { +// TITLE_MENU_BACKUP, + TITLE_MENU_DUMP, +// TITLE_MENU_BACKUP_DATA, +// TITLE_MENU_RESTORE_DATA, + TITLE_MENU_DELETE, + TITLE_MENU_BACK }; -static int cursor = 0; -static int scrolly = 0; -static int numberOfTitles = 0; - -static void moveCursor(int dir); - -static void printList(); -static void printTitleInfo(int num); - -static int getNumberOfTitles(); -static int getTitle(int num, char* title, char* path); - -static void subMenu(); - void titleMenu() { - cursor = 0; - scrolly = 0; - numberOfTitles = getNumberOfTitles(); + Menu* m = (Menu*)malloc(sizeof(Menu)); consoleSelect(&topScreen); - consoleClear(); + consoleClear(); consoleSelect(&bottomScreen); consoleClear(); + generateList(m); + //No titles error - if (numberOfTitles <= 0) + if (getNumberOfMenuItems(m) <= 0) { + consoleClear(); + iprintf("No titles found.\n"); iprintf("Back - B\n"); @@ -46,188 +46,70 @@ void titleMenu() } //Print data - consoleSelect(&topScreen); - printTitleInfo(cursor); - - consoleSelect(&bottomScreen); - printList(); + printItem(m); + printMenu(m); while (1) { swiWaitForVBlank(); scanKeys(); - int thisScrolly = scrolly; - int thisCursor = cursor; - - //Clear cursor - consoleSelect(&bottomScreen); - iprintf("\x1b[%d;0H ", cursor - scrolly); - - //Move cursor - if (keysDown() & KEY_DOWN) - moveCursor(1); - - if (keysDown() & KEY_UP) - moveCursor(-1); - - if (keysDown() & KEY_RIGHT) + if (moveCursor(m)) { - repeat (10) - moveCursor(1); + printItem(m); + printMenu(m); } - if (keysDown() & KEY_LEFT) - { - repeat (10) - moveCursor(-1); - } - - //Re-print list - if (thisCursor != cursor) - { - consoleSelect(&topScreen); - printTitleInfo(cursor); - } - - if (thisScrolly != scrolly) - { - consoleSelect(&bottomScreen); - printList(); - } - - //Print cursor - consoleSelect(&bottomScreen); - iprintf("\x1b[%d;0H>", cursor - scrolly); - - // if (keysDown() & KEY_B) break; + else if (keysDown() & KEY_A) { - subMenu(); - - consoleSelect(&topScreen); - printTitleInfo(cursor); - - consoleSelect(&bottomScreen); - printList(); - } - } -} - -void moveCursor(int dir) -{ - cursor += sign(dir); - - if (cursor < 0) - cursor = 0; - - if (cursor >= numberOfTitles - 1) - cursor = numberOfTitles - 1; - - if (cursor - scrolly >= 23) - scrolly += 1; - - if (cursor - scrolly < 0) - scrolly -= 1; -} - -void printList() -{ - consoleClear(); - - for (int i = scrolly; i < scrolly + 23; i++) - { - char title[256]; - if (getTitle(i, title, NULL) == 1) - iprintf(" %.30s\n", title); - } - - //Scroll arrows - if (scrolly > 0) - iprintf("\x1b[0;31H^"); - - if (scrolly < numberOfTitles - 23) - iprintf("\x1b[22;31Hv"); -} - -void printTitleInfo(int num) -{ - consoleClear(); - - char path[256]; - if (getTitle(num, NULL, path) == 1) - printFileInfo(path); -} - -int getNumberOfTitles() -{ - int count = 0; - - //Scan choice title directories - for (int i = 0; i < NUM_OF_DIRECTORIES; i++) - { - DIR* dir; - struct dirent* ent; - - char dirPath[256]; - sprintf(dirPath, "/title/%s", directories[i]); - - dir = opendir(dirPath); - - if (dir) - { - while ( (ent = readdir(dir)) != NULL ) + switch (subMenu()) { - if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0) - continue; + case TITLE_MENU_DUMP: + dump(m); + break; - if (ent->d_type == DT_DIR) - { - //Search for an .app file - char contentPath[384]; - sprintf(contentPath, "%s/%s/content", dirPath, ent->d_name); - - DIR* subdir; - struct dirent* subent; - - subdir = opendir(contentPath); - - if (subdir) - { - while ( (subent = readdir(subdir)) != NULL ) - { - if (strcmp(".", subent->d_name) == 0 || strcmp("..", subent->d_name) == 0) - continue; - - //Found a title - if (strstr(subent->d_name, ".app") != NULL) - count++; - } - } - - closedir(subdir); - } + case TITLE_MENU_DELETE: + delete(m); + break; } + + printMenu(m); } - - closedir(dir); } - return count; + free(m); } -int getTitle(int num, char* title, char* path) +#define NUM_OF_DIRECTORIES 3 +static const char* directories[] = { + "00030004", + "00030005", + "00030015" +}; + +//mode = 0: add items to menu +//mode = 1: return full path of menu item +static bool _walkList(Menu* m, int mode, char* out) { - int result = 0; + //Skip if no menu + if (m == NULL) + return false; + + //Reset menu + if (mode == 0) + clearMenu(m); + + bool result = false; int count = 0; //Scan choice title directories - for (int i = 0; i < NUM_OF_DIRECTORIES && result == 0; i++) + for (int i = 0; i < NUM_OF_DIRECTORIES && result == false; i++) { DIR* dir; - struct dirent* ent; + struct dirent* ent; char dirPath[256]; sprintf(dirPath, "/title/%s", directories[i]); @@ -236,7 +118,7 @@ int getTitle(int num, char* title, char* path) if (dir) { - while ( (ent = readdir(dir)) != NULL && result == 0) + while ( (ent = readdir(dir)) != NULL && result == false) { if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0) continue; @@ -254,7 +136,7 @@ int getTitle(int num, char* title, char* path) if (subdir) { - while ( (subent = readdir(subdir)) != NULL && result == 0) + while ( (subent = readdir(subdir)) != NULL && result == false) { if (strcmp(".", subent->d_name) == 0 || strcmp("..", subent->d_name) == 0) continue; @@ -263,61 +145,57 @@ int getTitle(int num, char* title, char* path) { if (strstr(subent->d_name, ".app") != NULL) { - if (count < num) - count++; + //Found requested title + char path[384]; + sprintf(path, "%s/%s", contentPath, subent->d_name); - else + //Generate list + if (mode == 0) { - //Found requested title - char filepath[384]; - sprintf(filepath, "%s/%s", contentPath, subent->d_name); + FILE* f = fopen(path, "rb"); - //Output title - if (title != NULL) + if (f) { - FILE* f = fopen(filepath, "rb"); + tNDSHeader* header = (tNDSHeader*)malloc(sizeof(tNDSHeader)); + tNDSBanner* banner = (tNDSBanner*)malloc(sizeof(tNDSBanner)); - if (!f) + fread(header, sizeof(tNDSHeader), 1, f); + fseek(f, header->bannerOffset, SEEK_SET); + fread(banner, sizeof(tNDSBanner), 1, f); + + char tstr[128+1]; + tstr[128] = '\0'; + + for (int i = 0; i < 128; i++) { - sprintf(title, " "); - } - else - { - tNDSHeader header; - tNDSBanner banner; + char c = banner->titles[1][i]; + + //Replace new line with space + if (c == '\n') + c = ' '; - fread(&header, sizeof(tNDSHeader), 1, f); - fseek(f, header.bannerOffset, SEEK_SET); - fread(&banner, sizeof(tNDSBanner), 1, f); - - char tstr[128+1]; - tstr[128] = '\0'; - - for (int i = 0; i < 128; i++) - { - char c = banner.titles[1][i]; - - if (c == '\n') - c = ' '; - - tstr[i] = c; - } - - sprintf(title, "%s", tstr); + tstr[i] = c; } - fclose(f); + free(banner); + free(header); + + addMenuItem(m, tstr); } - //Output path - if (path != NULL) + fclose(f); + } + + //Get item file name + else if (mode == 1) + { + if (count < m->cursor) + count++; + else { - sprintf(path, "%s", filepath); - } - - //Exit this mess - result = 1; - break; + sprintf(out, "%s", path); + result = true; + } } } } @@ -335,173 +213,228 @@ int getTitle(int num, char* title, char* path) return result; } -// -static int subCursor = 0; - -enum { -// TITLE_MENU_BACKUP, - TITLE_MENU_DUMP, - TITLE_MENU_DELETE, - TITLE_MENU_BACK -}; - -void subMenu() +void generateList(Menu* m) { - subCursor = 0; - + if (m == NULL) return; + consoleSelect(&bottomScreen); consoleClear(); + iprintf("Gathering files...\n"); swiWaitForVBlank(); + + clearMenu(m); + _walkList(m, 0, NULL); +} + +static void printItem(Menu* m) +{ + if (m == NULL) return; + + char path[256]; + if (_walkList(m, 1, path) == true) + printFileInfo(path); +} + +// +int subMenu() +{ + int result = -1; + + Menu* m = (Menu*)malloc(sizeof(Menu)); + clearMenu(m); + // iprintf("\tBackup\n"); - iprintf("\tDump\n"); - iprintf("\tDelete\n"); - iprintf("\tBack\n"); + addMenuItem(m, "Dump"); +// addMenuItem(m, "Backup Saved Data"); +// addMenuItem(m, "Restore Saved Data"); + addMenuItem(m, "Delete"); + addMenuItem(m, "Back"); + + printMenu(m); while (1) { swiWaitForVBlank(); scanKeys(); - //Clear cursor - iprintf("\x1b[%d;0H ", subCursor); + if (moveCursor(m)) + printMenu(m); - //Move cursor - if (keysDown() & KEY_DOWN) - { - if (subCursor < TITLE_MENU_BACK) - subCursor += 1; - } - - if (keysDown() & KEY_UP) - { - if (subCursor > 0) - subCursor -= 1; - } - - //Print cursor - iprintf("\x1b[%d;0H>", subCursor); - - if (keysDown() & KEY_A) - { - char title[256]; - char path[256]; - getTitle(cursor, title, path); - - //Only get first line of title - for (int i = 0; i < 256; i++) - { - if (title[i] == '\n') - { - title[i] = '\0'; - break; - } - } - -/* // - if (subCursor == TITLE_MENU_BACKUP) - { - char dir[256]; - sprintf(dir, "%.24s", path); - - char msg[512]; - sprintf(msg, "Are you sure you want to backup\n%s", dir); - - if (choiceBox(msg) == YES) - { - if (getDirSize(dir) > getSDCardFree()) - messageBox("Error, not enough space on SD card.\nTitle backup failed."); - - else - { - if (copyDir(dir, BACKUP_PATH) == 1) - messageBox("Title was backed up."); - else - messageBox("Title backup failed."); - } - } - - break; - } -*/ - if (subCursor == TITLE_MENU_DUMP) - { - char fpath[256]; - if (getTitle(cursor, NULL, fpath) == 1) - { - FILE* f = fopen(fpath, "rb"); - - if (!f) - messageBox("Can not dump title.\n"); - - else - { - int fsize = getFileSize(f); - - if (fsize > getSDCardFree()) - messageBox("Not enough free space on SD card.\n"); - - else - { - tNDSHeader header; - tNDSBanner banner; - - fread(&header, sizeof(tNDSHeader), 1, f); - fseek(f, header.bannerOffset, SEEK_SET); - fread(&banner, sizeof(tNDSBanner), 1, f); - fclose(f); - - char outpath[256]; - sprintf(outpath, "%s%.12s - %.4s.nds", ROM_PATH, header.gameTitle, header.gameCode); - - char msg[512]; - sprintf(msg, "Dump title to\n%s\n", outpath); - - if (choiceBox(msg) == YES) - { - if (copyFile(fpath, outpath) == 1) - messageBox("Title saved.\n"); - else - messageBox("Title dump failed.\n"); - } - } - } - - fclose(f); - } - - break; - } - - else if (subCursor == TITLE_MENU_DELETE) - { - char msg[512]; - sprintf(msg, "Are you sure you want to delete\n%s", title); - - if (choiceBox(msg) == YES) - { - char dirPath[256]; - sprintf(dirPath, "%.25s", path); - - if (deleteDir(dirPath) == 1) - messageBox("Title deleted.\n"); - else - messageBox("Title could not be deleted.\n"); - - //Reset main menu - cursor = 0; - scrolly = 0; - numberOfTitles = getNumberOfTitles(); - } - - break; - } - - else if (subCursor == TITLE_MENU_BACK) - { - break; - } - } - else if (keysDown() & KEY_B) + if (keysDown() & KEY_B) break; + + else if (keysDown() & KEY_A) + { + result = m->cursor; + break; + } } -} \ No newline at end of file + + free(m); + + return result; +} + +static void dump(Menu* m) +{ + char fpath[256]; + + if (_walkList(m, 1, fpath) == false) + { + messageBox("Failed to dump title.\n"); + } + else + { + FILE* f = fopen(fpath, "rb"); + + if (!f) + messageBox("Can not dump title.\n"); + + else + { + int fsize = getFileSize(f); + + if (fsize > getSDCardFree()) + { + messageBox("Not enough free space on SD card.\n"); + } + else + { + tNDSHeader* header = (tNDSHeader*)malloc(sizeof(tNDSHeader)); + tNDSBanner* banner = (tNDSBanner*)malloc(sizeof(tNDSBanner)); + + fread(header, sizeof(tNDSHeader), 1, f); + fseek(f, header->bannerOffset, SEEK_SET); + fread(banner, sizeof(tNDSBanner), 1, f); + + fclose(f); + + char outpath[256]; + sprintf(outpath, "%s%.12s - %.4s.nds", ROM_PATH, header->gameTitle, header->gameCode); + + int choice = NO; + { + char msg[512]; + sprintf(msg, "Dump title to\n%s\n", outpath); + choice = choiceBox(msg); + } + + if (choice == YES) + { + if (copyFile(fpath, outpath) == 1) + messageBox("Title saved.\n"); + else + messageBox("Title dump failed.\n"); + } + + free(banner); + free(header); + } + } + + fclose(f); + } +} + +static void delete(Menu* m) +{ + char fpath[256]; + + if (_walkList(m, 1, fpath) == false) + { + messageBox("Failed to delete title.\n"); + } + else + { + int choice = NO; + + { + //Get title name + char title[128+1]; + title[128] = '\0'; + + FILE* f = fopen(fpath, "rb"); + + if (!f) + { + messageBox("Failed to delete title.\n"); + } + else + { + tNDSHeader* header = (tNDSHeader*)malloc(sizeof(tNDSHeader)); + tNDSBanner* banner = (tNDSBanner*)malloc(sizeof(tNDSBanner)); + + fseek(f, 0, SEEK_SET); + fread(header, sizeof(tNDSHeader), 1, f); + fseek(f, header->bannerOffset, SEEK_SET); + fread(banner, sizeof(tNDSBanner), 1, f); + + for (int i = 0; i < 128; i++) + title[i] = (char)banner->titles[1][i]; + + free(banner); + free(header); + } + + fclose(f); + + char msg[512]; + sprintf(msg, "Are you sure you want to delete\n%s", title); + choice = choiceBox(msg); + } + + if (choice == YES) + { + char dirpath[256]; + sprintf(dirpath, "%.25s", fpath); + + if (deleteDir(dirpath) == 1) + messageBox("Title deleted.\n"); + else + messageBox("Title could not be deleted.\n"); + + generateList(m); + printItem(m); + } + } + + printMenu(m); +} +/* Incomplete +static void backupData(Menu* m) +{ + char msg[512]; + char dirPath[256]; + sprintf(dirPath, "/dsisave/%s", title); + sprintf(msg, "Backup data to\n%s", dirPath); + + if (choiceBox(msg) == YES) + { + mkdir(dirPath, 0777); + + FILE* f = fopen(path, "rb"); + + if (f) + { + tDSiHeader header; + fread(&header, sizeof(tDSiHeader), 1, f); + fclose(f); + + char titleID[8+1]; + sprintf(titleID, "%08x", (unsigned int)header.tid_low); + + DIR* dir; + struct dirent* ent; + + dir = opendir(ROM_PATH); + + closedir(dir); + } + + fclose(f); +} + +static void restoreData(Menu* m) +{ + +}*/ \ No newline at end of file diff --git a/src/titles.c b/src/titles.c new file mode 100644 index 0000000..72ddf2d --- /dev/null +++ b/src/titles.c @@ -0,0 +1,37 @@ +#include "titles.h" +#include +#include +#include + +bool titleIsUsed(u32 tidlow, u32 tidhigh) +{ + bool result = false; + + char path[256]; + sprintf(path, "/title/%08x/%08x/", (unsigned int)tidhigh, (unsigned int)tidlow); + + DIR* dir = opendir(path); + struct dirent* ent; + + if (dir) + { + while ( (ent = readdir(dir)) != NULL ) + { + if (strcmp(".", ent->d_name) == 0 || strcmp("..", ent->d_name) == 0) + continue; + + if (ent->d_type != DT_DIR) + { + if (strstr(ent->d_name, ".app") != NULL) + { + result = true; + break; + } + } + } + } + + closedir(dir); + + return result; +} \ No newline at end of file diff --git a/src/titles.h b/src/titles.h new file mode 100644 index 0000000..d8efc59 --- /dev/null +++ b/src/titles.h @@ -0,0 +1,10 @@ +#ifndef TITLES_H +#define TITLES_H + +#include + +#define gameCodeToTitleID(X) ( (X[0] << 24) | (X[1] << 16) | (X[2] << 8) | X[3] ) + +bool titleIsUsed(u32 tidlow, u32 tidhigh); + +#endif \ No newline at end of file