#include #include #include "types.h" #include "romfs.h" #include "utils.h" void romfs_init(romfs_context* ctx) { memset(ctx, 0, sizeof(romfs_context)); ivfc_init(&ctx->ivfc); } void romfs_set_file(romfs_context* ctx, FILE* file) { ctx->file = file; } void romfs_set_offset(romfs_context* ctx, u64 offset) { ctx->offset = offset; } void romfs_set_size(romfs_context* ctx, u64 size) { ctx->size = size; } void romfs_set_usersettings(romfs_context* ctx, settings* usersettings) { ctx->usersettings = usersettings; } void romfs_set_encrypted(romfs_context* ctx, u32 encrypted) { ctx->encrypted = encrypted; } void romfs_set_key(romfs_context* ctx, u8 key[16]) { memcpy(ctx->key, key, 16); } void romfs_set_counter(romfs_context* ctx, u8 counter[16]) { memcpy(ctx->counter, counter, 16); } void romfs_fseek(romfs_context* ctx, u64 offset) { u64 data_pos = offset - ctx->offset; fseeko64(ctx->file, offset, SEEK_SET); ctr_init_counter(&ctx->aes, ctx->key, ctx->counter); ctr_add_counter(&ctx->aes, (u32) (data_pos / 0x10)); } size_t romfs_fread(romfs_context* ctx, void* buffer, size_t size, size_t count) { size_t read; if ((read = fread(buffer, size, count, ctx->file)) != count) { //printf("romfs_fread() fail\n"); return read; } if (ctx->encrypted) { ctr_crypt_counter(&ctx->aes, buffer, buffer, size*read); } return read; } void romfs_process(romfs_context* ctx, u32 actions) { u32 dirblockoffset = 0; u32 dirblocksize = 0; u32 fileblockoffset = 0; u32 fileblocksize = 0; ivfc_set_offset(&ctx->ivfc, ctx->offset); ivfc_set_size(&ctx->ivfc, ctx->size); ivfc_set_file(&ctx->ivfc, ctx->file); ivfc_set_usersettings(&ctx->ivfc, ctx->usersettings); ivfc_set_counter(&ctx->ivfc, ctx->counter); ivfc_set_key(&ctx->ivfc, ctx->key); ivfc_set_encrypted(&ctx->ivfc, ctx->encrypted); ivfc_process(&ctx->ivfc, actions); romfs_fseek(ctx, ctx->offset); romfs_fread(ctx, &ctx->header, 1, sizeof(romfs_header)); if (getle32(ctx->header.magic) != MAGIC_IVFC) { fprintf(stdout, "Error, RomFS corrupted\n"); return; } ctx->infoblockoffset = (u32) (ctx->offset + 0x1000); romfs_fseek(ctx, ctx->infoblockoffset); romfs_fread(ctx, &ctx->infoheader, 1, sizeof(romfs_infoheader)); if (getle32(ctx->infoheader.headersize) != sizeof(romfs_infoheader)) { fprintf(stderr, "Error, info header mismatch\n"); return; } dirblockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.section[1].offset); dirblocksize = getle32(ctx->infoheader.section[1].size); fileblockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.section[3].offset); fileblocksize = getle32(ctx->infoheader.section[3].size); u32 hdrsize = getle32(ctx->infoheader.dataoffset); u8 *block = malloc(hdrsize); romfs_fseek(ctx, ctx->infoblockoffset); romfs_fread(ctx, block, hdrsize, 1); ctx->dirblock = malloc(dirblocksize); ctx->dirblocksize = dirblocksize; if(ctx->dirblock) memcpy(ctx->dirblock, block + getle32(ctx->infoheader.section[1].offset), dirblocksize); ctx->fileblock = malloc(fileblocksize); ctx->fileblocksize = fileblocksize; if (ctx->fileblock) memcpy(ctx->fileblock, block + getle32(ctx->infoheader.section[3].offset), fileblocksize); free(block); ctx->datablockoffset = ctx->infoblockoffset + getle32(ctx->infoheader.dataoffset); if (actions & InfoFlag) romfs_print(ctx); if (settings_get_romfs_dir_path(ctx->usersettings)->valid) ctx->extractdir = os_CopyConvertCharStr(settings_get_romfs_dir_path(ctx->usersettings)->pathname); else ctx->extractdir = NULL; romfs_visit_dir(ctx, 0, 0, actions, ctx->extractdir); free(ctx->extractdir); } int romfs_dirblock_read(romfs_context* ctx, u32 diroffset, u32 dirsize, void* buffer) { if (!ctx->dirblock) return 0; if (diroffset+dirsize > ctx->dirblocksize) return 0; memcpy(buffer, ctx->dirblock + diroffset, dirsize); return 1; } int romfs_dirblock_readentry(romfs_context* ctx, u32 diroffset, romfs_direntry* entry) { u32 size_without_name = sizeof(romfs_direntry) - ROMFS_MAXNAMESIZE; u32 namesize; if (!ctx->dirblock) return 0; if (!romfs_dirblock_read(ctx, diroffset, size_without_name, entry)) return 0; namesize = getle32(entry->namesize); if (namesize > (ROMFS_MAXNAMESIZE-2)) namesize = (ROMFS_MAXNAMESIZE-2); memset(entry->name + namesize, 0, 2); if (!romfs_dirblock_read(ctx, diroffset + size_without_name, namesize, entry->name)) return 0; return 1; } int romfs_fileblock_read(romfs_context* ctx, u32 fileoffset, u32 filesize, void* buffer) { if (!ctx->fileblock) return 0; if (fileoffset+filesize > ctx->fileblocksize) return 0; memcpy(buffer, ctx->fileblock + fileoffset, filesize); return 1; } int romfs_fileblock_readentry(romfs_context* ctx, u32 fileoffset, romfs_fileentry* entry) { u32 size_without_name = sizeof(romfs_fileentry) - ROMFS_MAXNAMESIZE; u32 namesize; if (!ctx->fileblock) return 0; if (!romfs_fileblock_read(ctx, fileoffset, size_without_name, entry)) return 0; namesize = getle32(entry->namesize); if (namesize > (ROMFS_MAXNAMESIZE-2)) namesize = (ROMFS_MAXNAMESIZE-2); memset(entry->name + namesize, 0, 2); if (!romfs_fileblock_read(ctx, fileoffset + size_without_name, namesize, entry->name)) return 0; return 1; } void romfs_visit_dir(romfs_context* ctx, u32 diroffset, u32 depth, u32 actions, const oschar_t* rootpath) { u32 siblingoffset; u32 childoffset; u32 fileoffset; oschar_t* currentpath; romfs_direntry* entry = &ctx->direntry; if (!romfs_dirblock_readentry(ctx, diroffset, entry)) return; // fprintf(stdout, "%08X %08X %08X %08X %08X ", // getle32(entry->parentoffset), getle32(entry->siblingoffset), getle32(entry->childoffset), // getle32(entry->fileoffset), getle32(entry->weirdoffset)); // fwprintf(stdout, L"%ls\n", entry->name); if (rootpath && os_strlen(rootpath)) { if (utf16_strlen((const utf16char_t*)entry->name) > 0) currentpath = os_AppendUTF16StrToPath(rootpath, (const utf16char_t*)entry->name); else // root dir, use the provided extract path instead of the empty root name. currentpath = os_CopyStr(rootpath); if (currentpath) { os_makedir(currentpath); } else { fputs("Error creating directory in root ", stderr); os_fputs(rootpath, stderr); fputs("\n", stderr); return; } } else { currentpath = os_CopyConvertUTF16Str((const utf16char_t*)entry->name); if (settings_get_list_romfs_files(ctx->usersettings)) { u32 i; for(i=0; isiblingoffset); childoffset = getle32(entry->childoffset); fileoffset = getle32(entry->fileoffset); if (fileoffset != (~0)) romfs_visit_file(ctx, fileoffset, depth+1, actions, currentpath); if (childoffset != (~0)) romfs_visit_dir(ctx, childoffset, depth+1, actions, currentpath); if (siblingoffset != (~0)) romfs_visit_dir(ctx, siblingoffset, depth, actions, rootpath); free(currentpath); } void romfs_visit_file(romfs_context* ctx, u32 fileoffset, u32 depth, u32 actions, const oschar_t* rootpath) { u32 siblingoffset = 0; oschar_t* currentpath = NULL; romfs_fileentry* entry = &ctx->fileentry; if (!romfs_fileblock_readentry(ctx, fileoffset, entry)) return; // fprintf(stdout, "%08X %08X %016llX %016llX %08X ", // getle32(entry->parentdiroffset), getle32(entry->siblingoffset), ctx->datablockoffset+getle64(entry->dataoffset), // getle64(entry->datasize), getle32(entry->unknown)); // fwprintf(stdout, L"%ls\n", entry->name); if (rootpath && os_strlen(rootpath)) { currentpath = os_AppendUTF16StrToPath(rootpath, (const utf16char_t*)entry->name); if (currentpath) { fputs("Saving ", stdout); os_fputs(currentpath, stdout); fputs("...\n", stdout); romfs_extract_datafile(ctx, getle64(entry->dataoffset), getle64(entry->datasize), currentpath); } else { fputs("Error creating file in root ", stderr); os_fputs(rootpath, stderr); fputs("\n", stderr); return; } } else { currentpath = os_CopyConvertUTF16Str((const utf16char_t*)entry->name); if (settings_get_list_romfs_files(ctx->usersettings)) { u32 i; for(i=0; isiblingoffset); if (siblingoffset != (~0)) romfs_visit_file(ctx, siblingoffset, depth, actions, rootpath); free(currentpath); } void romfs_extract_datafile(romfs_context* ctx, u64 offset, u64 size, const oschar_t* path) { FILE* outfile = 0; u32 max; u8 buffer[4096]; if (path == NULL || os_strlen(path) == 0) goto clean; offset += ctx->datablockoffset; romfs_fseek(ctx, offset); outfile = os_fopen(path, OS_MODE_WRITE); if (outfile == NULL) { fprintf(stderr, "Error opening file for writing\n"); goto clean; } while(size) { max = sizeof(buffer); if (max > size) max = (u32) size; if (max != romfs_fread(ctx, buffer, 1, max)) { fprintf(stderr, "Error reading file\n"); goto clean; } if (max != fwrite(buffer, 1, max, outfile)) { fprintf(stderr, "Error writing file\n"); goto clean; } size -= max; } clean: if (outfile) fclose(outfile); } void romfs_print(romfs_context* ctx) { u32 i; fprintf(stdout, "\nRomFS:\n"); fprintf(stdout, "Header size: 0x%08X\n", getle32(ctx->infoheader.headersize)); for(i=0; i<4; i++) { fprintf(stdout, "Section %d offset: 0x%08"PRIX64"\n", i, ctx->offset + 0x1000 + getle32(ctx->infoheader.section[i].offset)); fprintf(stdout, "Section %d size: 0x%08X\n", i, getle32(ctx->infoheader.section[i].size)); } fprintf(stdout, "Data offset: 0x%08"PRIX64"\n", ctx->offset + 0x1000 + getle32(ctx->infoheader.dataoffset)); }