int main(int argc, char* argv[]) { toolcontext ctx; u8 magic[4]; char infname[512]; int c; u32 ncchoffset = ~0; char keysetfname[512] = "keys.xml"; keyset tmpkeys; unsigned int checkkeysetfile = 0; memset(&ctx, 0, sizeof(toolcontext)); ctx.actions = InfoFlag | ExtractFlag; ctx.filetype = FILETYPE_UNKNOWN; settings_init(&ctx.usersettings); keyset_init(&ctx.usersettings.keys); keyset_init(&tmpkeys); while (1) { int option_index; static struct option long_options[] = { {"extract", 0, NULL, 'x'}, {"plain", 0, NULL, 'p'}, {"info", 0, NULL, 'i'}, {"exefs", 1, NULL, 0}, {"romfs", 1, NULL, 1}, {"exheader", 1, NULL, 2}, {"certs", 1, NULL, 3}, {"tik", 1, NULL, 4}, {"tmd", 1, NULL, 5}, {"contents", 1, NULL, 6}, {"meta", 1, NULL, 7}, {"exefsdir", 1, NULL, 8}, {"keyset", 1, NULL, 'k'}, {"ncch", 1, NULL, 'n'}, {"verbose", 0, NULL, 'v'}, {"verify", 0, NULL, 'y'}, {"raw", 0, NULL, 'r'}, {"unitsize", 1, NULL, 9}, {"showkeys", 0, NULL, 10}, {"commonkey", 1, NULL, 11}, {"ncchkey", 1, NULL, 12}, {"intype", 1, NULL, 't'}, {"lzssout", 1, NULL, 13}, {"firmdir", 1, NULL, 14}, {"ncchsyskey", 1, NULL, 15}, {"wav", 1, NULL, 16}, {"romfsdir", 1, NULL, 17}, {"listromfs", 0, NULL, 18}, {"wavloops", 1, NULL, 19}, {"logo", 1, NULL, 20}, {NULL}, }; c = getopt_long(argc, argv, "ryxivpk:n:t:", long_options, &option_index); if (c == -1) break; switch (c) { case 'x': ctx.actions |= ExtractFlag; break; case 'v': ctx.actions |= VerboseFlag; break; case 'y': ctx.actions |= VerifyFlag; break; case 'p': ctx.actions |= PlainFlag; break; case 'r': ctx.actions |= RawFlag; break; case 'i': ctx.actions |= InfoFlag; break; case 'n': ncchoffset = strtoul(optarg, 0, 0); break; case 'k': strncpy(keysetfname, optarg, sizeof(keysetfname)); checkkeysetfile = 1; break; case 't': if (!strcmp(optarg, "exheader")) ctx.filetype = FILETYPE_EXHEADER; else if (!strcmp(optarg, "ncch")) ctx.filetype = FILETYPE_CXI; else if (!strcmp(optarg, "ncsd")) ctx.filetype = FILETYPE_CCI; else if (!strcmp(optarg, "cia")) ctx.filetype = FILETYPE_CIA; else if (!strcmp(optarg, "tmd")) ctx.filetype = FILETYPE_TMD; else if (!strcmp(optarg, "lzss")) ctx.filetype = FILETYPE_LZSS; else if (!strcmp(optarg, "firm")) ctx.filetype = FILETYPE_FIRM; else if (!strcmp(optarg, "cwav")) ctx.filetype = FILETYPE_CWAV; else if (!strcmp(optarg, "romfs")) ctx.filetype = FILETYPE_ROMFS; break; case 0: settings_set_exefs_path(&ctx.usersettings, optarg); break; case 1: settings_set_romfs_path(&ctx.usersettings, optarg); break; case 2: settings_set_exheader_path(&ctx.usersettings, optarg); break; case 3: settings_set_certs_path(&ctx.usersettings, optarg); break; case 4: settings_set_tik_path(&ctx.usersettings, optarg); break; case 5: settings_set_tmd_path(&ctx.usersettings, optarg); break; case 6: settings_set_content_path(&ctx.usersettings, optarg); break; case 7: settings_set_content_path(&ctx.usersettings, optarg); break; case 8: settings_set_exefs_dir_path(&ctx.usersettings, optarg); break; case 9: settings_set_mediaunit_size(&ctx.usersettings, strtoul(optarg, 0, 0)); break; case 10: ctx.actions |= ShowKeysFlag; break; case 11: keyset_parse_commonkey(&tmpkeys, optarg, strlen(optarg)); break; case 12: keyset_parse_ncchkey(&tmpkeys, optarg, strlen(optarg)); break; case 13: settings_set_lzss_path(&ctx.usersettings, optarg); break; case 14: settings_set_firm_dir_path(&ctx.usersettings, optarg); break; case 15: keyset_parse_ncchfixedsystemkey(&tmpkeys, optarg, strlen(optarg)); break; case 16: settings_set_wav_path(&ctx.usersettings, optarg); break; case 17: settings_set_romfs_dir_path(&ctx.usersettings, optarg); break; case 18: settings_set_list_romfs_files(&ctx.usersettings, 1); break; case 19: settings_set_cwav_loopcount(&ctx.usersettings, strtoul(optarg, 0, 0)); break; case 20: settings_set_logo_path(&ctx.usersettings, optarg); break; default: usage(argv[0]); } } if (optind == argc - 1) { // Exactly one extra argument - an input file strncpy(infname, argv[optind], sizeof(infname)); } else if ( (optind < argc) || (argc == 1) ) { // Too many extra args usage(argv[0]); } keyset_load(&ctx.usersettings.keys, keysetfname, (ctx.actions & VerboseFlag) | checkkeysetfile); keyset_merge(&ctx.usersettings.keys, &tmpkeys); if (ctx.actions & ShowKeysFlag) keyset_dump(&ctx.usersettings.keys); ctx.infile = fopen(infname, "rb"); if (ctx.infile == 0) { fprintf(stderr, "error: could not open input file!\n"); return -1; } fseek(ctx.infile, 0, SEEK_END); ctx.infilesize = ftell(ctx.infile); fseek(ctx.infile, 0, SEEK_SET); if (ctx.filetype == FILETYPE_UNKNOWN) { fseek(ctx.infile, 0x100, SEEK_SET); fread(&magic, 1, 4, ctx.infile); switch(getle32(magic)) { case MAGIC_NCCH: ctx.filetype = FILETYPE_CXI; break; case MAGIC_NCSD: ctx.filetype = FILETYPE_CCI; break; default: break; } } if (ctx.filetype == FILETYPE_UNKNOWN) { fseek(ctx.infile, 0, SEEK_SET); fread(magic, 1, 4, ctx.infile); switch(getle32(magic)) { case 0x2020: ctx.filetype = FILETYPE_CIA; break; case MAGIC_FIRM: ctx.filetype = FILETYPE_FIRM; break; case MAGIC_CWAV: ctx.filetype = FILETYPE_CWAV; break; case MAGIC_IVFC: ctx.filetype = FILETYPE_ROMFS; // TODO: need to determine more here.. savegames use IVFC too, but is not ROMFS. break; } } if (ctx.filetype == FILETYPE_UNKNOWN) { fprintf(stdout, "Unknown file\n"); exit(1); } switch(ctx.filetype) { case FILETYPE_CCI: { ncsd_context ncsdctx; ncsd_init(&ncsdctx); ncsd_set_file(&ncsdctx, ctx.infile); ncsd_set_size(&ncsdctx, ctx.infilesize); ncsd_set_usersettings(&ncsdctx, &ctx.usersettings); ncsd_process(&ncsdctx, ctx.actions); break; } case FILETYPE_FIRM: { firm_context firmctx; firm_init(&firmctx); firm_set_file(&firmctx, ctx.infile); firm_set_size(&firmctx, ctx.infilesize); firm_set_usersettings(&firmctx, &ctx.usersettings); firm_process(&firmctx, ctx.actions); break; } case FILETYPE_CXI: { ncch_context ncchctx; ncch_init(&ncchctx); ncch_set_file(&ncchctx, ctx.infile); ncch_set_size(&ncchctx, ctx.infilesize); ncch_set_usersettings(&ncchctx, &ctx.usersettings); ncch_process(&ncchctx, ctx.actions); break; } case FILETYPE_CIA: { cia_context ciactx; cia_init(&ciactx); cia_set_file(&ciactx, ctx.infile); cia_set_size(&ciactx, ctx.infilesize); cia_set_usersettings(&ciactx, &ctx.usersettings); cia_process(&ciactx, ctx.actions); break; } case FILETYPE_EXHEADER: { exheader_context exheaderctx; exheader_init(&exheaderctx); exheader_set_file(&exheaderctx, ctx.infile); exheader_set_size(&exheaderctx, ctx.infilesize); settings_set_ignore_programid(&ctx.usersettings, 1); exheader_set_usersettings(&exheaderctx, &ctx.usersettings); exheader_process(&exheaderctx, ctx.actions); break; } case FILETYPE_TMD: { tmd_context tmdctx; tmd_init(&tmdctx); tmd_set_file(&tmdctx, ctx.infile); tmd_set_size(&tmdctx, ctx.infilesize); tmd_set_usersettings(&tmdctx, &ctx.usersettings); tmd_process(&tmdctx, ctx.actions); break; } case FILETYPE_LZSS: { lzss_context lzssctx; lzss_init(&lzssctx); lzss_set_file(&lzssctx, ctx.infile); lzss_set_size(&lzssctx, ctx.infilesize); lzss_set_usersettings(&lzssctx, &ctx.usersettings); lzss_process(&lzssctx, ctx.actions); break; } case FILETYPE_CWAV: { cwav_context cwavctx; cwav_init(&cwavctx); cwav_set_file(&cwavctx, ctx.infile); cwav_set_size(&cwavctx, ctx.infilesize); cwav_set_usersettings(&cwavctx, &ctx.usersettings); cwav_process(&cwavctx, ctx.actions); break; } case FILETYPE_ROMFS: { romfs_context romfsctx; romfs_init(&romfsctx); romfs_set_file(&romfsctx, ctx.infile); romfs_set_size(&romfsctx, ctx.infilesize); romfs_set_usersettings(&romfsctx, &ctx.usersettings); romfs_process(&romfsctx, ctx.actions); break; } } if (ctx.infile) fclose(ctx.infile); return 0; }
void cia_process(cia_context* ctx, u32 actions) { fseek(ctx->file, 0, SEEK_SET); if (fread(&ctx->header, 1, sizeof(ctr_ciaheader), ctx->file) != sizeof(ctr_ciaheader)) { fprintf(stderr, "Error reading CIA header\n"); goto clean; } ctx->sizeheader = getle32(ctx->header.headersize); ctx->sizecert = getle32(ctx->header.certsize); ctx->sizetik = getle32(ctx->header.ticketsize); ctx->sizetmd = getle32(ctx->header.tmdsize); ctx->sizecontent = (u32)getle64(ctx->header.contentsize); ctx->sizemeta = getle32(ctx->header.metasize); ctx->offsetcerts = align(ctx->sizeheader, 64); ctx->offsettik = align(ctx->offsetcerts + ctx->sizecert, 64); ctx->offsettmd = align(ctx->offsettik + ctx->sizetik, 64); ctx->offsetcontent = align(ctx->offsettmd + ctx->sizetmd, 64); ctx->offsetmeta = align(ctx->offsetcontent + ctx->sizecontent, 64); if (actions & InfoFlag) cia_print(ctx); tik_set_file(&ctx->tik, ctx->file); tik_set_offset(&ctx->tik, ctx->offsettik); tik_set_size(&ctx->tik, ctx->sizetik); tik_set_usersettings(&ctx->tik, ctx->usersettings); tik_process(&ctx->tik, actions); memset(ctx->iv, 0, 16); if (settings_get_common_key(ctx->usersettings)) tik_get_decrypted_titlekey(&ctx->tik, ctx->titlekey); tmd_set_file(&ctx->tmd, ctx->file); tmd_set_offset(&ctx->tmd, ctx->offsettmd); tmd_set_size(&ctx->tmd, ctx->sizetmd); tmd_set_usersettings(&ctx->tmd, ctx->usersettings); tmd_process(&ctx->tmd, actions); if (actions & VerifyFlag) { cia_verify_contents(ctx, actions); } if (actions & InfoFlag || actions & VerifyFlag) tmd_print(&ctx->tmd); if (actions & ExtractFlag) { cia_save(ctx, CIATYPE_CERTS, actions); cia_save(ctx, CIATYPE_TMD, actions); cia_save(ctx, CIATYPE_TIK, actions); cia_save(ctx, CIATYPE_META, actions); cia_save(ctx, CIATYPE_CONTENT, actions); } clean: return; }
int action_parse(toolcontext* ctx, char* fname) { u8 magic[4]; ctx->infile = fopen(fname, "rb"); if (ctx->infile == 0) { fprintf(stderr, "error: could not open input file!\n"); return -1; } fseek(ctx->infile, 0, SEEK_END); ctx->infilesize = ftell(ctx->infile); fseek(ctx->infile, 0, SEEK_SET); if (ctx->filetype == FILETYPE_UNKNOWN) { fseek(ctx->infile, 0x100, SEEK_SET); fread(&magic, 1, 4, ctx->infile); switch(getle32(magic)) { case MAGIC_NCCH: ctx->filetype = FILETYPE_CXI; break; case MAGIC_NCSD: ctx->filetype = FILETYPE_CCI; break; default: break; } } if (ctx->filetype == FILETYPE_UNKNOWN) { fseek(ctx->infile, 0, SEEK_SET); fread(magic, 1, 4, ctx->infile); switch(getle32(magic)) { case 0x2020: ctx->filetype = FILETYPE_CIA; break; case MAGIC_FIRM: ctx->filetype = FILETYPE_FIRM; break; case MAGIC_CWAV: ctx->filetype = FILETYPE_CWAV; break; case MAGIC_IVFC: ctx->filetype = FILETYPE_ROMFS; // TODO: need to determine more here.. savegames use IVFC too, but is not ROMFS. break; } } if (ctx->filetype == FILETYPE_UNKNOWN) { fprintf(stdout, "Unknown file\n"); exit(1); } switch(ctx->filetype) { case FILETYPE_CCI: { ncsd_context ncsdctx; ncsd_init(&ncsdctx); ncsd_set_file(&ncsdctx, ctx->infile); ncsd_set_size(&ncsdctx, ctx->infilesize); ncsd_set_usersettings(&ncsdctx, &ctx->usersettings); ncsd_process(&ncsdctx, ctx->actions); break; } case FILETYPE_FIRM: { firm_context firmctx; firm_init(&firmctx); firm_set_file(&firmctx, ctx->infile); firm_set_size(&firmctx, ctx->infilesize); firm_set_usersettings(&firmctx, &ctx->usersettings); firm_process(&firmctx, ctx->actions); break; } case FILETYPE_CXI: { ncch_context ncchctx; ncch_init(&ncchctx); ncch_set_file(&ncchctx, ctx->infile); ncch_set_size(&ncchctx, ctx->infilesize); ncch_set_usersettings(&ncchctx, &ctx->usersettings); ncch_process(&ncchctx, ctx->actions); break; } case FILETYPE_CIA: { cia_context ciactx; cia_init(&ciactx); cia_set_file(&ciactx, ctx->infile); cia_set_size(&ciactx, ctx->infilesize); cia_set_usersettings(&ciactx, &ctx->usersettings); cia_process(&ciactx, ctx->actions); break; } case FILETYPE_EXHEADER: { exheader_context exheaderctx; exheader_init(&exheaderctx); exheader_set_file(&exheaderctx, ctx->infile); exheader_set_size(&exheaderctx, ctx->infilesize); settings_set_ignore_programid(&ctx->usersettings, 1); exheader_set_usersettings(&exheaderctx, &ctx->usersettings); exheader_process(&exheaderctx, ctx->actions); break; } case FILETYPE_TMD: { tmd_context tmdctx; tmd_init(&tmdctx); tmd_set_file(&tmdctx, ctx->infile); tmd_set_size(&tmdctx, ctx->infilesize); tmd_set_usersettings(&tmdctx, &ctx->usersettings); tmd_process(&tmdctx, ctx->actions); break; } case FILETYPE_LZSS: { lzss_context lzssctx; lzss_init(&lzssctx); lzss_set_file(&lzssctx, ctx->infile); lzss_set_size(&lzssctx, ctx->infilesize); lzss_set_usersettings(&lzssctx, &ctx->usersettings); lzss_process(&lzssctx, ctx->actions); break; } case FILETYPE_CWAV: { cwav_context cwavctx; cwav_init(&cwavctx); cwav_set_file(&cwavctx, ctx->infile); cwav_set_size(&cwavctx, ctx->infilesize); cwav_set_usersettings(&cwavctx, &ctx->usersettings); cwav_process(&cwavctx, ctx->actions); break; } case FILETYPE_EXEFS: { exefs_context exefsctx; exefs_init(&exefsctx); exefs_set_file(&exefsctx, ctx->infile); exefs_set_size(&exefsctx, ctx->infilesize); exefs_set_usersettings(&exefsctx, &ctx->usersettings); exefs_process(&exefsctx, ctx->actions); break; } case FILETYPE_ROMFS: { romfs_context romfsctx; romfs_init(&romfsctx); romfs_set_file(&romfsctx, ctx->infile); romfs_set_size(&romfsctx, ctx->infilesize); romfs_set_usersettings(&romfsctx, &ctx->usersettings); romfs_process(&romfsctx, ctx->actions); break; } } if (ctx->infile) fclose(ctx->infile); return 0; }