void DrawMenu(MenuInfo* currMenu, u32 index, bool fullDraw, bool subMenu) { u32 emunand_state = CheckEmuNand(); bool top_screen = true; u32 menublock_x0 = (((top_screen) ? SCREEN_WIDTH_TOP : SCREEN_WIDTH_BOT) - 50 * FONT_WIDTH_EXT) / 2; u32 menublock_x1 = menublock_x0 - FONT_WIDTH_EXT; u32 menublock_y0 = 40; u32 menublock_y1 = menublock_y0 + currMenu->n_entries * 10; if (fullDraw) { // draw full menu ClearScreenFull(true, !top_screen); FileGetData("lima/border.bin", TOP_SCREEN0, 400 * 240 * 3, 0); DrawStringF(menublock_x0 - 25, menublock_y0 - 30, top_screen, "%s", currMenu->name); DrawStringF(menublock_x0, menublock_y1 + 95, top_screen, (subMenu) ? "A: Choose B: Return" : "A: Choose"); DrawStringF(menublock_x0, menublock_y1 + 105, top_screen, "SELECT: Unmount SD Card"); DrawStringF(menublock_x0, menublock_y1 + 115, top_screen, "START: Reboot / [+\x1B] Poweroff"); } if (!top_screen) DrawStringF(10, 10, true, "Selected: %-*.*s", 32, 32, currMenu->entries[index].name); for (u32 i = 0; i < currMenu->n_entries; i++) { // draw menu entries / selection [] char* name = currMenu->entries[i].name; DrawStringF(menublock_x0, menublock_y0 + (i*10), top_screen, (i == index) ? "[%s]" : " %s ", name); } }
u32 ScrollOutput() { u32 log_start = LogWrite(NULL); // careful, these areas are used by other functions in Decrypt9 char** logptr = (char**) 0x21100000; char* logtext = (char*) 0x21200000; u32 log_size = FileGetData(LOG_FILE, logtext, 1024 * 1024, log_start); // log size u32 l_total = 0; // total lines u32 l_curr = 0; // current line // read log file (second time) log_size = FileGetData(LOG_FILE, logtext, 1024 * 1024, log_start); // allow 1MB of text max if ((log_size == 0) || (log_size >= 1024 * 1024)) return 0; // organize lines logtext[log_size - 1] = '\0'; logptr[l_total++] = logtext; for (char* line = strchr(logtext, '\n'); line != NULL && l_total < 4000; line = strchr(line, '\n')) { *line = '\0'; logptr[l_total++] = ++line; } if (l_total >= 4000) // allow 4000 lines of text max return 0; for (; l_total < DBG_N_CHARS_Y; logptr[l_total++] = logtext + log_size - 1); // fill up with empty lines // here, the actual output starts l_curr = l_total - DBG_N_CHARS_Y; if (l_curr > 0) l_curr--; // start at the line before the last while (true) { DebugSet((const char**) logptr + l_curr); u32 pad_state = InputWait(); if (pad_state & BUTTON_X) { Screenshot(NULL); } else if ((pad_state & BUTTON_UP) && (l_curr > 0)) { l_curr--; } else if ((pad_state & BUTTON_DOWN) && (l_curr < l_total - DBG_N_CHARS_Y)) { l_curr++; } else if (pad_state & (BUTTON_B | BUTTON_START)) { return pad_state; } } return 0; }
u32 InitializeD9() { u32 errorlevel = 0; // 0 -> none, 1 -> autopause, 2 -> critical ClearScreenFull(true, true); DebugClear(); #ifndef BUILD_NAME Debug("-- Decrypt9 --"); #else Debug("-- %s --", BUILD_NAME); #endif // a little bit of information about the current menu if (sizeof(menu)) { u32 n_submenus = 0; u32 n_features = 0; for (u32 m = 0; menu[m].n_entries; m++) { n_submenus = m; for (u32 e = 0; e < menu[m].n_entries; e++) n_features += (menu[m].entries[e].function) ? 1 : 0; } Debug("Counting %u submenus and %u features", n_submenus, n_features); } Debug("Initializing, hold L+R to pause"); Debug(""); if (InitFS()) { Debug("Initializing SD card... success"); FileGetData("d9logo.bin", BOT_SCREEN0, 320 * 240 * 3, 0); memcpy(BOT_SCREEN1, BOT_SCREEN0, 320 * 240 * 3); if (SetupTwlKey0x03() != 0) // TWL KeyX / KeyY errorlevel = 2; if ((GetUnitPlatform() == PLATFORM_N3DS) && (LoadKeyFromFile(0x05, 'Y', NULL) != 0)) errorlevel = (((*(vu32*) 0x101401C0) == 0) || (errorlevel > 1)) ? 2 : 1; // N3DS CTRNAND KeyY if (LoadKeyFromFile(0x25, 'X', NULL)) // NCCH 7x KeyX errorlevel = (errorlevel < 1) ? 1 : errorlevel; if (LoadKeyFromFile(0x18, 'X', NULL)) // NCCH Secure3 KeyX errorlevel = (errorlevel < 1) ? 1 : errorlevel; if (LoadKeyFromFile(0x1B, 'X', NULL)) // NCCH Secure4 KeyX errorlevel = (errorlevel < 1) ? 1 : errorlevel; Debug("Finalizing Initialization..."); RemainingStorageSpace(); } else { Debug("Initializing SD card... failed"); errorlevel = 2; } Debug(""); Debug("Initialization: %s", (errorlevel == 0) ? "success!" : (errorlevel == 1) ? "partially failed" : "failed!"); if (((~HID_STATE & BUTTON_L1) && (~HID_STATE & BUTTON_R1)) || (errorlevel > 1)) { Debug("(A to %s)", (errorlevel > 1) ? "exit" : "continue"); while (!(InputWait() & BUTTON_A)); } return errorlevel; }
//================================================================== bool FileManagerAndroid::GrabFile(const char* pFileName, DVec<U8> &out_data, const char* pMode) { DFUNCTION(); DLOG("File: %s", pFileName); #if defined(ANDROID) bool prefs = isPrefsMode( pMode ); if (prefs) { // Read from the prefs storage DLOG("Grabbing file (prefs): %s", pFileName); char *contents = ::DoReadPrefs(pFileName); if (0 == contents) { DLOG("Failed to open the file"); return false; } // Decode into binary size_t binLength = 0; void *binData = FromBase64(contents, &binLength); free(contents); if (binData == 0) { DPRINT("!! File '%s' was not base64 format. Rejecting.", pFileName); return false; } // Copy into output array and free original // TODO: Could be sped up by pre-calculating length and not // copying. out_data.resize(binLength); memcpy(&out_data[0], binData, binLength); free(binData); DLOG("copied %d bytes", binLength); return true; } ::FileLoad(pFileName); int datasize; void *data; if (!FileGetData(pFileName, &data, &datasize)) { //DASSTHROW(0, ("failed loading file '%s'", pFileName)); return false; } out_data.resize(datasize); DVERBOSE("GrabFile: memcpy-ing '%s', %d bytes", pFileName, datasize); memcpy(&out_data[0], data, datasize); FileRemove(pFileName); DVERBOSE("GrabFile: released"); #endif return true; }
u32 SdPadgen(u32 param) { (void) (param); // param is unused here SdInfo *info = (SdInfo*) 0x20316000; // setup AES key from SD SetupSdKeyY0x34(false, NULL); if (!DebugFileOpen("SDinfo.bin")) return 1; if (!DebugFileRead(info, 4, 0)) { FileClose(); return 1; } if (!info->n_entries || info->n_entries > MAX_ENTRIES) { FileClose(); Debug("Bad number of entries!"); return 1; } if (!DebugFileRead(info->entries, info->n_entries * sizeof(SdInfoEntry), 4)) { FileClose(); return 1; } FileClose(); Debug("Number of entries: %i", info->n_entries); for(u32 i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb, .mode = AES_CNT_CTRNAND_MODE}; memcpy(padInfo.ctr, info->entries[i].ctr, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); Debug ("%2i: %s (%iMB)", i, info->entries[i].filename, info->entries[i].size_mb); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 SdPadgenDirect(u32 param) { (void) (param); // param is unused here SdInfo *info = (SdInfo*) 0x20316000; char basepath[256]; u8 movable_keyY[16]; if (SetupSdKeyY0x34(true, movable_keyY) != 0) return 1; // movable.sed has to be present in NAND Debug(""); if (SdFolderSelector(basepath, movable_keyY) != 0) return 1; Debug(""); if (SdInfoGen(info, basepath) != 0) return 1; if (!info->n_entries) { Debug("Nothing found in folder"); return 1; } Debug("Number of entries: %i", info->n_entries); for(u32 i = 0; i < info->n_entries; i++) { PadInfo padInfo = {.keyslot = 0x34, .setKeyY = 0, .size_mb = info->entries[i].size_mb, .mode = AES_CNT_CTRNAND_MODE}; memcpy(padInfo.ctr, info->entries[i].ctr, 16); memcpy(padInfo.filename, info->entries[i].filename, 180); Debug ("%2i: %s (%iMB)", i, info->entries[i].filename, info->entries[i].size_mb); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 AnyPadgen(u32 param) { (void) (param); // param is unused here AnyPadInfo *info = (AnyPadInfo*) 0x20316000; // get header if ((FileGetData("anypad.bin", info, 16, 0) != 16) || !info->n_entries || info->n_entries > MAX_ENTRIES) { Debug("Corrupt or not existing: anypad.bin"); return 1; } // get data u32 data_size = info->n_entries * sizeof(AnyPadInfoEntry); if (FileGetData("anypad.bin", (u8*) info + 16, data_size, 16) != data_size) { Debug("File is missing data: anypad.bin"); return 1; } Debug("Processing anypad.bin..."); Debug("Number of entries: %i", info->n_entries); for (u32 i = 0; i < info->n_entries; i++) { // this translates all entries to a standard padInfo struct AnyPadInfoEntry* entry = &(info->entries[i]); PadInfo padInfo = {.keyslot = entry->keyslot, .setKeyY = 0, .size_mb = 0, .size_b = entry->size_b, .mode = entry->mode}; memcpy(padInfo.filename, entry->filename, 80); memcpy(padInfo.ctr, entry->ctr, 16); // process keys if (entry->setNormalKey) setup_aeskey(entry->keyslot, entry->normalKey); if (entry->setKeyX) setup_aeskeyX(entry->keyslot, entry->keyX); if (entry->setKeyY) setup_aeskeyY(entry->keyslot, entry->keyY); use_aeskey(entry->keyslot); // process flags if (entry->flags & (AP_USE_NAND_CTR|AP_USE_SD_CTR)) { u32 ctr_add = getbe32(padInfo.ctr + 12); u8 shasum[32]; u8 cid[16]; sdmmc_get_cid((entry->flags & AP_USE_NAND_CTR) ? 1 : 0, (uint32_t*) cid); if (entry->mode == AES_CNT_TWLNAND_MODE) { sha_quick(shasum, cid, 16, SHA1_MODE); for (u32 i = 0; i < 16; i++) padInfo.ctr[i] = shasum[15-i]; } else { sha_quick(shasum, cid, 16, SHA256_MODE); memcpy(padInfo.ctr, shasum, 16); } add_ctr(padInfo.ctr, ctr_add); } // create the pad Debug ("%2i: %s (%ikB)", i, entry->filename, entry->size_b / 1024); if (CreatePad(&padInfo) != 0) return 1; // this can't fail anyways } return 0; } u32 CtrNandPadgen(u32 param) { char* filename = (param & PG_FORCESLOT4) ? "nand.fat16.slot0x04.xorpad" : "nand.fat16.xorpad"; u32 keyslot; u32 nand_size; // legacy sizes & offset, to work with Python 3DSFAT16Tool if (GetUnitPlatform() == PLATFORM_3DS) { if (param & PG_FORCESLOT4) { Debug("This is a N3DS only feature"); return 1; } keyslot = 0x4; nand_size = 758; } else { keyslot = (param & PG_FORCESLOT4) ? 0x4 : 0x5; nand_size = 1055; } Debug("Creating NAND FAT16 xorpad. Size (MB): %u", nand_size); Debug("Filename: %s", filename); PadInfo padInfo = { .keyslot = keyslot, .setKeyY = 0, .size_mb = nand_size, .mode = AES_CNT_CTRNAND_MODE }; strncpy(padInfo.filename, filename, 64); if(GetNandCtr(padInfo.ctr, 0xB930000) != 0) return 1; return CreatePad(&padInfo); } u32 TwlNandPadgen(u32 param) { (void) (param); // param is unused here PartitionInfo* twln_info = GetPartitionInfo(P_TWLN); u32 size_mb = (twln_info->size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating TWLN FAT16 xorpad. Size (MB): %u", size_mb); Debug("Filename: twlnand.fat16.xorpad"); PadInfo padInfo = { .keyslot = twln_info->keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "twlnand.fat16.xorpad", .mode = AES_CNT_TWLNAND_MODE }; if(GetNandCtr(padInfo.ctr, twln_info->offset) != 0) return 1; return CreatePad(&padInfo); } u32 Firm0Firm1Padgen(u32 param) { (void) (param); // param is unused here PartitionInfo* firm0_info = GetPartitionInfo(P_FIRM0); PartitionInfo* firm1_info = GetPartitionInfo(P_FIRM1); u32 size_mb = (firm0_info->size + firm1_info->size + (1024 * 1024) - 1) / (1024 * 1024); Debug("Creating FIRM0FIRM1 xorpad. Size (MB): %u", size_mb); Debug("Filename: firm0firm1.xorpad"); PadInfo padInfo = { .keyslot = firm0_info->keyslot, .setKeyY = 0, .size_mb = size_mb, .filename = "firm0firm1.xorpad", .mode = AES_CNT_CTRNAND_MODE }; if(GetNandCtr(padInfo.ctr, firm0_info->offset) != 0) return 1; return CreatePad(&padInfo); }