summaryrefslogtreecommitdiffstats
path: root/wii_tmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'wii_tmd.c')
-rw-r--r--wii_tmd.c254
1 files changed, 254 insertions, 0 deletions
diff --git a/wii_tmd.c b/wii_tmd.c
new file mode 100644
index 0000000..8f4002d
--- /dev/null
+++ b/wii_tmd.c
@@ -0,0 +1,254 @@
+/**
+ * wii_tmd.c
+ */
+
+#include <stdio.h>
+#include "wii_tmd.h"
+
+const char *region_names[] = {
+ "JAP/NTSC-J",
+ "NTSC-U",
+ "PAL",
+ "REGION FREE",
+ "UNKNOWN",
+ NULL
+};
+
+typedef struct {
+ u16 id;
+ char *name;
+} group;
+
+static group group_names[] = {
+ {0x0001, "Nintendo"},
+ {0x3031, "Nintendo"}
+};
+
+typedef struct {
+ u32 id;
+ char *name;
+} title_category;
+
+static title_category title_category_names[] = {
+ {0x00000001, "System"},
+ {0x00010000, "Savedata"},
+ {0x00010001, "Channel"},
+ {0x00010002, "System Channel"},
+ {0x00010004, "Game Channel"},
+ {0x00010005, "Game Content"},
+ {0x00010008, "Hidden Channel"}
+};
+
+char *tmd_lookup_title_category_name(u32 category) {
+ u32 i;
+ const title_category *c;
+
+ for (i = 0; i < (sizeof(title_category_names)/sizeof(title_category)); i++) {
+ c = &title_category_names[i];
+ if (c->id == category)
+ return c->name;
+ }
+
+ return "Unknown";
+}
+
+char *tmd_lookup_group_name(u16 group_id) {
+ u32 i;
+ const group *g;
+ for (i = 0; i < (sizeof(group_names)/sizeof(group)); i++) {
+ g = &group_names[i];
+ if (g->id == group_id)
+ return g->name;
+ }
+
+ return "Unknown";
+}
+
+typedef struct {
+ u32 id;
+ char *format;
+ u8 replace;
+} system_title_name;
+
+static system_title_name system_title_names[] = {
+ {0x00000001, "BOOT2", 0},
+ {0x00000002, "System Menu", 0},
+ {0x00000100, "BC", 0},
+ {0x00000101, "MIOS", 0},
+ {0x00000000, "IOS%d", 1}, /* last item is default */
+};
+
+int tmd_get_system_title_name(u32 title_id, char *name) {
+ u32 i;
+ const system_title_name *t;
+ for (i = 0; i < (sizeof(system_title_names)/sizeof(system_title_name)); i++) {
+ t = &system_title_names[i];
+ if(t->id == title_id) {
+ break;
+ }
+ }
+
+ if(t->replace == 1)
+ return sprintf(name, t->format, title_id);
+ else
+ return sprintf(name, t->format);
+}
+
+typedef struct {
+ u8 id;
+ char *name;
+} system_code_name;
+
+static system_code_name system_code_names[] = {
+ {'C', "Commodore 64 Virtual Console"},
+ {'E', "NeoGeo Virtual Console"},
+ {'F', "Nintendo Virtual Console"},
+ {'H', "General channel"},
+ {'J', "Super Nintendo Virtual Console"},
+ {'L', "Sega Master System Virtual Console"},
+ {'M', "Sega Megadrive Virtual Console"},
+ {'N', "Nintendo 64 Virtual Console"},
+ {'P', "TurboGraFX Virtual Console"},
+ {'Q', "TurboGraFX CD Virtual Console"},
+ {'R', "Wii Disc"},
+ {'W', "WiiWare"}
+};
+
+char *tmd_lookup_system_code_name(u32 title_id) {
+ u32 i;
+ const system_code_name *s;
+ char *unknown = "Unknown \"?\"";
+
+ for (i = 0; i < (sizeof(system_code_names)/sizeof(system_code_name)); i++) {
+ s = &system_code_names[i];
+ if (s->id == (title_id & 0xff))
+ return s->name;
+ }
+
+ unknown[9] = (u8)(title_id & 0xff);
+ return unknown;
+}
+
+void dump_tmd_raw(u8 *tmd) {
+ u32 i, n;
+ u8 *p;
+
+ printf("issuer : %s\n", tmd + 0x140);
+ printf("sys_version : %016llx\n", be64(tmd + 0x0184));
+ printf("title_id : %04x (%4s)\n", be16(tmd + 0x018c), tmd + 0x0190);
+ printf("title_type : %08x\n", be32(tmd + 0x0194));
+ printf("group_id : %04x\n", be16(tmd + 0x0198));
+ printf("region : %s\n", region_names[tmd[OFFSET_REGION]]);
+ printf("title_version: %04x\n", be16(tmd + 0x01dc));
+ printf("num_contents : %04x\n", be16(tmd + 0x01de));
+ printf("boot_index : %04x\n", be16(tmd + 0x01e0));
+
+ n = be16(tmd + 0x01de);
+ p = tmd + 0x01e4;
+ for (i = 0; i < n; i++) {
+ printf("cid %08x index %04x type %04x size %08llx\n",
+ be32(p), be16(p + 4), be16(p + 6), be64(p + 8));
+ p += 0x24;
+ }
+}
+
+void print_tmd(tmd_header *tmd) {
+ printf("- tmd_header\n");
+ printf("sig_type : %08x (%s)\n", tmd->sig_type, (tmd->sig_type == RSA_2048 ? "RSA_2048": "RSA_4096"));
+ printf("issuer : %.64s\n", tmd->issuer);
+ printf("version : %x\n", tmd->version);
+ printf("ca_crl_version : %x\n", tmd->ca_crl_version);
+ printf("signer_crl_version: %x\n", tmd->signer_crl_version);
+ printf("sys_version : %016llx (IOS%llu)\n", tmd->sys_version, tmd->sys_version & 0xff);
+ printf("title_category : %08x (%s)\n", tmd->title_category, tmd_lookup_title_category_name(tmd->title_category));
+ printf("title_id : %08x ", tmd->title_id);
+ if(tmd->title_category == TITLE_CATEGORY_SYSTEM) {
+ printf("(IOS%d)\n", be32((u8*)&tmd->title_id));
+ }
+ else
+ {
+ printf("(%.4s)\n", (char*)&tmd->title_id);
+ printf("system : %s\n", tmd_lookup_system_code_name(tmd->title_id));
+ }
+
+ printf("title_type : %08x\n", tmd->title_type);
+ printf("group_id : %04x (%s)\n", tmd->group_id, tmd_lookup_group_name(tmd->group_id));
+ printf("region_code : %s (%d)\n", region_names[tmd->region_code], tmd->region_code);
+ printf("access_rights : %08x\n", tmd->access_rights);
+ printf("title_version : %d (%04x)\n", tmd->title_version, tmd->title_version);
+ printf("num_contents : %04x\n", tmd->num_contents);
+ printf("boot_index : %04x\n", tmd->boot_index);
+}
+
+void print_tmd_content_records(tmd_header *tmd, tmd_content_record *tmd_content) {
+ u8 i, n;
+ tmd_content_record *cr;
+
+ for (i = 0; i < tmd->num_contents; i++) {
+ cr = &tmd_content[i];
+ printf("- tmd_content_record[%d]\n", i);
+ printf("cid : %08x\n", cr->cid);
+ printf("index: %04x\n", cr->index);
+ printf("type : %04x\n", cr->type);
+ printf("size : %1$016llx (%1$llu)\n", cr->size);
+ printf("hash : ");
+ for (n = 0; n < 20; n++)
+ printf("%02x", cr->hash[n]);
+ printf("\n");
+ }
+}
+
+u32 tmd_get_block_size(tmd_header *tmd, tmd_content_record *tmd_content) {
+ u32 blocks = 0;
+ u8 i;
+ u64 install_size = 0;
+ tmd_content_record *cr;
+
+ for (i = 0; i < tmd->num_contents; i++) {
+ cr = &tmd_content[i];
+ if (cr->type == 1) {
+ install_size += cr->size;
+ }
+ }
+
+ blocks = (install_size/WII_BLOCK_SIZE) + ((install_size % WII_BLOCK_SIZE) == 0 ? 0: 1);
+
+ return blocks;
+}
+
+void tmd_read(FILE *f, tmd_header *tmd) {
+ /* read head into struct */
+ fread(tmd, sizeof(tmd_header), 1, f);
+
+ /* convert to big endian */
+ tmd->sig_type = be32((u8*)&tmd->sig_type);
+ tmd->title_category = be32((u8*)&tmd->title_category);
+ tmd->sys_version = be64((u8*)&tmd->sys_version);
+ tmd->title_type = be32((u8*)&tmd->title_type);
+ tmd->group_id = be16((u8*)&tmd->group_id);
+ tmd->fill4 = be16((u8*)&tmd->fill4);
+ tmd->region_code = be16((u8*)&tmd->region_code);
+ tmd->access_rights = be32((u8*)&tmd->access_rights);
+ tmd->title_version = be16((u8*)&tmd->title_version);
+ tmd->num_contents = be16((u8*)&tmd->num_contents);
+ tmd->boot_index = be16((u8*)&tmd->boot_index);
+ tmd->fill6 = be16((u8*)&tmd->fill6);
+}
+
+void tmd_read_content_records(FILE *f, tmd_header *tmd, tmd_content_record *tmd_content) {
+ u8 i;
+ tmd_content_record *cr;
+
+ fseek(f, OFFSET_TMD + sizeof(tmd_header), SEEK_SET);
+
+ for (i = 0; i < tmd->num_contents; i++) {
+ cr = tmd_content + i;
+ fread(cr, sizeof(tmd_content_record), 1, f);
+
+ /* convert to big endian */
+ cr->cid = be32((u8*)&cr->cid);
+ cr->index = be16((u8*)&cr->index);
+ cr->type = be16((u8*)&cr->type);
+ cr->size = be64((u8*)&cr->size);
+ }
+}