/*----------------------------------------------------------------- Copyright (C) 2005 - 2013 Michael "Chishm" Chisholm Dave "WinterMute" Murphy Claudio "sverx" This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include "nds_loader_arm9.h" #include "config.h" #include "date.h" #include "driveMenu.h" #include "driveOperations.h" #include "file_browse.h" #include "fileOperations.h" #include "font.h" #include "language.h" #include "my_sd.h" #include "nitrofs.h" #include "tonccpy.h" #include "version.h" #include "gm9i_logo.h" char titleName[64] = {" "}; int screenMode = 0; bool appInited = false; bool screenSwapped = false; bool arm7SCFGLocked = false; bool isRegularDS = true; bool bios9iEnabled = false; bool is3DS = false; int ownNitroFSMounted; std::string prevTime; bool applaunch = false; static int bg3; //--------------------------------------------------------------------------------- void stop (void) { //--------------------------------------------------------------------------------- while (1) { swiWaitForVBlank(); } } //--------------------------------------------------------------------------------- void vblankHandler (void) { //--------------------------------------------------------------------------------- // Check if NDS cart ejected if(isDSiMode() && (REG_SCFG_MC & BIT(0)) && romTitle[0][0] != '\0') { romTitle[0][0] = '\0'; romSizeTrimmed = romSize[0] = 0; } // Check if GBA cart ejected if(isRegularDS && *(u8*)(0x080000B2) != 0x96 && romTitle[1][0] != '\0') { romTitle[1][0] = '\0'; romSize[1] = 0; } // Print time std::string time = RetTime(); if(time != prevTime) { prevTime = time; if(font) { font->print(lastCol, 0, true, time, alignEnd, Palette::blackGreen); font->update(true); } } } char filePath[PATH_MAX]; //--------------------------------------------------------------------------------- int main(int argc, char **argv) { //--------------------------------------------------------------------------------- // overwrite reboot stub identifier extern u64 *fake_heap_end; *fake_heap_end = 0; defaultExceptionHandler(); int pathLen; std::string filename; bool yHeld = false; sprintf(titleName, "GodMode9i %s", VER_NUMBER); // initialize video mode videoSetMode(MODE_5_2D); videoSetModeSub(MODE_5_2D); // initialize VRAM banks vramSetPrimaryBanks(VRAM_A_MAIN_BG, VRAM_B_MAIN_SPRITE, VRAM_C_SUB_BG, VRAM_D_LCD); vramSetBankI(VRAM_I_SUB_SPRITE); // Init built-in font font = new Font(nullptr); // Display GM9i logo bg3 = bgInit(3, BgType_Bmp8, BgSize_B8_256x256, 0, 0); bgInit(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0); bgInitSub(2, BgType_Bmp8, BgSize_B8_256x256, 3, 0); decompress(gm9i_logoBitmap, bgGetGfxPtr(bg3), LZ77Vram); tonccpy(BG_PALETTE, gm9i_logoPal, gm9i_logoPalLen); font->print(1, 1, false, titleName); font->print(1, 2, false, "----------------------------------------"); font->print(1, 3, false, "https://github.com/DS-Homebrew/GodMode9i"); fifoWaitValue32(FIFO_USER_06); if (fifoGetValue32(FIFO_USER_03) == 0) arm7SCFGLocked = true; u16 arm7_SNDEXCNT = fifoGetValue32(FIFO_USER_07); if (arm7_SNDEXCNT != 0) isRegularDS = false; // If sound frequency setting is found, then the console is not a DS Phat/Lite fifoSendValue32(FIFO_USER_07, 0); if (isDSiMode()) { bios9iEnabled = true; if (!arm7SCFGLocked) { //font->print(-2, -4, false, " Held - Disable NAND access", Alignment::right); font->print(-2, -3, false, " Held - Disable cart access", Alignment::right); font->print(-2, -2, false, "Do this if it crashes here", Alignment::right); } /*else { font->print(-2, -3, false, " Held - Disable NAND access", Alignment::right); font->print(-2, -2, false, "Do this if it crashes here", Alignment::right); }*/ } // Display for 2 seconds font->update(false); for (int i = 0; i < 60*2; i++) { swiWaitForVBlank(); } font->clear(false); font->print(1, 1, false, titleName); font->print(1, 2, false, "----------------------------------------"); font->print(1, 3, false, "https://github.com/DS-Homebrew/GodMode9i"); font->print(-2, -2, false, "Mounting drive(s)...", Alignment::right); font->update(false); sysSetCartOwner (BUS_OWNER_ARM9); // Allow arm9 to access GBA ROM if (isDSiMode() || !isRegularDS) { fifoSetValue32Handler(FIFO_USER_04, sdStatusHandler, nullptr); if (!sdRemoved) { sdMounted = sdMount(); } } if (isDSiMode()) { scanKeys(); yHeld = (keysHeld() & KEY_Y); *(vu32*)(0x0DFFFE0C) = 0x474D3969; // Check for 32MB of RAM bool ram32MB = *(vu32*)(0x0DFFFE0C) == 0x474D3969; ramdriveMount(ram32MB); if (ram32MB) { is3DS = fifoGetValue32(FIFO_USER_05) != 0xD2; } //if (!(keysHeld() & KEY_X)) { nandMount(); //} //is3DS = ((access("sd:/Nintendo 3DS", F_OK) == 0) && (*(vu32*)(0x0DFFFE0C) == 0x474D3969)); /*FILE* cidFile = fopen("sd:/gm9i/CID.bin", "wb"); fwrite((void*)0x2FFD7BC, 1, 16, cidFile); fclose(cidFile);*/ /*FILE* cidFile = fopen("sd:/gm9i/ConsoleID.bin", "wb"); fwrite((void*)0x2FFFD00, 1, 8, cidFile); fclose(cidFile);*/ } else if (REG_SCFG_EXT != 0) { *(vu32*)(0x0DFFFE0C) = 0x474D3969; // Check for 32MB of RAM bool ram32MB = *(vu32*)(0x0DFFFE0C) == 0x474D3969; ramdriveMount(ram32MB); if (ram32MB) { is3DS = fifoGetValue32(FIFO_USER_05) != 0xD2; } FILE* bios = fopen("sd:/_nds/bios9i.bin", "rb"); if (!bios) { bios = fopen("sd:/_nds/bios9i_part1.bin", "rb"); } if (bios) { tonccpy((u32*)0x02008000, (u32*)0x02000000, 0x4000); extern u32* copyBuf; copyBuf = new u32[0x8000/4]; fread((u32*)0x02000000, 1, 0x8000, bios); fclose(bios); // Relocate addresses *(u32*)0x020000CC -= 0xFFFF0000; *(u32*)0x02003264 -= 0xFFFF0000; *(u32*)0x02003268 -= 0xFFFF0000; *(u32*)0x0200326C -= 0xFFFF0000; *(u32*)0x020033E0 -= 0xFFFF0000; *(u32*)0x020042C0 -= 0xFFFF0000; *(u32*)0x02004B88 -= 0xFFFF0000; *(u32*)0x02004B90 -= 0xFFFF0000; *(u32*)0x02004B9C -= 0xFFFF0000; *(u32*)0x02004BA0 -= 0xFFFF0000; *(u32*)0x02004E1C -= 0xFFFF0000; *(u32*)0x02004F18 -= 0xFFFF0000; *(u32*)0x020000CC += 0x02000000; *(u32*)0x02003264 += 0x02000000; *(u32*)0x02003268 += 0x02000000; *(u32*)0x0200326C += 0x02000000; *(u32*)0x020033E0 += 0x02000000; *(u32*)0x020042C0 += 0x02000000; *(u32*)0x02004B88 += 0x02000000; *(u32*)0x02004B90 += 0x02000000; *(u32*)0x02004B9C += 0x02000000; *(u32*)0x02004BA0 += 0x02000000; *(u32*)0x02004E1C += 0x02000000; *(u32*)0x02004F18 += 0x02000000; u32* itcmAddr = (u32*)0x01000000; for (int i = 0; i < 8; i++) { itcmAddr[i] = 0xEA7FFFFE; } setVectorBase(0); bios9iEnabled = true; // nandMount(); // Returns corrupt data for some reason } } else if (isRegularDS && (io_dldi_data->ioInterface.features & FEATURE_SLOT_NDS)) { ramdriveMount(false); } if (!isDSiMode() || !yHeld) { flashcardMounted = flashcardMount(); flashcardMountSkipped = false; } // Try to init NitroFS char nandPath[64] = {0}; char sdnandPath[64] = {0}; if(isDSiMode()) { sprintf(nandPath, "nand:/title/%08x/%08x/content/000000%02x.app", *(unsigned int*)0x02FFE234, *(unsigned int*)0x02FFE230, *(u8*)0x02FFE01E); sprintf(sdnandPath, "sd:/title/%08x/%08x/content/000000%02x.app", *(unsigned int*)0x02FFE234, *(unsigned int*)0x02FFE230, *(u8*)0x02FFE01E); } ownNitroFSMounted = 0; nitroMounted = true; if (argc > 0 && nitroFSInit(argv[0])) nitroCurrentDrive = argv[0][0] == 's' ? Drive::sdCard : Drive::flashcard; else if (nandPath[0] && nitroFSInit(nandPath)) nitroCurrentDrive = Drive::nand; else if (sdnandPath[0] && nitroFSInit(sdnandPath)) nitroCurrentDrive = Drive::sdCard; else if (nitroFSInit("sd:/GodMode9i.nds")) nitroCurrentDrive = Drive::sdCard; else if (nitroFSInit("sd:/GodMode9i.dsi")) nitroCurrentDrive = Drive::sdCard; else if (nitroFSInit("fat:/GodMode9i.nds")) nitroCurrentDrive = Drive::flashcard; else if (nitroFSInit("fat:/GodMode9i.dsi")) nitroCurrentDrive = Drive::flashcard; else { ownNitroFSMounted = 1; nitroMounted = false; font->print(-2, -3, false, "NitroFS init failed...", Alignment::right); font->update(false); for (int i = 0; i < 30; i++) swiWaitForVBlank(); } // Ensure gm9i folder exists char folderPath[10]; sprintf(folderPath, "%s:/gm9i", (sdMounted ? "sd" : "fat")); if ((sdMounted || flashcardMounted) && access(folderPath, F_OK) != 0) mkdir(folderPath, 0777); // Load config config = new Config(); bgHide(bg3); // Reinit font, try to load default from SD this time delete font; if(access(config->fontPath().c_str(), F_OK) == 0) font = new Font(config->fontPath().c_str()); else if(config->languageIniPath().substr(17, 2) == "zh") font = new Font("nitro:/fonts/misaki-gothic-8x8.frf"); else font = new Font(nullptr); // Load translations langInit(false); keysSetRepeat(25,5); // Top bar font->printf(0, 0, true, Alignment::left, Palette::blackGreen, "%*c", 256 / font->width(), ' '); // Enable vblank handler irqSet(IRQ_VBLANK, vblankHandler); appInited = true; while(1) { if (screenMode == 0) { driveMenu(); } else { filename = browseForFile(); } if (applaunch) { // Construct a command line getcwd (filePath, PATH_MAX); pathLen = strlen (filePath); std::vector argarray; if ((strcasecmp (filename.c_str() + filename.size() - 5, ".argv") == 0) || (strcasecmp (filename.c_str() + filename.size() - 5, ".ARGV") == 0)) { FILE *argfile = fopen(filename.c_str(),"rb"); char str[PATH_MAX], *pstr; const char seps[]= "\n\r\t "; while( fgets(str, PATH_MAX, argfile) ) { // Find comment and end string there if( (pstr = strchr(str, '#')) ) *pstr= '\0'; // Tokenize arguments pstr= strtok(str, seps); while( pstr != NULL ) { argarray.push_back(strdup(pstr)); pstr= strtok(NULL, seps); } } fclose(argfile); filename = argarray[0]; } else { argarray.push_back(strdup(filename.c_str())); } if (extension(filename, {"nds", "dsi", "ids", "app", "srl"})) { char *name = argarray[0]; strcpy (filePath + pathLen, name); free(argarray[0]); argarray[0] = filePath; font->clear(false); font->printf(firstCol, 0, false, alignStart, Palette::white, STR_RUNNING_X_WITH_N_PARAMETERS.c_str(), argarray[0], argarray.size()); int err = runNdsFile(argarray[0], argarray.size(), (const char **)&argarray[0]); font->printf(firstCol, 1, false, alignStart, Palette::white, STR_START_FAILED_ERROR_N.c_str(), err); } if (extension(filename, {"firm"})) { char *name = argarray[0]; strcpy (filePath + pathLen, name); free(argarray[0]); argarray[0] = filePath; fcopy(argarray[0], "sd:/bootonce.firm"); fifoSendValue32(FIFO_USER_02, 1); // Reboot into selected .firm payload swiWaitForVBlank(); } while(argarray.size() !=0 ) { free(argarray[0]); argarray.erase(argarray.begin()); } while (1) { swiWaitForVBlank(); scanKeys(); if (!(keysHeld() & KEY_A)) break; } } } return 0; }