1
2 /***************************************************************************
3 * __ __ _ ___________ *
4 * \ \ / /| |____ ____| *
5 * \ \ / / | | | | *
6 * \ \ /\ / / | | | | *
7 * \ \/ \/ / | | | | *
8 * \ /\ / | | | | *
9 * \/ \/ |_| |_| *
10 * *
11 * Wiimms ISO Tools *
12 * http://wit.wiimm.de/ *
13 * *
14 ***************************************************************************
15 * *
16 * This file is part of the WIT project. *
17 * Visit http://wit.wiimm.de/ for project details and sources. *
18 * *
19 * Copyright (c) 2009-2013 by Dirk Clemens <wiimm@wiimm.de> *
20 * *
21 ***************************************************************************
22 * *
23 * This program is free software; you can redistribute it and/or modify *
24 * it under the terms of the GNU General Public License as published by *
25 * the Free Software Foundation; either version 2 of the License, or *
26 * (at your option) any later version. *
27 * *
28 * This program is distributed in the hope that it will be useful, *
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
31 * GNU General Public License for more details. *
32 * *
33 * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
34 * *
35 ***************************************************************************/
36
37 #include "file-formats.h"
38 #include "crypt.h"
39
40 //
41 ///////////////////////////////////////////////////////////////////////////////
42 /////////////// setup ///////////////
43 ///////////////////////////////////////////////////////////////////////////////
44
validate_file_format_sizes(int trace_sizes)45 int validate_file_format_sizes ( int trace_sizes )
46 {
47 // 1. trace sizeof
48
49 #ifdef TRACE_SIZEOF
50 if (trace_sizes)
51 {
52 TRACE_SIZEOF(u8);
53 TRACE_SIZEOF(u16);
54 TRACE_SIZEOF(u32);
55 TRACE_SIZEOF(u64);
56
57 TRACE_SIZEOF(be16_t);
58 TRACE_SIZEOF(be32_t);
59 TRACE_SIZEOF(be64_t);
60
61 TRACE_SIZEOF(dol_header_t);
62 TRACE_SIZEOF(dol_record_t);
63 TRACE_SIZEOF(wbfs_inode_info_t);
64 TRACE_SIZEOF(wd_header_128_t);
65 TRACE_SIZEOF(wd_header_t);
66 TRACE_SIZEOF(wd_boot_t);
67 TRACE_SIZEOF(wd_region_t);
68 TRACE_SIZEOF(wd_ptab_info_t);
69 TRACE_SIZEOF(wd_ptab_entry_t);
70 TRACE_SIZEOF(wd_ptab_t);
71 TRACE_SIZEOF(wd_ticket_t);
72 TRACE_SIZEOF(wd_part_header_t);
73 TRACE_SIZEOF(wd_tmd_content_t);
74 TRACE_SIZEOF(wd_tmd_t);
75 TRACE_SIZEOF(wd_part_control_t);
76 TRACE_SIZEOF(wd_part_sector_t);
77 TRACE_SIZEOF(wd_fst_item_t);
78
79 TRACE_SIZEOF(wbfs_head_t);
80 TRACE_SIZEOF(wbfs_disc_info_t);
81
82 TRACE_SIZEOF(WIT_PATCH_MAGIC);
83 TRACE_SIZEOF(wpat_magic);
84 TRACE_SIZEOF(wpat_comment_t);
85 TRACE_SIZEOF(wpat_data_t);
86 TRACE_SIZEOF(wpat_filename_t);
87 TRACE_SIZEOF(wpat_filenames_t);
88 TRACE_SIZEOF(wpat_header_t);
89 TRACE_SIZEOF(wpat_patch_file_t);
90 TRACE_SIZEOF(wpat_size_t);
91 TRACE_SIZEOF(wpat_toc_file_t);
92 TRACE_SIZEOF(wpat_toc_header_t);
93 TRACE_SIZEOF(wpat_type_t);
94
95
96 #ifdef DEBUG
97 wd_part_control_t pc, pc_saved;
98 ASSERT(!clear_part_control(&pc,
99 WII_TMD_GOOD_SIZE,
100 0xa00, 0x1000000 ));
101 memcpy(&pc_saved,&pc,sizeof(pc_saved));
102 ASSERT(!setup_part_control(&pc));
103 ASSERT(!memcmp(&pc_saved,&pc,sizeof(pc_saved)));
104 #endif
105 }
106 #endif
107
108 // 2. assertions
109
110 wd_tmd_t * tmd = 0;
111 wd_ticket_t * tik = 0;
112
113 #undef CHECK
114 #define CHECK ASSERT
115 #undef OFFSET
116 #define OFFSET(p,i) ((char*)&p->i-(char*)p)
117
118 CHECK( 1 == sizeof(u8) );
119 CHECK( 2 == sizeof(u16) );
120 CHECK( 4 == sizeof(u32) );
121 CHECK( 8 == sizeof(u64) );
122
123 CHECK( 2 == sizeof(be16_t) );
124 CHECK( 4 == sizeof(be32_t) );
125 CHECK( 8 == sizeof(be64_t) );
126
127 CHECK( sizeof(wbfs_inode_info_t) + WBFS_INODE_INFO_OFF == 0x100 );
128 CHECK( sizeof(wbfs_inode_info_t) == WBFS_INODE_INFO_SIZE );
129
130 CHECK( sizeof(dol_header_t) == DOL_HEADER_SIZE );
131 CHECK( sizeof(wd_header_128_t) == 0x80 );
132 CHECK( sizeof(wd_header_t) == 0x100 );
133 CHECK( sizeof(wd_region_t) == WII_REGION_SIZE );
134 CHECK( sizeof(wd_ptab_t) == WII_MAX_PTAB_SIZE );
135 CHECK( sizeof(wd_boot_t) == WII_BOOT_SIZE );
136 CHECK( sizeof(wd_part_sector_t) == WII_SECTOR_SIZE );
137 CHECK( sizeof(wd_fst_item_t) == 12 ); // test because of union
138
139 CHECK( OFFSET(tik,title_key) == WII_TICKET_KEY_OFF );
140 CHECK( OFFSET(tik,title_id) == WII_TICKET_IV_OFF );
141 CHECK( OFFSET(tik,issuer) == WII_TICKET_SIG_OFF );
142 CHECK( OFFSET(tik,fake_sign) == WII_TICKET_BRUTE_FORCE_OFF );
143 CHECK( sizeof(wd_ticket_t) == WII_TICKET_SIZE );
144
145 CHECK( OFFSET(tmd,issuer) == WII_TMD_SIG_OFF );
146 CHECK( OFFSET(tmd,fake_sign) == WII_TMD_BRUTE_FORCE_OFF );
147 CHECK( OFFSET(tmd,content[0].hash) == 0x1f4 );
148 CHECK( sizeof(wd_tmd_t) == 0x1e4 );
149 CHECK( sizeof(wd_tmd_content_t) == 0x24 );
150 CHECK( sizeof(wd_tmd_t) + sizeof(wd_tmd_content_t) == WII_TMD_GOOD_SIZE );
151
152 //----- 3. calculate return value
153
154 #undef CHECK
155 #define CHECK(a) if (!(a)) return __LINE__;
156
157 CHECK( 1 == sizeof(u8) );
158 CHECK( 2 == sizeof(u16) );
159 CHECK( 4 == sizeof(u32) );
160 CHECK( 8 == sizeof(u64) );
161
162 CHECK( 2 == sizeof(be16_t) );
163 CHECK( 4 == sizeof(be32_t) );
164 CHECK( 8 == sizeof(be64_t) );
165
166 CHECK( sizeof(wbfs_inode_info_t) + WBFS_INODE_INFO_OFF == 0x100 );
167 CHECK( sizeof(wbfs_inode_info_t) == WBFS_INODE_INFO_SIZE );
168
169 CHECK( sizeof(dol_header_t) == DOL_HEADER_SIZE );
170 CHECK( sizeof(wd_header_128_t) == 0x80 );
171 CHECK( sizeof(wd_header_t) == 0x100 );
172 CHECK( sizeof(wd_region_t) == WII_REGION_SIZE );
173 CHECK( sizeof(wd_ptab_t) == WII_MAX_PTAB_SIZE );
174 CHECK( sizeof(wd_boot_t) == WII_BOOT_SIZE );
175 CHECK( sizeof(wd_part_sector_t) == WII_SECTOR_SIZE );
176 CHECK( sizeof(wd_fst_item_t) == 12 ); // test because of union
177
178 CHECK( OFFSET(tik,title_key) == WII_TICKET_KEY_OFF );
179 CHECK( OFFSET(tik,title_id) == WII_TICKET_IV_OFF );
180 CHECK( OFFSET(tik,issuer) == WII_TICKET_SIG_OFF );
181 CHECK( OFFSET(tik,fake_sign) == WII_TICKET_BRUTE_FORCE_OFF );
182 CHECK( sizeof(wd_ticket_t) == WII_TICKET_SIZE );
183
184 CHECK( OFFSET(tmd,issuer) == WII_TMD_SIG_OFF );
185 CHECK( OFFSET(tmd,fake_sign) == WII_TMD_BRUTE_FORCE_OFF );
186 CHECK( OFFSET(tmd,content[0].hash) == 0x1f4 );
187 CHECK( sizeof(wd_tmd_t) == 0x1e4 );
188 CHECK( sizeof(wd_tmd_content_t) == 0x24 );
189 CHECK( sizeof(wd_tmd_t) + sizeof(wd_tmd_content_t) == WII_TMD_GOOD_SIZE );
190
191 return 0;
192 }
193
194 //
195 ///////////////////////////////////////////////////////////////////////////////
196 /////////////// endian conversions for structs ///////////////
197 ///////////////////////////////////////////////////////////////////////////////
198
ntoh_dol_header(dol_header_t * dest,const dol_header_t * src)199 void ntoh_dol_header ( dol_header_t * dest, const dol_header_t * src )
200 {
201 DASSERT(dest);
202
203 if (!src)
204 src = dest;
205 else if ( dest != src )
206 memcpy(dest,src,sizeof(*dest));
207
208 const u32 * src_ptr = src->sect_off;
209 u32 * dest_ptr = dest->sect_off;
210 u32 * dest_end = (u32*)&dest->padding;
211
212 while ( dest_ptr < dest_end )
213 *dest_ptr++ = ntohl(*src_ptr++);
214 }
215
216 //-----------------------------------------------------------------------------
217
hton_dol_header(dol_header_t * dest,const dol_header_t * src)218 void hton_dol_header ( dol_header_t * dest, const dol_header_t * src )
219 {
220 DASSERT(dest);
221
222 if (!src)
223 src = dest;
224 else if ( dest != src )
225 memcpy(dest,src,sizeof(*dest));
226
227 const u32 * src_ptr = src->sect_off;
228 u32 * dest_ptr = dest->sect_off;
229 u32 * dest_end = (u32*)&dest->padding;
230
231 while ( dest_ptr < dest_end )
232 *dest_ptr++ = htonl(*src_ptr++);
233 }
234
235 ///////////////////////////////////////////////////////////////////////////////
236
ntoh_boot(wd_boot_t * dest,const wd_boot_t * src)237 void ntoh_boot ( wd_boot_t * dest, const wd_boot_t * src )
238 {
239 DASSERT(dest);
240
241 if (!src)
242 src = dest;
243 else if ( dest != src )
244 memcpy(dest,src,sizeof(*dest));
245
246 dest->dol_off4 = ntohl(src->dol_off4);
247 dest->fst_off4 = ntohl(src->fst_off4);
248 dest->fst_size4 = ntohl(src->fst_size4);
249 dest->max_fst_size4 = ntohl(src->max_fst_size4);
250 }
251
252 //-----------------------------------------------------------------------------
253
hton_boot(wd_boot_t * dest,const wd_boot_t * src)254 void hton_boot ( wd_boot_t * dest, const wd_boot_t * src )
255 {
256 DASSERT(dest);
257
258 if (!src)
259 src = dest;
260 else if ( dest != src )
261 memcpy(dest,src,sizeof(*dest));
262
263 dest->dol_off4 = htonl(src->dol_off4);
264 dest->fst_off4 = htonl(src->fst_off4);
265 dest->fst_size4 = htonl(src->fst_size4);
266 dest->max_fst_size4 = htonl(src->max_fst_size4);
267 }
268
269 ///////////////////////////////////////////////////////////////////////////////
270
ntoh_part_header(wd_part_header_t * dest,const wd_part_header_t * src)271 void ntoh_part_header ( wd_part_header_t * dest, const wd_part_header_t * src )
272 {
273 DASSERT(dest);
274
275 if (!src)
276 src = dest;
277 else if ( dest != src )
278 memcpy(dest,src,sizeof(*dest));
279
280 dest->tmd_size = ntohl(src->tmd_size);
281 dest->tmd_off4 = ntohl(src->tmd_off4);
282 dest->cert_size = ntohl(src->cert_size);
283 dest->cert_off4 = ntohl(src->cert_off4);
284 dest->h3_off4 = ntohl(src->h3_off4);
285 dest->data_off4 = ntohl(src->data_off4);
286 dest->data_size4 = ntohl(src->data_size4);
287 }
288
289 //-----------------------------------------------------------------------------
290
hton_part_header(wd_part_header_t * dest,const wd_part_header_t * src)291 void hton_part_header ( wd_part_header_t * dest, const wd_part_header_t * src )
292 {
293 DASSERT(dest);
294
295 if (!src)
296 src = dest;
297 else if ( dest != src )
298 memcpy(dest,src,sizeof(*dest));
299
300 dest->tmd_size = htonl(src->tmd_size);
301 dest->tmd_off4 = htonl(src->tmd_off4);
302 dest->cert_size = htonl(src->cert_size);
303 dest->cert_off4 = htonl(src->cert_off4);
304 dest->h3_off4 = htonl(src->h3_off4);
305 dest->data_off4 = htonl(src->data_off4);
306 dest->data_size4 = htonl(src->data_size4);
307 }
308
309 ///////////////////////////////////////////////////////////////////////////////
310
ntoh_inode_info(wbfs_inode_info_t * dest,const wbfs_inode_info_t * src)311 void ntoh_inode_info ( wbfs_inode_info_t * dest, const wbfs_inode_info_t * src )
312 {
313 DASSERT(dest);
314
315 if (!src)
316 src = dest;
317 else if ( dest != src )
318 memcpy(dest,src,sizeof(*dest));
319
320 dest->n_hd_sec = ntohl(src->n_hd_sec);
321 dest->info_version = ntohl(src->info_version);
322
323 dest->itime = ntoh64(src->itime);
324 dest->mtime = ntoh64(src->mtime);
325 dest->ctime = ntoh64(src->ctime);
326 dest->atime = ntoh64(src->atime);
327 dest->dtime = ntoh64(src->dtime);
328 }
329
330 //-----------------------------------------------------------------------------
331
hton_inode_info(wbfs_inode_info_t * dest,const wbfs_inode_info_t * src)332 void hton_inode_info ( wbfs_inode_info_t * dest, const wbfs_inode_info_t * src )
333 {
334 DASSERT(dest);
335
336 if (!src)
337 src = dest;
338 else if ( dest != src )
339 memcpy(dest,src,sizeof(*dest));
340
341 dest->n_hd_sec = htonl(src->n_hd_sec);
342 dest->info_version = htonl(src->info_version);
343
344 dest->itime = hton64(src->itime);
345 dest->mtime = hton64(src->mtime);
346 dest->ctime = hton64(src->ctime);
347 dest->atime = hton64(src->atime);
348 dest->dtime = hton64(src->dtime);
349 }
350
351 //
352 ///////////////////////////////////////////////////////////////////////////////
353 /////////////// struct dol_record_t ///////////////
354 ///////////////////////////////////////////////////////////////////////////////
355
calc_dol_records(dol_record_t * rec,bool term_null,const dol_header_t * dol_head)356 uint calc_dol_records
357 (
358 dol_record_t *rec, // pointer to at least DOL_N_SECTIONS records
359 bool term_null, // true: add a NULL record at end of list
360 const dol_header_t *dol_head // source DOL header
361 )
362 {
363 DASSERT(rec);
364 DASSERT(dol_head);
365
366 dol_header_t dh;
367 ntoh_dol_header(&dh,dol_head);
368 dol_record_t *dest = rec;
369 u32 last_addr = 0xffffffff;
370
371 for(;;)
372 {
373 u32 max_addr = 0;
374 int i, found = -1;
375 for ( i = 0; i < DOL_N_SECTIONS; i++ )
376 {
377 if ( dh.sect_off[i]
378 && dh.sect_size[i]
379 && dh.sect_addr[i] < last_addr
380 && dh.sect_addr[i] > max_addr )
381 {
382 found = i;
383 max_addr = dh.sect_addr[i];
384 }
385 }
386 if ( found < 0 )
387 break;
388
389 memset(dest,0,sizeof(*dest));
390 dest->addr = dh.sect_addr[found];
391 dest->size = dh.sect_size[found];
392 dest->xsize = dest->size;
393 dest->delta = dest->addr - dh.sect_off[found];
394
395 if ( found < DOL_N_TEXT_SECTIONS )
396 {
397 dest->name[0] = 'T';
398 dest->name[1] = found+'0';
399 }
400 else
401 {
402 dest->name[0] = 'D';
403 dest->name[1] = found-DOL_N_TEXT_SECTIONS+'0';
404 }
405
406 if ( dest > rec
407 && dest[-1].delta == dest->delta
408 && dest->addr + dest->size == dest[-1].addr )
409 {
410 dest->xsize += dest[-1].xsize;
411 }
412 last_addr = dest->addr;
413 dest++;
414 }
415
416 if (term_null)
417 memset(dest,0,sizeof(*dest));
418 return dest - rec;
419 }
420
421 ///////////////////////////////////////////////////////////////////////////////
422 ///////////////////////////////////////////////////////////////////////////////
423
search_dol_record(dol_record_t * rec,uint n_rec,u32 addr,u32 size)424 const dol_record_t * search_dol_record
425 (
426 dol_record_t *rec, // pointer to at least records
427 uint n_rec, // number of records, set it to DOL_N_SECTIONS
428 // if record lsit is NULL termianted
429 u32 addr, // address to search
430 u32 size // size of object, if NULL, ignore it
431 )
432 {
433 DASSERT(rec);
434
435 while ( n_rec-- > 0 )
436 {
437 if (!rec->size)
438 break;
439
440 if ( addr >= rec->addr && addr+size <= rec->addr+rec->xsize )
441 return rec;
442 rec++;
443 }
444 return 0;
445 }
446
447 //
448 ///////////////////////////////////////////////////////////////////////////////
449 /////////////// enum wd_compression_t ///////////////
450 ///////////////////////////////////////////////////////////////////////////////
451
wd_get_compression_name(wd_compression_t compr,ccp invalid_result)452 ccp wd_get_compression_name
453 (
454 wd_compression_t compr, // compression mode
455 ccp invalid_result // return value if 'compr' is invalid
456 )
457 {
458 static ccp tab[] =
459 {
460 "NONE",
461 "PURGE",
462 "BZIP2",
463 "LZMA",
464 "LZMA2",
465 };
466
467 return (u32)compr < sizeof(tab)/sizeof(*tab) ? tab[compr] : invalid_result;
468 }
469
470 ///////////////////////////////////////////////////////////////////////////////
471
wd_print_compression(char * buf,size_t buf_size,wd_compression_t compr_method,int compr_level,u32 chunk_size,int mode)472 ccp wd_print_compression
473 (
474 char * buf, // result buffer
475 // If NULL, a local circulary static buffer is used
476 size_t buf_size, // size of 'buf', ignored if buf==NULL
477 wd_compression_t compr_method, // compression method
478 int compr_level, // compression level
479 u32 chunk_size, // compression chunk size, multiple of MiB
480 int mode // 1=number, 2=name, 3=number and name
481 )
482 {
483 if (!buf)
484 buf = GetCircBuf( buf_size = 20 );
485
486 if ( compr_method < 0 || compr_method >= WD_COMPR__N )
487 {
488 compr_method = WD_COMPR__N;
489 compr_level = 0;
490 }
491 else if ( compr_method < WD_COMPR__FIRST_REAL || compr_level < 0 )
492 compr_level = 0;
493 else if ( compr_level > 9 )
494 compr_level = 9;
495
496 char cbuf[10] = {0};
497 if ( compr_method != WD_COMPR__N )
498 {
499 chunk_size /= WII_GROUP_SIZE; // reduce chunk_size to a factor
500 if (chunk_size)
501 snprintf(cbuf,sizeof(cbuf),"@%u",chunk_size);
502 }
503
504
505 mode &= 3;
506 if ( mode == 1 )
507 {
508 if ( compr_method == WD_COMPR__N )
509 snprintf(buf,buf_size,"-");
510 else if (compr_level)
511 snprintf(buf,buf_size,"%u.%u%s",compr_method,compr_level,cbuf);
512 else
513 snprintf(buf,buf_size,"%u%s",compr_method,cbuf);
514 }
515 else if ( mode == 2 )
516 {
517 ccp name = wd_get_compression_name(compr_method,"-");
518 if ( compr_method == WD_COMPR__N )
519 snprintf(buf,buf_size,"-");
520 else if (compr_level)
521 snprintf(buf,buf_size,"%s.%u%s",name,compr_level,cbuf);
522 else
523 snprintf(buf,buf_size,"%s%s",name,cbuf);
524 }
525 else
526 {
527 ccp name = wd_get_compression_name(compr_method,"-");
528 if ( compr_method == WD_COMPR__N )
529 snprintf(buf,buf_size,"- -");
530 else if (compr_level)
531 snprintf(buf,buf_size,"%u.%u%s %s.%u%s",
532 compr_method, compr_level, cbuf,
533 name, compr_level, cbuf );
534 else
535 snprintf(buf,buf_size,"%u%s %s%s",
536 compr_method, cbuf, name, cbuf );
537 }
538
539 return buf;
540 }
541
542 //
543 ///////////////////////////////////////////////////////////////////////////////
544 /////////////// struct wd_header_t ///////////////
545 ///////////////////////////////////////////////////////////////////////////////
546
id_setup(void * dest_id,const void * source_id,int id_size)547 static void id_setup ( void * dest_id, const void * source_id, int id_size )
548 {
549 DASSERT(dest_id);
550 DASSERT(id_size>0);
551
552 u8 * dest = dest_id;
553 memset(dest,'0',id_size);
554
555 int i;
556 const u8 * src = source_id ? source_id : "WIT";
557 for ( i = 0; i < id_size && *src; i++ )
558 *dest++ = *src++;
559 }
560
561 ///////////////////////////////////////////////////////////////////////////////
562
header_128_setup(wd_header_128_t * dhead,const void * id6,ccp disc_title,bool is_gc)563 void header_128_setup
564 (
565 wd_header_128_t * dhead, // valid pointer
566 const void * id6, // NULL or pointer to ID
567 ccp disc_title, // NULL or pointer to disc title (truncated)
568 bool is_gc // true: GameCube setup
569 )
570 {
571 memset(dhead,0,sizeof(*dhead));
572 id_setup(&dhead->disc_id,id6,6);
573
574 if (!disc_title)
575 disc_title = "WIT: Wiimms ISO Tools, http://wit.wiimm.de/";
576 strncpy(dhead->disc_title,disc_title,sizeof(dhead->disc_title)-1);
577
578 if (is_gc)
579 dhead->gc_magic = htonl(GC_MAGIC);
580 else
581 dhead->wii_magic = htonl(WII_MAGIC);
582 }
583
584 ///////////////////////////////////////////////////////////////////////////////
585
header_setup(wd_header_t * dhead,const void * id6,ccp disc_title,bool is_gc)586 void header_setup
587 (
588 wd_header_t * dhead, // valid pointer
589 const void * id6, // NULL or pointer to ID
590 ccp disc_title, // NULL or pointer to disc title (truncated)
591 bool is_gc // true: GameCube setup
592 )
593 {
594 memset(dhead,0,sizeof(*dhead));
595 header_128_setup((wd_header_128_t*)dhead,id6,disc_title,is_gc);
596 }
597
598 ///////////////////////////////////////////////////////////////////////////////
599 ///////////////////////////////////////////////////////////////////////////////
600
get_header_128_disc_type(wd_header_128_t * dhead,wd_disc_attrib_t * attrib)601 wd_disc_type_t get_header_128_disc_type
602 (
603 wd_header_128_t * dhead, // valid pointer
604 wd_disc_attrib_t * attrib // not NULL: store disc attributes
605 )
606 {
607 DASSERT(dhead);
608
609 //----- check Wii disc
610
611 if ( ntohl(dhead->wii_magic) == WII_MAGIC )
612 {
613 if (attrib)
614 *attrib = WD_DA_WII;
615 return WD_DT_WII;
616 }
617
618
619 //----- check GameCube disc
620
621 if ( ntohl(dhead->gc_magic) == GC_MAGIC )
622 {
623 if (attrib)
624 {
625 wd_disc_attrib_t att = WD_DA_GAMECUBE;
626
627 ccp id6 = &dhead->disc_id;
628 if ( !memcmp(id6,"GCOPDV",6)
629 || !memcmp(id6,"COBRAM",6)
630 || !memcmp(id6,"GGCOSD",6)
631 || !memcmp(id6,"RGCOSD",6) )
632 {
633 att |= WD_DA_GC_MULTIBOOT;
634 if (!memcmp(id6+4,"DVD9",4))
635 att |= WD_DA_GC_DVD9;
636 }
637 *attrib = att;
638 }
639 return WD_DT_GAMECUBE;
640 }
641
642
643 //----- unknown disc
644
645 if (attrib)
646 *attrib = 0;
647 return WD_DT_UNKNOWN;
648 }
649
650 ///////////////////////////////////////////////////////////////////////////////
651
get_header_disc_type(wd_header_t * dhead,wd_disc_attrib_t * attrib)652 wd_disc_type_t get_header_disc_type
653 (
654 wd_header_t * dhead, // valid pointer
655 wd_disc_attrib_t * attrib // not NULL: store disc attributes
656 )
657 {
658 return get_header_128_disc_type((wd_header_128_t*)dhead,attrib);
659 }
660
661 //
662 ///////////////////////////////////////////////////////////////////////////////
663 /////////////// enum wd_age_rating_t ///////////////
664 ///////////////////////////////////////////////////////////////////////////////
665
wd_print_age_rating(char * buf,size_t buf_size,u8 * age_rating)666 ccp wd_print_age_rating
667 (
668 char * buf, // result buffer
669 // If NULL, a local circulary static buffer is used
670 size_t buf_size, // size of 'buf', ignored if buf==NULL
671 u8 * age_rating // valid buffer of 'WD_AGE__N' bytes
672 )
673 {
674 if (!buf)
675 buf = GetCircBuf( buf_size = 70 );
676
677 DASSERT( WD_AGE__N == 10 );
678 snprintf( buf, buf_size,
679 "Jap=%u USA=%u ?=%u Eur=%u,%u,%u,%u,%u,%u Kor=%u",
680 age_rating[WD_AGE_JAPAN],
681 age_rating[WD_AGE_USA],
682 age_rating[WD_AGE_UNKNOWN],
683 age_rating[WD_AGE_EUROPE1],
684 age_rating[WD_AGE_EUROPE2],
685 age_rating[WD_AGE_EUROPE3],
686 age_rating[WD_AGE_EUROPE4],
687 age_rating[WD_AGE_EUROPE5],
688 age_rating[WD_AGE_EUROPE6],
689 age_rating[WD_AGE_KOREA] );
690
691 return buf;
692 }
693
694 //
695 ///////////////////////////////////////////////////////////////////////////////
696 /////////////// struct wd_ticket_t ///////////////
697 ///////////////////////////////////////////////////////////////////////////////
698
699 const char not_encrypted_marker[] = "*** partition is not encrypted ***";
700
701 ///////////////////////////////////////////////////////////////////////////////
702
ticket_setup(wd_ticket_t * tik,const void * id4)703 void ticket_setup ( wd_ticket_t * tik, const void * id4 )
704 {
705 DASSERT(tik);
706 memset(tik,0,sizeof(*tik));
707 tik->sig_type = htonl(0x10001);
708 strncpy((char*)tik->issuer,"Root-CA00000001-XS00000003",sizeof(tik->issuer));
709
710 static u8 base_id[] = { 0,1,0,0, 0,0,0,0 };
711 memcpy(tik->title_id,base_id,8);
712 id_setup(tik->title_id+4,id4,4);
713
714 memset(tik->unknown7,0xff,sizeof(tik->unknown7));
715 tik->unknown3 = 0xffff;
716
717 RANDOM_FILL(tik->title_key,sizeof(tik->title_key));
718 RANDOM_FILL(tik->ticket_id,sizeof(tik->ticket_id));
719 tik->ticket_id[0] = 0;
720 tik->ticket_id[1] = 1;
721 }
722
723 ///////////////////////////////////////////////////////////////////////////////
724
ticket_clear_encryption(wd_ticket_t * tik,int mark_not_encrypted)725 void ticket_clear_encryption ( wd_ticket_t * tik, int mark_not_encrypted )
726 {
727 ASSERT(tik);
728
729 memset(tik->sig,0,sizeof(tik->sig));
730 memset(tik->sig_padding,0,sizeof(tik->sig_padding));
731 memset(tik->fake_sign,0,sizeof(tik->fake_sign));
732
733 if (mark_not_encrypted)
734 {
735 ASSERT( sizeof(not_encrypted_marker) < sizeof(tik->sig_padding));
736 ASSERT( sizeof(not_encrypted_marker) < sizeof(tik->fake_sign));
737 strncpy( (char*)tik->sig_padding, not_encrypted_marker, sizeof(tik->sig_padding)-1 );
738 strncpy( (char*)tik->fake_sign, not_encrypted_marker, sizeof(tik->fake_sign)-1 );
739 }
740 }
741
742 ///////////////////////////////////////////////////////////////////////////////
743
ticket_is_marked_not_encrypted(const wd_ticket_t * tik)744 bool ticket_is_marked_not_encrypted ( const wd_ticket_t * tik )
745 {
746 ASSERT(tik);
747 DASSERT( sizeof(not_encrypted_marker) < sizeof(tik->sig_padding));
748 DASSERT( sizeof(not_encrypted_marker) < sizeof(tik->fake_sign));
749
750 return !strncmp( (char*)tik->sig_padding, not_encrypted_marker, sizeof(tik->sig_padding) )
751 && !strncmp( (char*)tik->fake_sign, not_encrypted_marker, sizeof(tik->fake_sign) );
752 }
753
754 ///////////////////////////////////////////////////////////////////////////////
755
ticket_fake_sign(wd_ticket_t * tik,u32 tik_size)756 u32 ticket_fake_sign ( wd_ticket_t * tik, u32 tik_size )
757 {
758 ASSERT(tik);
759 ticket_clear_encryption(tik,0);
760
761 if (!tik_size) // auto calculation
762 tik_size = sizeof(wd_ticket_t);
763
764 // fake signing
765
766 #ifdef DEBUG
767 TRACE("FAKESIGN: start brute force\n");
768 //TRACE_HEXDUMP16(0,0,tik,tik_size);
769 #endif
770
771 u32 val = 0;
772 u8 hash[WII_HASH_SIZE];
773 do
774 {
775 memcpy(tik->fake_sign,&val,sizeof(val));
776 SHA1( ((u8*)tik)+WII_TICKET_SIG_OFF, tik_size-WII_TICKET_SIG_OFF, hash );
777 if (!*hash)
778 break;
779 //TRACE_HEXDUMP(0,0,1,WII_HASH_SIZE,hash,WII_HASH_SIZE);
780 val++;
781
782 } while (val);
783
784 TRACE("FAKESIGN: success, count=%u\n", val+1);
785 return *hash ? 0 : val+1;
786 }
787
788 ///////////////////////////////////////////////////////////////////////////////
789
ticket_is_fake_signed(const wd_ticket_t * tik,u32 tik_size)790 bool ticket_is_fake_signed ( const wd_ticket_t * tik, u32 tik_size )
791 {
792 ASSERT(tik);
793
794 if (!tik_size) // auto calculation
795 tik_size = sizeof(wd_ticket_t);
796
797 int i;
798 for ( i = 0; i < sizeof(tik->sig); i++ )
799 if (tik->sig[i])
800 return 0;
801
802 u8 hash[WII_HASH_SIZE];
803 SHA1( ((u8*)tik)+WII_TICKET_SIG_OFF, tik_size-WII_TICKET_SIG_OFF, hash );
804 return !*hash;
805 }
806
807 //
808 ///////////////////////////////////////////////////////////////////////////////
809 /////////////// struct wd_tmd_t ///////////////
810 ///////////////////////////////////////////////////////////////////////////////
811
tmd_setup(wd_tmd_t * tmd,u32 tmd_size,const void * id4)812 void tmd_setup ( wd_tmd_t * tmd, u32 tmd_size, const void * id4 )
813 {
814 DASSERT(tmd);
815 DASSERT(tmd_size>=sizeof(wd_tmd_t));
816
817 memset(tmd,0,tmd_size);
818 if ( tmd_size >= sizeof(wd_tmd_t) + sizeof(wd_tmd_content_t) )
819 {
820 tmd->n_content = htons(1);
821 tmd->content[0].type = htons(3);
822 tmd->content[0].size = hton64(0xff7c0000);
823 }
824
825 tmd->sig_type = htonl(0x10001);
826 strncpy((char*)tmd->issuer,"Root-CA00000001-CP00000004",sizeof(tmd->issuer));
827
828 tmd->sys_version = hton64(0x100000023ull);
829 tmd->title_type = htonl(1);
830 tmd->group_id = htons(0x3031);
831
832 static u8 base_id[] = { 0,1,0,0, 0,0,0,0 };
833 memcpy(tmd->title_id,base_id,8);
834 id_setup(tmd->title_id+4,id4,4);
835 }
836
837 ///////////////////////////////////////////////////////////////////////////////
838
tmd_clear_encryption(wd_tmd_t * tmd,int mark_not_encrypted)839 void tmd_clear_encryption ( wd_tmd_t * tmd, int mark_not_encrypted )
840 {
841 ASSERT(tmd);
842
843 memset(tmd->sig,0,sizeof(tmd->sig));
844 memset(tmd->sig_padding,0,sizeof(tmd->sig_padding));
845 memset(tmd->fake_sign,0,sizeof(tmd->fake_sign));
846
847 if (mark_not_encrypted)
848 {
849 ASSERT( sizeof(not_encrypted_marker) < sizeof(tmd->sig_padding));
850 ASSERT( sizeof(not_encrypted_marker) < sizeof(tmd->fake_sign));
851 strncpy( (char*)tmd->sig_padding, not_encrypted_marker, sizeof(tmd->sig_padding)-1 );
852 strncpy( (char*)tmd->fake_sign, not_encrypted_marker, sizeof(tmd->fake_sign)-1 );
853 }
854 }
855
856 ///////////////////////////////////////////////////////////////////////////////
857
tmd_is_marked_not_encrypted(const wd_tmd_t * tmd)858 bool tmd_is_marked_not_encrypted ( const wd_tmd_t * tmd )
859 {
860 DASSERT( !tmd || sizeof(not_encrypted_marker) < sizeof(tmd->sig_padding));
861 DASSERT( !tmd || sizeof(not_encrypted_marker) < sizeof(tmd->fake_sign));
862
863 return tmd
864 && !strncmp( (char*)tmd->sig_padding, not_encrypted_marker, sizeof(tmd->sig_padding) )
865 && !strncmp( (char*)tmd->fake_sign, not_encrypted_marker, sizeof(tmd->fake_sign) );
866 }
867
868 ///////////////////////////////////////////////////////////////////////////////
869
tmd_fake_sign(wd_tmd_t * tmd,u32 tmd_size)870 u32 tmd_fake_sign ( wd_tmd_t * tmd, u32 tmd_size )
871 {
872 ASSERT(tmd);
873 tmd_clear_encryption(tmd,0);
874
875 if (!tmd_size) // auto calculation
876 tmd_size = sizeof(wd_tmd_t) + tmd->n_content * sizeof(wd_tmd_content_t);
877
878 // fake signing
879
880 #ifdef DEBUG
881 TRACE("FAKESIGN: start brute force\n");
882 //TRACE_HEXDUMP16(0,0,tmd,tmd_size);
883 #endif
884
885 u32 val = 0;
886 u8 hash[WII_HASH_SIZE];
887 do
888 {
889 memcpy(tmd->fake_sign,&val,sizeof(val));
890 SHA1( ((u8*)tmd)+WII_TMD_SIG_OFF, tmd_size-WII_TMD_SIG_OFF, hash );
891 if (!*hash)
892 break;
893 //TRACE_HEXDUMP(0,0,1,WII_HASH_SIZE,hash,WII_HASH_SIZE);
894 val++;
895
896 } while (val);
897
898 TRACE("FAKESIGN: success, count=%u\n", val+1);
899 return *hash ? 0 : val+1;
900 }
901
902 ///////////////////////////////////////////////////////////////////////////////
903
tmd_is_fake_signed(const wd_tmd_t * tmd,u32 tmd_size)904 bool tmd_is_fake_signed ( const wd_tmd_t * tmd, u32 tmd_size )
905 {
906 if (!tmd)
907 return false;
908
909 if (!tmd_size) // auto calculation
910 tmd_size = sizeof(wd_tmd_t) + tmd->n_content * sizeof(wd_tmd_content_t);
911
912 int i;
913 for ( i = 0; i < sizeof(tmd->sig); i++ )
914 if (tmd->sig[i])
915 return 0;
916
917 u8 hash[WII_HASH_SIZE];
918 SHA1( ((u8*)tmd)+WII_TMD_SIG_OFF, tmd_size-WII_TMD_SIG_OFF, hash );
919 return !*hash;
920 }
921
922 //
923 ///////////////////////////////////////////////////////////////////////////////
924 /////////////// struct wd_part_control_t ///////////////
925 ///////////////////////////////////////////////////////////////////////////////
926
setup_part_control_helper(wd_part_control_t * pc)927 static int setup_part_control_helper ( wd_part_control_t * pc )
928 {
929 // tmd, tmd_size, cert, cert_size and h3 must 0 or be valid
930
931 ASSERT(pc);
932 TRACE("setup_part_control_helper(%p)\n",pc);
933
934 TRACE(" - PART: %p : %8x + %9zx\n", pc->part_bin, 0, sizeof(pc->part_bin) );
935
936 // setup head
937
938 wd_part_header_t * head = (wd_part_header_t*)pc->part_bin;
939 pc->head = head;
940 pc->head_size = sizeof(wd_part_header_t);
941 TRACE(" - HEAD: %p : %8x + %9x\n", pc->head, 0, pc->head_size );
942
943 u8* head_end = pc->part_bin + ALIGN32(sizeof(wd_part_header_t),4);
944 u8* part_end = pc->part_bin + sizeof(pc->part_bin);
945
946 // setup tmd
947
948 u8* ptr = pc->tmd
949 ? (u8*) pc->tmd
950 : pc->part_bin + ALIGN32(sizeof(wd_part_header_t),32);
951 pc->tmd = (wd_tmd_t*)ptr;
952 head->tmd_size = htonl(pc->tmd_size);
953 head->tmd_off4 = htonl(ptr-pc->part_bin>>2);
954 TRACE(" - TMD: %p : %8x + %9x\n", pc->tmd, ntohl(head->tmd_off4)<<2, pc->tmd_size );
955
956 // setup tmd_content
957
958 const u32 n_content = pc->tmd_size >= sizeof(wd_tmd_t)
959 ? ( pc->tmd_size - sizeof(wd_tmd_t) ) / sizeof(wd_tmd_content_t)
960 : 0;
961 if ( !pc->tmd->n_content || pc->tmd->n_content > n_content )
962 pc->tmd->n_content = n_content;
963
964 pc->tmd_content = n_content ? pc->tmd->content : 0;
965
966 TRACE(" - CON0: %p : %8zx + %9zx [N=%u/%u]\n",
967 pc->tmd_content,
968 (ntohl(head->tmd_off4)<<2) + sizeof(wd_tmd_t),
969 sizeof(wd_tmd_content_t), pc->tmd->n_content, n_content );
970
971 // setup cert
972
973 ptr = pc->cert
974 ? (u8*) pc->cert
975 : (u8*) pc->tmd + ALIGN32(pc->tmd_size,32);
976 pc->cert = ptr;
977 head->cert_size = htonl(pc->cert_size);
978 head->cert_off4 = htonl(ptr-pc->part_bin>>2);
979 TRACE(" - CERT: %p : %8x + %9x\n", pc->cert, ntohl(head->cert_off4)<<2, pc->cert_size );
980
981 // setup h3
982
983 if (!pc->h3)
984 pc->h3 = pc->part_bin + sizeof(pc->part_bin) - WII_H3_SIZE;
985 pc->h3_size = WII_H3_SIZE;
986 head->h3_off4 = htonl(pc->h3-pc->part_bin>>2);
987 TRACE(" - H3: %p : %8x + %9x\n", pc->h3, ntohl(head->h3_off4)<<2, WII_H3_SIZE );
988
989 // setup data
990
991 if (!pc->data_off)
992 pc->data_off = sizeof(pc->part_bin);
993 head->data_off4 = htonl(pc->data_off>>2);
994 head->data_size4 = htonl(pc->data_size>>2);
995 TRACE(" - DATA: %p : %8x + %9llx\n",
996 pc->part_bin + sizeof(pc->part_bin),
997 ntohl(head->data_off4)<<2, pc->data_size );
998
999 // validation check
1000
1001 pc->is_valid = 0;
1002
1003 if ( pc->tmd_size < WII_TMD_GOOD_SIZE
1004 || pc->cert_size < 0x10
1005 || pc->data_off < sizeof(pc->part_bin)
1006 || pc->data_size > WII_MAX_PART_SECTORS * (u64)WII_SECTOR_SIZE
1007 )
1008 return 1;
1009
1010 // check that all data is within part_bin
1011
1012 u8 * tmd_beg = (u8*)pc->tmd;
1013 u8 * tmd_end = tmd_beg + pc->tmd_size;
1014 u8 * cert_beg = (u8*)pc->cert;
1015 u8 * cert_end = cert_beg + pc->cert_size;
1016 u8 * h3_beg = (u8*)pc->h3;
1017 u8 * h3_end = h3_beg + pc->h3_size;
1018
1019 if ( tmd_beg < head_end || tmd_end > part_end
1020 || cert_beg < head_end || cert_end > part_end
1021 || h3_beg < head_end || h3_end > part_end
1022 )
1023 return 1;
1024
1025 // check overlays
1026
1027 if ( tmd_end >= cert_beg && tmd_beg < cert_end // overlay tmd <-> cert
1028 || tmd_end >= h3_beg && tmd_beg < h3_end // overlay tmd <-> h3
1029 || cert_end >= h3_beg && cert_beg < h3_end // overlay cert <-> h3
1030 )
1031 return 1;
1032
1033 // all seems ok
1034
1035 pc->is_valid = 1;
1036 return 0;
1037 }
1038
1039 ///////////////////////////////////////////////////////////////////////////////
1040
clear_part_control(wd_part_control_t * pc,u32 tmd_size,u32 cert_size,u64 data_size)1041 int clear_part_control
1042 ( wd_part_control_t * pc, u32 tmd_size, u32 cert_size, u64 data_size )
1043 {
1044 ASSERT(pc);
1045 TRACE("clear_part_control(%p,%x,%x,%llx)\n",pc,tmd_size,cert_size,data_size);
1046
1047 memset(pc,0,sizeof(*pc));
1048
1049 pc->tmd_size = tmd_size;
1050 pc->cert_size = cert_size;
1051 pc->data_size = ALIGN64(data_size,WII_SECTOR_SIZE);
1052
1053 return setup_part_control_helper(pc);
1054 }
1055
1056 ///////////////////////////////////////////////////////////////////////////////
1057
setup_part_control(wd_part_control_t * pc)1058 int setup_part_control ( wd_part_control_t * pc )
1059 {
1060 ASSERT(pc);
1061 TRACE("setup_part_control(%p)\n",pc);
1062
1063 // clear controling data
1064 memset( pc->part_bin+sizeof(pc->part_bin), 0, +sizeof(*pc)-sizeof(pc->part_bin) );
1065
1066 wd_part_header_t * head = (wd_part_header_t*)pc->part_bin;
1067
1068 pc->tmd = (wd_tmd_t*)
1069 ( pc->part_bin + ( ntohl(head->tmd_off4) << 2 ));
1070 pc->tmd_size = ntohl(head->tmd_size);
1071 pc->cert = pc->part_bin + ( ntohl(head->cert_off4) << 2 );
1072 pc->cert_size = ntohl(head->cert_size);
1073 pc->h3 = pc->part_bin + ( ntohl(head->h3_off4) << 2 );
1074 pc->data_off = (u64)ntohl(head->data_off4) << 2;
1075 pc->data_size = (u64)ntohl(head->data_size4) << 2;
1076
1077 return setup_part_control_helper(pc);
1078 }
1079
1080 ///////////////////////////////////////////////////////////////////////////////
1081
part_control_fake_sign(wd_part_control_t * pc,int calc_h4)1082 u32 part_control_fake_sign ( wd_part_control_t * pc, int calc_h4 )
1083 {
1084 ASSERT(pc);
1085
1086 u32 stat = 0;
1087 if (pc->is_valid)
1088 {
1089 // caluclate SHA1 hash 'h4'
1090 if ( calc_h4 && pc->tmd_content )
1091 SHA1( pc->h3, pc->h3_size, pc->tmd_content->hash );
1092
1093 // fake signing
1094 const u32 stat1 = tmd_fake_sign(pc->tmd,pc->tmd_size);
1095 const u32 stat2 = ticket_fake_sign(&pc->head->ticket,0);
1096 stat = stat1 && stat2 ? stat + stat2 : 0;
1097 }
1098
1099 return stat;
1100 }
1101
1102 ///////////////////////////////////////////////////////////////////////////////
1103
part_control_is_fake_signed(const wd_part_control_t * pc)1104 int part_control_is_fake_signed ( const wd_part_control_t * pc )
1105 {
1106 ASSERT(pc);
1107 return pc->is_valid && pc->tmd && tmd_is_fake_signed(pc->tmd,pc->tmd_size);
1108 }
1109
1110 //
1111 ///////////////////////////////////////////////////////////////////////////////
1112 /////////////// wit patch files ///////////////
1113 ///////////////////////////////////////////////////////////////////////////////
1114
1115 const char wpat_magic[12] = WIT_PATCH_MAGIC;
1116
1117 ///////////////////////////////////////////////////////////////////////////////
1118
wpat_get_type_name(wpat_type_t type,ccp return_if_invalid)1119 ccp wpat_get_type_name ( wpat_type_t type, ccp return_if_invalid )
1120 {
1121 static ccp tab[] =
1122 {
1123 0,
1124 "TOC:HEADER", // WPAT_HEADER
1125 "TOC:COMMENT", // WPAT_COMMENT
1126 "TOC:DATA", // WPAT_DATA
1127 "TOC:DELETE-FILE", // WPAT_DELETE_FILE
1128 "TOC:CREATE-FILE", // WPAT_CREATE_FILE
1129 "TOC:MOVE-FILE", // WPAT_MOVE_FILE
1130 "TOC:COPY-FILE", // WPAT_COPY_FILE
1131 "TOC:LINK-FILE", // WPAT_LINK_FILE
1132 "TOC:PATCH-FILE", // WPAT_PATCH_FILE
1133 };
1134
1135 const unsigned id = type &= WPAT_M_ID;
1136 ccp res = id < sizeof(tab)/sizeof(*tab) ? tab[id] : 0;
1137
1138 return !res
1139 ? return_if_invalid
1140 : type & WPAT_F_TOC
1141 ? res
1142 : res + 4;
1143 }
1144
1145 ///////////////////////////////////////////////////////////////////////////////
1146
wpat_get_size(wpat_size_t type_size)1147 u32 wpat_get_size ( wpat_size_t type_size )
1148 {
1149 return ( ntohl(type_size.size4) & 0xffffff ) << 2;
1150 }
1151
1152 ///////////////////////////////////////////////////////////////////////////////
1153
wpat_calc_size(wpat_type_t type,u32 size)1154 wpat_size_t wpat_calc_size ( wpat_type_t type, u32 size )
1155 {
1156 wpat_size_t type_size;
1157 type_size.size4 = htonl( size + 3 >> 2 );
1158 type_size.type = type;
1159 return type_size;
1160 }
1161
1162 //
1163 ///////////////////////////////////////////////////////////////////////////////
1164 /////////////// consts & vars ///////////////
1165 ///////////////////////////////////////////////////////////////////////////////
1166
1167 const char skeleton_marker[10] = "[SKELETON]";
1168
1169 //
1170 ///////////////////////////////////////////////////////////////////////////////
1171 /////////////// E N D ///////////////
1172 ///////////////////////////////////////////////////////////////////////////////
1173
1174