#include #include #include #include #include <3ds.h> #include "ctr/ctr_debug.h" #define FILE_CHUNK_SIZE 4096 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 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, 0); if (R_FAILED(failed)) { AM_CancelCIAInstall(outputHandle); if (R_DESCRIPTION(failed) == RD_ALREADY_EXISTS) return 1; return -1; } 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; 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) { /* not installed */ int error = installCia(ciaFile); if (error == -1) error_and_quit("Cant install CIA."); } 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; }