Title metadata

From WiiUBrew
Revision as of 03:20, 1 September 2022 by Hallowizer2 (talk | contribs) (→‎Main header: the last field is not padding “mod 64” and GPL ES defines it as minor version)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Title metadata is a format used to store information about a title (a single standalone game, channel, etc.) and all its installed contents, including which contents they consist of and their SHA1 hashes.

Many operations are done in terms of 64-byte blocks, which means you will often see padding out to the nearest 64-byte boundary at the end of a field.


Signed blob header

Absolute offset Length Description
0x000 4 Signature type
0x004 256 Signature covering Main header and root CMD group hash in the v1 header
0x104 60 Padding modulo 64

Main header

Absolute offset Length Description
0x140 64 Issuer
0x180 1 Version
0x181 1 ca_crl_version
0x182 1 signer_crl_version
0x183 1 Padding modulo 64
0x184 8 System Version (the OS used by the title. For non-Espresso binaries, set to 0-0.)
0x18C 8 Title ID
0x194 4 Title type
0x198 2 Group ID
0x19A 62 reserved
0x1D8 4 Access rights (flags for DVD-video access and full PPC hardware access)
0x1DC 2 Title version
0x1DE 2 Number of contents (nbr_cont)
0x1E0 2 boot index
0x1E2 2 Minor version

v1 header

Absolute offset Length Description
0x1E4 32 SHA-256 hash of CMD groups
0x204 2112 64 CMD groups
Offset Length Description
0x00 2 Offset
0x02 2 Number of CMDs in the group
0x04 32 SHA-256 hash of the CMDs in the group

Content metadata (CMD)

The TMD headers are followed by a variable number of CMDs (one per content).

Offset Length Description
0x00 4 Content ID
0x04 2 Index
0x06 2 Type
0x08 8 Size
0x10 32 SHA-1 hash padded with zeros to the length of a SHA-256 hash


Start Length Description
0x000 4 Signature type
0x004 256 Signature
0x104 64 Issuer
0x124 4 Tag
0x128 64 Name
0x168 Key

Example code application

 typedef unsigned char  u8;
 typedef unsigned short u16;
 typedef unsigned int   u32; 
/* On a 32bit system, long is only 4 bytes- use long long instead */
 typedef unsigned long u64;
 typedef struct {
   u32 cid;	      // content id
   u16 index;	      // # number of the file
   u16  type;	      // Wii: normal 0x0001; shared 0x8001 | Wii U: 0x2001; 0x2003; 0x6003; Bitmask, check last bit for .H3 hashes availability.
   u64 size;
   u8  hash [20];     //  SHA1 hash content
 } content_record;    // size: 0x24 bytes
 enum sig_type {
      RSA_2048 = 0x00010001,
      RSA_4096 = 0x00010000
 typedef struct {
        u32 sig_type; 
        u8 sig[256];
        u8 fill1[60];
        u8 issuer[64];         // Root-CA%08x-CP%08x
        u8 version;
        u8 ca_crl_version;
        u8 signer_crl_version;
        u8 fill2;
        u64 sys_version;
        u64 title_id;
        u32 title_type;
        u16 group_id;          // publisher
        u8 reserved[62];
        u32 access_rights;
        u16 title_version;
        u16 num_contents;
        u16 boot_index;
        u16 fill3;
        content_record contents[num_contents];
 } tmd;

The tmd is then followed by a chain of certificates, where each certificate is of the general form

  u32 sig_type; 
  u8 sig[256];    // 256 for RSA_2048, 512 for RSA_4096
  u8 issuer[32];
  u32 tag;        // identifies what is being signed
  u8 name[64];    // name of thing being signed
  u8 key[...];

There is also a structure called a TmdView which is select sections of the Tmd. It has a length of 0x60+0x10*number_of_contents. The structure is somewhat like this [as returned by ES_GetTmdView] :

struct tmd_view_content_t
	uint32_t id;
	uint16_t index;
	uint16_t type;
	uint64_t size;

struct tmd_view_t
	uint8_t version;               //0x0000;
	uint8_t filler[3];
	uint64_t ios_title_id;         //0x0004
	uint64_t title_id;             //0x00c
	uint32_t title_type;           //0x0014
	uint16_t group_id;             //0x0018
	uint8_t reserved[0x3e];        //0x001a This is the same reserved 0x3e bytes from the tmd.
	uint16_t title_version;        //0x0058
	uint16_t number_contents;      //0x005a
	tmd_view_content_t contents[]; //0x005c

Checking for .H3 hashes (Source: wud2app)

uint16_t type = __builtin_bswap16(tmd->Contents[curCont].Type);
if(type & 2) //.H3 Hashes are used/needed
 //Read|Create|DL .h3