├── FunKii ├── __init__.py └── __main__.py ├── wadtools ├── COPIA ├── COPYING ├── Makefile ├── tools.h ├── bn.c ├── imet_signer.c ├── wadsigncheck.c ├── wadunpacker.c ├── wadpacker.c └── tools.c ├── README.md └── setup.py /FunKii/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wadtools/COPIA: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AuroraWright/FunKii/HEAD/wadtools/COPIA -------------------------------------------------------------------------------- /wadtools/COPYING: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AuroraWright/FunKii/HEAD/wadtools/COPYING -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This tool is a modification of FunKiiU and just allows you to download Wii contents from the Nintendo CDN and package them into .wad files. You need to compile the provided wadpacker and place it in your PATH. A title key is needed for anything except system titles and some free titles. DLCs will be correctly handled as well. -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='FunKii', 5 | version='1.0', 6 | url='https://github.com/llakssz/FunKiiU', 7 | author='cearp/the cerea1killer/AuroraWright', 8 | license='GPLv3', 9 | description='Download Wii titles from the CDN', 10 | install_requires=[''], 11 | packages=find_packages(), 12 | entry_points={'console_scripts': ['FunKii=FunKii.__main__:main']}, 13 | ) 14 | -------------------------------------------------------------------------------- /wadtools/Makefile: -------------------------------------------------------------------------------- 1 | PROGS = wadpacker wadunpacker wadsigncheck imet_signer 2 | COMMON = tools.o bn.o 3 | DEFINES = -DLARGE_FILES -D_FILE_OFFSET_BITS=64 4 | LIBS = -lcrypto 5 | 6 | CC = gcc 7 | CFLAGS = -Wall -W -Os 8 | LDFLAGS = 9 | 10 | 11 | OBJS = $(patsubst %,%.o,$(PROGS)) $(COMMON) 12 | 13 | all: $(PROGS) 14 | 15 | $(PROGS): %: %.o $(COMMON) Makefile 16 | $(CC) $(CFLAGS) $(LDFLAGS) $< $(COMMON) -L/usr/local/opt/openssl/lib $(LIBS) -o $@ 17 | 18 | $(OBJS): %.o: %.c tools.h Makefile 19 | $(CC) $(CFLAGS) $(DEFINES) -I/usr/local/opt/openssl/include -c $< -o $@ 20 | 21 | clean: 22 | -rm -f $(OBJS) $(PROGS) 23 | -------------------------------------------------------------------------------- /wadtools/tools.h: -------------------------------------------------------------------------------- 1 | // Copyright 2007,2008 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #ifndef _TOOLS_H 6 | #define _TOOLS_H 7 | 8 | // basic data types 9 | typedef unsigned char u8; 10 | typedef unsigned short u16; 11 | typedef unsigned int u32; 12 | typedef unsigned long long u64; 13 | 14 | u16 be16(const u8 *p); 15 | u32 be32(const u8 *p); 16 | u64 be64(const u8 *p); 17 | u64 be34(const u8 *p); 18 | 19 | void wbe16(u8 *p, u16 x); 20 | void wbe32(u8 *p, u32 x); 21 | void wbe64(u8 *p, u64 x); 22 | 23 | //#define round_down(x,n) ((x) & -(n)) 24 | #define round_up(x,n) (-(-(x) & -(n))) 25 | 26 | // bignum 27 | int bn_compare(u8 *a, u8 *b, u32 n); 28 | void bn_sub_modulus(u8 *a, u8 *N, u32 n); 29 | void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n); 30 | void bn_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n); 31 | void bn_inv(u8 *d, u8 *a, u8 *N, u32 n); // only for prime N 32 | void bn_exp(u8 *d, u8 *a, u8 *N, u32 n, u8 *e, u32 en); 33 | 34 | // crypto 35 | void md5(u8 *data, u32 len, u8 *hash); 36 | void sha(u8 *data, u32 len, u8 *hash); 37 | void get_key(const char *name, u8 *key, u32 len); 38 | void aes_cbc_dec(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out); 39 | void aes_cbc_enc(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out); 40 | void decrypt_title_key(u8 *tik, u8 *title_key); 41 | int check_cert_chain(u8 *data, u32 data_len, u8 *cert, u32 cert_len); 42 | int TMD_resign(u8 *tmd, u32 tmd_len); 43 | int Ticket_resign(u8 *tmd, u32 tmd_len, u8 type); 44 | u64 getfilesize(u32 *file); 45 | void printHashSHA(u8 *hash); 46 | void printHashMD5(u8 *hash); 47 | 48 | // compression 49 | void do_yaz0(u8 *in, u32 in_size, u8 *out, u32 out_size); 50 | 51 | // error handling 52 | void fatal(const char *s, ...); 53 | 54 | // output formatting 55 | void print_bytes(u8 *x, u32 n); 56 | void hexdump(u8 *x, u32 n); 57 | void dump_tmd(u8 *tmd); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /wadtools/bn.c: -------------------------------------------------------------------------------- 1 | // Copyright 2007,2008 Segher Boessenkool 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | 5 | #include 6 | #include 7 | 8 | #include "tools.h" 9 | 10 | static void bn_print(char *name, u8 *a, u32 n) 11 | { 12 | u32 i; 13 | 14 | printf("%s = ", name); 15 | 16 | for (i = 0; i < n; i++) 17 | printf("%02x", a[i]); 18 | 19 | printf("\n"); 20 | } 21 | 22 | static void bn_zero(u8 *d, u32 n) 23 | { 24 | memset(d, 0, n); 25 | } 26 | 27 | static void bn_copy(u8 *d, u8 *a, u32 n) 28 | { 29 | memcpy(d, a, n); 30 | } 31 | 32 | int bn_compare(u8 *a, u8 *b, u32 n) 33 | { 34 | u32 i; 35 | 36 | for (i = 0; i < n; i++) { 37 | if (a[i] < b[i]) 38 | return -1; 39 | if (a[i] > b[i]) 40 | return 1; 41 | } 42 | 43 | return 0; 44 | } 45 | 46 | void bn_sub_modulus(u8 *a, u8 *N, u32 n) 47 | { 48 | u32 i; 49 | u32 dig; 50 | u8 c; 51 | 52 | c = 0; 53 | for (i = n - 1; i < n; i--) { 54 | dig = N[i] + c; 55 | c = (a[i] < dig); 56 | a[i] -= dig; 57 | } 58 | } 59 | 60 | void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n) 61 | { 62 | u32 i; 63 | u32 dig; 64 | u8 c; 65 | 66 | c = 0; 67 | for (i = n - 1; i < n; i--) { 68 | dig = a[i] + b[i] + c; 69 | c = (dig >= 0x100); 70 | d[i] = dig; 71 | } 72 | 73 | if (c) 74 | bn_sub_modulus(d, N, n); 75 | 76 | if (bn_compare(d, N, n) >= 0) 77 | bn_sub_modulus(d, N, n); 78 | } 79 | 80 | void bn_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n) 81 | { 82 | u32 i; 83 | u8 mask; 84 | 85 | bn_zero(d, n); 86 | 87 | for (i = 0; i < n; i++) 88 | for (mask = 0x80; mask != 0; mask >>= 1) { 89 | bn_add(d, d, d, N, n); 90 | if ((a[i] & mask) != 0) 91 | bn_add(d, d, b, N, n); 92 | } 93 | } 94 | 95 | void bn_exp(u8 *d, u8 *a, u8 *N, u32 n, u8 *e, u32 en) 96 | { 97 | u8 t[512]; 98 | u32 i; 99 | u8 mask; 100 | 101 | bn_zero(d, n); 102 | d[n-1] = 1; 103 | for (i = 0; i < en; i++) 104 | for (mask = 0x80; mask != 0; mask >>= 1) { 105 | bn_mul(t, d, d, N, n); 106 | if ((e[i] & mask) != 0) 107 | bn_mul(d, t, a, N, n); 108 | else 109 | bn_copy(d, t, n); 110 | } 111 | } 112 | 113 | // only for prime N -- stupid but lazy, see if I care 114 | void bn_inv(u8 *d, u8 *a, u8 *N, u32 n) 115 | { 116 | u8 t[512], s[512]; 117 | 118 | bn_copy(t, N, n); 119 | bn_zero(s, n); 120 | s[n-1] = 2; 121 | bn_sub_modulus(t, s, n); 122 | bn_exp(d, a, N, n, t, n); 123 | } 124 | -------------------------------------------------------------------------------- /wadtools/imet_signer.c: -------------------------------------------------------------------------------- 1 | // By BFGR 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | // BFGR WadTools v0.39a 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "tools.h" 14 | 15 | int main(int argc, char **argv) 16 | { 17 | u64 imet_len; 18 | u8 *imet; 19 | u8 hash[16]; 20 | FILE *fp; 21 | u32 i,j; 22 | u8 *text; 23 | u8 add = 0x00; 24 | u8 *banner; 25 | u8 *author; 26 | 27 | if (argc==3) add = 0x01; 28 | else if (argc==2) add = 0x00; 29 | else { 30 | printf("USAGE: imet_signer [ text_file ]\n"); 31 | exit(-1); 32 | } 33 | 34 | fp = fopen(argv[1], "rb"); 35 | imet_len = getfilesize(fp); 36 | imet = (u8 *)malloc(imet_len); 37 | fread(imet, imet_len, 1, fp); 38 | fclose(fp); 39 | memset(imet + 0x0630, 0x00, 0x0010); // Remove MD5 sum 40 | md5(imet + 0x0040, 0x600, hash); 41 | printf("Old MD5 sum: "); 42 | printHashMD5(hash); 43 | printf("\n"); 44 | memcpy(imet + 0x0630, hash, 0x0010); 45 | 46 | if(add) { 47 | 48 | text = (u8 *)malloc(6*0x0054); 49 | memset(text, 0x00, 6*0x0054); // Remove current channel names 50 | fp = fopen(argv[2], "rb"); 51 | if (!fp) { 52 | printf("Cannot read file %s.\n", argv[2]); 53 | exit(1); 54 | } 55 | banner = (u8 *)malloc(0x20); memset(banner, 0x00, 0x20); 56 | author = (u8 *)malloc(0x10); memset(author, 0x00, 0x10); 57 | if (fgets(banner, 0x20, fp)==NULL) { 58 | printf("Cannot read file %s.\n", argv[2]); 59 | exit(1); 60 | } 61 | banner[strlen(banner)-1] = 0x00; 62 | if (fgets(author, 0x10, fp)==NULL) { 63 | printf("Cannot read file %s.\n", argv[2]); 64 | exit(1); 65 | } 66 | author[strlen(author)-1] = 0x00; 67 | for (i=0;i<6;i++) { 68 | if (fgets(text + 0x0054*i, 0x002A, fp)==NULL) { 69 | printf("Cannot read file %s.\n", argv[2]); 70 | exit(1); 71 | } 72 | *(text + 0x0054*i + strlen(text + 0x0054*i) - 0x01) = 0x00; 73 | } 74 | fclose(fp); 75 | // Copy banner info 76 | memcpy(imet, banner, 0x20); 77 | // Copy author info 78 | memcpy(imet + 0x0030, author, 0x10); 79 | 80 | memset(imet + 0x00F0, 0x00, 0x0054*6); 81 | for (i=0;i<6;i++) { 82 | printf("%s...", text + i*0x0054); 83 | j=0; 84 | for (j=0;j<0x0054;j++) { 85 | if (*(text + i*0x0054 + j)!=0x00) { 86 | memcpy(imet + 0x00F0 + 0x0054*i + j*2 + 1, text + i*0x0054 + j, 1); 87 | *(imet + 0x00F0 + 0x0054*i + j*2) = 0x00; 88 | } else { 89 | break; 90 | } 91 | } 92 | printf("OK\n"); 93 | } 94 | } 95 | printf("Recalculating MD5 sum... "); 96 | memset(imet + 0x0630, 0x00, 0x0010); // Remove MD5 sum 97 | md5(imet + 0x0040, 0x600, hash); 98 | memcpy(imet + 0x0630, hash, 0x0010); 99 | printf("OK\n"); 100 | printf("New MD5 sum: "); 101 | printHashMD5(hash); 102 | printf("\n"); 103 | printf("Writing file... "); 104 | fp = fopen(argv[1], "wb"); 105 | if (!fp) { printf("Cannot write file %s.\n", argv[1]); exit(-1); } 106 | fwrite(imet, imet_len, 1, fp); 107 | fclose(fp); 108 | printf("OK\n"); 109 | 110 | return 0; 111 | } 112 | -------------------------------------------------------------------------------- /wadtools/wadsigncheck.c: -------------------------------------------------------------------------------- 1 | // By BFGR based on Zeventig by Segher 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | // BFGR WadTools v0.39a 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "tools.h" 14 | 15 | #define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0) 16 | 17 | static FILE *fp; 18 | 19 | static u8 *get_wad(u32 len) 20 | { 21 | u32 rounded_len; 22 | u8 *p; 23 | 24 | rounded_len = round_up(len, 0x40); 25 | p = malloc(rounded_len); 26 | if (p == 0) 27 | fatal("malloc"); 28 | if (len) 29 | if (fread(p, rounded_len, 1, fp) != 1) 30 | fatal("get_wad read, len = %x", len); 31 | 32 | return p; 33 | } 34 | 35 | static void do_install_wad(u8 *header) 36 | { 37 | u32 header_len; 38 | u32 cert_len; 39 | u32 tik_len; 40 | u32 tmd_len; 41 | u32 app_len; 42 | u32 trailer_len; 43 | u8 *cert; 44 | u8 *tik; 45 | u8 *tmd; 46 | u32 ret; 47 | 48 | header_len = be32(header); 49 | if (header_len != 0x20) 50 | fatal("bad install header length (%x)", header_len); 51 | 52 | cert_len = be32(header + 8); 53 | tik_len = be32(header + 0x10); 54 | tmd_len = be32(header + 0x14); 55 | app_len = be32(header + 0x18); 56 | trailer_len = be32(header + 0x1c); 57 | 58 | cert = get_wad(cert_len); 59 | tik = get_wad(tik_len); 60 | tmd = get_wad(tmd_len); 61 | printf("Normal sign check...\n"); 62 | ret = check_cert_chain(tik, tik_len, cert, cert_len); 63 | if (ret) 64 | fprintf(stderr, "ticket trucha cert failure (%d)\n", ret); 65 | 66 | ret = check_cert_chain(tmd, tmd_len, cert, cert_len); 67 | if (ret) 68 | fprintf(stderr, "tmd trucha cert failure (%d)\n", ret); 69 | printf("Trucha sign check...\n"); 70 | ret = check_cert_chain_trucha(tik, tik_len, cert, cert_len); 71 | if (ret) 72 | fprintf(stderr, "ticket cert failure (%d)\n", ret); 73 | 74 | ret = check_cert_chain_trucha(tmd, tmd_len, cert, cert_len); 75 | if (ret) 76 | fprintf(stderr, "tmd cert failure (%d)\n", ret); 77 | 78 | } 79 | 80 | static void do_wad(void) 81 | { 82 | u8 header[0x80]; 83 | u32 header_len; 84 | u32 header_type; 85 | 86 | if (fread(header, 0x40, 1, fp) != 1) { 87 | if (!feof(fp)) 88 | fatal("reading wad header"); 89 | else 90 | return; 91 | } 92 | header_len = be32(header); 93 | if (header_len >= 0x80) 94 | ERROR("wad header too big\n"); 95 | if (header_len >= 0x40) 96 | if (fread(header + 0x40, 0x40, 1, fp) != 1) 97 | fatal("reading wad header (2)"); 98 | 99 | header_type = be32(header + 4); 100 | switch (header_type) { 101 | case 0x49730000: 102 | do_install_wad(header); 103 | break; 104 | case 0x69620000: 105 | do_install_wad(header); 106 | break; 107 | default: 108 | fatal("unknown header type %08x", header_type); 109 | } 110 | } 111 | 112 | int main(int argc, char **argv) 113 | { 114 | printf("--- WAD Sign Checker ---\n"); 115 | 116 | if (argc!=2) { 117 | printf("--- USAGE: wadsigncheck wad_file ---\n"); 118 | exit(-1); 119 | } 120 | fp = fopen(argv[1], "rb"); 121 | if (!fp) { 122 | printf("Cannot open file %s.\n", argv[1]); 123 | exit(-1); 124 | } 125 | 126 | do_wad(); 127 | 128 | fclose(fp); 129 | 130 | return 0; 131 | } 132 | -------------------------------------------------------------------------------- /wadtools/wadunpacker.c: -------------------------------------------------------------------------------- 1 | // By BFGR based on Zeventig by Segher 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | // BFGR WadTools v0.39a 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "tools.h" 14 | 15 | #define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0) 16 | 17 | static FILE *fp; 18 | 19 | static u8 *get_wad(u32 len) 20 | { 21 | u32 rounded_len; 22 | u8 *p; 23 | 24 | rounded_len = round_up(len, 0x40); 25 | p = malloc(rounded_len); 26 | if (p == 0) 27 | fatal("malloc"); 28 | if (len) 29 | if (fread(p, rounded_len, 1, fp) != 1) 30 | fatal("get_wad read, len = %x", len); 31 | 32 | return p; 33 | } 34 | 35 | static void do_app_file(u8 *app, u32 app_len, u8 *tik, u8 *tmd) 36 | { 37 | u8 title_key[16]; 38 | u8 iv[16]; 39 | u32 i; 40 | u8 *p; 41 | u32 len; 42 | u32 rounded_len; 43 | u32 num_contents; 44 | u32 cid; 45 | u16 index; 46 | u16 type; 47 | char name[17]; 48 | FILE *fp; 49 | 50 | decrypt_title_key(tik, title_key); 51 | 52 | sprintf(name, "%016llx", be64(tmd + 0x018c)); 53 | mkdir(name, 0777); 54 | if (chdir(name)) 55 | fatal("chdir %s", name); 56 | 57 | num_contents = be16(tmd + 0x01de); 58 | p = app; 59 | 60 | for (i = 0; i < num_contents; i++) { 61 | cid = be32(tmd + 0x01e4 + 0x24*i); 62 | index = be16(tmd + 0x01e8 + 0x24*i); 63 | type = be16(tmd + 0x01ea + 0x24*i); 64 | len = be64(tmd + 0x01ec + 0x24*i); 65 | rounded_len = round_up(len, 0x40); 66 | fprintf(stderr, "--- cid=%08x index=%04x type=%04x len=%08x\n", 67 | cid, index, type, len); 68 | 69 | memset(iv, 0, sizeof iv); 70 | memcpy(iv, tmd + 0x01e8 + 0x24*i, 2); 71 | aes_cbc_dec(title_key, iv, p, rounded_len, p); 72 | 73 | sprintf(name, "%08x.app", index); 74 | fp = fopen(name, "wb"); 75 | if (fp == 0) 76 | fatal("open %s", name); 77 | if (fwrite(p, len, 1, fp) != 1) 78 | fatal("write %s", name); 79 | fclose(fp); 80 | 81 | p += rounded_len; 82 | } 83 | 84 | if (chdir("..")) 85 | fatal("chdir .."); 86 | } 87 | 88 | static void do_install_wad(u8 *header) 89 | { 90 | u32 header_len; 91 | u32 cert_len; 92 | u32 tik_len; 93 | u32 tmd_len; 94 | u32 app_len; 95 | u32 trailer_len; 96 | u8 *cert; 97 | u8 *tik; 98 | u8 *tmd; 99 | u8 *app; 100 | u8 *trailer; 101 | u32 ret; 102 | char name[25]; 103 | 104 | header_len = be32(header); 105 | if (header_len != 0x20) 106 | fatal("bad install header length (%x)", header_len); 107 | 108 | cert_len = be32(header + 8); 109 | // 0 = be32(header + 0x0c); 110 | tik_len = be32(header + 0x10); 111 | tmd_len = be32(header + 0x14); 112 | app_len = be32(header + 0x18); 113 | trailer_len = be32(header + 0x1c); 114 | 115 | cert = get_wad(cert_len); 116 | tik = get_wad(tik_len); 117 | tmd = get_wad(tmd_len); 118 | app = get_wad(app_len); 119 | trailer = get_wad(trailer_len); 120 | 121 | // File Dump 122 | // Create/Select Folder 123 | sprintf(name, "%016llx", be64(tmd + 0x018c)); 124 | if (mkdir(name, 0777)) 125 | fatal("mkdir %s", name); 126 | if (chdir(name)) 127 | fatal("chdir %s", name); 128 | // File Dump 129 | FILE *cf = fopen("title.cert", "w"); 130 | fwrite(cert, cert_len, 1, cf); 131 | fclose(cf); 132 | 133 | if (trailer_len>0) { 134 | cf = fopen("title.trailer", "w"); 135 | fwrite(trailer, trailer_len, 1, cf); 136 | fclose(cf); 137 | } 138 | cf = fopen("title.tmd", "w"); 139 | fwrite(tmd, tmd_len, 1, cf); 140 | fclose(cf); 141 | 142 | cf = fopen("title.tik", "w"); 143 | fwrite(tik, tik_len, 1, cf); 144 | fclose(cf); 145 | 146 | /*printf("Normal sign check...\n"); 147 | ret = check_cert_chain(tik, tik_len, cert, cert_len); 148 | if (ret) 149 | fprintf(stderr, "ticket cert failure (%d)\n", ret); 150 | 151 | ret = check_cert_chain(tmd, tmd_len, cert, cert_len); 152 | if (ret) 153 | fprintf(stderr, "tmd cert failure (%d)\n", ret); 154 | printf("Trucha sign check...\n"); 155 | ret = check_cert_chain_trucha(tik, tik_len, cert, cert_len); 156 | if (ret) 157 | fprintf(stderr, "ticket cert failure (%d)\n", ret); 158 | 159 | ret = check_cert_chain_trucha(tmd, tmd_len, cert, cert_len); 160 | if (ret) 161 | fprintf(stderr, "tmd cert failure (%d)\n", ret);*/ 162 | 163 | 164 | if (chdir("..")) 165 | fatal("chdir ..", name); 166 | do_app_file(app, app_len, tik, tmd); 167 | } 168 | 169 | static void do_wad(void) 170 | { 171 | u8 header[0x80]; 172 | u32 header_len; 173 | u32 header_type; 174 | 175 | if (fread(header, 0x40, 1, fp) != 1) { 176 | if (!feof(fp)) 177 | fatal("reading wad header"); 178 | else 179 | return; 180 | } 181 | header_len = be32(header); 182 | if (header_len >= 0x80) 183 | ERROR("wad header too big\n"); 184 | if (header_len >= 0x40) 185 | if (fread(header + 0x40, 0x40, 1, fp) != 1) 186 | fatal("reading wad header (2)"); 187 | fprintf(stderr, "wad header:\n"); 188 | hexdump(header, header_len); 189 | 190 | header_type = be32(header + 4); 191 | switch (header_type) { 192 | case 0x49730000: 193 | do_install_wad(header); 194 | break; 195 | case 0x69620000: 196 | do_install_wad(header); 197 | break; 198 | default: 199 | fatal("unknown header type %08x", header_type); 200 | } 201 | } 202 | 203 | int main(int argc, char **argv) 204 | { 205 | if (argc!=2) { 206 | printf("USAGE: wadunpacker wad_file\n"); 207 | exit(-1); 208 | } 209 | fp = fopen(argv[1], "rb"); 210 | if (!fp) { 211 | printf("Cannot open file %s.\n", argv[1]); 212 | exit(-1); 213 | } 214 | 215 | while (!feof(fp)) 216 | do_wad(); 217 | 218 | fclose(fp); 219 | 220 | return 0; 221 | } 222 | -------------------------------------------------------------------------------- /wadtools/wadpacker.c: -------------------------------------------------------------------------------- 1 | // By BFGR based on Zeventig by Segher 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | // BFGR WadTools v0.39a 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "tools.h" 13 | 14 | #define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0) 15 | 16 | void printUsage(char *u) { 17 | printf("USAGE: %s folder file.wad [ options ]\n\n", u); 18 | printf("Options: -sign\t\tTrucha sign Ticket and TMD\n"); 19 | printf(" -T\t\tTrucha sign Ticket\n"); 20 | printf(" -M\t\tTrucha sign TMD\n"); 21 | printf(" -i ABCD\tChange the title id\n"); 22 | printf(" -w\t\tUses a compatible generic ticket.\n\n"); 23 | printf(" -e\t\tDisable content encryption.\n\n"); 24 | exit(-1); 25 | } 26 | 27 | int main(int argc, char **argv) { 28 | FILE *ftmd; 29 | FILE *ftik; 30 | FILE *fapp; 31 | FILE *fcert; 32 | u8 title_key[16]; 33 | u8 *tmd; 34 | u8 *cert; 35 | u8 *tik; 36 | u8 *apps = NULL; //Encrypted files, with 64 bytes boundary 37 | u32 len_tmd; 38 | u32 len_tmd_nb; 39 | u32 len_tik; 40 | u32 len_tik_nb; 41 | u16 num_app; 42 | u32 len_cert; 43 | u32 len_cert_nb; 44 | u64 len_apps = 0; 45 | char name[17]; 46 | u8 hash[20]; 47 | u16 i; 48 | u64 temp, temp2; 49 | u8 iv[16]; 50 | u8 sign_tik = 0; 51 | u8 sign_tmd = 0; 52 | u8 sign_type = 1; // Sign type. if 1 watermark enabled. if 0 watermark disabled 53 | u8 encrypt_apps = 1; 54 | u8 *new_id = NULL; 55 | 56 | if (argc<3) { 57 | printUsage(argv[0]); 58 | } 59 | if (argc>3) { 60 | for(i=4;i<=argc;i++) { 61 | if (strcmp(argv[i-1], "-sign")==0) { // Trucha sign Ticket and TMD 62 | sign_tik = 1; 63 | sign_tmd = 1; 64 | printf("Trucha sign enabled\n"); 65 | } else if (strcmp(argv[i-1], "-T")==0) { // Trucha sign Ticket 66 | sign_tik = 1; 67 | printf("Trucha sign enabled for Ticket\n"); 68 | } else if (strcmp(argv[i-1], "-M")==0) { // Trucha Sign TMD 69 | sign_tmd = 1; 70 | printf("Trucha sign enabled for TMD\n"); 71 | } else if (strcmp(argv[i-1], "-i")==0) { // Change title id 72 | if (strlen(argv[i])==4) { 73 | new_id = (u8 *)malloc(4); 74 | memset(new_id, 0, 4); 75 | strncpy(new_id, argv[i], 4); 76 | i++; 77 | printf("New Title ID: %s\n", new_id); 78 | } else { 79 | printUsage(argv[0]); 80 | } 81 | } else if (strcmp(argv[i-1], "-w")==0) { // Disable watermark 82 | sign_type = 0; 83 | printf("A generic ticket will be used.\n"); 84 | } else if (strcmp(argv[i-1], "-e")==0) { // Disable .app encryption 85 | encrypt_apps = 0; 86 | printf("Contents will not be encrypted.\n"); 87 | } 88 | fflush(stdout); 89 | } 90 | } 91 | 92 | char cwd[1024]; 93 | if(getcwd(cwd, sizeof(cwd)) == NULL) { 94 | printf("Could not save the current directory.\n"); 95 | return 1; 96 | } 97 | if(chdir(argv[1]) == -1) { 98 | printf("Could not open the source directory (%s).\n", argv[1]); 99 | return 1; 100 | } 101 | //CERT 102 | printf("Cert... "); 103 | fcert = fopen("title.cert", "rb"); if (!fcert) {printf("Could not find cert_file\n"); exit(-1);} 104 | temp = getfilesize(fcert); 105 | len_cert_nb = temp; 106 | len_cert = round_up(temp, 0x40); 107 | cert = malloc(len_cert); 108 | memset(cert, 0, len_cert); 109 | fread(cert, temp, 1, fcert); 110 | fclose(fcert); 111 | printf("OK\n"); 112 | //TMD 113 | printf("TMD... "); 114 | ftmd = fopen("title.tmd", "rb"); if (!ftmd) {printf("Could not find tmd_file\n"); exit(-1);} 115 | temp = getfilesize(ftmd); 116 | len_tmd_nb = temp; 117 | len_tmd = round_up(temp, 0x40); 118 | tmd = malloc(len_tmd); 119 | memset(tmd, 0, len_tmd); 120 | fread(tmd, temp, 1, ftmd); 121 | num_app = be16(tmd + 0x01de); 122 | fclose(ftmd); 123 | printf("OK\n"); 124 | printf("There are %d files described on the TMD.\n", num_app); 125 | // Ticket 126 | printf("Ticket... "); 127 | ftik = fopen("title.tik", "rb"); if (!ftik) {printf("Could not find ticket_file\n"); exit(-1);} 128 | temp = getfilesize(ftik); 129 | len_tik = round_up(temp, 0x40); 130 | len_tik_nb = temp; 131 | tik = malloc(len_tik); 132 | memset(tik, 0, len_tik); 133 | fread(tik, temp, 1, ftik); 134 | fclose(ftik); 135 | printf("OK\n"); 136 | // Change title id if required 137 | if (new_id!=NULL) { 138 | memcpy(tmd + 0x0190, new_id, 4); 139 | memcpy(tik + 0x01E0, new_id, 4); 140 | } 141 | // Sign 142 | if (sign_tik) { 143 | printf("Signing Ticket... "); 144 | int ok = Ticket_resign(tik, len_tik_nb, sign_type); 145 | printf("OK\n", ok); 146 | } 147 | 148 | if(encrypt_apps) { 149 | // Get Title key 150 | decrypt_title_key(tik, title_key); 151 | } 152 | 153 | // Read app files 154 | for (i=0;i 2 | // Licensed under the terms of the GNU GPL, version 2 3 | // http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt 4 | // Modified by BFGR 5 | 6 | #include "tools.h" 7 | 8 | #include // to accommodate certain broken versions of openssl 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // 18 | // basic data types 19 | // 20 | 21 | u8 generic_tik[676]={ 22 | 0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 23 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 24 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 25 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 26 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 27 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 28 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 29 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 30 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 31 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 32 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 33 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 34 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 35 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 36 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 37 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 38 | 82,111,111,116,45,67,65,48,48,48,48,48,48,48,49,45,88,83,48,48, 39 | 48,48,48,48,48,51,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 40 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 41 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 42 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 43 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 44 | 0,0,0,0,0,0,0,123,-114,73,-63,-55,-3,49,106,-101,-108,20,-128,-106, 45 | -102,49,-62,0,0,1,-84,-27,-29,-114,76,104,0,0,0,0,0,1,0,1, 46 | 74,66,68,80,-1,-1,0,0,0,0,0,0,0,0,0,0,0,86,0,0, 47 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 48 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 49 | 0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 50 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,0, 51 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 52 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 53 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 54 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 55 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 56 | 57 | u16 be16(const u8 *p) 58 | { 59 | return (p[0] << 8) | p[1]; 60 | } 61 | 62 | u32 be32(const u8 *p) 63 | { 64 | return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]; 65 | } 66 | 67 | u64 be64(const u8 *p) 68 | { 69 | return ((u64)be32(p) << 32) | be32(p + 4); 70 | } 71 | 72 | u64 be34(const u8 *p) 73 | { 74 | return 4 * (u64)be32(p); 75 | } 76 | 77 | void wbe16(u8 *p, u16 x) 78 | { 79 | p[0] = x >> 8; 80 | p[1] = x; 81 | } 82 | 83 | void wbe32(u8 *p, u32 x) 84 | { 85 | wbe16(p, x >> 16); 86 | wbe16(p + 2, x); 87 | } 88 | 89 | void wbe64(u8 *p, u64 x) 90 | { 91 | wbe32(p, x >> 32); 92 | wbe32(p + 4, x); 93 | } 94 | 95 | // 96 | // crypto 97 | // 98 | 99 | void md5(u8 *data, u32 len, u8 *hash) 100 | { 101 | MD5(data, len, hash); 102 | } 103 | 104 | void sha(u8 *data, u32 len, u8 *hash) 105 | { 106 | SHA1(data, len, hash); 107 | } 108 | 109 | void get_key(const char *name, u8 *key, u32 len) 110 | { 111 | char path[256]; 112 | char *home; 113 | FILE *fp; 114 | 115 | home = getenv("HOME"); 116 | if (home == 0) 117 | fatal("cannot find HOME"); 118 | 119 | snprintf(path, sizeof path, "%s/.wii/%s", home, name); 120 | 121 | fp = fopen(path, "rb"); 122 | if (fp == 0) 123 | fatal("cannot open %s", name); 124 | if (fread(key, len, 1, fp) != 1) 125 | fatal("error reading %s", name); 126 | fclose(fp); 127 | } 128 | 129 | void aes_cbc_dec(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out) 130 | { 131 | AES_KEY aes_key; 132 | 133 | AES_set_decrypt_key(key, 128, &aes_key); 134 | AES_cbc_encrypt(in, out, len, &aes_key, iv, AES_DECRYPT); 135 | } 136 | 137 | void aes_cbc_enc(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out) 138 | { 139 | AES_KEY aes_key; 140 | 141 | AES_set_encrypt_key(key, 128, &aes_key); 142 | AES_cbc_encrypt(in, out, len, &aes_key, iv, AES_ENCRYPT); 143 | } 144 | 145 | void decrypt_title_key(u8 *tik, u8 *title_key) 146 | { 147 | u8 common_key[16]; 148 | u8 iv[16]; 149 | 150 | get_key("common-key.bin", common_key, 16); 151 | 152 | memset(iv, 0, sizeof iv); 153 | memcpy(iv, tik + 0x01dc, 8); 154 | aes_cbc_dec(common_key, iv, tik + 0x01bf, 16, title_key); 155 | } 156 | 157 | static u8 root_key[0x204]; 158 | static u8 *get_root_key(void) 159 | { 160 | get_key("root-key.bin", root_key, sizeof root_key); 161 | return root_key; 162 | } 163 | 164 | static u32 get_sig_len(u8 *sig) 165 | { 166 | u32 type; 167 | 168 | type = be32(sig); 169 | switch (type - 0x10000) { 170 | case 0: 171 | return 0x240; 172 | 173 | case 1: 174 | return 0x140; 175 | 176 | case 2: 177 | return 0x80; 178 | } 179 | 180 | fprintf(stderr, "get_sig_len(): unhandled sig type %08x\n", type); 181 | return 0; 182 | } 183 | 184 | static u32 get_sub_len(u8 *sub) 185 | { 186 | u32 type; 187 | 188 | type = be32(sub + 0x40); 189 | switch (type) { 190 | case 0: 191 | return 0x2c0; 192 | 193 | case 1: 194 | return 0x1c0; 195 | 196 | case 2: 197 | return 0x100; 198 | } 199 | 200 | fprintf(stderr, "get_sub_len(): unhandled sub type %08x\n", type); 201 | return 0; 202 | } 203 | 204 | static int check_rsa(u8 *h, u8 *sig, u8 *key, u32 n) 205 | { 206 | u8 correct[0x200]; 207 | u8 x[0x200]; 208 | static const u8 ber[16] = "\x00\x30\x21\x30\x09\x06\x05\x2b" 209 | "\x0e\x03\x02\x1a\x05\x00\x04\x14"; 210 | 211 | //fprintf(stderr, "n = %x\n", n); 212 | //fprintf(stderr, "key:\n"); 213 | //hexdump(key, n); 214 | //fprintf(stderr, "sig:\n"); 215 | //hexdump(sig, n); 216 | 217 | correct[0] = 0; 218 | correct[1] = 1; 219 | memset(correct + 2, 0xff, n - 38); 220 | memcpy(correct + n - 36, ber, 16); 221 | memcpy(correct + n - 20, h, 20); 222 | //fprintf(stderr, "correct is:\n"); 223 | //hexdump(correct, n); 224 | 225 | bn_exp(x, sig, key, n, key + n, 4); 226 | //fprintf(stderr, "x is:\n"); 227 | //hexdump(x, n); 228 | 229 | if (memcmp(correct, x, n) == 0) 230 | return 0; 231 | 232 | return -5; 233 | } 234 | 235 | static int check_rsa_trucha(u8 *h, u8 *sig, u8 *key, u32 n) 236 | { 237 | u8 correct[0x200]; 238 | u8 x[0x200]; 239 | static const u8 ber[16] = "\x00\x30\x21\x30\x09\x06\x05\x2b" 240 | "\x0e\x03\x02\x1a\x05\x00\x04\x14"; 241 | 242 | //fprintf(stderr, "n = %x\n", n); 243 | //fprintf(stderr, "key:\n"); 244 | //hexdump(key, n); 245 | //fprintf(stderr, "sig:\n"); 246 | //hexdump(sig, n); 247 | 248 | correct[0] = 0; 249 | correct[1] = 1; 250 | memset(correct + 2, 0xff, n - 38); 251 | memcpy(correct + n - 36, ber, 16); 252 | memcpy(correct + n - 20, h, 20); 253 | //fprintf(stderr, "correct is:\n"); 254 | //hexdump(correct, n); 255 | 256 | bn_exp(x, sig, key, n, key + n, 4); 257 | //fprintf(stderr, "x is:\n"); 258 | //hexdump(x, n); 259 | 260 | if (strncmp(correct, x, n) == 0) 261 | return 0; 262 | 263 | return -5; 264 | } 265 | 266 | static int check_hash(u8 *h, u8 *sig, u8 *key) 267 | { 268 | u32 type; 269 | 270 | type = be32(sig) - 0x10000; 271 | if (type != be32(key + 0x40)) 272 | return -6; 273 | 274 | switch (type) { 275 | case 1: 276 | return check_rsa(h, sig + 4, key + 0x88, 0x100); 277 | } 278 | 279 | return -7; 280 | } 281 | 282 | static int check_hash_trucha(u8 *h, u8 *sig, u8 *key) 283 | { 284 | u32 type; 285 | 286 | type = be32(sig) - 0x10000; 287 | if (type != be32(key + 0x40)) 288 | return -6; 289 | 290 | switch (type) { 291 | case 1: 292 | return check_rsa_trucha(h, sig + 4, key + 0x88, 0x100); 293 | } 294 | 295 | return -7; 296 | } 297 | 298 | static u8 *find_cert_in_chain(u8 *sub, u8 *cert, u32 cert_len) 299 | { 300 | char parent[64]; 301 | char *child; 302 | u32 sig_len, sub_len; 303 | u8 *p; 304 | u8 *issuer; 305 | 306 | strncpy(parent, sub, sizeof parent); 307 | parent[sizeof parent - 1] = 0; 308 | child = strrchr(parent, '-'); 309 | if (child) 310 | *child++ = 0; 311 | else { 312 | *parent = 0; 313 | child = sub; 314 | } 315 | 316 | for (p = cert; p < cert + cert_len; p += sig_len + sub_len) { 317 | sig_len = get_sig_len(p); 318 | if (sig_len == 0) 319 | return 0; 320 | issuer = p + sig_len; 321 | sub_len = get_sub_len(issuer); 322 | if (sub_len == 0) 323 | return 0; 324 | 325 | if (strcmp(parent, issuer) == 0 326 | && strcmp(child, issuer + 0x44) == 0) 327 | return p; 328 | } 329 | 330 | return 0; 331 | } 332 | 333 | int check_cert_chain(u8 *data, u32 data_len, u8 *cert, u32 cert_len) 334 | { 335 | u8 *sig; 336 | u8 *sub; 337 | u32 sig_len; 338 | u32 sub_len; 339 | u8 h[20]; 340 | u8 *key_cert; 341 | u8 *key; 342 | int ret; 343 | 344 | sig = data; 345 | sig_len = get_sig_len(sig); 346 | if (sig_len == 0) 347 | return -1; 348 | sub = data + sig_len; 349 | sub_len = data_len - sig_len; 350 | if (sub_len == 0) 351 | return -2; 352 | 353 | for (;;) { 354 | fprintf(stderr, ">>>>>> checking sig by %s...\n", sub); 355 | if (strcmp(sub, "Root") == 0) { 356 | key = get_root_key(); 357 | sha(sub, sub_len, h); 358 | if (be32(sig) != 0x10000) 359 | return -8; 360 | return check_rsa(h, sig + 4, key, 0x200); 361 | } 362 | 363 | key_cert = find_cert_in_chain(sub, cert, cert_len); 364 | if (key_cert == 0) 365 | return -3; 366 | 367 | key = key_cert + get_sig_len(key_cert); 368 | 369 | sha(sub, sub_len, h); 370 | ret = check_hash(h, sig, key); 371 | if (ret) 372 | return ret; 373 | 374 | sig = key_cert; 375 | sig_len = get_sig_len(sig); 376 | if (sig_len == 0) 377 | return -4; 378 | sub = sig + sig_len; 379 | sub_len = get_sub_len(sub); 380 | if (sub_len == 0) 381 | return -5; 382 | } 383 | } 384 | 385 | int check_cert_chain_trucha(u8 *data, u32 data_len, u8 *cert, u32 cert_len) 386 | { 387 | u8 *sig; 388 | u8 *sub; 389 | u32 sig_len; 390 | u32 sub_len; 391 | u8 h[20]; 392 | u8 *key_cert; 393 | u8 *key; 394 | int ret; 395 | 396 | sig = data; 397 | sig_len = get_sig_len(sig); 398 | if (sig_len == 0) 399 | return -1; 400 | sub = data + sig_len; 401 | sub_len = data_len - sig_len; 402 | if (sub_len == 0) 403 | return -2; 404 | 405 | for (;;) { 406 | fprintf(stderr, ">>>>>> checking trucha sig by %s...\n", sub); 407 | if (strcmp(sub, "Root") == 0) { 408 | key = get_root_key(); 409 | sha(sub, sub_len, h); 410 | if (be32(sig) != 0x10000) 411 | return -8; 412 | return check_rsa_trucha(h, sig + 4, key, 0x200); 413 | } 414 | 415 | key_cert = find_cert_in_chain(sub, cert, cert_len); 416 | if (key_cert == 0) 417 | return -3; 418 | 419 | key = key_cert + get_sig_len(key_cert); 420 | 421 | sha(sub, sub_len, h); 422 | ret = check_hash_trucha(h, sig, key); 423 | if (ret) 424 | return ret; 425 | 426 | sig = key_cert; 427 | sig_len = get_sig_len(sig); 428 | if (sig_len == 0) 429 | return -4; 430 | sub = sig + sig_len; 431 | sub_len = get_sub_len(sub); 432 | if (sub_len == 0) 433 | return -5; 434 | } 435 | } 436 | 437 | // 438 | // compression 439 | // 440 | 441 | void do_yaz0(u8 *in, u32 in_size, u8 *out, u32 out_size) 442 | { 443 | u32 nout; 444 | u8 bits; 445 | u32 nbits; 446 | u32 n, d, i; 447 | 448 | bits = 0; 449 | nbits = 0; 450 | in += 0x10; 451 | for (nout = 0; nout < out_size; ) { 452 | if (nbits == 0) { 453 | bits = *in++; 454 | nbits = 8; 455 | } 456 | 457 | if ((bits & 0x80) != 0) { 458 | *out++ = *in++; 459 | nout++; 460 | } else { 461 | n = *in++; 462 | d = *in++; 463 | d |= (n << 8) & 0xf00; 464 | n >>= 4; 465 | if (n == 0) 466 | n = 0x10 + *in++; 467 | n += 2; 468 | d++; 469 | 470 | for (i = 0; i < n; i++) { 471 | *out = *(out - d); 472 | out++; 473 | } 474 | nout += n; 475 | } 476 | 477 | nbits--; 478 | bits <<= 1; 479 | }; 480 | } 481 | 482 | // 483 | // error handling 484 | // 485 | 486 | void fatal(const char *s, ...) 487 | { 488 | char message[256]; 489 | va_list ap; 490 | 491 | va_start(ap, s); 492 | vsnprintf(message, sizeof message, s, ap); 493 | 494 | perror(message); 495 | 496 | exit(1); 497 | } 498 | 499 | // 500 | // output formatting 501 | // 502 | 503 | void print_bytes(u8 *x, u32 n) 504 | { 505 | u32 i; 506 | 507 | for (i = 0; i < n; i++) 508 | fprintf(stderr, "%02x", x[i]); 509 | } 510 | 511 | void hexdump(u8 *x, u32 n) 512 | { 513 | u32 i, j; 514 | 515 | for (i = 0; i < n; i += 16) { 516 | fprintf(stderr, "%04x:", i); 517 | for (j = 0; j < 16 && i + j < n; j++) { 518 | if ((j & 3) == 0) 519 | fprintf(stderr, " "); 520 | fprintf(stderr, "%02x", *x++); 521 | } 522 | fprintf(stderr, "\n"); 523 | } 524 | } 525 | 526 | void dump_tmd(u8 *tmd) 527 | { 528 | u32 i, n; 529 | u8 *p; 530 | 531 | printf(" issuer: %s\n", tmd + 0x140); 532 | printf(" sys_version: %016llx\n", be64(tmd + 0x0184)); 533 | printf(" title_id: %016llx\n", be64(tmd + 0x018c)); 534 | printf(" title_type: %08x\n", be32(tmd + 0x0194)); 535 | printf(" group_id: %04x\n", be16(tmd + 0x0198)); 536 | printf("title_version: %04x\n", be16(tmd + 0x01dc)); 537 | printf(" num_contents: %04x\n", be16(tmd + 0x01de)); 538 | printf(" boot_index: %04x\n", be16(tmd + 0x01e0)); 539 | 540 | n = be16(tmd + 0x01de); 541 | p = tmd + 0x01e4; 542 | for (i = 0; i < n; i++) { 543 | printf("cid %08x index %04x type %04x size %08llx\n", 544 | be32(p), be16(p + 4), be16(p + 6), be64(p + 8)); 545 | p += 0x24; 546 | } 547 | } 548 | 549 | void printHashSHA(u8 *hash) { 550 | int i=0; 551 | //printf("File SHA Hash: 0x"); 552 | for (i=0; i<20; i++) { 553 | printf("%02x", hash[i]); 554 | } 555 | //printf("\n"); 556 | } 557 | 558 | void printHashMD5(u8 *hash) { 559 | int i=0; 560 | //printf("File MD5 Hash: 0x"); 561 | for (i=0; i<16; i++) { 562 | printf("%02x", hash[i]); 563 | } 564 | //printf("\n"); 565 | } 566 | 567 | u64 getfilesize(u32 *fd) { 568 | u64 len_b; 569 | u64 len; 570 | len_b = ftell(fd); 571 | fseek(fd, 0, SEEK_END); 572 | len = ftell(fd); 573 | fseek(fd, len_b, SEEK_SET); 574 | return len; 575 | } 576 | 577 | // TMD trucha sign 578 | int TMD_resign(u8 *tmd, u32 tmd_len) { 579 | u8 hash[20]; 580 | u16 num = 0x0000; 581 | u8 *sig; 582 | u8 *sub; 583 | u32 sig_len; 584 | u32 sub_len; 585 | //tmd_len += ((be16(tmd + 0x01de)-1)*0x24); 586 | sig = tmd; 587 | sig_len = get_sig_len(sig); 588 | if (sig_len == 0) 589 | return -1; 590 | sub = tmd + sig_len; 591 | sub_len = tmd_len - sig_len; 592 | if (sub_len == 0) 593 | return -2; 594 | 595 | // Set Sign to 0x00 596 | memset(tmd + 0x0004, 0, 256); 597 | // Resign TMD 598 | while(1) { 599 | sha(sub, sub_len,hash); 600 | //printHashSHA(hash); 601 | if (hash[0]==0x00) break; 602 | // Reserved field modification 603 | memcpy(tmd + 0x01E2, &num, sizeof(num)); 604 | num++; 605 | } 606 | // the TMD is trucha signed 607 | printf(" %d it. ", num); 608 | return 1; 609 | } 610 | 611 | // Ticket trucha sign 612 | int Ticket_resign(u8 *tik, u32 tik_len, u8 homebrew) { 613 | u8 hash[20]; 614 | u16 num = 0x0000; 615 | u8 *sig; 616 | u8 *sub; 617 | u32 sig_len; 618 | u32 sub_len; 619 | u8 *new_tik; 620 | 621 | if (!homebrew) { // option -w enabled (watermark disabled) 622 | new_tik = (u8 *)malloc(676); 623 | memcpy(new_tik, generic_tik, 676); 624 | // Set title key & title id 625 | memcpy(new_tik+0x1BF, tik+0x1BF, 0x10); 626 | memcpy(new_tik+0x1DC, tik+0x1DC, 0x8); 627 | memcpy(tik, new_tik, 676); 628 | free(new_tik); 629 | } 630 | 631 | sig = tik; 632 | sig_len = get_sig_len(sig); 633 | if (sig_len == 0) 634 | return -1; 635 | sub = tik + sig_len; 636 | sub_len = tik_len - sig_len; 637 | if (sub_len == 0) 638 | return -2; 639 | // Check if the ticket is already signed 640 | sha(sub, sub_len, hash); 641 | if (hash[0]==0x00) { printf(" %d it. ", num); return 1; } 642 | 643 | // Resign Ticket 644 | while(1) { 645 | sha(sub, sub_len,hash); 646 | if (hash[0]==0x00) break; 647 | 648 | if (num==65535) return 0; 649 | // Title key mod 650 | memcpy(tik + 0x0262, &num, sizeof(num)); 651 | num++; 652 | } 653 | // the Ticket is trucha signed 654 | printf(" %d it. ", num); 655 | return 1; 656 | } 657 | 658 | -------------------------------------------------------------------------------- /FunKii/__main__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | # FunKii 4 | 5 | from __future__ import unicode_literals, print_function 6 | 7 | __VERSION__ = 1.0 8 | 9 | import argparse 10 | import binascii 11 | import string 12 | import os 13 | import re 14 | import sys 15 | 16 | try: 17 | from urllib.request import urlopen 18 | from urllib.error import URLError, HTTPError 19 | except ImportError: 20 | from urllib2 import urlopen, URLError, HTTPError 21 | 22 | try: 23 | real_input = raw_input # Python2 24 | except NameError: 25 | real_input = input # Python3 26 | 27 | SYMBOLS = { 28 | 'customary': ('B', 'KB', 'MB', 'GB', 'T', 'P', 'E', 'Z', 'Y'), 29 | } 30 | MAGIC = binascii.a2b_hex('00010000B3ADB3226B3C3DFF1B4B407716FF4F7AD76486C895AC562D21F10601D4F66428191C07768FDF1AE2CE7B27C90FBC0AD0312578EC0779B657D4372413A7F86F0C14C0EF6E0941ED2B05EC3957360789004A878D2E9DF8C7A5A9F8CAB311B1187957BBF898E2A25402CF5439CF2BBFA0E1F85C066E839AE094CA47E01558F56E6F34E92AA2DC38937E37CD8C5C4DFD2F114FE868C9A8D9FED86E0C2175A2BD7E89B9C7B513F41A7961443910EFF9D7FE572218D56DFB7F497AA4CB90D4F1AEB176E4685DA7944060982F0448401FCFC6BAEBDA1630B473B415233508070A9F4F8978E62CEC5E9246A5A8BDA0857868750C3A112FAF95E838C8990E87B162CD10DAB3319665EF889B541BB336BB67539FAFC2AE2D0A2E75C02374EA4EAC8D99507F59B95377305F2635C608A99093AC8FC6DE23B97AEA70B4C4CF66B30E58320EC5B6720448CE3BB11C531FCB70287CB5C27C674FBBFD8C7FC94220A473231D587E5A1A1A82E37579A1BB826ECE0171C97563474B1D46E679B282376211CDC7002F4687C23C6DC0D5B5786EE1F273FF0192500FF4C7506AEE72B6F43DF608FEA583A1F9860F87AF524454BB47C3060C94E99BF7D632A7C8AB4B4FF535211FC18047BB7AFA5A2BD7B884AD8E564F5B89FF379737F1F5013B1F9EC4186F922AD5C4B3C0D5870B9C04AF1AB5F3BC6D0AF17D4708E443E973F7B7707754BAF3ECD2AC49000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F7400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001434130303030303030310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005BFA7D5CB279C9E2EEE121C6EAF44FF639F88F078B4B77ED9F9560B0358281B50E55AB721115A177703C7A30FE3AE9EF1C60BC1D974676B23A68CC04B198525BC968F11DE2DB50E4D9E7F071E562DAE2092233E9D363F61DD7C19FF3A4A91E8F6553D471DD7B84B9F1B8CE7335F0F5540563A1EAB83963E09BE901011F99546361287020E9CC0DAB487F140D6626A1836D27111F2068DE4772149151CF69C61BA60EF9D949A0F71F5499F2D39AD28C7005348293C431FFBD33F6BCA60DC7195EA2BCC56D200BAF6D06D09C41DB8DE9C720154CA4832B69C08C69CD3B073A0063602F462D338061A5EA6C915CD5623579C3EB64CE44EF586D14BAAA8834019B3EEBEED3790001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100014E005FF13F86758DB69C45630FD49BF4CC5D54CFCC22347257ABA4BA53D2B33DE6EC9EA1575453AE5F933D96BFF7CC7A79566E847B1B6077C2A93871301A8CD3C93D4DB326E9879266E9D3BA9F79BC4638FA2D20A03A7067A411A7A0B7D912AD116A3AC46E324247C208BAB4949CC52ED02F19F651E0DF2E3653AAAF97A692BBA91DD86E242EB308775511CE98F6A2F426C92704D0FC8DD4809ED761BD11B785948CD6D07ADBA408D0F086F65AAE1914B2889AA8AE4AA2AAC761A90D412CB15009AB3E93FCA924DECE4F7C06ABDC2E609D68BE0073FA80576A145EEDC48B7432870793C8FCA6D83E096EC5F2A9C421E748B373405BE2FA8AE15878E9D5238875000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D43413030303030303031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000143503030303030303034000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F1B8A064C16DF3832955C3295B72F0332E97EF14848A68049CA68EACDE145033B86C108D48335C5D0CAB770462544755452A900070B156925C1786E2CD206DCCDC2C2E376E27FCB42066CC0A8CE9FEE85704E6CA631A2E7E917E947C3991773629D1556185BBD7B773CA37479E5FAAA3B605E001E1ACE58DD8F84782D645FCE3A1CD03AB36F0F386B1A2D13740A1948A53BA1B0D8C4863CD6B2C2E206494804C62FAA93A7E33A9EA786B59CAE3AB3645F4CB8FD7906B8268CDACF17B3AEC46831B91F6DE186183BC4B326793C72E50D91E36A0DCE2B97DA0213E4696021F331CBEAE8DFC928732AA44DC78E7199A3DDD57227E9E77DE326386936C11ACA70F8119D33A990001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100017D9D5EBA5281DCA7065D2F0868DB8AC73ACE7EA991F1969FE1D0F2C11FAEC0C3F01ADCB446ADE5CA03B625219462C6E1410DB9E63FDE98D1AF263B4CB28784278272EF27134B87C258D67B62F2B5BF9CB6BA8C89192EC50689AC7424A022094003EE98A4BD2F013B593FE5666CD5EB5AD7A49310F34EFBB43D46CBF1B523CF82F68EB56DB904A7C2A82BE11D78D39BA20D90D30742DB5E7AC1EFF221510962CFA914A880DCF417BA99930AEE08B0B0E51A3E9FAFCDC2D7E3CBA12F3AC00790DE447AC3C538A8679238078BD4C4B245AC2916886D2A0E594EED5CC835698B4D6238DF05724DCCF681808A7074065930BFF8514137E815FABAA172B8E0696C61E4000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D43413030303030303031000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000158533030303030303033000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000F1B89FD1AD07A9378A7B100C7DC739BE9EDDB7320089AB25B1F871AF5AA9F4589ED18302328E811A1FEFD009C8063643F854B9E13BBB613A7ACF8714856BA45BAAE7BBC64EB2F75D87EBF267ED0FA441A933665E577D5ADEABFB462E7600CA9CE94DC4CB983992AB7A2FB3A39EA2BF9C53ECD0DCFA6B8B5EB2CBA40FFA4075F8F2B2DE973811872DF5E2A6C38B2FDC8E57DDBD5F46EB27D61952F6AEF862B7EE9AC682A2B19AA9B558FBEBB3892FBD50C9F5DC4A6E9C9BFE458034A942182DDEB75FE0D1B3DF0E97E39980877018C2B283F135757C5A30FC3F3084A49AAAC01EE706694F8E1448DA123ACC4FFA26AA38F7EFBF278F369779775DB7C5ADC78991DCF8438D0001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') 31 | TIKTEM = binascii.a2b_hex('0001000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000526F6F742D434130303030303030312D585330303030303030330000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA00000000000000000000000000AAAAAAAAAAAAAAAAFFFFAAAA00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') 32 | execname = 'wadpacker' 33 | 34 | parser = argparse.ArgumentParser() 35 | parser.add_argument('-outputdir', action='store', dest='output_dir', 36 | help='The custom output directory to store output in, if desired') 37 | parser.add_argument('-nobuild', action='store_false', default=True, dest='build', 38 | help='Turn OFF generation of WAD files, titles will be downloaded only.') 39 | parser.add_argument('-retry', type=int, default=4, dest='retry_count', 40 | choices=range(0, 10), help='How many times a file download will be attempted') 41 | parser.add_argument('-title', nargs='+', dest='titles', default=[], 42 | help='Give TitleIDs to be specifically downloaded') 43 | parser.add_argument('-key', nargs='+', dest='keys', default=[], 44 | help='Encrypted Title Key for the Title IDs. Must be in the same order as TitleIDs if multiple') 45 | parser.add_argument('-version', nargs='+', dest='versions', default=[], 46 | help='Version of the title to download. Must be in the same order as TitleIDs if multiple') 47 | parser.add_argument('-getcetk', action='store_true', default=False, dest='getcetk', 48 | help='Get legit cetk from the CDN, this is only available for system titles and some other free titles.') 49 | 50 | 51 | def bytes2human(n, f='%(value).2f %(symbol)s', symbols='customary'): 52 | n = int(n) 53 | if n < 0: 54 | raise ValueError("n < 0") 55 | symbols = SYMBOLS[symbols] 56 | prefix = {} 57 | for i, s in enumerate(symbols[1:]): 58 | prefix[s] = 1 << (i + 1) * 10 59 | for symbol in reversed(symbols[1:]): 60 | if n >= prefix[symbol]: 61 | value = float(n) / prefix[symbol] 62 | return f % locals() 63 | return f % dict(symbol=symbols[0], value=n) 64 | 65 | 66 | RE_16_HEX = re.compile(r'^[0-9a-f]{16}$', re.IGNORECASE) 67 | RE_32_HEX = re.compile(r'^[0-9a-f]{32}$', re.IGNORECASE) 68 | 69 | check_title_id = RE_16_HEX.match 70 | check_title_key = RE_32_HEX.match 71 | 72 | 73 | def retry(count): 74 | for i in range(1, count + 1): 75 | if i > 1: 76 | print("*Attempt {} of {}".format(i, count)) 77 | yield i 78 | 79 | 80 | def progress_bar(part, total, length=10, char='#', blank=' ', left='[', right=']'): 81 | percent = int((float(part) / float(total) * 100) % 100) 82 | bar_len = int((float(part) / float(total) * length) % length) 83 | bar = char * bar_len 84 | blanks = blank * (length - bar_len) 85 | return '{}{}{}{} {} of {}, {}%'.format( 86 | left, bar, blanks, right, bytes2human(part), bytes2human(total), percent 87 | ) + ' ' * 20 88 | 89 | 90 | def download_file(url, outfname, retry_count=3, ignore_404=False, expected_size=None, chunk_size=0x4096): 91 | for _ in retry(retry_count): 92 | try: 93 | infile = urlopen(url) 94 | # start of modified code 95 | if os.path.isfile(outfname): 96 | statinfo = os.stat(outfname) 97 | diskFilesize = statinfo.st_size 98 | else: 99 | diskFilesize = 0 100 | log('-Downloading {}.\n-File size is {}.\n-File in disk is {}.'.format(outfname, expected_size,diskFilesize)) 101 | 102 | if expected_size is None or expected_size > diskFilesize: 103 | with open(outfname, 'wb') as outfile: 104 | downloaded_size = 0 105 | while True: 106 | buf = infile.read(chunk_size) 107 | if not buf: 108 | break 109 | downloaded_size += len(buf) 110 | if expected_size and len(buf) == chunk_size: 111 | print(' Downloaded {}'.format(progress_bar(downloaded_size, expected_size)), end='\r') 112 | outfile.write(buf) 113 | else: 114 | print('-File skipped.') 115 | downloaded_size = statinfo.st_size 116 | # end of modified code 117 | 118 | if expected_size is not None: 119 | if int(os.path.getsize(outfname)) < expected_size: 120 | print('Content download not correct size\n') 121 | continue 122 | else: 123 | print('Download complete: {}\n'.format(bytes2human(downloaded_size)) + ' ' * 40) 124 | except HTTPError as e: 125 | if e.code == 404 and ignore_404: 126 | # We are ignoring this because its a 404 error, not a failure 127 | return True 128 | except URLError: 129 | print('Could not download file...\n') 130 | else: 131 | return True 132 | return False 133 | 134 | 135 | def make_ticket(title_id, title_key, title_version, fulloutputpath): 136 | tikdata = bytearray(TIKTEM) 137 | tikdata[0x1E6:0x01E8] = title_version 138 | tikdata[0x01DC:0x01E4] = binascii.a2b_hex(title_id) 139 | tikdata[0x01BF:0x01CF] = binascii.a2b_hex(title_key) 140 | if title_id[0:8] == '00010005': 141 | tikdata[0x1EC:0x01F0] = binascii.a2b_hex('FFFFFFFF') 142 | open(fulloutputpath, 'wb').write(tikdata) 143 | 144 | def process_title_id(title_id, title_key, version, output_dir=None, build=False, retry_count=3, tickets_only=False): 145 | 146 | typecheck = title_id[0:8] 147 | 148 | islegit = title_key == 1 or typecheck == '00000001' or typecheck == '00010002' or typecheck == '00010008' 149 | 150 | if not version: 151 | rawdir = os.path.join('raw', title_id) 152 | else: 153 | rawdir = os.path.join('raw', title_id + 'v' + version) 154 | 155 | log('Starting work in: "{}"'.format(rawdir)) 156 | 157 | if output_dir is not None: 158 | rawdir = os.path.join(output_dir, rawdir) 159 | 160 | if not os.path.exists(rawdir): 161 | os.makedirs(os.path.join(rawdir)) 162 | 163 | # download stuff 164 | print('Downloading TMD...') 165 | 166 | baseurl = 'http://ccs.cdn.c.shop.nintendowifi.net/ccs/download/{}'.format(title_id) 167 | tmd_path = os.path.join(rawdir, 'title.tmd') 168 | if not version: 169 | tmd_url = baseurl + '/tmd' 170 | else: 171 | tmd_url = baseurl + '/tmd' + '.' + version 172 | if not download_file(tmd_url, tmd_path, retry_count): 173 | print('ERROR: Could not download TMD...') 174 | print('MAYBE YOU ARE BLOCKING CONNECTIONS TO NINTENDO? IF YOU ARE, DON\'T...! :)') 175 | print('Skipping title...') 176 | return 177 | 178 | with open(tmd_path, 'rb+') as f: 179 | tmd = f.read() 180 | content_count = int(binascii.hexlify(tmd[0x1DE:0x1E0]), 16) 181 | tmdsize = 0x1E4 + (36 * content_count) 182 | if islegit: 183 | f.seek(tmdsize + 0x300) 184 | cetk = f.read(0x400) 185 | f.seek(tmdsize) 186 | cetk = cetk + f.read(0x300) 187 | f.truncate(tmdsize) 188 | 189 | title_version = tmd[0x1DC:0x1DE] 190 | 191 | # get ticket from keysite, from cdn if game update, or generate ticket 192 | if islegit: 193 | print('\nWe are getting the legit ticket straight from Nintendo.') 194 | tik_path = os.path.join(rawdir, 'title.tik') 195 | if not download_file(baseurl + '/cetk', tik_path, retry_count): 196 | print('ERROR: Could not download ticket from {}'.format(baseurl + '/cetk')) 197 | print('Skipping title...') 198 | return 199 | with open(tik_path, 'rb+') as f: 200 | tik = f.read(0x2A4) 201 | cetk = cetk + f.read(0x300) 202 | f.truncate(0x2A4) 203 | with open(os.path.join(rawdir, 'title.cert'), 'wb') as f: 204 | print('Building cert...') 205 | f.write(cetk) 206 | else: 207 | make_ticket(title_id, title_key, title_version, os.path.join(rawdir, 'title.tik')) 208 | with open(os.path.join(rawdir, 'title.cert'), 'wb') as f: 209 | f.write(MAGIC) 210 | 211 | print('Downloading Contents...') 212 | 213 | total_size = 0 214 | for i in range(content_count): 215 | c_offs = 0x1E4 + (0x24 * i) 216 | total_size += int(binascii.hexlify(tmd[c_offs + 0x08:c_offs + 0x10]), 16) 217 | print('Total size is {}\n'.format(bytes2human(total_size))) 218 | 219 | for i in range(content_count): 220 | c_offs = 0x1E4 + (0x24 * i) 221 | c_id = binascii.hexlify(tmd[c_offs:c_offs + 0x04]).decode() 222 | i_id = '0000' + binascii.hexlify(tmd[c_offs + 0x04:c_offs + 0x06]).decode() 223 | expected_size = int(binascii.hexlify(tmd[c_offs + 0x08:c_offs + 0x10]), 16) 224 | print('Downloading {} of {}.'.format(i + 1, content_count)) 225 | outfname = os.path.join(rawdir, i_id + '.app') 226 | 227 | if not download_file('{}/{}'.format(baseurl, c_id), outfname, retry_count, expected_size=expected_size): 228 | print('ERROR: Could not download content file... Skipping title') 229 | return 230 | 231 | log('\nTitle download complete in "{}"\n'.format(rawdir)) 232 | 233 | if build: 234 | if not version: 235 | waddir = os.path.join('wad', title_id) 236 | else: 237 | waddir = os.path.join('wad', title_id + 'v' + version) 238 | 239 | if output_dir is not None: 240 | waddir = os.path.join(output_dir, waddir) 241 | 242 | if not os.path.exists(waddir): 243 | os.makedirs(os.path.join(waddir)) 244 | 245 | if not version: 246 | path = os.path.join(waddir, title_id + '.wad') 247 | else: 248 | path = os.path.join(waddir, title_id + 'v' + version + '.wad') 249 | 250 | makecommand = ' ' + os.path.join(rawdir) + ' ' + path + ' -e' 251 | if not islegit: 252 | makecommand = makecommand + ' -T' 253 | os.system(execname + makecommand) 254 | if(os.path.isfile(path)): 255 | print('WAD created ok!') 256 | else: 257 | print('WAD not created...') 258 | print('') 259 | print('') 260 | 261 | 262 | def main(args=None): 263 | if len(sys.argv) == 1: 264 | parser.print_usage() 265 | sys.exit(1) 266 | 267 | arguments = parser.parse_args() 268 | titles=arguments.titles 269 | keys=arguments.keys 270 | versions=arguments.versions 271 | output_dir=arguments.output_dir 272 | build=arguments.build 273 | getcetk=arguments.getcetk 274 | retry_count=arguments.retry_count 275 | 276 | print('*******\nFunKii {} by cearp, the cerea1killer and AuroraWright\n*******\n'.format(__VERSION__)) 277 | titlekeys_data = [] 278 | 279 | if (not getcetk) and keys and (len(keys)!=len(titles)): 280 | print('Number of keys and Title IDs do not match up') 281 | sys.exit(0) 282 | 283 | if titles and (not keys) and (not getcetk): 284 | print('You also need to provide \'-keys\'') 285 | sys.exit(0) 286 | 287 | if versions and (len(versions)!=len(titles)): 288 | print('Number of versions and Title IDs do not match up') 289 | sys.exit(0) 290 | 291 | for title_id in titles: 292 | title_id = title_id.lower() 293 | if not check_title_id(title_id): 294 | print('The Title ID(s) must be 16 hexadecimal characters long') 295 | print('{} - is not ok.'.format(title_id)) 296 | sys.exit(0) 297 | title_key = 1 if getcetk else None 298 | version = None 299 | 300 | if (not getcetk) and keys: 301 | title_key = keys.pop() 302 | if not check_title_key(title_key): 303 | print('The key(s) must be 32 hexadecimal characters long') 304 | print('{} - is not ok.'.format(title_id)) 305 | sys.exit(0) 306 | 307 | if not (title_key): 308 | print('ERROR: Could not find title or ticket for {}'.format(title_id)) 309 | continue 310 | 311 | if versions: 312 | version = versions.pop() 313 | if (len(version) > 5) or any(c not in string.digits for c in version): 314 | print('The version(s) must be 5 decimal digits at most') 315 | print(version + ' - is not ok.') 316 | sys.exit(0) 317 | 318 | process_title_id(title_id, title_key, version, output_dir, build, retry_count) 319 | 320 | def log(output): 321 | output = output.encode(sys.stdout.encoding, errors='replace') 322 | if sys.version_info[0] == 3: 323 | output = output.decode(sys.stdout.encoding, errors='replace') 324 | print(output) 325 | 326 | 327 | if __name__ == '__main__': 328 | main() 329 | --------------------------------------------------------------------------------