#include #include #include #include #include #include <3ds.h> #include "ctr/ctr_debug.h" #define FILE_CHUNK_SIZE 4096 PrintConsole debugConsoleTop; typedef struct { u32 argc; char args[0x300 - 0x4]; }ciaParam; char argvHmac[0x20] = {0x1d, 0x78, 0xff, 0xb9, 0xc5, 0xbc, 0x78, 0xb7, 0xac, 0x29, 0x1d, 0x3e, 0x16, 0xd0, 0xcf, 0x53, 0xef, 0x12, 0x58, 0x83, 0xb6, 0x9e, 0x2f, 0x79, 0x47, 0xf9, 0x35, 0x61, 0xeb, 0x50, 0xd7, 0x67}; static int isCiaInstalled(u64 titleId, u16 version) { u32 titlesToRetrieve; u32 titlesRetrieved; u64* titleIds; u32 titlesToCheck; AM_TitleEntry titleInfo; bool titleExists = false; Result failed = AM_GetTitleCount(MEDIATYPE_SD, &titlesToRetrieve); if (R_FAILED(failed)) return -1; titleIds = malloc(titlesToRetrieve * sizeof(uint64_t)); if (!titleIds) return -1; failed = AM_GetTitleList(&titlesRetrieved, MEDIATYPE_SD, titlesToRetrieve, titleIds); if (R_FAILED(failed)) return -1; for(titlesToCheck = 0; titlesToCheck < titlesRetrieved; titlesToCheck++) { if (titleIds[titlesToCheck] == titleId) { titleExists = true; break; } } free(titleIds); if (titleExists) { failed = AM_GetTitleInfo(MEDIATYPE_SD, 1 /*titleCount*/, &titleId, &titleInfo); if (R_FAILED(failed)) return -1; if (titleInfo.version == version) return 1; } return 0; } static int deleteCia(u64 TitleId) { u64 currTitleId = 0; /* Do not delete if the titleid is currently running. */ if (R_FAILED(APT_GetAppletInfo((NS_APPID) envGetAptAppId(), &currTitleId, NULL, NULL, NULL, NULL)) || TitleId != currTitleId) { AM_DeleteTitle(MEDIATYPE_SD, TitleId); AM_DeleteTicket(TitleId); } } static int installCia(Handle ciaFile) { Handle outputHandle; u64 fileSize; u32 bytesRead; u32 bytesWritten; u8 transferBuffer[FILE_CHUNK_SIZE]; u64 fileOffset = 0; Result failed = AM_StartCiaInstall(MEDIATYPE_SD, &outputHandle); if (R_FAILED(failed)) return -1; failed = FSFILE_GetSize(ciaFile, &fileSize); if (R_FAILED(failed)) return -1; while(fileOffset < fileSize) { u64 bytesRemaining = fileSize - fileOffset; failed = FSFILE_Read(ciaFile, &bytesRead, fileOffset, transferBuffer, bytesRemaining < FILE_CHUNK_SIZE ? bytesRemaining : FILE_CHUNK_SIZE); if (R_FAILED(failed)) { AM_CancelCIAInstall(outputHandle); return -1; } failed = FSFILE_Write(outputHandle, &bytesWritten, fileOffset, transferBuffer, bytesRead, FS_WRITE_FLUSH); if (R_FAILED(failed)) { AM_CancelCIAInstall(outputHandle); if (R_DESCRIPTION(failed) == RD_ALREADY_EXISTS) return 1; return -1; } printf(PRINTFPOS(27,0)" %llu / %llu\n\n", fileOffset, fileSize); int progress = roundf(((float)fileOffset/(float)fileSize)*50); for (int i = 0; i < progress; i++) { printf("#"); } if (bytesWritten != bytesRead) { AM_CancelCIAInstall(outputHandle); return -1; } fileOffset += bytesWritten; } failed = AM_FinishCiaInstall(outputHandle); if (R_FAILED(failed)) return -1; return 1; } int exec_cia(const char* path, const char** args) { struct stat sBuff; bool fileExists; bool inited; #ifdef USE_CTRULIB_2 if (!gspHasGpuRight()) #endif gfxInitDefault(); consoleInit(GFX_TOP, &debugConsoleTop); if (!path || path[0] == '\0') { errno = EINVAL; return -1; } fileExists = stat(path, &sBuff) == 0; if (!fileExists) { errno = ENOENT; return -1; } else if (S_ISDIR(sBuff.st_mode)) { errno = EINVAL; return -1; } inited = R_SUCCEEDED(amInit()) && R_SUCCEEDED(fsInit()); if (inited) { AM_TitleEntry ciaInfo; FS_Archive ciaArchive; Handle ciaFile; int ciaInstalled; ciaParam param; int argsLength; /* open CIA file */ Result res = FSUSER_OpenArchive(&ciaArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")); if (R_FAILED(res)) error_and_quit("Cant open SD FS archive."); res = FSUSER_OpenFile(&ciaFile, ciaArchive, fsMakePath(PATH_ASCII, path + 5/*skip "sdmc:"*/), FS_OPEN_READ, 0); if (R_FAILED(res)) error_and_quit("Cant open CIA file."); res = AM_GetCiaFileInfo(MEDIATYPE_SD, &ciaInfo, ciaFile); if (R_FAILED(res)) error_and_quit("Cant get CIA file info."); ciaInstalled = isCiaInstalled(ciaInfo.titleID, ciaInfo.version); if (ciaInstalled == -1) { /* error */ error_and_quit("Could not read title ID list."); } else if (ciaInstalled == 0) { /* titleid with version not installed. */ consoleSelect(&debugConsoleTop); printf(PRINTFPOS(2,0)"Do not close RetroArch or turn off your console."); printf(PRINTFPOS(5,0)"Installing core:\n%s\n", path); /* Delete existing content of the pending titleid. */ deleteCia(ciaInfo.titleID); int error = installCia(ciaFile); if (error == -1) { printf(PRINTFPOS(25,0)"CIA install failed.\n"); wait_for_input(); } } FSFILE_Close(ciaFile); FSUSER_CloseArchive(ciaArchive); param.argc = 0; argsLength = 0; char *argLocation = param.args; while(args[param.argc]) { strcpy(argLocation, args[param.argc]); argLocation += strlen(args[param.argc]) + 1; argsLength += strlen(args[param.argc]) + 1; param.argc++; } res = APT_PrepareToDoApplicationJump(0, ciaInfo.titleID, 0x1); if (R_FAILED(res)) error_and_quit("CIA cant run, cant prepare."); res = APT_DoApplicationJump(¶m, sizeof(param.argc) + argsLength, argvHmac); if (R_FAILED(res)) error_and_quit("CIA cant run, cant jump."); /* wait for application jump, for some reason its not instant */ while(1); } /* should never be reached */ amExit(); fsExit(); errno = ENOTSUP; return -1; }