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 "wiidisc.h"
38 #include <ctype.h>
39
40 //-----------------------------------------------------------------------------
41
42 #ifdef __CYGWIN__
43 #define ISALNUM(a) isalnum((int)(a))
44 #else
45 #define ISALNUM isalnum
46 #endif
47
48 //-----------------------------------------------------------------------------
49
50 #define WDPRINT noPRINT
51
52 //
53 ///////////////////////////////////////////////////////////////////////////////
54 /////////////// common key & encryption ///////////////
55 ///////////////////////////////////////////////////////////////////////////////
56
57 // The real common key must be set by the application.
58 // Only the first WII_KEY_SIZE bytes of 'common_key' are used.
59
60 static u8 common_key_templ[WII_KEY_SIZE] = "common key unset";
61 static u8 common_key[WD_CKEY__N][WII_KEY_SIZE] = {"",""};
62
63 static const u8 iv0[WII_KEY_SIZE] = {0}; // always NULL
64
65 ///////////////////////////////////////////////////////////////////////////////
66
wd_setup_common_key()67 static void wd_setup_common_key()
68 {
69 static bool done = false;
70 if (!done)
71 {
72 done = true;
73 memset(common_key,0,sizeof(common_key));
74
75 int i;
76 for ( i = 0; i < WD_CKEY__N; i++ )
77 memcpy(common_key[i],common_key_templ,sizeof(common_key[0]));
78 }
79 }
80
81 ///////////////////////////////////////////////////////////////////////////////
82
wd_get_common_key(wd_ckey_index_t ckey_index)83 const u8 * wd_get_common_key
84 (
85 wd_ckey_index_t ckey_index // index of common key
86 )
87 {
88 wd_setup_common_key();
89 return (unsigned)ckey_index < WD_CKEY__N ? common_key[ckey_index] : 0;
90 }
91
92 ///////////////////////////////////////////////////////////////////////////////
93
wd_set_common_key(wd_ckey_index_t ckey_index,const void * new_key)94 const u8 * wd_set_common_key
95 (
96 wd_ckey_index_t ckey_index, // index of common key
97 const void * new_key // the new key
98 )
99 {
100 wd_setup_common_key();
101 if ( (unsigned)ckey_index >= WD_CKEY__N )
102 return 0;
103
104 u8 * ckey = common_key[ckey_index];
105
106 if (new_key)
107 {
108 memcpy(ckey,new_key,WII_KEY_SIZE);
109 TRACE("new common key[%d]:\n",ckey_index);
110 TRACE_HEXDUMP16(8,0,ckey,WII_KEY_SIZE);
111 }
112 return ckey;
113 }
114
115 ///////////////////////////////////////////////////////////////////////////////
116
wd_decrypt_title_key(const wd_ticket_t * tik,u8 * title_key)117 void wd_decrypt_title_key
118 (
119 const wd_ticket_t * tik, // pointer to ticket
120 u8 * title_key // the result
121 )
122 {
123 u8 iv[WII_KEY_SIZE];
124 memset( iv, 0, sizeof(iv) );
125 memcpy( iv, tik->title_id, 8 );
126
127 const wd_ckey_index_t ckey_index = tik->common_key_index < WD_CKEY__N
128 ? tik->common_key_index : 0;
129 aes_key_t akey;
130 wd_aes_set_key( &akey, common_key[ckey_index] );
131 wd_aes_decrypt( &akey, iv, &tik->title_key, title_key, WII_KEY_SIZE );
132
133 TRACE("| IV: "); TRACE_HEXDUMP(0,0,1,WII_KEY_SIZE,iv,WII_KEY_SIZE);
134 TRACE("| IN: "); TRACE_HEXDUMP(0,0,1,WII_KEY_SIZE,&tik->title_key,WII_KEY_SIZE);
135 TRACE("| OUT: "); TRACE_HEXDUMP(0,0,1,WII_KEY_SIZE,title_key,WII_KEY_SIZE);
136 }
137
138 ///////////////////////////////////////////////////////////////////////////////
139
wd_encrypt_title_key(wd_ticket_t * tik,const u8 * title_key)140 void wd_encrypt_title_key
141 (
142 wd_ticket_t * tik, // pointer to ticket (modified)
143 const u8 * title_key // valid pointer to wanted title key
144 )
145 {
146 u8 iv[WII_KEY_SIZE];
147 memset( iv, 0, sizeof(iv) );
148 memcpy( iv, tik->title_id, 8 );
149
150 const wd_ckey_index_t ckey_index = tik->common_key_index < WD_CKEY__N
151 ? tik->common_key_index : 0;
152 aes_key_t akey;
153 wd_aes_set_key( &akey, common_key[ckey_index] );
154 wd_aes_encrypt( &akey, iv, title_key, &tik->title_key, WII_KEY_SIZE );
155
156 TRACE("| IV: "); TRACE_HEXDUMP(0,0,1,WII_KEY_SIZE,iv,WII_KEY_SIZE);
157 TRACE("| IN: "); TRACE_HEXDUMP(0,0,1,WII_KEY_SIZE,&tik->title_key,WII_KEY_SIZE);
158 TRACE("| OUT: "); TRACE_HEXDUMP(0,0,1,WII_KEY_SIZE,title_key,WII_KEY_SIZE);
159 }
160
161 ///////////////////////////////////////////////////////////////////////////////
162
wd_decrypt_sectors(wd_part_t * part,const aes_key_t * akey,const void * sect_src,void * sect_dest,void * hash_dest,u32 n_sectors)163 void wd_decrypt_sectors
164 (
165 wd_part_t * part, // NULL or pointer to partition
166 const aes_key_t * akey, // aes key, if NULL use 'part'
167 const void * sect_src, // pointer to first source sector
168 void * sect_dest, // pointer to destination
169 void * hash_dest, // if not NULL: store hash tables here
170 u32 n_sectors // number of wii sectors to decrypt
171 )
172 {
173 if (!akey)
174 {
175 DASSERT(part);
176 wd_disc_t * disc = part->disc;
177 DASSERT(disc);
178 if ( disc->akey_part != part )
179 {
180 disc->akey_part = part;
181 wd_aes_set_key(&disc->akey,part->key);
182 }
183 akey = &disc->akey;
184 }
185
186 const u8 * src = sect_src;
187 u8 * dest = sect_dest;
188 u8 tempbuf[WII_SECTOR_SIZE];
189
190 if (hash_dest)
191 {
192 u8 * hdest = hash_dest;
193 while ( n_sectors-- > 0 )
194 {
195 const u8 * src1 = src;
196 if ( src == dest )
197 {
198 // inplace decryption not possible => use tempbuf
199 memcpy(tempbuf,src,WII_SECTOR_SIZE);
200 src1 = tempbuf;
201 }
202
203 // decrypt header first, because of possible overlay of 'src1' & 'dest'
204 wd_aes_decrypt( akey,
205 iv0,
206 src1,
207 hdest,
208 WII_SECTOR_HASH_SIZE);
209
210 // decrypt data
211 wd_aes_decrypt( akey,
212 src1 + WII_SECTOR_IV_OFF,
213 src1 + WII_SECTOR_HASH_SIZE,
214 dest,
215 WII_SECTOR_DATA_SIZE );
216
217
218 src += WII_SECTOR_SIZE;
219 dest += WII_SECTOR_DATA_SIZE;
220 hdest += WII_SECTOR_HASH_SIZE;
221 }
222 }
223 else
224 {
225 while ( n_sectors-- > 0 )
226 {
227 const u8 * src1 = src;
228 if ( src == dest )
229 {
230 // inplace decryption not possible => use tempbuf
231 memcpy(tempbuf,src,WII_SECTOR_SIZE);
232 src1 = tempbuf;
233 }
234
235 // decrypt data
236 wd_aes_decrypt( akey,
237 src1 + WII_SECTOR_IV_OFF,
238 src1 + WII_SECTOR_HASH_SIZE,
239 dest + WII_SECTOR_HASH_SIZE,
240 WII_SECTOR_DATA_SIZE );
241
242 // decrypt header
243 wd_aes_decrypt( akey,
244 iv0,
245 src1,
246 dest,
247 WII_SECTOR_HASH_SIZE);
248
249 src += WII_SECTOR_SIZE;
250 dest += WII_SECTOR_SIZE;
251 }
252 }
253 }
254
255 ///////////////////////////////////////////////////////////////////////////////
256
wd_encrypt_sectors(wd_part_t * part,const aes_key_t * akey,const void * sect_src,const void * hash_src,void * sect_dest,u32 n_sectors)257 void wd_encrypt_sectors
258 (
259 wd_part_t * part, // NULL or pointer to partition
260 const aes_key_t * akey, // aes key, if NULL use 'part'
261 const void * sect_src, // pointer to first source sector
262 const void * hash_src, // if not NULL: source of hash tables
263 void * sect_dest, // pointer to first destination sector
264 u32 n_sectors // number of wii sectors to encrypt
265 )
266 {
267 if (!akey)
268 {
269 DASSERT(part);
270 wd_disc_t * disc = part->disc;
271 DASSERT(disc);
272 if ( disc->akey_part != part )
273 {
274 disc->akey_part = part;
275 wd_aes_set_key(&disc->akey,part->key);
276 }
277 akey = &disc->akey;
278 }
279
280 const u8 * src = sect_src;
281 u8 * dest = sect_dest;
282 u8 tempbuf[WII_SECTOR_SIZE];
283
284 if (hash_src)
285 {
286 // copy reverse to allow overlap of 'data_src' and 'sect_dest'
287 const u8 * hsrc = (const u8*)hash_src + n_sectors * WII_SECTOR_HASH_SIZE;
288 src += n_sectors * WII_SECTOR_DATA_SIZE;
289 dest += n_sectors * WII_SECTOR_SIZE;
290
291 while ( n_sectors-- > 0 )
292 {
293 src -= WII_SECTOR_DATA_SIZE;
294 hsrc -= WII_SECTOR_HASH_SIZE;
295 dest -= WII_SECTOR_SIZE;
296
297 // encrypt header
298 wd_aes_encrypt( akey,
299 iv0,
300 hsrc,
301 tempbuf,
302 WII_SECTOR_HASH_SIZE );
303
304 // encrypt data
305 wd_aes_encrypt( akey,
306 tempbuf + WII_SECTOR_IV_OFF,
307 src,
308 tempbuf + WII_SECTOR_HASH_SIZE,
309 WII_SECTOR_DATA_SIZE );
310
311 memcpy(dest,tempbuf,WII_SECTOR_SIZE);
312 }
313 }
314 else
315 {
316 while ( n_sectors-- > 0 )
317 {
318 // encrypt header
319 wd_aes_encrypt( akey,
320 iv0,
321 src,
322 dest,
323 WII_SECTOR_HASH_SIZE );
324
325 // encrypt data
326 wd_aes_encrypt( akey,
327 dest + WII_SECTOR_IV_OFF,
328 src + WII_SECTOR_HASH_SIZE,
329 dest + WII_SECTOR_HASH_SIZE,
330 WII_SECTOR_DATA_SIZE );
331
332 src += WII_SECTOR_SIZE;
333 dest += WII_SECTOR_SIZE;
334 }
335 }
336 }
337
338 ///////////////////////////////////////////////////////////////////////////////
339
wd_split_sectors(const void * sect_src,void * data_dest,void * hash_dest,u32 n_sectors)340 void wd_split_sectors
341 (
342 const void * sect_src, // pointer to first source sector
343 void * data_dest, // pointer to data destination
344 void * hash_dest, // pointer to hash destination
345 u32 n_sectors // number of wii sectors to decrypt
346 )
347 {
348 DASSERT(sect_src);
349 DASSERT(data_dest);
350 DASSERT(hash_dest);
351 DASSERT( sect_src != hash_dest );
352
353 const u8 * src = sect_src;
354 u8 * data = data_dest;
355 u8 * hash = hash_dest;
356
357 while ( n_sectors-- > 0 )
358 {
359 memcpy( hash, src, WII_SECTOR_HASH_SIZE );
360 hash += WII_SECTOR_HASH_SIZE;
361 src += WII_SECTOR_HASH_SIZE;
362
363 memmove( data, src, WII_SECTOR_DATA_SIZE );
364 data += WII_SECTOR_DATA_SIZE;
365 src += WII_SECTOR_DATA_SIZE;
366 }
367 }
368
369 ///////////////////////////////////////////////////////////////////////////////
370
wd_join_sectors(const void * data_src,const void * hash_src,void * sect_dest,u32 n_sectors)371 void wd_join_sectors
372 (
373 const void * data_src, // pointer to data source
374 const void * hash_src, // pointer to hash source
375 void * sect_dest, // pointer to first destination sector
376 u32 n_sectors // number of wii sectors to encrypt
377 )
378 {
379 DASSERT(data_src);
380 DASSERT(hash_src);
381 DASSERT(sect_dest);
382 DASSERT( hash_src != sect_dest );
383
384 // copy reverse to allow overlap of 'data_src' and 'sect_dest'
385 const u8 * data = (const u8*)data_src + n_sectors * WII_SECTOR_DATA_SIZE;
386 const u8 * hash = (const u8*)hash_src + n_sectors * WII_SECTOR_HASH_SIZE;
387 u8 * dest = (u8*) sect_dest + n_sectors * WII_SECTOR_SIZE;
388
389 while ( n_sectors-- > 0 )
390 {
391 dest -= WII_SECTOR_DATA_SIZE;
392 data -= WII_SECTOR_DATA_SIZE;
393 memmove( dest, data, WII_SECTOR_DATA_SIZE );
394
395 dest -= WII_SECTOR_HASH_SIZE;
396 hash -= WII_SECTOR_HASH_SIZE;
397 memcpy( dest, hash, WII_SECTOR_HASH_SIZE );
398 }
399 }
400
401 //
402 ///////////////////////////////////////////////////////////////////////////////
403 /////////////// names, ids, titles, ... ///////////////
404 ///////////////////////////////////////////////////////////////////////////////
405
406 const char * wd_disc_type_name[]
407 = { "unknown", "GameCube", "Wii", 0 };
408
409 //-----------------------------------------------------------------------------
410
wd_get_disc_type_name(wd_disc_type_t disc_type,ccp not_found)411 const char * wd_get_disc_type_name
412 (
413 wd_disc_type_t disc_type, // disc type
414 ccp not_found // result if no name found
415 )
416 {
417 return (unsigned)disc_type < WD_DT__N
418 ? wd_disc_type_name[disc_type]
419 : not_found;
420 }
421
422 ///////////////////////////////////////////////////////////////////////////////
423
424 const char * wd_part_name[]
425 = { "DATA", "UPDATE", "CHANNEL", 0 };
426
427 //-----------------------------------------------------------------------------
428
wd_get_part_name(u32 ptype,ccp not_found)429 const char * wd_get_part_name
430 (
431 u32 ptype, // partition type
432 ccp not_found // result if no name found
433 )
434 {
435 return ptype < sizeof(wd_part_name)/sizeof(*wd_part_name)-1
436 ? wd_part_name[ptype]
437 : not_found;
438 }
439
440 ///////////////////////////////////////////////////////////////////////////////
441
wd_print_part_name(char * buf,size_t buf_size,u32 ptype,wd_pname_mode_t mode)442 char * wd_print_part_name
443 (
444 char * buf, // result buffer
445 // If NULL, a local circulary static buffer is used
446 size_t buf_size, // size of 'buf', ignored if buf==NULL
447 u32 ptype, // partition type
448 wd_pname_mode_t mode // print mode
449 )
450 {
451 if (!buf)
452 buf = GetCircBuf( buf_size = 20 );
453
454 ccp id4 = (ccp)&ptype;
455 const bool is_id = ISALNUM(id4[0]) && ISALNUM(id4[1])
456 && ISALNUM(id4[2]) && ISALNUM(id4[3]);
457 const ccp name = ptype < sizeof(wd_part_name)/sizeof(*wd_part_name)-1
458 ? wd_part_name[ptype] : 0;
459
460 switch (mode)
461 {
462 case WD_PNAME_NAME_NUM_9:
463 if (name)
464 snprintf(buf,buf_size,"%7s=%x",name,ptype);
465 else if (is_id)
466 {
467 ptype = htonl(ptype); // we need big endian here
468 snprintf(buf,buf_size," \"%.4s\"",id4);
469 }
470 else
471 snprintf(buf,buf_size,"%9x",ptype);
472 break;
473
474 case WD_PNAME_COLUMN_9:
475 if (name)
476 snprintf(buf,buf_size,"%7s %x",name,ptype);
477 else if (is_id)
478 {
479 ptype = htonl(ptype); // we need big endian here
480 snprintf(buf,buf_size," \"%.4s\"",id4);
481 }
482 else
483 snprintf(buf,buf_size,"%9x",ptype);
484 break;
485
486 case WD_PNAME_NAME:
487 if (name)
488 {
489 snprintf(buf,buf_size,"%s",name);
490 break;
491 }
492 // fall through
493
494 case WD_PNAME_NUM:
495 if (is_id)
496 {
497 ptype = htonl(ptype); // we need big endian here
498 snprintf(buf,buf_size,"%.4s",id4);
499 }
500 else
501 snprintf(buf,buf_size,"%x",ptype);
502 break;
503
504 case WD_PNAME_P_NAME:
505 if (name)
506 snprintf(buf,buf_size,"%s",name);
507 else if (is_id)
508 {
509 ptype = htonl(ptype); // we need big endian here
510 snprintf(buf,buf_size,"P-%.4s",id4);
511 }
512 else
513 snprintf(buf,buf_size,"P%x",ptype);
514 break;
515
516 //case WD_PNAME_NUM_INFO:
517 default:
518 if (name)
519 snprintf(buf,buf_size,"%x [%s]",ptype,name);
520 else if (is_id)
521 {
522 ptype = htonl(ptype); // we need big endian here
523 snprintf(buf,buf_size,"0x%x [\"%.4s\"]",ptype,id4);
524 }
525 else
526 snprintf(buf,buf_size,"0x%x [%u]",ptype,ptype);
527 break;
528 }
529
530 return buf;
531 }
532
533 ///////////////////////////////////////////////////////////////////////////////
534 // returns a pointer to a printable ID, teminated with 0
535
wd_print_id(const void * id,size_t id_len,void * buf)536 char * wd_print_id
537 (
538 const void * id, // ID to convert in printable format
539 size_t id_len, // len of 'id'
540 void * buf // Pointer to buffer, size >= id_len + 1
541 // If NULL, a local circulary static buffer is used
542 )
543 {
544 if (!buf)
545 buf = GetCircBuf( id_len + 1);
546
547 ccp src = id;
548 char * dest = buf;
549 while ( id_len-- > 0 )
550 {
551 const char ch = *src++;
552 *dest++ = ch >= ' ' && ch < 0x7f ? ch : '.';
553 }
554 *dest = 0;
555
556 return buf;
557 }
558
559 ///////////////////////////////////////////////////////////////////////////////
560 // rename ID and title of a ISO header
561
wd_rename(void * p_data,ccp new_id,ccp new_title)562 int wd_rename
563 (
564 void * p_data, // pointer to ISO data
565 ccp new_id, // if !NULL: take the first 6 chars as ID
566 ccp new_title // if !NULL: take the first 0x39 chars as title
567 )
568 {
569 DASSERT(p_data);
570
571 int done = 0;
572
573 if ( new_id )
574 {
575 ccp end_id;
576 char * dest = p_data;
577 for ( end_id = new_id + 6; new_id < end_id && *new_id; new_id++, dest++ )
578 if ( *new_id != '.' && *new_id != *dest )
579 {
580 *dest = *new_id;
581 done |= 1;
582 }
583 }
584
585 if ( new_title && *new_title )
586 {
587 char buf[WII_TITLE_SIZE];
588 memset(buf,0,WII_TITLE_SIZE);
589 const size_t slen = strlen(new_title);
590 memcpy(buf, new_title, slen < WII_TITLE_SIZE ? slen : WII_TITLE_SIZE-1 );
591
592 u8 *dest = p_data;
593 dest += WII_TITLE_OFF;
594 if ( memcmp(dest,buf,WII_TITLE_SIZE) )
595 {
596 memcpy(dest,buf,WII_TITLE_SIZE);
597 done |= 2;
598 }
599 }
600
601 return done;
602 }
603
604 //
605 ///////////////////////////////////////////////////////////////////////////////
606 /////////////// read functions ///////////////
607 ///////////////////////////////////////////////////////////////////////////////
608
wd_read_raw(wd_disc_t * disc,u32 disc_offset4,void * dest_buf,u32 read_size,wd_usage_t usage_id)609 enumError wd_read_raw
610 (
611 wd_disc_t * disc, // valid disc pointer
612 u32 disc_offset4, // disc offset/4
613 void * dest_buf, // destination buffer
614 u32 read_size, // number of bytes to read
615 wd_usage_t usage_id // not 0: mark usage usage_tab with this value
616 )
617 {
618 DASSERT(disc);
619 DASSERT(disc->read_func);
620 TRACE("#WD# wd_read_raw() %9llx %8x dest=%p\n",
621 (u64)disc_offset4<<2, read_size, dest_buf );
622
623 if (!read_size)
624 return ERR_OK;
625
626 if (dest_buf)
627 {
628 noTRACE("WD-READ: ++ %9llx %5x ++\n",(u64)disc_offset4<<2, read_size );
629
630 u8 *dest = dest_buf;
631 u32 len = read_size;
632 u32 offset4 = disc_offset4;
633
634 // force reading only whole HD SECTORS
635 char hd_buf[HD_SECTOR_SIZE];
636
637 const int HD_SECTOR_SIZE4 = HD_SECTOR_SIZE >> 2;
638 u32 skip4 = offset4 - offset4/HD_SECTOR_SIZE4 * HD_SECTOR_SIZE4;
639
640 if (skip4)
641 {
642 const u32 off1 = offset4 - skip4;
643 const int ret = disc->read_func(disc->read_data,off1,HD_SECTOR_SIZE,hd_buf);
644 if (ret)
645 {
646 WD_ERROR(ERR_READ_FAILED,"Error while reading disc%s",disc->error_term);
647 return ERR_READ_FAILED;
648 }
649
650 const u32 skip = skip4 << 2;
651 DASSERT( skip < HD_SECTOR_SIZE );
652 u32 copy_len = HD_SECTOR_SIZE - skip;
653 if ( copy_len > len )
654 copy_len = len;
655
656 noTRACE("WD-READ/PRE: %9llx %5x -> %5zx %5x, skip=%x,%x\n",
657 (u64)off1<<2, HD_SECTOR_SIZE, dest-(u8*)dest_buf, copy_len, skip,skip4 );
658
659 memcpy(dest,hd_buf+skip,copy_len);
660 dest += copy_len;
661 len -= copy_len;
662 offset4 += copy_len >> 2;
663 }
664
665 const u32 copy_len = len / HD_SECTOR_SIZE * HD_SECTOR_SIZE;
666 if ( copy_len > 0 )
667 {
668 noTRACE("WD-READ/MAIN: %9llx %5x -> %5zx %5x\n",
669 (u64)offset4<<2, copy_len, dest-(u8*)dest_buf, copy_len );
670 const int ret = disc->read_func(disc->read_data,offset4,copy_len,dest);
671 if (ret)
672 {
673 WD_ERROR(ERR_READ_FAILED,"Error while reading disc%s",disc->error_term);
674 return ERR_READ_FAILED;
675 }
676
677 dest += copy_len;
678 len -= copy_len;
679 offset4 += copy_len >> 2;
680 }
681
682 if ( len > 0 )
683 {
684 ASSERT( len < HD_SECTOR_SIZE );
685
686 noTRACE("WD-READ/POST: %9llx %5x -> %5zx %5x\n",
687 (u64)offset4<<2, HD_SECTOR_SIZE, dest-(u8*)dest_buf, len );
688 const int ret = disc->read_func(disc->read_data,offset4,HD_SECTOR_SIZE,hd_buf);
689 if (ret)
690 {
691 WD_ERROR(ERR_READ_FAILED,"Error while reading disc%s",disc->error_term);
692 return ERR_READ_FAILED;
693 }
694
695 memcpy(dest,hd_buf,len);
696 }
697 }
698
699 if ( usage_id )
700 {
701 u32 first_block = disc_offset4 >> WII_SECTOR_SIZE_SHIFT-2;
702 u32 end_block = disc_offset4 + ( read_size + WII_SECTOR_SIZE - 1 >> 2 )
703 >> WII_SECTOR_SIZE_SHIFT-2;
704 if ( end_block > WII_MAX_SECTORS )
705 end_block = WII_MAX_SECTORS;
706 noTRACE("mark %x+%x => %x..%x [%x]\n",
707 disc_offset4, read_size, first_block, end_block, end_block-first_block );
708
709 if ( first_block < end_block )
710 {
711 memset( disc->usage_table + first_block,
712 usage_id, end_block - first_block );
713 if ( disc->usage_max < end_block )
714 disc->usage_max = end_block;
715 }
716 }
717
718 return ERR_OK;
719 }
720
721 ///////////////////////////////////////////////////////////////////////////////
722
wd_read_part_raw(wd_part_t * part,u32 offset4,void * dest_buf,u32 read_size,bool mark_block)723 enumError wd_read_part_raw
724 (
725 wd_part_t * part, // valid pointer to a disc partition
726 u32 offset4, // offset/4 to partition start
727 void * dest_buf, // destination buffer
728 u32 read_size, // number of bytes to read
729 bool mark_block // true: mark block in 'usage_table'
730 )
731 {
732 DASSERT(part);
733 return wd_read_raw( part->disc,
734 part->part_off4 + offset4,
735 dest_buf,
736 read_size,
737 mark_block ? part->usage_id : 0 );
738 }
739
740 ///////////////////////////////////////////////////////////////////////////////
741
wd_read_part_block(wd_part_t * part,u32 block_num,u8 * block,bool mark_block)742 enumError wd_read_part_block
743 (
744 wd_part_t * part, // valid pointer to a disc partition
745 u32 block_num, // block number of partition
746 u8 * block, // destination buf
747 bool mark_block // true: mark block in 'usage_table'
748 )
749 {
750 TRACE("#WD# #%08x wd_read_part_block()\n",block_num);
751 DASSERT(part);
752 DASSERT(part->disc);
753
754 wd_disc_t * disc = part->disc;
755
756 enumError err = ERR_OK;
757 if ( disc->block_part != part || disc->block_num != block_num )
758 {
759
760 err = wd_read_raw( part->disc,
761 part->data_off4 + block_num * WII_SECTOR_SIZE4,
762 disc->temp_buf,
763 WII_SECTOR_SIZE,
764 mark_block ? part->usage_id | WD_USAGE_F_CRYPT : 0 );
765
766 if (err)
767 {
768 memset(disc->block_buf,0,sizeof(disc->block_buf));
769 disc->block_part = 0;
770 }
771 else
772 {
773 disc->block_part = part;
774 disc->block_num = block_num;
775
776 if (part->is_encrypted)
777 {
778 if ( disc->akey_part != part )
779 {
780 disc->akey_part = part;
781 wd_aes_set_key(&disc->akey,part->key);
782 }
783
784 // decrypt data
785 wd_aes_decrypt( &disc->akey,
786 disc->temp_buf + WII_SECTOR_IV_OFF,
787 disc->temp_buf + WII_SECTOR_HASH_SIZE,
788 disc->block_buf,
789 WII_SECTOR_DATA_SIZE );
790 }
791 else
792 memcpy( disc->block_buf,
793 disc->temp_buf + WII_SECTOR_HASH_SIZE,
794 WII_SECTOR_DATA_SIZE );
795 }
796 }
797
798 memcpy( block, disc->block_buf, WII_SECTOR_DATA_SIZE );
799 return err;
800 }
801
802 ///////////////////////////////////////////////////////////////////////////////
803
wd_read_part(wd_part_t * part,u32 data_offset4,void * dest_buf,u32 read_size,bool mark_block)804 enumError wd_read_part
805 (
806 wd_part_t * part, // valid pointer to a disc partition
807 u32 data_offset4, // partition data offset/4
808 void * dest_buf, // estination buffer
809 u32 read_size, // number of bytes to read
810 bool mark_block // true: mark block in 'usage_table'
811 )
812 {
813 TRACE("#WD# %8x %8x wd_read_part()\n",data_offset4,read_size);
814 DASSERT(part);
815 DASSERT(part->disc);
816 DASSERT(dest_buf);
817
818 wd_disc_t * disc = part->disc;
819
820 if (part->is_gc)
821 {
822 noPRINT("%llx+%x + %8x\n", (u64)part->part_off4<<2, data_offset4, read_size );
823 ASSERT(!(data_offset4&3));
824
825 const u64 off = part->part_off4 + ( data_offset4 >> 2 );
826 const u64 mark_end = ( off << 2 ) + read_size;
827 if ( part->max_marked < mark_end )
828 part->max_marked = mark_end;
829
830 return wd_read_raw ( disc, off, dest_buf, read_size,
831 mark_block ? part->usage_id : 0 );
832 }
833
834 const u64 mark_end = ( (u64)( part->data_off4 + data_offset4 ) << 2 ) + read_size;
835 if ( part->max_marked < mark_end )
836 part->max_marked = mark_end;
837
838 u8 * temp = disc->temp_buf;
839 u8 * dest = dest_buf;
840
841 enumError err = ERR_OK;
842 while ( read_size && err == ERR_OK )
843 {
844 u32 offset4_in_block = data_offset4 % WII_SECTOR_DATA_SIZE4;
845 u32 len_in_block = WII_SECTOR_DATA_SIZE - ( offset4_in_block << 2 );
846 if ( len_in_block > read_size )
847 len_in_block = read_size;
848
849 err = wd_read_part_block( part,
850 data_offset4 / WII_SECTOR_DATA_SIZE4,
851 temp,
852 mark_block );
853 memcpy( dest, temp + (offset4_in_block<<2), len_in_block );
854
855 dest += len_in_block;
856 data_offset4 += len_in_block >> 2;
857 read_size -= len_in_block;
858 }
859 return err;
860 }
861
862 ///////////////////////////////////////////////////////////////////////////////
863
wd_mark_part(wd_part_t * part,u32 data_offset4,u32 data_size)864 void wd_mark_part
865 (
866 wd_part_t * part, // valid pointer to a disc partition
867 u32 data_offset4, // partition data offset/4
868 u32 data_size // number of bytes to mark
869 )
870 {
871 DASSERT(part);
872 DASSERT(part->disc);
873
874 u32 first_block, end_block;
875 wd_usage_t marker;
876
877 if (part->is_gc)
878 {
879 const u32 block_delta = part->data_off4 / WII_SECTOR_SIZE4;
880 first_block = block_delta + data_offset4 / WII_SECTOR_SIZE;
881 end_block = ( data_offset4 + data_size + WII_SECTOR_SIZE - 1 )
882 / WII_SECTOR_SIZE + block_delta;
883 marker = part->usage_id;
884
885 const u64 mark_end = data_offset4 + data_size;
886 if ( part->max_marked < mark_end )
887 part->max_marked = mark_end;
888 }
889 else
890 {
891 const u32 block_delta = part->data_off4 / WII_SECTOR_SIZE4;
892 first_block = data_offset4 / WII_SECTOR_DATA_SIZE4 + block_delta;
893 end_block = (( data_size + WII_SECTOR_DATA_SIZE - 1 >> 2 )
894 + data_offset4 ) / WII_SECTOR_DATA_SIZE4
895 + block_delta;
896 marker = part->usage_id | WD_USAGE_F_CRYPT;
897
898 const u64 mark_end = ( (u64)data_offset4 << 2 ) + data_size;
899 if ( part->max_marked < mark_end )
900 part->max_marked = mark_end;
901 }
902
903 if ( end_block > WII_MAX_SECTORS )
904 end_block = WII_MAX_SECTORS;
905
906 if ( first_block < end_block )
907 {
908 wd_disc_t * disc = part->disc;
909 memset( disc->usage_table + first_block, marker, end_block - first_block );
910 if ( part->end_sector < end_block )
911 part->end_sector = end_block;
912 if ( disc->usage_max < end_block )
913 disc->usage_max = end_block;
914 }
915 }
916
917 ///////////////////////////////////////////////////////////////////////////////
918
wd_calc_disc_offset(wd_part_t * part,u64 data_offset4)919 u64 wd_calc_disc_offset
920 (
921 wd_part_t * part, // valid pointer to a disc partition
922 u64 data_offset4 // partition offset
923 )
924 {
925 DASSERT(part);
926
927 if (part->is_gc) // GC: non shifted offsets
928 return (u64)part->part_off4 + data_offset4;
929 //return (u64)( part->part_off4 + data_offset4 ) << 2;
930
931 const u32 offset_in_block = data_offset4 % WII_SECTOR_DATA_SIZE4;
932 const u32 block = data_offset4 / WII_SECTOR_DATA_SIZE4;
933 return (u64)( part->data_sector + block ) * WII_SECTOR_SIZE
934 + ( offset_in_block << 2 )
935 + WII_SECTOR_HASH_SIZE;
936 }
937
938 //
939 ///////////////////////////////////////////////////////////////////////////////
940 /////////////// get sector status ///////////////
941 ///////////////////////////////////////////////////////////////////////////////
942
wd_get_part_sector_status(wd_part_t * part,u32 block_num,bool silent)943 wd_sector_status_t wd_get_part_sector_status
944 (
945 wd_part_t * part, // valid pointer to a disc partition
946 u32 block_num, // block number of partition
947 bool silent // true: don't print error messages
948 )
949 {
950 DASSERT(part);
951 DASSERT(part->disc);
952 TRACE("wd_get_part_sector_status(%p,%d,%d)\n",part,block_num,silent);
953
954
955 //----- setup
956
957 // extra test '!part->is_loaded' to avoid circulary calling
958 if ( !part->is_loaded && wd_load_part(part,false,false,silent) )
959 return WD_SS_READ_ERROR;
960
961 if ( block_num >= part->end_sector - part->data_sector )
962 return WD_SS_INVALID_SECTOR;
963
964
965 //----- load block
966
967 wd_disc_t * disc = part->disc;
968 u8 * rdbuf = part->disc->temp_buf;
969 u8 * data = rdbuf + WII_SECTOR_HASH_SIZE;
970 const u32 abs_block_num = block_num + part->data_sector;
971 enumError err = wd_read_raw(disc,
972 abs_block_num * WII_SECTOR_SIZE4,
973 rdbuf,
974 WII_SECTOR_SIZE,
975 0 );
976 if (err)
977 return WD_SS_READ_ERROR|WD_SS_F_PART_DATA;
978
979
980 //----- check for 'SKELETON'
981
982 wd_sector_status_t stat = WD_SS_F_PART_DATA;
983
984 if ( !*rdbuf
985 && !memcmp(data-sizeof(skeleton_marker),skeleton_marker,sizeof(skeleton_marker))
986 && !memcmp(rdbuf,rdbuf+1,WII_SECTOR_HASH_SIZE-sizeof(skeleton_marker)-1) )
987 {
988 stat |= WD_SS_F_SKELETON;
989 }
990
991
992 //----- check cleared HASH
993
994 if ( !memcmp(rdbuf,rdbuf+1,WII_SECTOR_HASH_SIZE-1) )
995 {
996 stat |= WD_SS_HASH_CLEARED;
997 if (!*rdbuf)
998 stat |= WD_SS_HASH_ZEROED;
999 }
1000
1001
1002 //----- check cleared DATA
1003
1004 if ( !memcmp(data,data+1,WII_SECTOR_DATA_SIZE-1))
1005 {
1006 stat |= WD_SS_DATA_CLEARED;
1007 if (!*data)
1008 stat |= WD_SS_DATA_ZEROED;
1009 if ( stat & WD_SS_HASH_CLEARED && *rdbuf == *data )
1010 {
1011 stat |= WD_SS_SECTOR_CLEARED;
1012 if (!disc->usage_table[abs_block_num])
1013 stat |= WD_SS_F_SCRUBBED;
1014 }
1015 }
1016
1017
1018 //----- check cleared encryption
1019
1020 if ( !(stat & (WD_SS_HASH_CLEARED|WD_SS_F_SKELETON)) )
1021 {
1022 u8 hash[WII_HASH_SIZE];
1023 u8 dcbuf[WII_SECTOR_SIZE];
1024
1025 int h0idx;
1026 for ( h0idx = 0; h0idx < WII_N_ELEMENTS_H0; h0idx++ )
1027 {
1028 //--- check for decryption
1029
1030 SHA1( data + h0idx * WII_H0_DATA_SIZE, WII_H0_DATA_SIZE, hash );
1031 if (!memcmp(rdbuf + h0idx * WII_HASH_SIZE , hash, WII_HASH_SIZE))
1032 {
1033 noPRINT("- DEC #%02u!\n",h0idx);
1034 stat |= WD_SS_DECRYPTED;
1035 break;
1036 }
1037
1038 //--- decrypt
1039
1040 if (!h0idx) // decrypt only once
1041 wd_decrypt_sectors(part,0,rdbuf,dcbuf,0,1);
1042
1043
1044 //--- check for encryption
1045
1046 SHA1( dcbuf + WII_SECTOR_HASH_SIZE + h0idx * WII_H0_DATA_SIZE,
1047 WII_H0_DATA_SIZE, hash );
1048 if (!memcmp(dcbuf + h0idx * WII_HASH_SIZE , hash, WII_HASH_SIZE))
1049 {
1050 noPRINT("- ENC #%02u!\n",h0idx);
1051 stat |= WD_SS_ENCRYPTED;
1052 break;
1053 }
1054 }
1055 }
1056
1057 TRACELINE;
1058 return stat;
1059 }
1060
1061 ///////////////////////////////////////////////////////////////////////////////
1062
wd_get_disc_sector_status(wd_disc_t * disc,u32 block_num,bool silent)1063 wd_sector_status_t wd_get_disc_sector_status
1064 (
1065 wd_disc_t * disc, // valid disc pointer
1066 u32 block_num, // block number of disc
1067 bool silent // true: don't print error messages
1068 )
1069 {
1070 DASSERT(disc);
1071 PRINT("wd_get_disc_sector_status(%p,%d,%d)\n",disc,block_num,silent);
1072
1073 if ( block_num >= WII_MAX_SECTORS )
1074 return WD_SS_INVALID_SECTOR;
1075
1076 //----- block in usage table?
1077
1078 wd_usage_t usage = disc->usage_table[block_num];
1079 wd_part_t * part = 0;
1080 if (!usage)
1081 {
1082 //--- find partition
1083
1084 const u64 block_off = block_num * (u64)WII_SECTOR_SIZE;
1085
1086 int pi;
1087 for ( pi = disc->n_part-1; pi >= 0; pi-- )
1088 {
1089 wd_part_t * test_part = disc->part + pi;
1090 u64 off = (u64)test_part->part_off4 << 2;
1091 noPRINT(" %llx: %llx .. %llx\n", block_off, off, off+test_part->part_size );
1092 if ( block_off >= off
1093 && wd_load_part(test_part,false,false,silent)
1094 && block_off < off + test_part->part_size )
1095 {
1096 part = test_part;
1097 break;
1098 }
1099 }
1100 }
1101 else if ( usage & WD_USAGE_F_CRYPT )
1102 {
1103 const uint idx = ( usage & WD_USAGE__MASK ) - WD_USAGE_PART_0;
1104 if ( idx < disc->n_part )
1105 part = disc->part + idx;
1106 }
1107
1108 //----- if partiton found -> ...
1109
1110 if (part)
1111 return wd_load_part(part,false,false,silent)
1112 ? WD_SS_READ_ERROR
1113 : wd_get_part_sector_status(part,block_num-part->data_sector,silent);
1114
1115
1116 //----- not partiton data: load block
1117
1118 u8 * rdbuf = disc->temp_buf;
1119 DASSERT(rdbuf);
1120 enumError err = wd_read_raw(disc,
1121 block_num * WII_SECTOR_SIZE4,
1122 rdbuf,
1123 WII_SECTOR_SIZE,
1124 0 );
1125 if (err)
1126 return WD_SS_READ_ERROR|WD_SS_F_PART_DATA;
1127
1128
1129 //----- check cleared DATA
1130
1131 wd_sector_status_t stat = 0;
1132
1133 if ( !memcmp(rdbuf,rdbuf+1,WII_SECTOR_SIZE-1) )
1134 {
1135 stat |= WD_SS_DATA_CLEARED | WD_SS_SECTOR_CLEARED;
1136 if (!*rdbuf)
1137 stat |= WD_SS_DATA_ZEROED;
1138 }
1139
1140 return stat;
1141 }
1142
1143 ///////////////////////////////////////////////////////////////////////////////
1144
1145 typedef struct info_t
1146 {
1147 ccp text; // info to print if 'condition'
1148 u32 condition; // condition
1149 u32 clear_flags; // clear flags if 'condition'
1150
1151 } info_t;
1152
1153 //-----------------------------------------------------------------------------
1154
proceed_info_tab(char * buf,ccp buf_end,info_t * info,u32 flags,bool sep)1155 static char * proceed_info_tab
1156 (
1157 char * buf, // destination buffer
1158 ccp buf_end, // end of destination buffer
1159 info_t * info, // info table
1160 u32 flags, // flags
1161 bool sep // use ", " as initial separator
1162 )
1163 {
1164 DASSERT(buf);
1165 DASSERT( buf < buf_end );
1166 DASSERT( info );
1167
1168 char * dest = buf;
1169 if (flags)
1170 {
1171 for ( ; info->text; info++ )
1172 {
1173 if ( flags & info->condition )
1174 {
1175 flags &= ~info->clear_flags;
1176 const int len = strlen(info->text);
1177 if ( dest + len + 2 < buf_end )
1178 {
1179 if (sep)
1180 {
1181 *dest++ = ',';
1182 *dest++ = ' ';
1183 }
1184 else
1185 sep = true;
1186 memcpy(dest,info->text,len);
1187 dest += len;
1188 DASSERT( dest <= buf_end );
1189 }
1190 }
1191 }
1192 }
1193 DASSERT( dest < buf_end );
1194 *dest = 0;
1195 return dest;
1196 }
1197
1198 ///////////////////////////////////////////////////////////////////////////////
1199
wd_log_sector_status(char * buf,size_t buf_size,wd_sector_status_t sect_stat)1200 ccp wd_log_sector_status
1201 (
1202 char * buf, // result buffer
1203 // If NULL, a local circulary static buffer is used
1204 size_t buf_size, // size of 'buf', ignored if buf==NULL
1205 wd_sector_status_t sect_stat // sector status
1206 )
1207 {
1208 static info_t info_tab[] =
1209 {
1210 { "err:sect", WD_SS_INVALID_SECTOR, WD_SS_INVALID_SECTOR },
1211 { "err:read", WD_SS_READ_ERROR, WD_SS_READ_ERROR },
1212 { "err:?", WD_SS_M_ERROR, WD_SS_M_ERROR },
1213
1214 { "Ac", WD_SS_SECTOR_CLEARED, WD_SS_HASH_CLEARED|WD_SS_DATA_CLEARED },
1215 { "Hz", WD_SS_HASH_ZEROED, WD_SS_HASH_CLEARED },
1216 { "Dz", WD_SS_DATA_ZEROED, WD_SS_DATA_CLEARED },
1217 { "Hc", WD_SS_HASH_CLEARED, 0 },
1218 { "Dc", WD_SS_DATA_CLEARED, 0 },
1219
1220 { "enc", WD_SS_ENCRYPTED, 0 },
1221 { "dec", WD_SS_DECRYPTED, 0 },
1222
1223 { "pdata", WD_SS_F_PART_DATA, 0 },
1224 { "scrub", WD_SS_F_SCRUBBED, 0 },
1225 { "skel", WD_SS_F_SKELETON, 0 },
1226
1227 {0,0,0}
1228 };
1229
1230 if (!buf)
1231 buf = GetCircBuf( buf_size = 50 );
1232 proceed_info_tab( buf, buf+buf_size, info_tab, sect_stat, false );
1233 return buf;
1234 }
1235
1236 ///////////////////////////////////////////////////////////////////////////////
1237
wd_print_sector_status(char * buf,size_t buf_size,wd_sector_status_t sect_stat,cert_stat_t sig_stat)1238 ccp wd_print_sector_status
1239 (
1240 char * buf, // result buffer
1241 // If NULL, a local circulary static buffer is used
1242 size_t buf_size, // size of 'buf', ignored if buf==NULL
1243 wd_sector_status_t sect_stat, // sector status
1244 cert_stat_t sig_stat // not NULL: add signature status
1245 )
1246 {
1247 static info_t info_tab_sect[] =
1248 {
1249 { "encrypted", WD_SS_ENCRYPTED, 0 },
1250 { "decrypted", WD_SS_DECRYPTED, 0 },
1251 { "skeleton", WD_SS_F_SKELETON, WD_SS_F_SCRUBBED },
1252 { "scrubbed", WD_SS_F_SCRUBBED, 0 },
1253
1254 {0,0,0}
1255 };
1256
1257 static info_t info_tab_sig[] =
1258 {
1259 { "not signed", CERT_F_HASH_FAILED, CERT_F_HASH_OK },
1260 { "fake signed", CERT_F_HASH_FAKED, CERT_F_HASH_OK },
1261 { "well signed", CERT_F_HASH_OK, 0 },
1262
1263 {0,0,0}
1264 };
1265
1266 if (!buf)
1267 buf = GetCircBuf( buf_size = 80 );
1268 char * dest = proceed_info_tab( buf, buf+buf_size, info_tab_sect, sect_stat, false );
1269 proceed_info_tab( dest, buf+buf_size, info_tab_sig, sig_stat, dest > buf );
1270 return buf;
1271 }
1272
1273 ///////////////////////////////////////////////////////////////////////////////
1274
wd_is_block_encrypted(wd_part_t * part,u32 block_num,int unknown_result,bool silent)1275 int wd_is_block_encrypted
1276 (
1277 // returns -1 on read error
1278
1279 wd_part_t * part, // valid pointer to a disc partition
1280 u32 block_num, // block number of partition
1281 int unknown_result, // result if status is unknown
1282 bool silent // true: don't print error messages
1283 )
1284 {
1285 DASSERT(part);
1286 DASSERT(part->disc);
1287
1288 const wd_sector_status_t ss = wd_get_part_sector_status(part,block_num,silent);
1289 if ( ss & WD_SS_F_PART_DATA )
1290 {
1291 part->sector_stat |= ss;
1292 part->disc->sector_stat |= part->sector_stat;
1293 }
1294
1295 noPRINT("wd_is_block_encrypted(%d) -> %04x '%s'\n",
1296 block_num, ss, wd_log_sector_status(0,0,ss) );
1297 return ss & WD_SS_ENCRYPTED
1298 ? 1
1299 : ss & (WD_SS_DECRYPTED|WD_SS_F_SKELETON)
1300 ? 0
1301 : unknown_result;
1302 }
1303
1304 ///////////////////////////////////////////////////////////////////////////////
1305
wd_is_part_scrubbed(wd_part_t * part,bool silent)1306 bool wd_is_part_scrubbed
1307 (
1308 wd_part_t * part, // valid pointer to a disc partition
1309 bool silent // true: don't print error messages
1310 )
1311 {
1312 DASSERT(part);
1313 DASSERT(part->disc);
1314
1315 if (!part->scrub_test_done)
1316 {
1317 part->scrub_test_done = true;
1318
1319 const enumError err = wd_load_part(part,false,false,silent);
1320 if (err)
1321 return false;
1322
1323 int try_count = 5, sector;
1324 for ( sector = part->data_sector;
1325 sector < part->end_sector
1326 && try_count
1327 && !(part->sector_stat&WD_SS_F_SCRUBBED);
1328 sector++ )
1329 {
1330 DASSERT( sector < WII_MAX_SECTORS );
1331 if (!part->disc->usage_table[sector])
1332 {
1333 try_count--;
1334 wd_sector_status_t ss = wd_get_part_sector_status(part,sector,silent);
1335 if ( ss & WD_SS_F_PART_DATA )
1336 part->sector_stat |= ss;
1337 }
1338 }
1339 part->disc->sector_stat |= part->sector_stat;
1340 }
1341
1342 return ( part->sector_stat & WD_SS_F_SCRUBBED ) != 0;
1343 }
1344
1345 ///////////////////////////////////////////////////////////////////////////////
1346
wd_is_disc_scrubbed(wd_disc_t * disc,bool silent)1347 bool wd_is_disc_scrubbed
1348 (
1349 wd_disc_t * disc, // valid disc pointer
1350 bool silent // true: don't print error messages
1351 )
1352 {
1353 DASSERT(disc);
1354
1355 if (!disc->scrub_test_done)
1356 {
1357 disc->scrub_test_done = true;
1358
1359 int pi;
1360 for ( pi = 0; pi < disc->n_part; pi++ )
1361 wd_is_part_scrubbed(disc->part+pi,silent);
1362 }
1363
1364 return ( disc->sector_stat & WD_SS_F_SCRUBBED ) != 0;
1365 }
1366
1367 //
1368 ///////////////////////////////////////////////////////////////////////////////
1369 /////////////// open and close ///////////////
1370 ///////////////////////////////////////////////////////////////////////////////
1371
wd_initialize_part(wd_disc_t * disc,u32 part_index)1372 static wd_part_t * wd_initialize_part
1373 (
1374 wd_disc_t * disc, // a valid disc pointer
1375 u32 part_index // index of partition
1376 )
1377 {
1378 DASSERT(disc);
1379 DASSERT( part_index <= disc->n_part ); // = because delayed increment
1380 wd_part_t * part = disc->part + part_index;
1381
1382 cert_initialize(&part->cert_chain);
1383
1384 part->index = part_index;
1385 part->usage_id = part_index + WD_USAGE_PART_0;
1386 part->is_enabled = true;
1387 part->disc = disc;
1388
1389 return part;
1390 }
1391
1392 ///////////////////////////////////////////////////////////////////////////////
1393
wd_open_gc_disc(wd_disc_t * disc,enumError * error_code)1394 static wd_disc_t * wd_open_gc_disc
1395 (
1396 wd_disc_t * disc, // a valid disc pointer
1397 enumError * error_code // store error code if not NULL
1398 )
1399 {
1400 DASSERT(disc);
1401 DASSERT( disc->disc_type == WD_DT_GAMECUBE );
1402 DASSERT( disc->disc_attrib & WD_DA_GAMECUBE );
1403 disc->region.region = *(u32*)(disc->temp_buf+WII_BI2_OFF+WII_BI2_REGION_OFF);
1404 memcpy(&disc->ptab,disc->temp_buf,sizeof(disc->ptab));
1405
1406 if ( disc->disc_attrib & WD_DA_GC_MULTIBOOT )
1407 {
1408 //----- setup multiboot gamecube disc
1409
1410 const int dvd9 = disc->disc_attrib & WD_DA_GC_DVD9;
1411
1412 u32 * ptab_beg = (u32*)( (char*)&disc->dhead + GC_MULTIBOOT_PTAB_OFF );
1413 u32 * ptab_end = ptab_beg + GC_MULTIBOOT_PTAB_SIZE/sizeof(u32);
1414 u32 * ptab;
1415
1416 u32 n_part = 1; // assume primary partition
1417 for ( ptab = ptab_beg; ptab < ptab_end; ptab++ )
1418 if (ntohl(*ptab))
1419 n_part++;
1420
1421 WDPRINT("MULTIBOOT '%.6s', N=%d, dvd9=%d\n",&disc->dhead.disc_id,n_part,dvd9);
1422
1423 wd_part_t * part = CALLOC(n_part,sizeof(*disc->part));
1424 disc->part = part;
1425
1426 //----- check primary partition
1427
1428 disc->n_part = 1;
1429 wd_initialize_part(disc,0);
1430 part->part_type = be32(&disc->dhead);
1431 part->is_gc = true;
1432 if ( wd_load_part(part,false,false,true) == ERR_OK )
1433 {
1434 WDPRINT("PRIMARY PARTITION FOUND\n");
1435 disc->disc_attrib |= WD_DA_GC_START_PART;
1436 part++;
1437 }
1438 else
1439 {
1440 disc->n_part--;
1441 memset(part,0,sizeof(*part));
1442 }
1443
1444 //----- check secondary partitions
1445
1446 for ( ptab = ptab_beg; ptab < ptab_end; ptab++ )
1447 {
1448 u32 off = ntohl(*ptab);
1449 if (off)
1450 {
1451 if (!dvd9)
1452 off >>= 2;
1453
1454 WDPRINT("PART #%u: off=%llx\n",disc->n_part,(u64)off<<2);
1455
1456 part = wd_initialize_part(disc,disc->n_part);
1457 part->part_type = WD_PART_DATA; // will be changed after loading
1458 part->part_off4 = off;
1459 part->is_gc = true;
1460
1461 disc->n_part++;
1462 }
1463 }
1464 }
1465 else
1466 {
1467 //----- setup the data partition of a standard gamecube disc
1468
1469 disc->n_part = 1;
1470 wd_part_t * part = CALLOC(1,sizeof(*disc->part));
1471 disc->part = part;
1472
1473 wd_initialize_part(disc,0);
1474 part->part_type = WD_PART_DATA; // will be changed after loading
1475 part->is_gc = true;
1476 }
1477
1478 //----- terminate
1479
1480 #ifdef DEBUG
1481 wd_print_disc(TRACE_FILE,10,disc,0);
1482 #endif
1483
1484 wd_load_all_part(disc,false,false,false);
1485 if (error_code)
1486 *error_code = ERR_OK;
1487 return disc;
1488 }
1489
1490 ///////////////////////////////////////////////////////////////////////////////
1491
wd_open_disc(wd_read_func_t read_func,void * read_data,u64 iso_size,ccp file_name,int force,enumError * error_code)1492 wd_disc_t * wd_open_disc
1493 (
1494 wd_read_func_t read_func, // read function, always valid
1495 void * read_data, // data pointer for read function
1496 u64 iso_size, // size of iso file, unknown if 0
1497 ccp file_name, // used for error messages if not NULL
1498 int force, // force level
1499 enumError * error_code // store error code if not NULL
1500 )
1501 {
1502 DASSERT(read_func);
1503 TRACE("wd_open_disc(%p,%p,%llx=%llu,%s,%d,%p)\n",
1504 read_func, read_data, iso_size, iso_size,
1505 file_name, force, error_code );
1506
1507 //----- setup
1508
1509 wd_disc_t * disc = MALLOC(sizeof(*disc));
1510 memset(disc,0,sizeof(*disc));
1511 disc->cache_sector = ~(u32)0;
1512
1513 disc->read_func = read_func;
1514 disc->read_data = read_data;
1515 disc->iso_size = iso_size;
1516 disc->force = force;
1517 disc->open_count = 1;
1518
1519
1520 //----- calculate termination string for error messages
1521
1522 if ( file_name && *file_name )
1523 {
1524 const size_t max_len = sizeof(disc->error_term) - 5;
1525 const size_t flen = strlen(file_name);
1526 if ( flen > max_len )
1527 snprintf(disc->error_term,sizeof(disc->error_term),": ...%s\n",
1528 file_name + flen - max_len + 5 );
1529 else
1530 snprintf(disc->error_term,sizeof(disc->error_term),": %s\n",file_name);
1531 }
1532 else
1533 snprintf(disc->error_term,sizeof(disc->error_term),".\n");
1534
1535
1536 //----- read disc header
1537
1538 enumError err = wd_read_raw( disc, 0, disc->temp_buf,
1539 WII_BOOT_SIZE+WII_BI2_SIZE, WD_USAGE_DISC );
1540 memcpy(&disc->dhead,disc->temp_buf,sizeof(disc->dhead));
1541
1542 disc->disc_type = get_header_disc_type(&disc->dhead,&disc->disc_attrib);
1543 switch(disc->disc_type)
1544 {
1545 case WD_DT_GAMECUBE:
1546 return wd_open_gc_disc(disc,error_code);
1547
1548 case WD_DT_WII:
1549 // nothing to do
1550 break;
1551
1552 default:
1553 WD_ERROR(ERR_WDISC_NOT_FOUND,"Disc magic not found%s",disc->error_term);
1554 err = ERR_WDISC_NOT_FOUND;
1555 break;
1556 }
1557
1558
1559 //----- read partition data
1560
1561 if (!err)
1562 err = wd_read_raw( disc, WII_PTAB_REF_OFF>>2,
1563 &disc->ptab_info, sizeof(disc->ptab_info), WD_USAGE_DISC );
1564
1565 if (!err)
1566 {
1567 u32 n_part = 0, ipt;
1568 for ( ipt = 0; ipt < WII_MAX_PTAB; ipt++ )
1569 n_part += ntohl(disc->ptab_info[ipt].n_part);
1570 if ( n_part > WII_MAX_PARTITIONS )
1571 n_part = WII_MAX_PARTITIONS;
1572 disc->n_part = n_part;
1573
1574 disc->ptab_entry = CALLOC(n_part,sizeof(*disc->ptab_entry));
1575 disc->part = CALLOC(n_part,sizeof(*disc->part));
1576
1577 disc->n_ptab = 0;
1578 for ( n_part = ipt = 0; ipt < WII_MAX_PTAB && !err; ipt++ )
1579 {
1580 wd_ptab_info_t * pt = disc->ptab_info + ipt;
1581 int n = ntohl(pt->n_part);
1582 if ( n > disc->n_part - n_part ) // limit n
1583 n = disc->n_part - n_part;
1584
1585 if (n)
1586 {
1587 disc->n_ptab++;
1588 wd_ptab_entry_t * pe = disc->ptab_entry + n_part;
1589 err = wd_read_raw( disc, ntohl(pt->off4),
1590 pe, n*sizeof(*pe), WD_USAGE_DISC );
1591 if (!err)
1592 {
1593 int pi;
1594 for ( pi = 0; pi < n; pi++, pe++ )
1595 {
1596 wd_part_t * part = wd_initialize_part(disc,n_part+pi);
1597 part->ptab_index = ipt;
1598 part->ptab_part_index = pi;
1599 part->part_type = ntohl(pe->ptype);
1600 part->part_off4 = ntohl(pe->off4);
1601 }
1602 }
1603 n_part += n;
1604 }
1605 }
1606 DASSERT( err || n_part == disc->n_part );
1607 }
1608
1609
1610 //----- read region settings and magic2
1611
1612 if (!err)
1613 err = wd_read_raw( disc, WII_REGION_OFF>>2, &disc->region,
1614 sizeof(disc->region), WD_USAGE_DISC );
1615
1616 if (!err)
1617 err = wd_read_raw( disc, WII_MAGIC2_OFF>>2, &disc->magic2,
1618 sizeof(disc->magic2), WD_USAGE_DISC );
1619
1620
1621 //----- check unused head sectors
1622
1623 if (!err)
1624 {
1625 int sector;
1626 for ( sector = 0; sector < WII_PART_OFF/WII_SECTOR_SIZE; sector++ )
1627 if ( disc->usage_table[sector] == WD_USAGE_UNUSED )
1628 {
1629 err = wd_read_raw( disc, sector * WII_SECTOR_SIZE4, disc->temp_buf,
1630 WII_SECTOR_SIZE, WD_USAGE_UNUSED );
1631 if (err)
1632 break;
1633
1634 u32 * ptr = (u32*)disc->temp_buf;
1635 u32 * end = ptr + WII_SECTOR_SIZE4;
1636 while ( ptr < end )
1637 if (*ptr++)
1638 {
1639 WDPRINT("MARK SECTOR %u AS USED\n",sector);
1640 disc->usage_table[sector] = WD_USAGE_DISC;
1641 break;
1642 }
1643 }
1644 }
1645
1646 //----- terminate
1647
1648 #ifdef DEBUG
1649 wd_print_disc(TRACE_FILE,10,disc,0);
1650 #endif
1651
1652 if (err)
1653 {
1654 WD_ERROR(ERR_WDISC_NOT_FOUND,"Can't open wii disc%s",disc->error_term);
1655 wd_close_disc(disc);
1656 disc = 0;
1657 }
1658
1659 if (error_code)
1660 *error_code = err;
1661
1662 #if 0 && defined(TEST) && defined(DEBUG) // [[2do]]
1663 if (disc)
1664 {
1665 static int tab[] = { 0, 4, 7935, 7936, 7940, 7941, -1 };
1666
1667 PRINT("+++++++\n");
1668 int i;
1669 for ( i = 0; tab[i] >= 0; i++ )
1670 {
1671 wd_sector_status_t ss = wd_get_disc_sector_status(disc,tab[i],false);
1672 printf("%5d: %04x |%s|\n",
1673 tab[i], ss, wd_log_sector_status(0,0,ss) );
1674 }
1675 PRINT("-------\n");
1676 }
1677 #endif
1678
1679 return disc;
1680 }
1681
1682 ///////////////////////////////////////////////////////////////////////////////
1683
wd_dup_disc(wd_disc_t * disc)1684 wd_disc_t * wd_dup_disc
1685 (
1686 wd_disc_t * disc // NULL or a valid disc pointer
1687 )
1688 {
1689 TRACE("wd_dup_disc(%p) open_count=%d\n", disc, disc ? disc->open_count : -1 );
1690 if (disc)
1691 disc->open_count++;
1692 return disc;
1693 }
1694
1695 ///////////////////////////////////////////////////////////////////////////////
1696
wd_close_disc(wd_disc_t * disc)1697 void wd_close_disc
1698 (
1699 wd_disc_t * disc // NULL or valid disc pointer
1700 )
1701 {
1702 TRACE("wd_close_disc(%p) open_count=%d\n", disc, disc ? disc->open_count : -1 );
1703
1704 if ( disc && --disc->open_count <= 0 )
1705 {
1706 wd_reset_memmap(&disc->patch);
1707 if (disc->part)
1708 {
1709 int pi;
1710 wd_part_t * part = disc->part;
1711 for ( pi = 0; pi < disc->n_part; pi++, part++ )
1712 {
1713 wd_reset_memmap(&part->patch);
1714 cert_reset(&part->cert_chain);
1715 FREE(part->tmd);
1716 FREE(part->cert);
1717 FREE(part->h3);
1718 FREE(part->setup_txt);
1719 FREE(part->setup_sh);
1720 FREE(part->setup_bat);
1721 FREE(part->fst);
1722 }
1723 FREE(disc->part);
1724 }
1725 FREE(disc->ptab_entry);
1726 FREE(disc->reloc);
1727 FREE(disc->group_cache);
1728 FREE(disc);
1729 }
1730 }
1731
1732 //
1733 ///////////////////////////////////////////////////////////////////////////////
1734 /////////////// get status ///////////////
1735 ///////////////////////////////////////////////////////////////////////////////
1736
wd_disc_has_ptab(wd_disc_t * disc)1737 bool wd_disc_has_ptab
1738 (
1739 wd_disc_t *disc // valid disc pointer
1740 )
1741 {
1742 DASSERT(disc);
1743 return disc->disc_type != WD_DT_GAMECUBE;
1744 }
1745
1746 ///////////////////////////////////////////////////////////////////////////////
1747
wd_disc_has_region_setting(wd_disc_t * disc)1748 bool wd_disc_has_region_setting
1749 (
1750 wd_disc_t *disc // valid disc pointer
1751 )
1752 {
1753 DASSERT(disc);
1754 return disc->disc_type != WD_DT_GAMECUBE;
1755 }
1756
1757 ///////////////////////////////////////////////////////////////////////////////
1758
wd_part_has_ticket(wd_part_t * part)1759 bool wd_part_has_ticket
1760 (
1761 wd_part_t *part // valid disc partition pointer
1762 )
1763 {
1764 DASSERT(part);
1765 return !part->is_gc;
1766 }
1767
1768 ///////////////////////////////////////////////////////////////////////////////
1769
wd_part_has_tmd(wd_part_t * part)1770 bool wd_part_has_tmd
1771 (
1772 wd_part_t *part // valid disc partition pointer
1773 )
1774 {
1775 DASSERT(part);
1776 return !part->is_gc && part->tmd;
1777 }
1778
1779 ///////////////////////////////////////////////////////////////////////////////
1780
wd_part_has_cert(wd_part_t * part)1781 bool wd_part_has_cert
1782 (
1783 wd_part_t *part // valid disc partition pointer
1784 )
1785 {
1786 DASSERT(part);
1787 return !part->is_gc && part->ph.cert_off4;
1788 }
1789
1790 ///////////////////////////////////////////////////////////////////////////////
1791
wd_part_has_h3(wd_part_t * part)1792 bool wd_part_has_h3
1793 (
1794 wd_part_t *part // valid disc partition pointer
1795 )
1796 {
1797 DASSERT(part);
1798 return !part->is_gc && part->ph.h3_off4;
1799 }
1800
1801 //
1802 ///////////////////////////////////////////////////////////////////////////////
1803 /////////////// get partition data ///////////////
1804 ///////////////////////////////////////////////////////////////////////////////
1805
wd_get_part_by_index(wd_disc_t * disc,int index,int load_data)1806 wd_part_t * wd_get_part_by_index
1807 (
1808 wd_disc_t *disc, // valid disc pointer
1809 int index, // zero based partition index within wd_disc_t
1810 int load_data // >0: load partition data from disc
1811 // >1: load cert too
1812 // >2: load h3 too
1813 )
1814 {
1815 TRACE("wd_get_part_by_index(%p,%d,%d)\n",disc,index,load_data);
1816 ASSERT(disc);
1817
1818 if ( index < 0 || index >= disc->n_part )
1819 return 0;
1820
1821 wd_part_t * part = disc->part + index;
1822 part->disc = disc;
1823
1824 if ( load_data > 0 )
1825 wd_load_part( part, load_data > 1, load_data > 2, false );
1826
1827 return part;
1828 }
1829
1830 ///////////////////////////////////////////////////////////////////////////////
1831
wd_get_part_by_usage(wd_disc_t * disc,u8 usage_id,int load_data)1832 wd_part_t * wd_get_part_by_usage
1833 (
1834 wd_disc_t * disc, // valid disc pointer
1835 u8 usage_id, // value of 'usage_table'
1836 int load_data // >0: load partition data from disc
1837 // >1: load cert too
1838 // >2: load h3 too
1839 )
1840 {
1841 TRACE("wd_get_part_by_usage(%p,%02x,%d)\n",disc,usage_id,load_data);
1842 const int idx = (int)( usage_id & WD_USAGE__MASK ) - WD_USAGE_PART_0;
1843 return wd_get_part_by_index(disc,idx,load_data);
1844 }
1845
1846 ///////////////////////////////////////////////////////////////////////////////
1847
wd_get_part_by_ptab(wd_disc_t * disc,int ptab_index,int ptab_part_index,int load_data)1848 wd_part_t * wd_get_part_by_ptab
1849 (
1850 wd_disc_t *disc, // valid disc pointer
1851 int ptab_index, // zero based index of partition table
1852 int ptab_part_index,// zero based index within owning partition table
1853 int load_data // >0: load partition data from disc
1854 // >1: load cert too
1855 // >2: load h3 too
1856 )
1857 {
1858 TRACE("wd_get_part_by_ptab(%p,%d,%d,%d)\n",
1859 disc, ptab_index, ptab_part_index, load_data );
1860 ASSERT(disc);
1861
1862 wd_part_t * part = disc->part;
1863 int i;
1864 for ( i = 0; i < disc->n_part; i++, part++ )
1865 if ( part->ptab_index == ptab_index
1866 && part->ptab_part_index == ptab_part_index )
1867 {
1868 return wd_get_part_by_index(disc,i,load_data);
1869 }
1870 return 0;
1871 }
1872
1873 ///////////////////////////////////////////////////////////////////////////////
1874
wd_get_part_by_type(wd_disc_t * disc,u32 part_type,int load_data)1875 wd_part_t * wd_get_part_by_type
1876 (
1877 wd_disc_t *disc, // valid disc pointer
1878 u32 part_type, // find first partition with this type
1879 int load_data // >0: load partition data from disc
1880 // >1: load cert too
1881 // >2: load h3 too
1882 )
1883 {
1884 TRACE("wd_get_part_by_ptab(%p,%x,%d)\n",disc,part_type,load_data);
1885 ASSERT(disc);
1886
1887 wd_part_t * part = disc->part;
1888 int i;
1889 for ( i = 0; i < disc->n_part; i++, part++ )
1890 if ( part->part_type == part_type )
1891 return wd_get_part_by_index(disc,i,load_data);
1892 return 0;
1893 }
1894
1895 //
1896 ///////////////////////////////////////////////////////////////////////////////
1897 /////////////// load partition data ///////////////
1898 ///////////////////////////////////////////////////////////////////////////////
1899
wd_check_part_offset(wd_part_t * part,u64 off,u64 size,ccp name,bool silent)1900 static enumError wd_check_part_offset
1901 (
1902 wd_part_t *part, // valid disc partition pointer
1903 u64 off, // offset to test
1904 u64 size, // size to test
1905 ccp name, // name of object
1906 bool silent // true: don't print error messages
1907 )
1908 {
1909 DASSERT(part);
1910 DASSERT(part->disc);
1911 const u64 iso_size = part->disc->iso_size;
1912 if (iso_size)
1913 {
1914 noTRACE("TEST '%s': %llx + %llx <= %llx\n",name,off,size,iso_size);
1915 if ( off > iso_size )
1916 {
1917 if (!silent)
1918 WD_ERROR(ERR_WPART_INVALID,
1919 "Partition %s: Offset of %s (%#llx) behind end of file (%#llx)%s",
1920 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
1921 name, off, iso_size, part->disc->error_term );
1922 return part->disc->force > 0 ? ERR_WARNING : ERR_WPART_INVALID;
1923 }
1924
1925 off += size;
1926 if ( off > iso_size )
1927 {
1928 if (!silent)
1929 WD_ERROR(ERR_WPART_INVALID,
1930 "Partition %s: End of %s (%#llx) behind end of file (%#llx)%s",
1931 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
1932 name, off, iso_size, part->disc->error_term );
1933 return part->disc->force > 0 ? ERR_WARNING : ERR_WPART_INVALID;
1934 }
1935 }
1936
1937 return ERR_OK;
1938 }
1939
1940 //-----------------------------------------------------------------------------
1941
1942 const int SYS_DIR_COUNT = 3;
1943 const int SYS_FILE_COUNT = 12;
1944
1945 const int SYS_DIR_COUNT_GC = 2;
1946 const int SYS_FILE_COUNT_GC = 6;
1947
1948 //-----------------------------------------------------------------------------
1949
wd_load_part(wd_part_t * part,bool load_cert,bool load_h3,bool silent)1950 enumError wd_load_part
1951 (
1952 wd_part_t *part, // valid disc partition pointer
1953 bool load_cert, // true: load cert data too
1954 bool load_h3, // true: load h3 data too
1955 bool silent // true: don't print error messages
1956 )
1957 {
1958 TRACE("wd_load_part(%p,%d,%d)\n",part,load_cert,load_h3);
1959
1960 ASSERT(part);
1961 wd_disc_t * disc = part->disc;
1962 ASSERT(disc);
1963
1964 const wd_part_header_t * ph = &part->ph;
1965
1966 const bool load_now = !part->is_loaded;
1967 if (load_now)
1968 {
1969 TRACE(" - load now\n");
1970
1971 // assume loaded, but invalid
1972 part->is_loaded = true;
1973 part->is_valid = false;
1974 part->disc->invalid_part++;
1975
1976 FREE(part->tmd); part->tmd = 0;
1977 FREE(part->cert); part->cert = 0;
1978 FREE(part->h3); part->h3 = 0;
1979 FREE(part->fst); part->fst = 0;
1980
1981
1982 //----- scan partition header
1983
1984 if (part->is_gc)
1985 part->data_off4 = part->part_off4;
1986 else
1987 {
1988 //----- load partition header
1989
1990 const u64 part_off = (u64)part->part_off4 << 2;
1991 if (wd_check_part_offset(part,part_off,sizeof(part->ph),"TICKET",silent))
1992 return ERR_WPART_INVALID;
1993
1994 enumError err = wd_read_part_raw(part,0,&part->ph,sizeof(part->ph),true);
1995 if (err)
1996 {
1997 if (!silent)
1998 WD_ERROR(ERR_WPART_INVALID,"Can't read TICKET of partition '%s'%s",
1999 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2000 disc->error_term );
2001 return ERR_WPART_INVALID;
2002 }
2003
2004 ntoh_part_header(&part->ph,&part->ph);
2005 wd_decrypt_title_key(&ph->ticket,part->key);
2006
2007 part->data_off4 = part->part_off4 + ph->data_off4;
2008 part->data_sector = part->data_off4 / WII_SECTOR_SIZE4;
2009 part->part_size = (u64)( part->data_off4 + ph->data_size4 - part->part_off4 ) << 2;
2010
2011 TRACE("part=%llx,%llx, tmd=%llx,%x, cert=%llx,%x, h3=%llx, data=%llx,%llx\n",
2012 (u64)part->part_off4 << 2, part->part_size,
2013 (u64)ph->tmd_off4 << 2, ph->tmd_size,
2014 (u64)ph->cert_off4 << 2, ph->cert_size,
2015 (u64)ph->h3_off4 << 2,
2016 (u64)ph->data_off4 << 2, (u64)ph->data_size4 << 2 );
2017
2018
2019 //----- file size tests
2020
2021 if (wd_check_part_offset(part, part_off+((u64)ph->data_off4<<2),
2022 (u64)ph->data_size4<<2, "data", silent ) > ERR_WARNING )
2023 return ERR_WPART_INVALID;
2024
2025
2026 //----- load tmd
2027
2028 if ( ph->tmd_size < sizeof(wd_tmd_t)
2029 || ((u64)ph->tmd_off4<<2) + ph->tmd_size > ((u64)ph->data_off4<<2) )
2030 {
2031 if (!silent)
2032 WD_ERROR(ERR_WPART_INVALID,"Invalid TMD size (0x%x) in partition '%s'%s",
2033 ph->tmd_size,
2034 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2035 disc->error_term );
2036 return ERR_WPART_INVALID;
2037 }
2038
2039 wd_tmd_t * tmd = MALLOC(ph->tmd_size);
2040 part->tmd = tmd;
2041 err = wd_read_part_raw( part, ph->tmd_off4, tmd, ph->tmd_size, true );
2042 if (err)
2043 {
2044 if (!silent)
2045 WD_ERROR(ERR_WPART_INVALID,"Can't read TMD of partition '%s'%s",
2046 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2047 disc->error_term );
2048 return ERR_WPART_INVALID;
2049 }
2050
2051
2052 //----- check encryption
2053 // -> call wd_is_block_encrypted() first because of 'sector_stat' setup
2054
2055 part->is_encrypted = wd_is_block_encrypted(part,0,1,false)
2056 && !tmd_is_marked_not_encrypted(tmd);
2057 TRACE("is_encrypted=%d\n",part->is_encrypted);
2058
2059 } // !is_gc
2060
2061
2062 //----- load boot.bin
2063
2064 enumError err = wd_read_part(part,WII_BOOT_OFF>>2,
2065 disc->temp_buf,WII_BOOT_SIZE+WII_BI2_SIZE,true);
2066 if (err)
2067 {
2068 if (!silent)
2069 WD_ERROR(ERR_WPART_INVALID,
2070 "Can't read BOOT.BIN of partition '%s'%s",
2071 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2072 disc->error_term );
2073 return ERR_WPART_INVALID;
2074 }
2075
2076 if (part->is_gc)
2077 {
2078 const uint ssm = sizeof(skeleton_marker);
2079 const uint off = WII_BI2_OFF + WII_BI2_SIZE - ssm;
2080 if ( !memcmp( disc->temp_buf + off, skeleton_marker, ssm ))
2081 {
2082 part->sector_stat |= WD_SS_F_SKELETON;
2083 PRINT("*** wd_load_part() SKELETON ***\n");
2084 }
2085 }
2086
2087 wd_boot_t * boot = &part->boot;
2088 ntoh_boot(boot,(wd_boot_t*)disc->temp_buf);
2089 part->region = be32(disc->temp_buf+WII_BI2_OFF+WII_BI2_REGION_OFF);
2090 if (part->is_gc)
2091 part->part_type = be32(boot);
2092
2093 if ( !boot->dol_off4 && !boot->fst_off4 )
2094 {
2095 if (!silent)
2096 WD_ERROR(ERR_WPART_INVALID,
2097 "Invalid BOOT.BIN (no MAIN.DOL and no FST), partition '%s'%s",
2098 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2099 disc->error_term );
2100 return ERR_WPART_INVALID;
2101 }
2102
2103
2104 //----- setup "setup.txt", "setup.sh", "setup.bat"
2105
2106 // get data
2107
2108 ccp disc_type = wd_get_disc_type_name(disc->disc_type,"?");
2109 ccp image_type = disc->image_type ? disc->image_type : "?";
2110 ccp image_ext = disc->image_ext ? disc->image_ext : "";
2111 ccp part_id = wd_print_id(&boot->dhead.disc_id,6,0);
2112 ccp part_title = boot->dhead.disc_title;
2113
2114 char norm_title[WII_TITLE_SIZE+1];
2115 {
2116 ccp src = part_title;
2117 ccp end = part_title + WII_TITLE_SIZE;
2118 char *dest = norm_title;
2119 while ( src < end && *src )
2120 {
2121 uchar ch = *src++;
2122 *dest++ = ch < ' ' || ch == '"' ? ' ' : ch;
2123 }
2124 *dest = 0;
2125 }
2126
2127 // "setup.txt"
2128 part->setup_txt_len
2129 = snprintf((char*)disc->temp_buf,sizeof(disc->temp_buf),
2130 "# setup.txt : scanned by wit+wwt while composing a disc.\n"
2131 "# remove the '!' before names to activate parameters.\n"
2132 "\n"
2133 "disc-type = %s\n"
2134 "image-type = %s\n"
2135 "\n"
2136 "!part-id = %.6s\n"
2137 "!part-name = %.64s\n"
2138 "!part-offset = 0x%llx\n"
2139 "\n"
2140 ,disc_type
2141 ,image_type
2142 ,part_id
2143 ,part_title
2144 ,(u64)part->part_off4 << 2
2145 );
2146 part->setup_txt = MEMDUP(disc->temp_buf,part->setup_txt_len);
2147
2148 // "setup.sh"
2149 part->setup_sh_len
2150 = snprintf((char*)disc->temp_buf,sizeof(disc->temp_buf),
2151 "DISC_TYPE=\"%s\"\n"
2152 "IMAGE_TYPE=\"%s\"\n"
2153 "IMAGE_EXT=\"%s\"\n"
2154 "PART_ID=\"%.6s\"\n"
2155 "PART_NAME=\"%s\"\n"
2156 ,disc_type
2157 ,image_type
2158 ,image_ext
2159 ,part_id
2160 ,norm_title
2161 );
2162 part->setup_sh = MEMDUP(disc->temp_buf,part->setup_sh_len);
2163
2164 // "setup.bat"
2165 part->setup_bat_len
2166 = snprintf((char*)disc->temp_buf,sizeof(disc->temp_buf),
2167 "set DISC_TYPE=%s\r\n"
2168 "set IMAGE_TYPE=%s\r\n"
2169 "set IMAGE_EXT=%s\r\n"
2170 "set PART_ID=%.6s\r\n"
2171 "set PART_NAME=%s\r\n"
2172 ,disc_type
2173 ,image_type
2174 ,image_ext
2175 ,part_id
2176 ,norm_title
2177 );
2178 part->setup_bat = MEMDUP(disc->temp_buf,part->setup_bat_len);
2179
2180
2181 //----- calculate size of main.dol
2182
2183 u32 fst_n = 0;
2184 u32 fst_dir_count = part->is_gc ? SYS_DIR_COUNT_GC : SYS_DIR_COUNT;
2185 u32 fst_file_count = part->is_gc ? SYS_FILE_COUNT_GC : SYS_FILE_COUNT;
2186 u32 fst_max_off4 = part->data_off4;
2187 u32 fst_max_size = WII_H3_SIZE;
2188
2189 if (!boot->dol_off4)
2190 fst_file_count--;
2191 else
2192 {
2193 dol_header_t * dol = (dol_header_t*) disc->temp_buf;
2194
2195 err = wd_read_part(part,boot->dol_off4,dol,DOL_HEADER_SIZE,true);
2196 if (err)
2197 {
2198 if (!silent)
2199 WD_ERROR(ERR_WPART_INVALID,"Can't read MAIN.DOL in partition '%s'%s",
2200 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2201 disc->error_term );
2202 return ERR_WPART_INVALID;
2203 }
2204 ntoh_dol_header(dol,dol);
2205
2206 // iterate through all segments
2207 int i;
2208 u32 dol_size = 0;
2209 for ( i = 0; i < DOL_N_SECTIONS; i++ )
2210 {
2211 const u32 size = dol->sect_off[i] + dol->sect_size[i];
2212 if ( dol_size < size )
2213 dol_size = size;
2214 }
2215 TRACE("DOL-SIZE: %x <= %x\n",dol_size,boot->fst_off4-boot->dol_off4<<2);
2216 if ( dol_size > boot->fst_off4 - boot->dol_off4 << 2 )
2217 {
2218 WDPRINT("!!! DOL-SIZE: %x <= %x & off=%x\n",
2219 dol_size, boot->fst_off4-boot->dol_off4<<2, boot->dol_off4 );
2220 if (!silent)
2221 WD_ERROR(ERR_WPART_INVALID,"Invalid MAIN.DOL in partition '%s'%s",
2222 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2223 disc->error_term );
2224 return ERR_WPART_INVALID;
2225 }
2226 part->dol_size = dol_size;
2227 if ( fst_max_size < dol_size )
2228 fst_max_size = dol_size;
2229
2230 wd_mark_part(part,boot->dol_off4,dol_size);
2231 }
2232
2233
2234 //----- calculate size of apploader.img
2235
2236 {
2237 u8 * apl_header = (u8*) disc->temp_buf;
2238 const u32 apl_off4 = part->is_gc ? WII_APL_OFF : WII_APL_OFF >> 2;
2239 err = wd_read_part(part,apl_off4,apl_header,0x20,false);
2240 if (err)
2241 {
2242 if (!silent)
2243 WD_ERROR(ERR_WPART_INVALID,"Can't read APPLOADER of partition '%s'%s",
2244 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2245 disc->error_term );
2246 return ERR_WPART_INVALID;
2247 }
2248 part->apl_size = 0x20 + be32(apl_header+0x14) + be32(apl_header+0x18);
2249 if ( fst_max_size < part->apl_size )
2250 fst_max_size = part->apl_size;
2251
2252 wd_mark_part(part,WII_APL_OFF>>2,part->apl_size);
2253 }
2254
2255
2256 //----- load and iterate fst
2257
2258 u32 mgr_sect = part->end_sector;
2259 u32 fst_size = boot->fst_size4;
2260
2261 if (fst_size)
2262 {
2263 if (!part->is_gc)
2264 fst_size <<= 2;
2265 TRACE("fst_size=%x\n",fst_size);
2266
2267 wd_fst_item_t * fst = MALLOC(fst_size);
2268 part->fst = fst;
2269 err = wd_read_part(part,boot->fst_off4,fst,fst_size,true);
2270 if (err)
2271 {
2272 if (!silent)
2273 WD_ERROR(ERR_WPART_INVALID,"Can't read FST of partition '%s'%s",
2274 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO),
2275 disc->error_term );
2276 return ERR_WPART_INVALID;
2277 }
2278 mgr_sect = part->end_sector;
2279
2280 // mark used blocks
2281
2282 fst_dir_count++; // directory './files/'
2283 fst_n = ntohl(fst->size);
2284 const wd_fst_item_t *fst_end = fst + fst_n;
2285
2286 bool in_wad_dir = false;
2287
2288 for ( fst++; fst < fst_end; fst++ )
2289 if (fst->is_dir)
2290 {
2291 fst_dir_count++;
2292 in_wad_dir = false;
2293 if ( part->part_type == WD_PART_UPDATE )
2294 {
2295 ccp fname = (ccp)fst_end + (ntohl(fst->name_off)&0xffffff);
2296 in_wad_dir = fname < (ccp)part->fst + fst_size
2297 && !strcasecmp(fname,"_sys");
2298 }
2299 }
2300 else
2301 {
2302 fst_file_count++;
2303 const u32 off4 = ntohl(fst->offset4);
2304 if ( fst_max_off4 < off4 )
2305 fst_max_off4 = off4;
2306 const u32 size = ntohl(fst->size);
2307 if ( fst_max_size < size )
2308 fst_max_size = size;
2309 wd_mark_part(part,off4,size);
2310
2311 if ( in_wad_dir )
2312 {
2313 ccp fname = (ccp)fst_end + (ntohl(fst->name_off)&0xffffff);
2314 if ( fname < (ccp)part->fst + fst_size
2315 && !strncasecmp(fname,"RVL-WiiSystemmenu-v",19)
2316 && !strncasecmp(fname+strlen(fname)-4,".wad",4) )
2317 {
2318 const u32 num = strtoul(fname+19,0,10);
2319 if ( disc->system_menu < num )
2320 {
2321 noPRINT("SYS-MENU: %u -> %u [%s]\n",
2322 disc->system_menu,num,fname);
2323 disc->system_menu = num;
2324 }
2325 }
2326 }
2327 }
2328 }
2329
2330 part->fst_n = fst_n;
2331 part->fst_max_off4 = fst_max_off4;
2332 part->fst_max_size = fst_max_size;
2333 part->fst_dir_count = fst_dir_count;
2334 part->fst_file_count = fst_file_count;
2335
2336
2337 //----- gamecube settings
2338
2339 if (part->is_gc)
2340 {
2341 part->data_sector = part->data_off4 / WII_SECTOR_SIZE4;
2342 part->end_sector = part->data_sector
2343 + ( part->max_marked + WII_SECTOR_SIZE - 1 ) / WII_SECTOR_SIZE;
2344 if ( part->end_sector > WII_MAX_SECTORS )
2345 part->end_sector = WII_MAX_SECTORS;
2346
2347 part->part_size = part->max_marked - ( (u64)part->data_off4 << 2 );
2348 noPRINT(">> S=%x .. %x / %llx .. %llx / %llx / %llx\n",
2349 part->data_sector, part->end_sector,
2350 (u64)part->data_off4<<2, part->part_size,
2351 part->max_marked, disc->iso_size );
2352
2353 if (wd_check_part_offset( part, (u64)part->data_off4<<2,
2354 part->part_size, "PART", silent ) > ERR_WARNING )
2355 return ERR_WPART_INVALID;
2356 }
2357 else
2358 {
2359 const u32 last_sect = part->data_sector + ph->data_size4 / WII_SECTOR_SIZE4;
2360 noPRINT("last_sect=%x, end_sector=%x\n",last_sect,part->end_sector);
2361 if ( part->end_sector < last_sect )
2362 part->end_sector = last_sect;
2363 }
2364
2365 if ( part->end_sector > WII_MAX_SECTORS )
2366 part->end_sector = WII_MAX_SECTORS;
2367
2368 mgr_sect = ( mgr_sect - part->data_sector + WII_GROUP_SECTORS - 1 )
2369 / WII_GROUP_SECTORS * WII_GROUP_SECTORS + part->data_sector;
2370 if ( mgr_sect > part->end_sector )
2371 mgr_sect = part->end_sector;
2372 part->end_mgr_sector = mgr_sect;
2373
2374 WDPRINT("PART #%u, sectors: %llx .. %llx .. %llx .. %llx\n",
2375 part->index,
2376 (u64)WII_SECTOR_SIZE * part->data_sector,
2377 (u64)WII_SECTOR_SIZE * part->end_mgr_sector,
2378 (u64)WII_SECTOR_SIZE * part->end_sector,
2379 (u64)WII_SECTOR_SIZE * WII_MAX_SECTORS );
2380
2381
2382 //----- all done => mark as valid
2383
2384 part->is_valid = true;
2385 wd_calc_fst_statistics(disc,false);
2386
2387
2388 //----- check overlays
2389
2390 int p2;
2391 u32 end4a = part->part_off4 + (u32)(part->part_size>>2);
2392 for ( p2 = 0; p2 < disc->n_part; p2++ )
2393 {
2394 wd_part_t * part2 = disc->part + p2;
2395 if ( part == part2 )
2396 continue;
2397
2398 if ( part2->part_off4 >= part->part_off4
2399 && part2->part_off4 < end4a )
2400 {
2401 part->is_overlay = true;
2402 part2->is_overlay = true;
2403 continue;
2404 }
2405
2406 if ( part2->is_loaded && part2->is_valid )
2407 {
2408 u32 end4b = part2->part_off4 + (u32)(part2->part_size>>2);
2409 if ( part->part_off4 >= part2->part_off4
2410 && part->part_off4 < end4b )
2411 {
2412 part->is_overlay = true;
2413 part2->is_overlay = true;
2414 continue;
2415 }
2416 }
2417 }
2418
2419 #if 0 && defined(TEST) && defined(DEBUG) // [[2do]]
2420 {
2421 int i;
2422 for ( i = -1; i < 10; i += 1 )
2423 {
2424 wd_sector_status_t ss = wd_get_part_sector_status(part,i,silent);
2425 printf("%2zu.%03d: %04x |%s|\n",
2426 part-disc->part, i, ss, wd_log_sector_status(0,0,ss) );
2427 }
2428 }
2429 #endif
2430
2431 }
2432
2433 if (part->is_valid)
2434 {
2435 if ( ph->cert_off4 )
2436 {
2437 if ( !part->cert && load_cert )
2438 {
2439 part->cert = MALLOC(ph->cert_size);
2440 wd_read_part_raw( part, ph->cert_off4, part->cert,
2441 ph->cert_size, true );
2442 }
2443 else if (load_now)
2444 wd_read_part_raw( part, ph->cert_off4, 0, ph->cert_size, true );
2445 }
2446
2447 if ( ph->h3_off4 )
2448 {
2449 if ( !part->h3 && load_h3 )
2450 {
2451 part->h3 = MALLOC(WII_H3_SIZE);
2452 wd_read_part_raw( part, ph->h3_off4, part->h3, WII_H3_SIZE, true );
2453 }
2454 else if (load_now)
2455 wd_read_part_raw( part, ph->h3_off4, 0, WII_H3_SIZE, true );
2456 }
2457 }
2458
2459 TRACELINE;
2460 return part->is_valid ? ERR_OK : ERR_WPART_INVALID;
2461 }
2462
2463 ///////////////////////////////////////////////////////////////////////////////
2464
wd_load_all_part(wd_disc_t * disc,bool load_cert,bool load_h3,bool silent)2465 enumError wd_load_all_part
2466 (
2467 wd_disc_t * disc, // valid disc pointer
2468 bool load_cert, // true: load cert data too
2469 bool load_h3, // true: load h3 data too
2470 bool silent // true: don't print error messages
2471 )
2472 {
2473 ASSERT(disc);
2474
2475 enumError max_err = ERR_OK;
2476 int pi;
2477 for ( pi = 0; pi < disc->n_part; pi++ )
2478 {
2479 DASSERT(disc->part);
2480 const enumError err = wd_load_part(disc->part+pi,load_cert,load_h3,silent);
2481 if ( max_err < err )
2482 max_err = err;
2483 }
2484
2485 return max_err;
2486 }
2487
2488 ///////////////////////////////////////////////////////////////////////////////
2489
wd_calc_fst_statistics(wd_disc_t * disc,bool sum_all)2490 enumError wd_calc_fst_statistics
2491 (
2492 wd_disc_t * disc, // valid disc pointer
2493 bool sum_all // false: summarize only enabled partitions
2494 )
2495 {
2496 DASSERT(disc);
2497
2498 u32 invalid = 0;
2499 u32 n = 0;
2500 u32 max_off4 = 0;
2501 u32 max_size = 0;
2502 u32 dir_count = 0;
2503 u32 file_count = 0;
2504 bool have_overlays = false;
2505
2506 disc->data_part = disc->update_part = disc->channel_part = disc->main_part = 0;
2507 wd_part_t *part, *part_end = disc->part + disc->n_part;
2508 for ( part = disc->part; part < part_end; part++ )
2509 {
2510 invalid += !part->is_valid;
2511 part->is_ok = part->is_loaded && part->is_valid && part->is_enabled;
2512 if ( sum_all || part->is_ok )
2513 {
2514 n += part->fst_n;
2515 dir_count += part->fst_dir_count;
2516 file_count += part->fst_file_count;
2517
2518 if ( max_off4 < part->fst_max_off4 )
2519 max_off4 = part->fst_max_off4;
2520 if ( max_size < part->fst_max_size )
2521 max_size = part->fst_max_size;
2522
2523 if ( part->is_overlay )
2524 have_overlays = true;
2525 }
2526
2527 if (part->is_ok)
2528 {
2529 switch (part->part_type)
2530 {
2531 case WD_PART_DATA:
2532 if (!disc->data_part)
2533 disc->data_part = part;
2534 break;
2535
2536 case WD_PART_UPDATE:
2537 if (!disc->update_part)
2538 disc->update_part = part;
2539 break;
2540
2541 case WD_PART_CHANNEL:
2542 if (!disc->channel_part)
2543 disc->channel_part = part;
2544 break;
2545 }
2546 if (!disc->main_part)
2547 disc->main_part = part;
2548 }
2549 }
2550
2551 if (disc->data_part)
2552 disc->main_part = disc->data_part;
2553 else if (disc->update_part)
2554 disc->main_part = disc->update_part;
2555 else if (disc->channel_part)
2556 disc->main_part = disc->channel_part;
2557 TRACE("D_PART=%2zd U_PART=%2zd C_PART=%2zd M_PART=%2zd\n",
2558 disc->data_part ? disc->data_part - disc->part : -1,
2559 disc->update_part ? disc->update_part - disc->part : -1,
2560 disc->channel_part ? disc->channel_part - disc->part : -1,
2561 disc->main_part ? disc->main_part - disc->part : -1 );
2562
2563 disc->invalid_part = invalid;
2564 disc->fst_n = n;
2565 disc->fst_max_off4 = max_off4;
2566 disc->fst_max_size = max_size;
2567 disc->fst_dir_count = dir_count;
2568 disc->fst_file_count = file_count;
2569 disc->have_overlays = have_overlays;
2570
2571 return invalid ? ERR_WDISC_INVALID : ERR_OK;
2572 }
2573
2574 //
2575 ///////////////////////////////////////////////////////////////////////////////
2576 /////////////// interface: certificate support ///////////////
2577 ///////////////////////////////////////////////////////////////////////////////
2578
wd_load_cert_chain(wd_part_t * part,bool silent)2579 const cert_chain_t * wd_load_cert_chain
2580 (
2581 wd_part_t * part, // valid disc partition pointer
2582 bool silent // true: don't print errors while loading cert
2583 )
2584 {
2585 DASSERT(part);
2586
2587 if (!part->cert)
2588 wd_load_part(part,true,false,silent);
2589
2590 if ( part->cert && !part->cert_chain.used )
2591 cert_append_data( &part->cert_chain, part->cert, part->ph.cert_size, false );
2592
2593 return &part->cert_chain;
2594 }
2595
2596 ///////////////////////////////////////////////////////////////////////////////
2597
wd_get_cert_ticket_stat(wd_part_t * part,bool silent)2598 cert_stat_t wd_get_cert_ticket_stat
2599 (
2600 wd_part_t * part, // valid disc partition pointer
2601 bool silent // true: don't print errors while loading cert
2602 )
2603 {
2604 DASSERT(part);
2605 DASSERT(part->disc);
2606
2607 if (!part->cert_tik_stat)
2608 {
2609 const cert_chain_t * cc = wd_load_cert_chain(part,silent);
2610 part->cert_tik_stat = cert_check_ticket(cc,&part->ph.ticket,0);
2611 part->disc->cert_summary |= part->cert_tik_stat;
2612 }
2613
2614 return part->cert_tik_stat;
2615 }
2616
2617 ///////////////////////////////////////////////////////////////////////////////
2618
wd_get_cert_tmd_stat(wd_part_t * part,bool silent)2619 cert_stat_t wd_get_cert_tmd_stat
2620 (
2621 wd_part_t * part, // valid disc partition pointer
2622 bool silent // true: don't print errors while loading cert
2623 )
2624 {
2625 DASSERT(part);
2626 DASSERT(part->disc);
2627
2628 if (!part->cert_tmd_stat)
2629 {
2630 const cert_chain_t * cc = wd_load_cert_chain(part,silent);
2631 part->cert_tmd_stat = cert_check_tmd(cc,part->tmd,0);
2632 part->disc->cert_summary |= part->cert_tmd_stat;
2633 }
2634
2635 return part->cert_tmd_stat;
2636 }
2637
2638 ///////////////////////////////////////////////////////////////////////////////
2639
wd_get_sig_status_short_text(cert_stat_t sig_stat)2640 ccp wd_get_sig_status_short_text
2641 (
2642 cert_stat_t sig_stat
2643 )
2644 {
2645 return sig_stat & CERT_F_HASH_FAILED
2646 ? "no-sig"
2647 : sig_stat & CERT_F_HASH_FAKED
2648 ? "faked"
2649 : sig_stat & CERT_F_HASH_OK
2650 ? "signed"
2651 : 0;
2652 }
2653
2654 ///////////////////////////////////////////////////////////////////////////////
2655
wd_get_sig_status_text(cert_stat_t sig_stat)2656 ccp wd_get_sig_status_text
2657 (
2658 cert_stat_t sig_stat
2659 )
2660 {
2661 return sig_stat & CERT_F_HASH_FAILED
2662 ? "not signed"
2663 : sig_stat & CERT_F_HASH_FAKED
2664 ? "fake signed"
2665 : sig_stat & CERT_F_HASH_OK
2666 ? "well signed"
2667 : 0;
2668 }
2669
2670
2671 ///////////////////////////////////////////////////////////////////////////////
2672
wd_print_sig_status(char * buf,size_t buf_size,wd_part_t * part,bool silent,bool add_enc_info)2673 char * wd_print_sig_status
2674 (
2675 char * buf, // result buffer
2676 // If NULL, a local circulary static buffer is used
2677 size_t buf_size, // size of 'buf', ignored if buf==NULL
2678 wd_part_t * part, // valid disc partition pointer
2679 bool silent, // true: don't print errors while loading data
2680 bool add_enc_info // true: append " Partition is *crypted."
2681 )
2682 {
2683 DASSERT(part);
2684
2685 if (!buf)
2686 buf = GetCircBuf( buf_size = 80 );
2687
2688 ccp enc_info = !add_enc_info
2689 ? ""
2690 : part->is_encrypted
2691 ? " Partition is encrypted"
2692 : " Partition is decrypted";
2693 ccp scrub_info = !add_enc_info
2694 ? ""
2695 : wd_is_part_scrubbed(part,silent)
2696 ? " and scrubbed."
2697 : ".";
2698
2699 // const cert_stat_t mask = CERT_F_HASH_OK | CERT_F_HASH_FAKED | CERT_F_HASH_FAILED;
2700 // const cert_stat_t tik_stat = wd_get_cert_ticket_stat(part,silent) & mask;
2701 // const cert_stat_t tmd_stat = wd_get_cert_tmd_stat(part,silent) & mask;
2702
2703 ccp tik_text = wd_get_sig_status_text(wd_get_cert_ticket_stat(part,silent));
2704 ccp tmd_text = wd_get_sig_status_text(wd_get_cert_tmd_stat(part,silent));
2705
2706 if ( tik_text == tmd_text )
2707 {
2708 if (!tik_text)
2709 snprintf(buf,buf_size,
2710 "Signing of TICKET & TMD is unknown.%s%s", enc_info, scrub_info );
2711 else
2712 snprintf(buf,buf_size,
2713 "TICKET & TMD are %s.%s%s", tik_text, enc_info, scrub_info );
2714 }
2715 else
2716 {
2717 if (!tik_text)
2718 snprintf(buf,buf_size,
2719 "Signing of TICKET is unknown. TMD is %s.%s%s",
2720 tmd_text, enc_info, scrub_info );
2721 else if (!tmd_text)
2722 snprintf(buf,buf_size,
2723 "TICKET is %s. Signing of TMD is unknown.%s%s",
2724 tik_text, enc_info, scrub_info );
2725 else
2726 {
2727 DASSERT( tik_text && tmd_text );
2728 snprintf(buf,buf_size,
2729 "TICKET is %s. TMD is %s.%s%s",
2730 tik_text, tmd_text, enc_info, scrub_info );
2731 }
2732 }
2733
2734 return buf;
2735 }
2736
2737 ///////////////////////////////////////////////////////////////////////////////
2738
wd_print_part_status(char * buf,size_t buf_size,wd_part_t * part,bool silent)2739 char * wd_print_part_status
2740 (
2741 char * buf, // result buffer
2742 // If NULL, a local circulary static buffer is used
2743 size_t buf_size, // size of 'buf', ignored if buf==NULL
2744 wd_part_t * part, // valid disc partition pointer
2745 bool silent // true: don't print errors while loading cert
2746 )
2747 {
2748 DASSERT(part);
2749
2750 if (!buf)
2751 buf = GetCircBuf( buf_size = 20 );
2752
2753 if (part->is_enabled)
2754 {
2755 ccp enc_info = part->is_encrypted ? "enc" : "dec";
2756 ccp sign_info = wd_get_sig_status_short_text
2757 ( wd_get_cert_ticket_stat(part,silent)
2758 | wd_get_cert_tmd_stat(part,silent));
2759 ccp scrub_info = wd_is_part_scrubbed(part,silent) ? ",scrub" : "";
2760 if ( part->sector_stat & WD_SS_F_SKELETON )
2761 scrub_info = ",skel";
2762 snprintf(buf,buf_size,"%s,%s%s",enc_info,sign_info,scrub_info);
2763 }
2764 else
2765 snprintf(buf,buf_size,"disabled");
2766
2767 return buf;
2768 }
2769
2770 ///////////////////////////////////////////////////////////////////////////////
2771
wd_calc_part_status(wd_part_t * part,bool silent)2772 enumError wd_calc_part_status
2773 (
2774 wd_part_t * part, // valid pointer to a disc partition
2775 bool silent // true: don't print error messages
2776 )
2777 {
2778 DASSERT(part);
2779 DASSERT(part->disc);
2780
2781 const enumError err = wd_load_part(part,true,false,silent);
2782 wd_get_cert_ticket_stat(part,silent);
2783 wd_get_cert_tmd_stat(part,silent);
2784 wd_is_part_scrubbed(part,silent);
2785 return err;
2786 }
2787
2788 ///////////////////////////////////////////////////////////////////////////////
2789
wd_calc_disc_status(wd_disc_t * disc,bool silent)2790 enumError wd_calc_disc_status
2791 (
2792 wd_disc_t * disc, // valid disc pointer
2793 bool silent // true: don't print error messages
2794 )
2795 {
2796 DASSERT(disc);
2797
2798 int ip;
2799 enumError max_err = ERR_OK;
2800 for ( ip = 0; ip < disc->n_part; ip++ )
2801 {
2802 const enumError err = wd_calc_part_status(disc->part+ip,silent);
2803 if ( max_err < err )
2804 max_err = err;
2805 }
2806
2807 disc->scrub_test_done = true;
2808 return max_err;
2809 }
2810
2811 //
2812 ///////////////////////////////////////////////////////////////////////////////
2813 /////////////// select partitions ///////////////
2814 ///////////////////////////////////////////////////////////////////////////////
2815
wd_initialize_select(wd_select_t * select)2816 void wd_initialize_select
2817 (
2818 wd_select_t * select // valid pointer to a partition selector
2819 )
2820 {
2821 DASSERT(select);
2822 memset(select,0,sizeof(*select));
2823 }
2824
2825 ///////////////////////////////////////////////////////////////////////////////
2826
wd_reset_select(wd_select_t * select)2827 void wd_reset_select
2828 (
2829 wd_select_t * select // valid pointer to a partition selector
2830 )
2831 {
2832 DASSERT(select);
2833 FREE(select->list);
2834 memset(select,0,sizeof(*select));
2835 }
2836
2837 ///////////////////////////////////////////////////////////////////////////////
2838
wd_append_select_item(wd_select_t * select,wd_select_mode_t mode,u32 table,u32 part)2839 wd_select_item_t * wd_append_select_item
2840 (
2841 wd_select_t * select, // valid pointer to a partition selector
2842 wd_select_mode_t mode, // select mode of new item
2843 u32 table, // partition table of new item
2844 u32 part // partition type or index of new item
2845 )
2846 {
2847 DASSERT(select);
2848 if ( select->used == select->size )
2849 {
2850 select->size += 10;
2851 select->list = REALLOC(select->list, select->size*sizeof(*select->list));
2852 }
2853
2854 DASSERT( select->used < select->size );
2855 wd_select_item_t * item = select->list + select->used++;
2856 item->mode = mode;
2857 item->table = table;
2858 item->part = part;
2859
2860 return item;
2861 }
2862
2863 ///////////////////////////////////////////////////////////////////////////////
2864
wd_copy_select(wd_select_t * dest,const wd_select_t * source)2865 void wd_copy_select
2866 (
2867 wd_select_t * dest, // valid pointer to a partition selector
2868 const wd_select_t * source // NULL or pointer to a partition selector
2869 )
2870 {
2871 DASSERT(dest);
2872 wd_reset_select(dest);
2873 if (source)
2874 {
2875 memcpy(dest,source,sizeof(*dest));
2876 dest->size = source->used;
2877 if (dest->size)
2878 {
2879 const int list_size = dest->size * sizeof(*dest->list);
2880 dest->list = MALLOC(list_size);
2881 memcpy(dest->list,source->list,list_size);
2882 }
2883 }
2884 }
2885
2886 ///////////////////////////////////////////////////////////////////////////////
2887
wd_move_select(wd_select_t * dest,wd_select_t * source)2888 void wd_move_select
2889 (
2890 wd_select_t * dest, // valid pointer to a partition selector
2891 wd_select_t * source // NULL or pointer to a partition selector
2892 )
2893 {
2894 DASSERT(dest);
2895 if ( source != dest )
2896 {
2897 wd_reset_select(dest);
2898 if (source)
2899 {
2900 memmove(dest,source,sizeof(*dest));
2901 wd_initialize_select(source);
2902 }
2903 }
2904 }
2905
2906 ///////////////////////////////////////////////////////////////////////////////
2907
wd_print_select(FILE * f,int indent,wd_select_t * select)2908 void wd_print_select
2909 (
2910 FILE * f, // valid output file
2911 int indent, // indention of the output
2912 wd_select_t * select // valid pointer to a partition selector
2913 )
2914 {
2915 DASSERT(f);
2916 DASSERT(select);
2917
2918 indent = wd_normalize_indent(indent);
2919
2920 if ( select->whole_disc )
2921 fprintf(f,"%*sFLAG: Copy whole disc (=raw mode), ignore all others.\n", indent, "" );
2922
2923 if ( select->whole_part )
2924 fprintf(f,"%*sFLAG: Copy whole partitions.\n", indent, "" );
2925
2926 bool default_deny = false;
2927 const wd_select_item_t * item = select->list;
2928 const wd_select_item_t * end = item + select->used;
2929 for ( ; item < end; item++ )
2930 {
2931 bool allow = !( item->mode & WD_SM_F_DENY );
2932 ccp verb = allow ? "ALLOW" : "DENY ";
2933 switch ( item->mode & WD_SM_M_MODE )
2934 {
2935 case WD_SM_ALLOW_PTYPE:
2936 fprintf(f,"%*s%s partition type %s\n",
2937 indent, "", verb,
2938 wd_print_part_name(0,0,item->part,WD_PNAME_NUM_INFO) );
2939 default_deny = allow;
2940 break;
2941
2942 case WD_SM_ALLOW_PTAB:
2943 fprintf(f,"%*s%s partition table %u (ignored for GameCube partitions)\n",
2944 indent, "", verb, item->table );
2945 default_deny = allow;
2946 break;
2947
2948 case WD_SM_ALLOW_INDEX:
2949 fprintf(f,"%*s%s partition index #%u\n",
2950 indent, "", verb, item->part );
2951 default_deny = allow;
2952 break;
2953
2954 case WD_SM_ALLOW_LT_INDEX:
2955 fprintf(f,"%*s%s partition index < #%u\n",
2956 indent, "", verb, item->part );
2957 default_deny = allow;
2958 break;
2959
2960 case WD_SM_ALLOW_GT_INDEX:
2961 fprintf(f,"%*s%s partition index > #%u\n",
2962 indent, "", verb, item->part );
2963 default_deny = allow;
2964 break;
2965
2966 case WD_SM_ALLOW_PTAB_INDEX:
2967 fprintf(f,"%*s%s partition index #%u.%u (ignored for GameCube partitions)\n",
2968 indent, "", verb, item->table, item->part );
2969 default_deny = allow;
2970 break;
2971
2972 case WD_SM_ALLOW_ID:
2973 fprintf(f,"%*s%s ID partitions.\n",
2974 indent, "", verb );
2975 default_deny = allow;
2976 break;
2977
2978 case WD_SM_ALLOW_GC_BOOT:
2979 fprintf(f,"%*s%s GameCube boot partition (ignored for Wii partitions)\n",
2980 indent, "", verb );
2981 default_deny = allow;
2982 break;
2983
2984 case WD_SM_ALLOW_ALL:
2985 fprintf(f,"%*s%s all partitions.\n", indent, "", verb );
2986 default_deny = allow;
2987 break;
2988 }
2989 }
2990 fprintf(f,"%*s%s all partitions (default rule)\n",
2991 indent, "", default_deny ? "DENY " : "ALLOW" );
2992 }
2993
2994 ///////////////////////////////////////////////////////////////////////////////
2995 ///////////////////////////////////////////////////////////////////////////////
2996
wd_is_part_selected(wd_part_t * part,const wd_select_t * select)2997 bool wd_is_part_selected
2998 (
2999 wd_part_t * part, // valid pointer to a disc partition
3000 const wd_select_t * select // NULL or pointer to a partition selector
3001 )
3002 {
3003 DASSERT(part);
3004
3005 if ( !select || part->is_gc && !( part->disc->disc_attrib & WD_DA_GC_MULTIBOOT ) )
3006 return true;
3007
3008 bool allow = false;
3009 const wd_select_item_t * item = select->list;
3010 const wd_select_item_t * end = item + select->used;
3011 for ( ; item < end; item++ )
3012 {
3013 int match;
3014 switch ( item->mode & WD_SM_M_MODE )
3015 {
3016 case WD_SM_ALLOW_PTYPE:
3017 match = item->part == part->part_type;
3018 break;
3019
3020 case WD_SM_ALLOW_PTAB:
3021 match = part->is_gc ? -1 : item->table == part->ptab_index;
3022 break;
3023
3024 case WD_SM_ALLOW_INDEX:
3025 match = part->index == item->part;
3026 break;
3027
3028 case WD_SM_ALLOW_LT_INDEX:
3029 match = part->index < item->part;
3030 break;
3031
3032 case WD_SM_ALLOW_GT_INDEX:
3033 match = part->index > item->part;
3034 break;
3035
3036 case WD_SM_ALLOW_PTAB_INDEX:
3037 match = part->is_gc
3038 ? -1
3039 : item->table == part->ptab_index
3040 && item->part == part->ptab_part_index;
3041 break;
3042
3043 case WD_SM_ALLOW_ID:
3044 {
3045 ccp d = (ccp)&part->part_type;
3046 match = ISALNUM(d[0]) && ISALNUM(d[1]) && ISALNUM(d[2]) && ISALNUM(d[3]);
3047 }
3048 break;
3049
3050 case WD_SM_ALLOW_GC_BOOT:
3051 match = part->is_gc
3052 ? !part->part_off4 && part->disc->disc_attrib & WD_DA_GC_MULTIBOOT
3053 : -1;
3054 break;
3055
3056 case WD_SM_ALLOW_ALL:
3057 match = 1;
3058 break;
3059
3060 default:
3061 match = -1;
3062 }
3063
3064 if ( match >= 0 )
3065 {
3066 allow = !( item->mode & WD_SM_F_DENY );
3067 if ( match > 0 )
3068 return allow;
3069 }
3070 }
3071
3072 return !allow;
3073 }
3074
3075 ///////////////////////////////////////////////////////////////////////////////
3076
wd_select(wd_disc_t * disc,const wd_select_t * select)3077 bool wd_select // return true if selection changed
3078 (
3079 wd_disc_t * disc, // valid disc pointer
3080 const wd_select_t * select // NULL or pointer to a partition selector
3081 )
3082 {
3083 DASSERT(disc);
3084
3085 disc->whole_disc = select && select->whole_disc;
3086 disc->whole_part = select && select->whole_part;
3087
3088 bool selection_changed = false;
3089 bool any_part_disabled = false;
3090
3091 int ip;
3092 for ( ip = 0; ip < disc->n_part; ip++ )
3093 {
3094 wd_part_t * part = disc->part + ip;
3095 if (!part->is_loaded)
3096 selection_changed = true;
3097 const bool is_enabled = part->is_enabled;
3098 wd_load_part(part,false,false,false);
3099 part->is_enabled = wd_is_part_selected(part,select);
3100 if ( part->is_enabled != is_enabled )
3101 selection_changed = true;
3102 part->is_ok = part->is_loaded && part->is_valid && part->is_enabled;
3103 if ( !part->is_ok )
3104 any_part_disabled = true;
3105 }
3106
3107 wd_calc_fst_statistics(disc,false);
3108 disc->patch_ptab_recommended = any_part_disabled && !disc->whole_disc;
3109
3110 noPRINT("wd_select(%p,%p) recom=%d chg=%d\n",
3111 disc, select,
3112 disc->patch_ptab_recommended, selection_changed );
3113 return selection_changed;
3114 }
3115
3116 //
3117 ///////////////////////////////////////////////////////////////////////////////
3118 /////////////// usage table ///////////////
3119 ///////////////////////////////////////////////////////////////////////////////
3120
3121 const char wd_usage_name_tab[256] =
3122 {
3123 ".*ABCDEFGHIJKLMNOPQRSTUVWXYZABCD"
3124 "EFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJ"
3125 "KLMNOPQRSTUVWXYZABCDEFGHIJKLMNOP"
3126 "QRSTUVWXYZABCDEFGHIJKLMNOPQRSTUV"
3127
3128 "?!abcdefghijklmnopqrstuvwxyzabcd"
3129 "efghijklmnopqrstuvwxyzabcdefghij"
3130 "klmnopqrstuvwxyzabcdefghijklmnop"
3131 "qrstuvwxyzabcdefghijklmnopqrstuv"
3132 };
3133
3134 const u8 wd_usage_class_tab[256] =
3135 {
3136 0,1,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2,
3137 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2,
3138 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2,
3139 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2, 2,2,2,2,
3140
3141 4,5,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3,
3142 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3,
3143 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3,
3144 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3, 3,3,3,3
3145 };
3146
3147 ///////////////////////////////////////////////////////////////////////////////
3148
wd_calc_usage_table(wd_disc_t * disc)3149 u8 * wd_calc_usage_table
3150 (
3151 wd_disc_t * disc // valid disc partition pointer
3152 )
3153 {
3154 DASSERT(disc);
3155 wd_load_all_part(disc,false,false,false);
3156 return disc->usage_table;
3157 }
3158
3159 ///////////////////////////////////////////////////////////////////////////////
3160
wd_filter_usage_table(wd_disc_t * disc,u8 * usage_table,const wd_select_t * select)3161 u8 * wd_filter_usage_table
3162 (
3163 wd_disc_t * disc, // valid disc pointer
3164 u8 * usage_table, // NULL or result. If NULL -> MALLOC()
3165 const wd_select_t * select // NULL or a new selector
3166 )
3167 {
3168 TRACE("wd_filter_usage_table()\n");
3169
3170 if (select)
3171 wd_select(disc,select);
3172
3173 wd_calc_usage_table(disc);
3174
3175 if (!usage_table)
3176 usage_table = MALLOC(WII_MAX_SECTORS);
3177
3178 memcpy(usage_table,disc->usage_table,WII_MAX_SECTORS);
3179
3180 u8 transform[0x100];
3181 memset( transform, disc->whole_disc ? WD_USAGE_DISC : WD_USAGE_UNUSED, sizeof(transform) );
3182 transform[WD_USAGE_DISC] = WD_USAGE_DISC;
3183
3184 disc->patch_ptab_recommended = false;
3185
3186 int ip;
3187 for ( ip = 0; ip < disc->n_part; ip++ )
3188 {
3189 wd_part_t * part = disc->part + ip;
3190
3191 if ( part->is_valid && part->is_enabled )
3192 {
3193 u8 val = part->usage_id & WD_USAGE__MASK;
3194 transform[val] = val;
3195 val |= WD_USAGE_F_CRYPT;
3196 transform[val] = val;
3197
3198 if ( disc->whole_part && !part->is_overlay )
3199 {
3200 const u32 first_block = part->data_off4 / WII_SECTOR_SIZE4;
3201 u32 end_block = ( part->ph.data_size4 + WII_SECTOR_SIZE4 - 1 )
3202 / WII_SECTOR_SIZE4 + first_block;
3203 if ( end_block > WII_MAX_SECTORS )
3204 end_block = WII_MAX_SECTORS;
3205
3206 noTRACE("mark %llx+%llx => %x..%x [%x]\n",
3207 (u64)part->data_off4<<2,
3208 (u64)part->ph.data_size4,
3209 first_block, end_block, end_block-first_block );
3210
3211 if ( first_block < end_block )
3212 memset( usage_table + first_block,
3213 part->usage_id | WD_USAGE_F_CRYPT,
3214 end_block - first_block );
3215 }
3216 }
3217 else
3218 disc->patch_ptab_recommended = !disc->whole_disc;
3219 }
3220
3221 u32 n_sect = disc->iso_size
3222 ? ( disc->iso_size + WII_SECTOR_SIZE - 1 ) / WII_SECTOR_SIZE
3223 : WII_MAX_SECTORS;
3224 u8 *ptr, *end = usage_table + ( n_sect < WII_MAX_SECTORS ? n_sect : WII_MAX_SECTORS );
3225 for ( ptr = usage_table; ptr < end; ptr++ )
3226 *ptr = transform[*ptr];
3227
3228 #if HAVE_PRINT0
3229 //wd_print_usage_tab(stdout,2,usage_table,disc->iso_size,false);
3230 //wd_print_usage_tab(stdout,2,usage_table,WII_MAX_DISC_SIZE,false);
3231 #endif
3232
3233 return usage_table;
3234 }
3235
3236 ///////////////////////////////////////////////////////////////////////////////
3237 ///////////////////////////////////////////////////////////////////////////////
3238
wd_pack_disc_usage_table(u8 * dest_table,wd_disc_t * disc,u32 block_size,const wd_select_t * select)3239 u32 wd_pack_disc_usage_table // returns the index if the 'last_used_sector + 1'
3240 (
3241 u8 * dest_table, // valid pointer to destination table
3242 wd_disc_t * disc, // valid pointer to a disc
3243 u32 block_size, // if >1: count every 'block_size'
3244 // continuous sectors as one block
3245 const wd_select_t * select // NULL or a new selector
3246 )
3247 {
3248 if (select)
3249 wd_select(disc,select);
3250 wd_calc_usage_table(disc);
3251 return wd_pack_usage_table(dest_table,disc->usage_table,block_size);
3252 }
3253
3254 ///////////////////////////////////////////////////////////////////////////////
3255
wd_pack_usage_table(u8 * dest_table,const u8 * usage_table,u32 block_size)3256 u32 wd_pack_usage_table // returns the index if the 'last_used_sector + 1'
3257 (
3258 u8 * dest_table, // valid pointer to destination table
3259 const u8 * usage_table, // valid pointer to usage table
3260 u32 block_size // if >1: count every 'block_size'
3261 // continuous sectors as one block
3262 )
3263 {
3264 DASSERT(dest_table);
3265 DASSERT(usage_table);
3266
3267 const u8 * end_tab = usage_table + WII_MAX_SECTORS;
3268 u8 * dest = dest_table;
3269
3270 if ( block_size <= 1 )
3271 {
3272 // optimization for single block count
3273
3274 for ( ; usage_table < end_tab; usage_table++ )
3275 if ( *usage_table )
3276 *dest++ = *usage_table;
3277 }
3278 else
3279 {
3280 //----- block_size > 1
3281
3282 if ( block_size < WII_MAX_SECTORS )
3283 {
3284 //----- process all but last block
3285
3286 end_tab -= block_size;
3287 for ( ; usage_table < end_tab; usage_table += block_size )
3288 {
3289 int i;
3290 for ( i = 0; i < block_size; i++ )
3291 if (usage_table[i])
3292 {
3293 memcpy(dest,usage_table,block_size);
3294 dest += block_size;
3295 break;
3296 }
3297 }
3298 end_tab += block_size;
3299 }
3300
3301 //----- process last blocks
3302
3303 const u8 * ptr = usage_table;
3304 while ( ptr < end_tab )
3305 if ( *ptr++ )
3306 {
3307 block_size = end_tab - usage_table;
3308 memcpy(dest,usage_table,block_size);
3309 dest += block_size;
3310 break;
3311 }
3312
3313 //---- find last used sector
3314
3315 while ( dest > dest_table && !dest[-1] )
3316 dest--;
3317 }
3318
3319 memset(dest,0,dest_table+WII_MAX_SECTORS-dest);
3320 return dest - dest_table;
3321 }
3322
3323 ///////////////////////////////////////////////////////////////////////////////
3324 ///////////////////////////////////////////////////////////////////////////////
3325
wd_count_used_disc_size(wd_disc_t * disc,int block_size,const wd_select_t * select)3326 u64 wd_count_used_disc_size
3327 (
3328 wd_disc_t * disc, // valid pointer to a disc
3329 int block_size, // if >1: count every 'block_size'
3330 // continuous sectors as one block
3331 // and return the block count
3332 // if <0: like >1, but give the result as multiple
3333 // of WII_SECTOR_SIZE and reduce the count
3334 // for non needed sectors at the end.
3335 const wd_select_t * select // NULL or a new selector
3336 )
3337 {
3338 return wd_count_used_disc_blocks(disc,block_size,select)
3339 * (u64)block_size
3340 * (u64)WII_SECTOR_SIZE;
3341 }
3342
3343 ///////////////////////////////////////////////////////////////////////////////
3344
wd_count_used_disc_blocks(wd_disc_t * disc,int block_size,const wd_select_t * select)3345 u32 wd_count_used_disc_blocks
3346 (
3347 wd_disc_t * disc, // valid pointer to a disc
3348 int block_size, // if >1: count every 'block_size'
3349 // continuous sectors as one block
3350 // and return the block count
3351 // if <0: like >1, but give the result as multiple
3352 // of WII_SECTOR_SIZE and reduce the count
3353 // for non needed sectors at the end.
3354 const wd_select_t * select // NULL or a new selector
3355 )
3356 {
3357 u8 utab[WII_MAX_SECTORS];
3358 wd_filter_usage_table(disc,utab,select);
3359 return wd_count_used_blocks(utab,block_size);
3360 }
3361
3362 ///////////////////////////////////////////////////////////////////////////////
3363
wd_count_used_blocks(const u8 * usage_table,int block_size)3364 u32 wd_count_used_blocks
3365 (
3366 const u8 * usage_table, // valid pointer to usage table
3367 int block_size // if >1: count every 'block_size'
3368 // continuous sectors as one block
3369 // and return the block count
3370 // if <0: like >1, but give the result as multiple
3371 // of WII_SECTOR_SIZE and reduce the count
3372 // for non needed sectors at the end.
3373 )
3374 {
3375 DASSERT(usage_table);
3376
3377 const u8 * end_tab = usage_table + WII_MAX_SECTORS;
3378 u32 count = 0;
3379
3380 const bool return_wii_sectors = block_size < 0;
3381 if (return_wii_sectors)
3382 block_size = -block_size;
3383 TRACE("wd_count_used_blocks() => %d,%d\n",return_wii_sectors,block_size);
3384
3385 if ( block_size > 1 )
3386 {
3387 //----- find last used sector
3388
3389 end_tab--;
3390 while ( end_tab >= usage_table && !*end_tab )
3391 end_tab--;
3392 end_tab++;
3393
3394 //----- count all but last block
3395
3396 if ( block_size < end_tab - usage_table )
3397 {
3398 end_tab -= block_size;
3399
3400 for ( ; usage_table < end_tab; usage_table += block_size )
3401 {
3402 int i;
3403 for ( i = 0; i < block_size; i++ )
3404 if (usage_table[i])
3405 {
3406 count++;
3407 break;
3408 }
3409 }
3410
3411 end_tab += block_size;
3412 }
3413
3414 //----- count last block
3415
3416 if (return_wii_sectors)
3417 count = count * block_size + ( end_tab - usage_table );
3418 else if ( usage_table < end_tab )
3419 count++;
3420 }
3421 else
3422 {
3423 // optimization for single block count
3424
3425 while ( usage_table < end_tab )
3426 if ( *usage_table++ )
3427 count++;
3428 }
3429
3430 return count;
3431 }
3432
3433 ///////////////////////////////////////////////////////////////////////////////
3434
wd_is_block_used(const u8 * usage_table,u32 block_index,u32 block_size)3435 bool wd_is_block_used
3436 (
3437 const u8 * usage_table, // valid pointer to usage table
3438 u32 block_index, // index of block
3439 u32 block_size // if >1: number of sectors per block
3440 )
3441 {
3442 DASSERT(usage_table);
3443
3444 if ( block_size <= 1 )
3445 return block_index < WII_MAX_SECTORS && usage_table[block_index];
3446
3447 block_index *= block_size;
3448 u32 end = block_index + block_size;
3449 if ( end > WII_MAX_SECTORS )
3450 end = WII_MAX_SECTORS;
3451
3452 while ( block_index < end )
3453 if ( usage_table[block_index++] )
3454 return true;
3455
3456 return false;
3457 }
3458
3459 //
3460 ///////////////////////////////////////////////////////////////////////////////
3461 /////////////// file iteration ///////////////
3462 ///////////////////////////////////////////////////////////////////////////////
3463
wd_iterate_fst_helper(wd_iterator_t * it,const wd_fst_item_t * fst_base,wd_file_func_t func,wd_part_t * sys_files,int ignore_files,wd_file_func_t exec_func,bool is_gc)3464 static int wd_iterate_fst_helper
3465 (
3466 wd_iterator_t * it, // valid pointer to iterator data
3467 const wd_fst_item_t *fst_base, // NULL or pointer to FST data
3468 wd_file_func_t func, // call back function
3469 wd_part_t * sys_files, // not NULL: process sys files too
3470 int ignore_files, // >0: ignore all real files
3471 // >1: ignore fst.bin + main.dol too
3472 wd_file_func_t exec_func, // NULL or call back function
3473 // that is called if func() returns 1
3474 bool is_gc // true if FST is GC formatted
3475 )
3476 {
3477 DASSERT(it);
3478 DASSERT(func);
3479
3480 if (!fst_base)
3481 return 0;
3482
3483 int stat = 0, mod = 0;
3484 it->fst_item = 0;
3485
3486 if (sys_files)
3487 {
3488 it->icm = WD_ICM_DIRECTORY;
3489 it->off4 = 0;
3490 it->size = 5;
3491 it->data = 0;
3492 strcpy(it->fst_name,"sys/");
3493 stat = func(it);
3494 if ( stat == 1 && exec_func )
3495 {
3496 mod = 1;
3497 exec_func(it);
3498 }
3499 else if (stat)
3500 return stat;
3501
3502 it->icm = WD_ICM_FILE;
3503 it->off4 = is_gc ? WII_BOOT_OFF : WII_BOOT_OFF >> 2;
3504 it->size = WII_BOOT_SIZE;
3505 DASSERT(!it->data);
3506 strcpy(it->fst_name,"sys/boot.bin");
3507 stat = func(it);
3508 if ( stat == 1 && exec_func )
3509 {
3510 mod = 1;
3511 exec_func(it);
3512 }
3513 else if (stat)
3514 return stat;
3515
3516 DASSERT( it->icm == WD_ICM_FILE );
3517 it->off4 = is_gc ? WII_BI2_OFF : WII_BI2_OFF >> 2;
3518 it->size = WII_BI2_SIZE;
3519 DASSERT(!it->data);
3520 strcpy(it->fst_name,"sys/bi2.bin");
3521 stat = func(it);
3522 if ( stat == 1 && exec_func )
3523 {
3524 mod = 1;
3525 exec_func(it);
3526 }
3527 else if (stat)
3528 return stat;
3529
3530 DASSERT( it->icm == WD_ICM_FILE );
3531 it->off4 = is_gc ? WII_APL_OFF : WII_APL_OFF >> 2;
3532 it->size = sys_files->apl_size;
3533 DASSERT(!it->data);
3534 strcpy(it->fst_name,"sys/apploader.img");
3535 stat = func(it);
3536 if ( stat == 1 && exec_func )
3537 {
3538 mod = 1;
3539 exec_func(it);
3540 }
3541 else if (stat)
3542 return stat;
3543
3544 if ( ignore_files < 2 )
3545 {
3546 if (sys_files->boot.dol_off4)
3547 {
3548 DASSERT( it->icm == WD_ICM_FILE );
3549 it->off4 = sys_files->boot.dol_off4;
3550 it->size = sys_files->dol_size;
3551 DASSERT(!it->data);
3552 strcpy(it->fst_name,"sys/main.dol");
3553 stat = func(it);
3554 if ( stat == 1 && exec_func )
3555 {
3556 mod = 1;
3557 exec_func(it);
3558 }
3559 else if (stat)
3560 return stat;
3561 }
3562
3563 DASSERT( it->icm == WD_ICM_FILE );
3564 it->off4 = sys_files->boot.fst_off4;
3565 it->size = sys_files->boot.fst_size4 << 2;
3566 DASSERT(!it->data);
3567 strcpy(it->fst_name,"sys/fst.bin");
3568 stat = func(it);
3569 if ( stat == 1 && exec_func )
3570 {
3571 mod = 1;
3572 exec_func(it);
3573 }
3574 else if (stat)
3575 return stat;
3576 }
3577 }
3578
3579 if ( ignore_files < 1 )
3580 {
3581
3582 //----- setup stack
3583
3584 const int MAX_DEPTH = 25; // maximal supported directory depth
3585 typedef struct stack_t
3586 {
3587 const wd_fst_item_t * dir_end;
3588 char * path;
3589 } stack_t;
3590 stack_t stack_buf[MAX_DEPTH];
3591 stack_t *stack = stack_buf;
3592 stack_t *stack_max = stack_buf + MAX_DEPTH;
3593
3594
3595 //----- setup path
3596
3597 const wd_fst_item_t *fst = fst_base;
3598 const int n_fst = ntohl(fst->size);
3599 char *path_end = it->path + sizeof(it->path) - MAX_DEPTH - 1;
3600 it->icm = WD_ICM_DIRECTORY;
3601 it->off4 = 0;
3602 it->size = n_fst-1;
3603 DASSERT(!it->data);
3604 strcpy(it->fst_name,"files/");
3605
3606 stat = func(it);
3607 if ( stat == 1 && exec_func )
3608 {
3609 mod = 1;
3610 exec_func(it);
3611 }
3612 else if (stat)
3613 return stat;
3614
3615
3616 //----- main loop
3617
3618 const wd_fst_item_t *fst_end = fst + n_fst;
3619 const wd_fst_item_t *dir_end = fst_end;
3620 char * path_ptr = it->fst_name + 6;
3621
3622 stat = 0;
3623 for ( fst++; fst < fst_end && !stat; fst++ )
3624 {
3625 while ( fst >= dir_end && stack > stack_buf )
3626 {
3627 // leave a directory
3628 stack--;
3629 dir_end = stack->dir_end;
3630 path_ptr = stack->path;
3631 }
3632
3633 ccp fname = (ccp)fst_end + (ntohl(fst->name_off)&0xffffff);
3634 char * path_dest = path_ptr;
3635 while ( path_dest < path_end && *fname )
3636 {
3637 uchar ch = *fname++;
3638 if ( ch < 0x80 )
3639 *path_dest++ = ch;
3640 else
3641 {
3642 *path_dest++ = ch >> 6 | 0xc0;
3643 *path_dest++ = ch & 0x3f | 0x80;
3644 }
3645 }
3646
3647 it->fst_item = (wd_fst_item_t*)fst;
3648 if (fst->is_dir)
3649 {
3650 *path_dest++ = '/';
3651 *path_dest = 0;
3652
3653 ASSERT(stack<stack_max);
3654 if ( stack < stack_max )
3655 {
3656 stack->dir_end = dir_end;
3657 stack->path = path_ptr;
3658 stack++;
3659 dir_end = fst_base + ntohl(fst->size);
3660 path_ptr = path_dest;
3661 }
3662
3663 it->icm = WD_ICM_DIRECTORY;
3664 it->off4 = 0;
3665 it->size = dir_end-fst-1;
3666 stat = func(it);
3667 }
3668 else
3669 {
3670 *path_dest = 0;
3671 it->icm = WD_ICM_FILE;
3672 it->off4 = ntohl(fst->offset4);
3673 it->size = ntohl(fst->size);
3674 stat = func(it);
3675 }
3676
3677 if ( stat == 1 && exec_func )
3678 {
3679 mod = 1;
3680 stat = 0;
3681 exec_func(it);
3682 }
3683 }
3684 }
3685
3686 it->fst_item = 0;
3687 return stat ? stat : mod;
3688 }
3689
3690 ///////////////////////////////////////////////////////////////////////////////
3691
true_file_func(wd_iterator_t * it)3692 static int true_file_func ( wd_iterator_t *it )
3693 {
3694 return true;
3695 }
3696
3697 ///////////////////////////////////////////////////////////////////////////////
3698
wd_iterate_files(wd_disc_t * disc,wd_file_func_t func,void * param,int ignore_files,wd_ipm_t prefix_mode)3699 int wd_iterate_files
3700 (
3701 wd_disc_t * disc, // valid pointer to a disc
3702 wd_file_func_t func, // call back function
3703 void * param, // user defined parameter
3704 int ignore_files, // >0: ignore all real files
3705 // >1: ignore fst.bin + main.dol too
3706 wd_ipm_t prefix_mode // prefix mode
3707 )
3708 {
3709 DASSERT(disc);
3710 DASSERT(func);
3711
3712 if ( !disc->part || !disc->n_part )
3713 return 0;
3714
3715 wd_load_all_part(disc,false,false,false);
3716 wd_part_t *part, *part_end = disc->part + disc->n_part;
3717
3718 wd_iterator_t it;
3719 memset(&it,0,sizeof(it));
3720 it.param = param;
3721 it.disc = disc;
3722
3723
3724 //----- prefix mode
3725
3726 if ( prefix_mode <= WD_IPM_AUTO )
3727 {
3728 int count = 0;
3729 for ( part = disc->part; part < part_end; part++ )
3730 if ( part->is_valid && part->is_enabled )
3731 {
3732 wd_part_t *part2;
3733 for ( part2 = disc->part; part2 < part; part2++ )
3734 if ( part2->is_valid
3735 && part2->is_enabled
3736 && part2->part_type == part->part_type )
3737 {
3738 prefix_mode = WD_IPM_COMBI;
3739 goto exit_auto_text;
3740 }
3741
3742 if ( part->part_type == WD_PART_DATA )
3743 count++;
3744 else
3745 count += 2;
3746 }
3747 prefix_mode = count == 1 ? WD_IPM_POINT : WD_IPM_PART_NAME;
3748 }
3749 exit_auto_text:
3750 it.prefix_mode = prefix_mode;
3751
3752
3753 //----- iterate partitions
3754
3755 int stat = 0;
3756 for ( part = disc->part; part < part_end && !stat; part++ )
3757 {
3758 if ( !part->is_valid || !part->is_enabled )
3759 continue;
3760
3761 it.part = part;
3762
3763 switch(prefix_mode)
3764 {
3765 case WD_IPM_NONE:
3766 break;
3767
3768 case WD_IPM_SLASH:
3769 strcpy(it.prefix,"/");
3770 break;
3771
3772 case WD_IPM_POINT:
3773 strcpy(it.prefix,"./");
3774 break;
3775
3776 case WD_IPM_PART_ID:
3777 snprintf(it.prefix,sizeof(it.prefix),"P%x/", part->part_type );
3778 break;
3779
3780 case WD_IPM_PART_INDEX:
3781 snprintf(it.prefix,sizeof(it.prefix),"P%u.%u/",
3782 part->ptab_index, part->ptab_part_index );
3783 break;
3784
3785 case WD_IPM_COMBI:
3786 snprintf(it.prefix,sizeof(it.prefix),"P%u.%u-%s/",
3787 part->ptab_index, part->ptab_part_index,
3788 wd_print_part_name(0,0,part->part_type,WD_PNAME_NAME));
3789 break;
3790
3791 //case WD_IPM_PART_NAME:
3792 default:
3793 wd_print_part_name(it.prefix,sizeof(it.prefix)-1,part->part_type,WD_PNAME_P_NAME);
3794 strcat(it.prefix,"/");
3795 break;
3796 }
3797
3798 it.prefix_len = strlen(it.prefix);
3799 strcpy(it.path,it.prefix);
3800 it.fst_name = it.path + it.prefix_len;
3801
3802 it.icm = WD_ICM_OPEN_PART;
3803 const u32 off4 = it.off4 = part->part_off4;
3804 stat = func(&it);
3805 if (stat)
3806 break;
3807
3808
3809 //----- './' files
3810
3811 const wd_part_header_t * ph = &part->ph;
3812
3813 it.icm = WD_ICM_DIRECTORY;
3814 it.off4 = 0;
3815 it.size = part->is_gc
3816 ? SYS_DIR_COUNT_GC + SYS_FILE_COUNT_GC + part->fst_n - 1
3817 : SYS_DIR_COUNT + SYS_FILE_COUNT + part->fst_n - 1;
3818 it.data = 0;
3819 stat = func(&it);
3820 if (stat)
3821 break;
3822
3823 it.icm = WD_ICM_DATA;
3824 it.off4 = 0;
3825 it.size = part->setup_txt_len;
3826 it.data = part->setup_txt;
3827 strcpy(it.fst_name,"setup.txt");
3828 stat = func(&it);
3829 if (stat)
3830 break;
3831
3832 it.size = part->setup_sh_len;
3833 it.data = part->setup_sh;
3834 strcpy(it.fst_name,"setup.sh");
3835 stat = func(&it);
3836 if (stat)
3837 break;
3838
3839 it.size = part->setup_bat_len;
3840 it.data = part->setup_bat;
3841 strcpy(it.fst_name,"setup.bat");
3842 stat = func(&it);
3843 if (stat)
3844 break;
3845
3846 if (!part->is_gc)
3847 {
3848 DASSERT( it.icm == WD_ICM_DATA );
3849 it.off4 = off4;
3850 it.size = sizeof(part->ph.ticket);
3851 it.data = &part->ph.ticket;
3852 strcpy(it.fst_name,"ticket.bin");
3853 stat = func(&it);
3854 if (stat)
3855 break;
3856
3857 DASSERT( it.icm == WD_ICM_DATA );
3858 it.off4 = off4 + ph->tmd_off4;
3859 it.size = ph->tmd_size;
3860 it.data = part->tmd;
3861 strcpy(it.fst_name,"tmd.bin");
3862 stat = func(&it);
3863 if (stat)
3864 break;
3865
3866 it.icm = WD_ICM_COPY;
3867 it.off4 = off4 + ph->cert_off4;
3868 it.size = ph->cert_size;
3869 it.data = 0;
3870 strcpy(it.fst_name,"cert.bin");
3871 stat = func(&it);
3872 if (stat)
3873 break;
3874
3875 DASSERT( it.icm == WD_ICM_COPY );
3876 it.off4 = off4 + ph->h3_off4;
3877 it.size = WII_H3_SIZE;
3878 DASSERT ( !it.data );
3879 strcpy(it.fst_name,"h3.bin");
3880 stat = func(&it);
3881 if (stat)
3882 break;
3883 }
3884
3885 //----- 'disc/' files
3886
3887 if (!part->is_gc)
3888 {
3889 it.icm = WD_ICM_DIRECTORY;
3890 it.off4 = 0;
3891 it.size = 2;
3892 it.data = 0;
3893 strcpy(it.fst_name,"disc/");
3894 stat = func(&it);
3895 if (stat)
3896 break;
3897
3898 it.icm = WD_ICM_DATA;
3899 it.off4 = 0;
3900 it.size = sizeof(disc->dhead);
3901 it.data = &disc->dhead;
3902 strcpy(it.fst_name,"disc/header.bin");
3903 stat = func(&it);
3904 if (stat)
3905 break;
3906
3907 DASSERT( it.icm == WD_ICM_DATA );
3908 it.off4 = WII_REGION_OFF >> 2;
3909 it.size = sizeof(disc->region);
3910 it.data = &disc->region;
3911 strcpy(it.fst_name,"disc/region.bin");
3912 stat = func(&it);
3913 if (stat)
3914 break;
3915 }
3916
3917 //----- SYS + FST files
3918
3919 stat = wd_iterate_fst_helper( &it, part->fst, func,part,
3920 ignore_files, 0, part->is_gc );
3921 if (stat)
3922 break;
3923
3924
3925 //----- WD_ICM_CLOSE_PART
3926
3927 *it.fst_name = 0;
3928 it.icm = WD_ICM_CLOSE_PART;
3929 it.off4 = off4;
3930 it.size = 0;
3931 it.data = 0;
3932 stat = func(&it);
3933 }
3934
3935 return stat;
3936 }
3937
3938 ///////////////////////////////////////////////////////////////////////////////
3939
wd_iterate_fst_files(const wd_fst_item_t * fst_base,wd_file_func_t func,void * param)3940 int wd_iterate_fst_files
3941 (
3942 const wd_fst_item_t *fst_base, // valid pointer to FST data
3943 wd_file_func_t func, // call back function
3944 void * param // user defined parameter
3945 )
3946 {
3947 DASSERT(fst_base);
3948 DASSERT(func);
3949
3950 wd_iterator_t it;
3951 memset(&it,0,sizeof(it));
3952 it.param = param;
3953 it.fst_name = it.path;
3954
3955 return wd_iterate_fst_helper(&it,fst_base,func,0,0,0,0);
3956 }
3957
3958 ///////////////////////////////////////////////////////////////////////////////
3959 ///////////////////////////////////////////////////////////////////////////////
3960
wd_remove_disc_files(wd_disc_t * disc,wd_file_func_t func,void * param,bool calc_usage_tab)3961 int wd_remove_disc_files
3962 (
3963 // Call wd_remove_part_files() for each enabled partition.
3964 // Returns 0 if nothing is removed, 1 if at least one file is removed
3965 // Other values are abort codes from func()
3966
3967 wd_disc_t * disc, // valid pointer to a disc
3968 wd_file_func_t func, // call back function
3969 // return 0 for don't touch file
3970 // return 1 for zero file
3971 // return other for abort
3972 void * param, // user defined parameter
3973 bool calc_usage_tab // true: calc usage table again by using
3974 // wd_select_part_files(func:=NULL)
3975 // if at least one file was removed
3976 )
3977 {
3978 DASSERT(disc);
3979
3980 int stat = 0, mod = 0;
3981 wd_part_t *part, *part_end = disc->part + disc->n_part;
3982 for ( part = disc->part; part < part_end && !stat; part++ )
3983 {
3984 if (part->is_enabled)
3985 {
3986 wd_load_part(part,false,false,false);
3987 if (part->is_ok)
3988 {
3989 stat = wd_remove_part_files(part,func,param,calc_usage_tab);
3990 if ( stat == 1 )
3991 {
3992 stat = 0;
3993 mod = 1;
3994 }
3995 }
3996 }
3997 }
3998 return stat ? stat : mod;
3999 }
4000
4001 ///////////////////////////////////////////////////////////////////////////////
4002
exec_mark_file(wd_iterator_t * it)4003 static int exec_mark_file ( wd_iterator_t *it )
4004 {
4005 DASSERT(it);
4006
4007 if ( it->fst_item )
4008 it->fst_item->is_dir |= 0x40;
4009 return 0;
4010 };
4011
4012 ///////////////////////////////////////////////////////////////////////////////
4013
wd_remove_part_files(wd_part_t * part,wd_file_func_t func,void * param,bool calc_usage_tab)4014 int wd_remove_part_files
4015 (
4016 // Remove files and directories from internal FST copy.
4017 // Only empty directories are removed. If at least 1 files/dir
4018 // is removed the new FST.BIN is added to the patching map.
4019 // Returns 0 if nothing is removed, 1 if at least one file is removed
4020 // Other values are abort codes from func()
4021
4022 wd_part_t * part, // valid pointer to a partition
4023 wd_file_func_t func, // call back function
4024 // return 0 for don't touch file
4025 // return 1 for zero file
4026 // return other for abort
4027 void * param, // user defined parameter
4028 bool calc_usage_tab // true: calc usage table again by using
4029 // wd_select_part_files(func:=NULL)
4030 // if at least one file was removed
4031 )
4032 {
4033 DASSERT(part);
4034
4035 if (!part->fst)
4036 return 0;
4037
4038 wd_iterator_t it;
4039 memset(&it,0,sizeof(it));
4040 it.disc = part->disc;
4041 it.part = part;
4042 it.param = param;
4043 it.fst_name = it.path;
4044
4045 const int stat
4046 = wd_iterate_fst_helper(&it,part->fst,func,0,0,exec_mark_file,part->is_gc);
4047
4048 if ( stat == 1 )
4049 {
4050 //----- setup
4051
4052 wd_fst_item_t * fst = part->fst;
4053 fst->is_dir &= 1; // never remove first entry
4054 int n_fst = ntohl(fst->size);
4055 wd_fst_item_t *fst_end = fst + n_fst;
4056
4057 HEXDUMP16(0,0,fst_end,16);
4058
4059 //----- 1. loop: normalize directories
4060
4061 for ( fst = fst_end-1; fst >= part->fst; fst-- )
4062 if ( fst->is_dir & 1 )
4063 {
4064 //--- transform size into number of dir elemes in host order
4065 int count = 0, i, n = ntohl(fst->size) - (fst - part->fst) - 1;
4066 for ( i = 1; i <= n; i++ )
4067 if ( !(fst[i].is_dir & 0x40 ))
4068 count++;
4069 if (count)
4070 fst->is_dir &= 1;
4071 WDPRINT("#%zu: N=%u/%u [%02x]\n",fst-part->fst,count,n,fst->is_dir);
4072 fst->size = count;
4073 }
4074
4075 //----- 2. loop: move data
4076
4077 u32 name_delta = ( n_fst - part->fst->size - 1 ) * sizeof(*fst);
4078 noTRACE("NAME-DELTA=%x=%u\n",name_delta,name_delta);
4079
4080 wd_fst_item_t * dest;
4081 for ( fst = dest = part->fst; fst < fst_end; fst++ )
4082 {
4083 const u8 is_dir = fst->is_dir;
4084 if ( is_dir & 0x40 )
4085 continue;
4086
4087 WDPRINT("COPY %zu -> %zu size=%zu\n",fst-part->fst,dest-part->fst,sizeof(*dest));
4088 memcpy(dest,fst,sizeof(*dest));
4089
4090 WDPRINT("NAME-OFF: %x -> %x\n",
4091 ntohl(dest->name_off) & 0xffffff,
4092 ( ntohl(dest->name_off) & 0xffffff ) + name_delta );
4093
4094 dest->name_off = htonl( ( ntohl(dest->name_off) & 0xffffff ) + name_delta );
4095 if ( is_dir )
4096 {
4097 dest->is_dir = 1;
4098 dest->size = htonl( dest->size + (dest-part->fst) + 1 );
4099 }
4100 dest++;
4101 }
4102
4103 WDPRINT("N: %u -> %zu\n", part->fst_n, dest - part->fst);
4104 part->fst_n = dest - part->fst;
4105 DASSERT( part->fst->size = htonl(part->fst_n) );
4106 part->fst->name_off = htonl(0);
4107 part->fst->is_dir = 1;
4108
4109 WDPRINT("fst_end=%p new_end=%p, n=%u->%u\n",
4110 fst_end, part->fst + part->fst_n, n_fst, part->fst_n );
4111 HEXDUMP16(0,0,fst_end,16);
4112 HEXDUMP16(0,0, (ccp)( part->fst + part->fst_n ) + name_delta,16);
4113
4114 //----- insert patch
4115
4116 wd_insert_patch_fst(part);
4117 if (calc_usage_tab)
4118 wd_select_part_files(part,0,0);
4119 }
4120
4121 return stat;
4122 }
4123
4124 ///////////////////////////////////////////////////////////////////////////////
4125 ///////////////////////////////////////////////////////////////////////////////
4126
wd_zero_disc_files(wd_disc_t * disc,wd_file_func_t func,void * param,bool calc_usage_tab)4127 int wd_zero_disc_files
4128 (
4129 // Call wd_remove_part_files() for each enabled partition.
4130 // Returns 0 if nothing is zeroed, 1 if at least one file is zeroed
4131 // Other values are abort codes from func()
4132
4133 wd_disc_t * disc, // valid pointer to a disc
4134 wd_file_func_t func, // call back function
4135 // return 0 for don't touch file
4136 // return 1 for zero file
4137 // return other for abort
4138 void * param, // user defined parameter
4139 bool calc_usage_tab // true: calc usage table again by using
4140 // wd_select_part_files(func:=NULL)
4141 // if at least one file was zeroed
4142 )
4143 {
4144 DASSERT(disc);
4145 DASSERT(func);
4146
4147 int stat = 0, mod = 0;
4148 wd_part_t *part, *part_end = disc->part + disc->n_part;
4149 for ( part = disc->part; part < part_end && !stat; part++ )
4150 {
4151 if (part->is_enabled)
4152 {
4153 wd_load_part(part,false,false,false);
4154 if (part->is_ok)
4155 {
4156 stat = wd_zero_part_files(part,func,param,calc_usage_tab);
4157 if ( stat == 1 )
4158 {
4159 stat = 0;
4160 mod = 1;
4161 }
4162 }
4163 }
4164 }
4165 return stat ? stat : mod;
4166 }
4167
4168 ///////////////////////////////////////////////////////////////////////////////
4169
exec_zero_file(wd_iterator_t * it)4170 static int exec_zero_file ( wd_iterator_t *it )
4171 {
4172 DASSERT(it);
4173
4174 if ( it->icm == WD_ICM_FILE )
4175 {
4176 DASSERT_MSG(it->fst_item,"%s",it->path);
4177 const u32 zero = htonl(0);
4178 it->fst_item->offset4 = zero;
4179 it->fst_item->size = zero;
4180 }
4181
4182 return 0;
4183 };
4184
4185 ///////////////////////////////////////////////////////////////////////////////
4186
wd_zero_part_files(wd_part_t * part,wd_file_func_t func,void * param,bool calc_usage_tab)4187 int wd_zero_part_files
4188 (
4189 // Zero files from internal FST copy (set offset and size to NULL).
4190 // If at least 1 file is zeroed the new FST.BIN is added to the patching map.
4191 // Returns 0 if nothing is removed, 1 if at least one file is removed
4192 // Other values are abort codes from func()
4193
4194 wd_part_t * part, // valid pointer to a partition
4195 wd_file_func_t func, // call back function
4196 // return 0 for don't touch file
4197 // return 1 for zero file
4198 // return other for abort
4199 void * param, // user defined parameter
4200 bool calc_usage_tab // true: calc usage table again by using
4201 // wd_select_part_files(func:=NULL)
4202 // if at least one file was zeroed
4203 )
4204 {
4205 DASSERT(part);
4206
4207 if (!part->fst)
4208 return 0;
4209
4210 wd_iterator_t it;
4211 memset(&it,0,sizeof(it));
4212 it.disc = part->disc;
4213 it.part = part;
4214 it.param = param;
4215 it.fst_name = it.path;
4216
4217 const int stat
4218 = wd_iterate_fst_helper(&it,part->fst,func,0,0,exec_zero_file,part->is_gc);
4219 if ( stat == 1 )
4220 {
4221 wd_insert_patch_fst(part);
4222 if (calc_usage_tab)
4223 wd_select_part_files(part,0,0);
4224 }
4225
4226 return stat;
4227 }
4228
4229 ///////////////////////////////////////////////////////////////////////////////
4230 ///////////////////////////////////////////////////////////////////////////////
4231
wd_select_disc_files(wd_disc_t * disc,wd_file_func_t func,void * param)4232 int wd_select_disc_files
4233 (
4234 // Call wd_remove_part_files() for each enabled partition.
4235 // Returns 0 if nothing is ignored, 1 if at least one file is ignored
4236 // Other values are abort codes from func()
4237
4238 wd_disc_t * disc, // valid pointer to a disc
4239 wd_file_func_t func, // call back function
4240 // return 0 for don't touch file
4241 // return 1 for zero file
4242 // return other for abort
4243 void * param // user defined parameter
4244 )
4245 {
4246 DASSERT(disc);
4247
4248 int stat = 0, mod = 0;
4249 wd_part_t *part, *part_end = disc->part + disc->n_part;
4250 for ( part = disc->part; part < part_end && !stat; part++ )
4251 {
4252 if (part->is_enabled)
4253 {
4254 wd_load_part(part,false,false,false);
4255 if (part->is_ok)
4256 {
4257 stat = wd_select_part_files(part,func,param);
4258 if ( stat == 1 )
4259 {
4260 stat = 0;
4261 mod = 1;
4262 }
4263 }
4264 }
4265 }
4266
4267 return stat ? stat : mod;
4268 }
4269
4270 ///////////////////////////////////////////////////////////////////////////////
4271
exec_select_file(wd_iterator_t * it)4272 static int exec_select_file ( wd_iterator_t *it )
4273 {
4274 DASSERT(it);
4275 if ( it->icm == WD_ICM_FILE )
4276 wd_mark_part(it->part,it->off4,it->size);
4277 return 0;
4278 };
4279
4280 ///////////////////////////////////////////////////////////////////////////////
4281
wd_select_part_files(wd_part_t * part,wd_file_func_t func,void * param)4282 int wd_select_part_files
4283 (
4284 // Calculate the usage map for the partition by using the internal
4285 // and perhaps modified FST.BIN. If func() is not NULL ignore system
4286 // and real files (/sys/... and /files/...) for return value 1.
4287 // If at least 1 file is zeroed the new FST.BIN is added to the patching map.
4288 // Returns 0 if nothing is removed, 1 if at least one file is removed
4289 // Other values are abort codes from func()
4290
4291 wd_part_t * part, // valid pointer to a partition
4292 wd_file_func_t func, // call back function
4293 // If NULL nor file is ignored and only
4294 // the usage map is re calculated.
4295 // return 0 for don't touch file
4296 // return 1 for zero file
4297 // return other for abort
4298 void * param // user defined parameter
4299 )
4300 {
4301 DASSERT(part);
4302 DASSERT(part->disc);
4303
4304 if (!part->fst)
4305 return 0;
4306
4307 if (!func)
4308 func = true_file_func;
4309
4310 //----- reset usage table
4311
4312 const u8 usage_id = part->usage_id | WD_USAGE_F_CRYPT;
4313 u8 * utab = part->disc->usage_table;
4314 u32 sector;
4315 for ( sector = part->data_sector; sector < part->end_sector; sector++ )
4316 if ( utab[sector] == usage_id )
4317 utab[sector] = WD_USAGE_UNUSED;
4318
4319 //----- mark needed system files
4320
4321 wd_mark_part( part, WII_BOOT_OFF>>2, WII_BOOT_SIZE ); // boot.bin
4322 wd_mark_part( part, WII_BI2_OFF>>2, WII_BI2_SIZE ); // bi2.bin
4323 wd_mark_part( part, WII_APL_OFF>>2, part->apl_size ); // apploader.img
4324 wd_mark_part( part, part->boot.dol_off4, part->dol_size ); // main.dol
4325 wd_mark_part( part, part->boot.fst_off4,
4326 part->boot.fst_size4 << 2 ); // fst.bin
4327
4328
4329 //----- call iterator for files/...
4330
4331 wd_iterator_t it;
4332 memset(&it,0,sizeof(it));
4333 it.disc = part->disc;
4334 it.part = part;
4335 it.param = param;
4336 it.fst_name = it.path;
4337
4338 return wd_iterate_fst_helper(&it,part->fst,func,0,0,exec_select_file,part->is_gc);
4339 }
4340
4341 //
4342 ///////////////////////////////////////////////////////////////////////////////
4343 /////////////// print files ///////////////
4344 ///////////////////////////////////////////////////////////////////////////////
4345
4346 const char wd_sep_200[201] = // 200 * '-' + NULL
4347 "--------------------------------------------------"
4348 "--------------------------------------------------"
4349 "--------------------------------------------------"
4350 "--------------------------------------------------";
4351
4352 ///////////////////////////////////////////////////////////////////////////////
4353
wd_initialize_print_fst(wd_print_fst_t * pf,wd_pfst_t mode,FILE * f,int indent,u32 max_off4,u32 max_size)4354 void wd_initialize_print_fst
4355 (
4356 wd_print_fst_t * pf, // valid pointer
4357 wd_pfst_t mode, // mode for setup
4358 FILE * f, // NULL or output file
4359 int indent, // indention of the output
4360 u32 max_off4, // NULL or maximal offset4 value of all files
4361 u32 max_size // NULL or maximal size value of all files
4362 )
4363 {
4364 DASSERT(pf);
4365 memset(pf,0,sizeof(*pf));
4366
4367 pf->f = f ? f : stdout;
4368 pf->indent = wd_normalize_indent(indent);
4369 pf->mode = mode;
4370
4371 char buf[50];
4372 const int fw_offset = max_off4 > 0
4373 ? snprintf(buf,sizeof(buf),"%llx",(u64)max_off4<<2)
4374 : 9;
4375
4376 if ( mode & WD_PFST_UNUSED )
4377 pf->fw_unused = fw_offset;
4378
4379 if ( mode & WD_PFST_OFFSET )
4380 pf->fw_offset = fw_offset;
4381
4382 if ( mode & WD_PFST_SIZE_HEX )
4383 pf->fw_size_hex = max_size > 0
4384 ? snprintf(buf,sizeof(buf),"%x",max_size)
4385 : 7;
4386
4387 if ( mode & WD_PFST_SIZE_DEC )
4388 pf->fw_size_dec = max_size > 0
4389 ? snprintf(buf,sizeof(buf),"%u",max_size)
4390 : 8;
4391 }
4392
4393 ///////////////////////////////////////////////////////////////////////////////
4394
wd_print_fst_header(wd_print_fst_t * pf,int max_name_len)4395 void wd_print_fst_header
4396 (
4397 wd_print_fst_t * pf, // valid pointer
4398 int max_name_len // max name len, needed for separator line
4399 )
4400 {
4401 ASSERT(pf);
4402 ASSERT(pf->f);
4403 TRACE("wd_print_fst_header()\n");
4404
4405 pf->indent = wd_normalize_indent(pf->indent);
4406 if ( max_name_len < 11 )
4407 max_name_len = 11;
4408
4409 //----- first header line
4410
4411 fprintf(pf->f,"%*s",pf->indent,"");
4412
4413 if ( pf->fw_unused )
4414 {
4415 if ( pf->fw_unused < 6 )
4416 pf->fw_unused = 6;
4417 fprintf(pf->f,"%*s ",pf->fw_unused,"unused");
4418 max_name_len += 1 + pf->fw_unused;
4419 }
4420
4421 if ( pf->fw_offset )
4422 {
4423 if ( pf->fw_offset < 6 )
4424 pf->fw_offset = 6;
4425 fprintf(pf->f,"%*s ",pf->fw_offset,"offset");
4426 max_name_len += 2 + pf->fw_offset;
4427 }
4428
4429 if ( pf->fw_size_hex )
4430 {
4431 if ( pf->fw_size_hex < 4 )
4432 pf->fw_size_hex = 4;
4433 fprintf(pf->f,"%*s ",pf->fw_size_hex,"size");
4434 max_name_len += 1 + pf->fw_size_hex;
4435 }
4436
4437 if ( pf->fw_size_dec )
4438 {
4439 if ( pf->fw_size_dec < 4 )
4440 pf->fw_size_dec = 4;
4441 fprintf(pf->f,"%*s ",pf->fw_size_dec,"size");
4442 max_name_len += 1 + pf->fw_size_dec;
4443 }
4444
4445 //----- second header line
4446
4447 fprintf(pf->f,"\n%*s",pf->indent,"");
4448
4449 if ( pf->fw_unused )
4450 fprintf(pf->f,"%*s ",pf->fw_unused,"hex");
4451
4452 if ( pf->fw_offset )
4453 fprintf(pf->f,"%*s ",pf->fw_offset,"hex");
4454
4455 if ( pf->fw_size_hex )
4456 fprintf(pf->f,"%*s ",pf->fw_size_hex,"hex");
4457
4458 if ( pf->fw_size_dec )
4459 fprintf(pf->f,"%*s ",pf->fw_size_dec,"dec");
4460
4461 ccp sep = "";
4462 if ( pf->fw_unused || pf->fw_offset || pf->fw_size_hex || pf->fw_size_dec )
4463 {
4464 sep = " ";
4465 max_name_len++;
4466 }
4467
4468 fprintf(pf->f,
4469 "%spath + file\n"
4470 "%*s%.*s\n",
4471 sep, pf->indent, "", max_name_len, wd_sep_200 );
4472 }
4473
4474 ///////////////////////////////////////////////////////////////////////////////
4475
wd_print_fst_item(wd_print_fst_t * pf,wd_part_t * part,wd_icm_t icm,u32 offset4,u32 size,ccp fname1,ccp fname2)4476 void wd_print_fst_item
4477 (
4478 wd_print_fst_t * pf, // valid pointer
4479 wd_part_t * part, // valid pointer to a disc partition
4480 wd_icm_t icm, // iterator call mode
4481 u32 offset4, // offset/4 to read
4482 u32 size, // size of object
4483 ccp fname1, // NULL or file name, part 1
4484 ccp fname2 // NULL or file name, part 2
4485 )
4486 {
4487 DASSERT(pf);
4488 DASSERT(pf->f);
4489
4490 char buf[200];
4491 ccp sep = pf->fw_unused || pf->fw_offset || pf->fw_size_hex || pf->fw_size_dec ? " " : "";
4492
4493 switch (icm)
4494 {
4495 case WD_ICM_OPEN_PART:
4496 if ( pf->mode & WD_PFST_PART && part && !part->is_gc )
4497 {
4498 int indent = pf->indent;
4499 if (pf->fw_unused)
4500 indent += 1 + pf->fw_unused;
4501 if (pf->fw_offset)
4502 indent += 2 + pf->fw_offset;
4503 if (pf->fw_size_hex)
4504 indent += 1 + pf->fw_size_hex;
4505 if (pf->fw_size_dec)
4506 indent += 1 + pf->fw_size_dec;
4507 fprintf(pf->f,"%*s%s** Partition #%u.%u, %s **\n",
4508 indent, "", sep,
4509 part->ptab_index, part->ptab_part_index,
4510 wd_print_part_name(buf,sizeof(buf),part->part_type,WD_PNAME_NUM_INFO));
4511 }
4512 break;
4513
4514 case WD_ICM_DIRECTORY:
4515 fprintf(pf->f,"%*s",pf->indent,"");
4516 if (pf->fw_unused)
4517 fprintf(pf->f,"%*s ",pf->fw_unused,"-");
4518 if (pf->fw_offset)
4519 fprintf(pf->f,"%*s ",pf->fw_offset,"-");
4520 if (pf->fw_size_dec)
4521 {
4522 if (pf->fw_size_hex)
4523 fprintf(pf->f,"%*s ",pf->fw_size_hex,"-");
4524 fprintf(pf->f,"N=%-*u",pf->fw_size_dec-1,size);
4525 }
4526 else if (pf->fw_size_hex)
4527 fprintf(pf->f,"N=%-*u",pf->fw_size_hex-1,size);
4528 fprintf(pf->f,"%s%s%s\n", sep, fname1 ? fname1 : "", fname2 ? fname2 : "" );
4529 break;
4530
4531 case WD_ICM_FILE:
4532 case WD_ICM_COPY:
4533 case WD_ICM_DATA:
4534 fprintf(pf->f,"%*s",pf->indent,"");
4535
4536 u64 offset = offset4;
4537 if ( !part || !part->is_gc )
4538 offset <<= 2;
4539
4540 if (pf->fw_unused)
4541 {
4542 if ( pf->last_icm == icm && offset > pf->last_end )
4543 fprintf(pf->f,"%*llx ", pf->fw_unused, offset-pf->last_end );
4544 else
4545 fprintf(pf->f,"%*s ",pf->fw_unused,"-");
4546 }
4547 if (pf->fw_offset)
4548 fprintf(pf->f,"%*llx%c ",
4549 pf->fw_offset, offset, icm == WD_ICM_FILE ? '+' : ' ' );
4550 if (pf->fw_size_hex)
4551 fprintf(pf->f,"%*x ", pf->fw_size_hex, size);
4552 if (pf->fw_size_dec)
4553 fprintf(pf->f,"%*u ", pf->fw_size_dec, size);
4554 fprintf(pf->f,"%s%s%s\n", sep, fname1 ? fname1 : "", fname2 ? fname2 : "" );
4555
4556 pf->last_end = offset + size;
4557 pf->last_icm = icm;
4558 break;
4559
4560 case WD_ICM_CLOSE_PART:
4561 break;
4562 }
4563 }
4564
4565 ///////////////////////////////////////////////////////////////////////////////
4566
wd_print_fst_item_wrapper(struct wd_iterator_t * it)4567 static int wd_print_fst_item_wrapper
4568 (
4569 struct wd_iterator_t *it // iterator struct with all infos
4570 )
4571 {
4572 DASSERT(it);
4573 wd_print_fst_t *d = it->param;
4574 DASSERT(d);
4575 bool print = !d->filter_func;
4576 if (!print)
4577 {
4578 it->param = d->filter_param;
4579 print = !d->filter_func(it);
4580 it->param = d;
4581 }
4582 if (print)
4583 wd_print_fst_item( d, it->part, it->icm, it->off4, it->size, it->path, 0 );
4584 return 0;
4585 }
4586
4587 ///////////////////////////////////////////////////////////////////////////////
4588
wd_print_fst(FILE * f,int indent,wd_disc_t * disc,wd_ipm_t prefix_mode,wd_pfst_t pfst_mode,wd_file_func_t filter_func,void * filter_param)4589 void wd_print_fst
4590 (
4591 FILE * f, // valid output file
4592 int indent, // indention of the output
4593 wd_disc_t * disc, // valid pointer to a disc
4594 wd_ipm_t prefix_mode, // prefix mode
4595 wd_pfst_t pfst_mode, // print mode
4596 wd_file_func_t filter_func, // NULL or filter function
4597 void * filter_param // user defined parameter
4598 )
4599 {
4600 ASSERT(f);
4601 TRACE("wd_print_fst() pfst=%x, filter_func=%p\n",pfst_mode,filter_func);
4602 indent = wd_normalize_indent(indent);
4603
4604 //----- setup pf and calc fw
4605
4606 if ( pfst_mode & WD_PFST__OFF_SIZE )
4607 {
4608 wd_load_all_part(disc,false,false,false);
4609 wd_calc_fst_statistics(disc,false);
4610 }
4611
4612 wd_print_fst_t pf;
4613 wd_initialize_print_fst(&pf,pfst_mode,f,indent,disc->fst_max_off4,disc->fst_max_size);
4614 pf.filter_func = filter_func;
4615 pf.filter_param = filter_param;
4616
4617 //----- print out
4618
4619 if ( pfst_mode & WD_PFST_HEADER )
4620 wd_print_fst_header(&pf,50);
4621
4622 wd_iterate_files(disc,wd_print_fst_item_wrapper,&pf,0,prefix_mode);
4623 }
4624
4625 //
4626 ///////////////////////////////////////////////////////////////////////////////
4627 /////////////// memmap helpers ///////////////
4628 ///////////////////////////////////////////////////////////////////////////////
4629
wd_reset_memmap(wd_memmap_t * mm)4630 void wd_reset_memmap
4631 (
4632 wd_memmap_t * mm // NULL or patching data
4633 )
4634 {
4635 if (mm)
4636 {
4637 if (mm->item)
4638 {
4639 int idx;
4640 for ( idx = 0; idx < mm->used; idx++ )
4641 {
4642 wd_memmap_item_t * item = mm->item + idx;
4643 if (item->data_alloced)
4644 FREE(item->data);
4645 }
4646 FREE(mm->item);
4647 }
4648 memset(mm,0,sizeof(*mm));
4649 }
4650 }
4651
4652 ///////////////////////////////////////////////////////////////////////////////
4653
wd_insert_patch_helper(wd_memmap_t * mm,u64 offset,u64 size,bool * result_found)4654 static u32 wd_insert_patch_helper
4655 (
4656 wd_memmap_t * mm, // NULL or patching data
4657 u64 offset, // offset of object
4658 u64 size, // size of object
4659 bool * result_found // true, if item found
4660 )
4661 {
4662 DASSERT(mm);
4663 DASSERT(result_found);
4664
4665 int beg = 0;
4666 int end = mm->used - 1;
4667 while ( beg <= end )
4668 {
4669 const u32 idx = (beg+end)/2;
4670 wd_memmap_item_t * item = mm->item + idx;
4671 if ( offset < item->offset )
4672 end = idx - 1 ;
4673 else if ( offset > item->offset )
4674 beg = idx + 1;
4675 else if ( size < item->size )
4676 end = idx - 1 ;
4677 else if ( size > item->size )
4678 beg = idx + 1;
4679 else
4680 {
4681 *result_found = true;
4682 return idx;
4683 }
4684 }
4685
4686 *result_found = false;
4687 return beg;
4688 }
4689
4690 ///////////////////////////////////////////////////////////////////////////////
4691
wd_find_memmap(wd_memmap_t * mm,u32 mode,u64 offset,u64 size)4692 wd_memmap_item_t * wd_find_memmap
4693 (
4694 wd_memmap_t * mm, // patching data
4695 u32 mode, // memmap mode (e.g. wd_patch_mode_t)
4696 u64 offset, // offset of object
4697 u64 size // size of object
4698 )
4699 {
4700 DASSERT(mm);
4701
4702 if ( mm->used == mm->size )
4703 {
4704 mm->size += 10;
4705 mm->item = REALLOC(mm->item,mm->size*sizeof(*mm->item));
4706 }
4707
4708 bool found;
4709 u32 idx = wd_insert_patch_helper(mm,offset,size,&found);
4710
4711 return found ? mm->item + idx : 0;
4712 }
4713
4714 ///////////////////////////////////////////////////////////////////////////////
4715
wd_insert_memmap(wd_memmap_t * mm,u32 mode,u64 offset,u64 size)4716 wd_memmap_item_t * wd_insert_memmap
4717 (
4718 wd_memmap_t * mm, // patching data
4719 u32 mode, // memmap mode (e.g. wd_patch_mode_t)
4720 u64 offset, // offset of object
4721 u64 size // size of object
4722 )
4723 {
4724 DASSERT(mm);
4725
4726 if ( mm->used == mm->size )
4727 {
4728 mm->size += 10;
4729 mm->item = REALLOC(mm->item,mm->size*sizeof(*mm->item));
4730 }
4731
4732 bool found;
4733 u32 idx = wd_insert_patch_helper(mm,offset,size,&found);
4734
4735 DASSERT( idx <= mm->used );
4736 wd_memmap_item_t * item = mm->item + idx;
4737 if (!found)
4738 {
4739 memmove(item+1,item,(mm->used-idx)*sizeof(*item));
4740 memset(item,0,sizeof(*item));
4741 mm->used++;
4742 }
4743 else if (item->data_alloced)
4744 {
4745 FREE(item->data);
4746 item->data = 0;
4747 item->data_alloced = 0;
4748 }
4749
4750 item->mode = mode;
4751 item->offset = offset;
4752 item->size = size;
4753 return item;
4754 }
4755
4756 ///////////////////////////////////////////////////////////////////////////////
4757
wd_insert_memmap_alloc(wd_memmap_t * mm,u32 mode,u64 offset,u64 size)4758 wd_memmap_item_t * wd_insert_memmap_alloc
4759 (
4760 wd_memmap_t * mm, // patching data
4761 u32 mode, // memmap mode (e.g. wd_patch_mode_t)
4762 u64 offset, // offset of object
4763 u64 size // size of object
4764 )
4765 {
4766 wd_memmap_item_t * item = wd_insert_memmap(mm,mode,offset,size);
4767 DASSERT(!item->data_alloced);
4768 item->data_alloced = true;
4769 item->data = MALLOC(size);
4770 memset(item->data,0,size);
4771 return item;
4772 }
4773
4774 ///////////////////////////////////////////////////////////////////////////////
4775 ///////////////////////////////////////////////////////////////////////////////
4776
wd_insert_memmap_disc_part(wd_memmap_t * mm,wd_disc_t * disc,wd_memmap_func_t func,void * param,wd_patch_mode_t wii_head_mode,wd_patch_mode_t wii_mgr_mode,wd_patch_mode_t wii_data_mode,wd_patch_mode_t gc_mgr_mode,wd_patch_mode_t gc_data_mode)4777 int wd_insert_memmap_disc_part
4778 (
4779 wd_memmap_t * mm, // patching data
4780 wd_disc_t * disc, // valid disc pointer
4781
4782 wd_memmap_func_t func, // not NULL: Call func() for each inserted item
4783 void * param, // user defined paramater for 'func()'
4784
4785 // creation modes:
4786 // value WD_PAT_IGNORE means: do not create such entires
4787
4788 wd_patch_mode_t wii_head_mode, // value for the Wii partition header
4789 wd_patch_mode_t wii_mgr_mode, // value for the Wii partition mgr data
4790 wd_patch_mode_t wii_data_mode, // value for the Wii partition data
4791 wd_patch_mode_t gc_mgr_mode, // value for the GC partition mgr header
4792 wd_patch_mode_t gc_data_mode // value for the GC partition header
4793 )
4794 {
4795 DASSERT(disc);
4796
4797 wd_load_all_part(disc,false,false,false);
4798
4799 int ip, count = 0;
4800 for ( ip = 0; ip < disc->n_part; ip++ )
4801 {
4802 wd_part_t * part = disc->part + ip;
4803 if ( part->is_valid && part->is_enabled )
4804 count += wd_insert_memmap_part
4805 ( mm, part, func, param,
4806 wii_head_mode, wii_mgr_mode, wii_data_mode,
4807 gc_mgr_mode, gc_data_mode );
4808 }
4809
4810 return count;
4811 }
4812
4813 ///////////////////////////////////////////////////////////////////////////////
4814
wd_insert_memmap_part(wd_memmap_t * mm,wd_part_t * part,wd_memmap_func_t func,void * param,wd_patch_mode_t wii_head_mode,wd_patch_mode_t wii_mgr_mode,wd_patch_mode_t wii_data_mode,wd_patch_mode_t gc_mgr_mode,wd_patch_mode_t gc_data_mode)4815 int wd_insert_memmap_part
4816 (
4817 wd_memmap_t * mm, // patching data
4818 wd_part_t * part, // valid pointer to a disc partition
4819
4820 wd_memmap_func_t func, // not NULL: Call func() for each inserted item
4821 void * param, // user defined paramater for 'func()'
4822
4823 // creation modes:
4824 // value WD_PAT_IGNORE means: do not create such entires
4825
4826 wd_patch_mode_t wii_head_mode, // value for the Wii partition header
4827 wd_patch_mode_t wii_mgr_mode, // value for the Wii partition mgr data
4828 wd_patch_mode_t wii_data_mode, // value for the Wii partition data
4829 wd_patch_mode_t gc_mgr_mode, // value for the GC partition mgr header
4830 wd_patch_mode_t gc_data_mode // value for the GC partition header
4831 )
4832 {
4833 DASSERT(mm);
4834 DASSERT(part);
4835
4836 if ( !part->is_valid || !part->is_enabled )
4837 return 0;
4838
4839 char intro[30];
4840 u32 count = mm->used;
4841
4842 if (part->is_gc)
4843 {
4844 snprintf( intro, sizeof(intro), "GC P.%u, %s",
4845 part->index, wd_print_id(&part->boot,6,0) );
4846
4847 wii_mgr_mode = gc_mgr_mode;
4848 wii_data_mode = gc_mgr_mode;
4849 }
4850 else
4851 {
4852 snprintf( intro, sizeof(intro), "P.%u.%u, %s",
4853 part->ptab_index, part->ptab_part_index,
4854 wd_print_part_name(0,0,part->part_type,WD_PNAME_NUM_INFO) );
4855
4856 if ( wii_head_mode != WD_PAT_IGNORE )
4857 {
4858 WDPRINT("INSERT WII PART-HEAD PATCH %u\n",wii_head_mode);
4859
4860 const u64 head_off = (u64)part->part_off4 << 2;
4861 const u64 head_size = (u64)part->data_sector * WII_SECTOR_SIZE - head_off;
4862
4863 wd_memmap_item_t * item
4864 = wd_insert_memmap(mm,wii_head_mode,head_off,head_size);
4865 DASSERT(item);
4866 item->index = part->index;
4867 snprintf(item->info,sizeof(item->info),"%s, partition head",intro);
4868 if (func)
4869 func(param,mm,item);
4870
4871 u64 off = (u64)part->ph.tmd_off4 << 2;
4872 if ( off && off + part->ph.tmd_size > head_size )
4873 {
4874 item = wd_insert_memmap(mm,wii_head_mode,head_off+off,part->ph.tmd_size);
4875 DASSERT(item);
4876 item->index = part->index;
4877 snprintf(item->info,sizeof(item->info),"%s, tmd",intro);
4878 if (func)
4879 func(param,mm,item);
4880 }
4881
4882 off = (u64)part->ph.cert_off4 << 2;
4883 if ( off && off + part->ph.cert_size > head_size )
4884 {
4885 item = wd_insert_memmap(mm,wii_head_mode,head_off+off,part->ph.cert_size);
4886 DASSERT(item);
4887 item->index = part->index;
4888 snprintf(item->info,sizeof(item->info),"%s, cert",intro);
4889 if (func)
4890 func(param,mm,item);
4891 }
4892
4893 off = (u64)part->ph.h3_off4 << 2;
4894 if ( off && off + WII_H3_SIZE > head_size )
4895 {
4896 item = wd_insert_memmap(mm,wii_head_mode,head_off+off,WII_H3_SIZE);
4897 DASSERT(item);
4898 item->index = part->index;
4899 snprintf(item->info,sizeof(item->info),"%s, h3",intro);
4900 if (func)
4901 func(param,mm,item);
4902 }
4903 }
4904 }
4905
4906
4907 if ( wii_mgr_mode != WD_PAT_IGNORE && wii_mgr_mode == wii_data_mode )
4908 {
4909 WDPRINT("INSERT PART-DATA PATCH %u\n",wii_data_mode);
4910
4911 wd_memmap_item_t * item = wd_insert_memmap
4912 ( mm,
4913 wii_mgr_mode,
4914 (u64)part->data_sector * WII_SECTOR_SIZE,
4915 (u64)( part->end_sector - part->data_sector ) * WII_SECTOR_SIZE
4916 );
4917 DASSERT(item);
4918 item->index = part->index;
4919 snprintf(item->info,sizeof(item->info),"%s, partition data",intro);
4920 if (func)
4921 func(param,mm,item);
4922 }
4923 else
4924 {
4925 if ( wii_mgr_mode != WD_PAT_IGNORE )
4926 {
4927 WDPRINT("INSERT WII PART-DATA #0 PATCH %u\n",wii_data_mode);
4928
4929 wd_memmap_item_t * item = wd_insert_memmap
4930 ( mm,
4931 wii_mgr_mode,
4932 (u64)part->data_sector * WII_SECTOR_SIZE,
4933 (u64)( part->end_mgr_sector - part->data_sector ) * WII_SECTOR_SIZE
4934 );
4935 DASSERT(item);
4936 item->index = part->index;
4937 snprintf(item->info,sizeof(item->info),"%s, partition data #0",intro);
4938 if (func)
4939 func(param,mm,item);
4940 }
4941
4942 if ( wii_data_mode != WD_PAT_IGNORE && part->end_mgr_sector < part->end_sector)
4943 {
4944 WDPRINT("INSERT WII PART-DATA #1 PATCH %u\n",wii_data_mode);
4945
4946 wd_memmap_item_t * item = wd_insert_memmap
4947 ( mm,
4948 wii_data_mode,
4949 (u64)part->end_mgr_sector * WII_SECTOR_SIZE,
4950 (u64)( part->end_sector - part->end_mgr_sector ) * WII_SECTOR_SIZE
4951 );
4952 DASSERT(item);
4953 item->index = part->index;
4954 snprintf(item->info,sizeof(item->info),"%s, partition data #1",intro);
4955 if (func)
4956 func(param,mm,item);
4957 }
4958 }
4959
4960 return mm->used - count;
4961 }
4962
4963 ///////////////////////////////////////////////////////////////////////////////
4964
wd_print_memmap(FILE * f,int indent,wd_memmap_t * mm)4965 void wd_print_memmap
4966 (
4967 FILE * f, // valid output file
4968 int indent, // indention of the output
4969 wd_memmap_t * mm // NULL or patching data
4970 )
4971 {
4972 ASSERT(mm);
4973 if ( !f || !mm->used )
4974 return;
4975
4976 indent = wd_normalize_indent(indent);
4977
4978 int fw = 7;
4979 wd_memmap_item_t *item, *last = mm->item + mm->used;
4980 for ( item = mm->item; item < last; item++ )
4981 {
4982 const int len = strlen(item->info);
4983 if ( fw < len )
4984 fw = len;
4985 }
4986 fprintf(f,"\n%*s offset .. offset end size comment\n"
4987 "%*s%.*s\n",
4988 indent,"", indent,"", fw + 37, wd_sep_200 );
4989
4990 u64 prev_end = 0;
4991 for ( item = mm->item; item < last; item++ )
4992 {
4993 const u64 end = item->offset + item->size;
4994 fprintf(f,"%*s%c %9llx .. %9llx %9llx %s\n", indent,"",
4995 item->offset < prev_end ? '!' : ' ',
4996 item->offset, end, item->size, item->info );
4997 if ( prev_end < end )
4998 prev_end = end;
4999 }
5000 }
5001
5002 //
5003 ///////////////////////////////////////////////////////////////////////////////
5004 /////////////// patch helpers ///////////////
5005 ///////////////////////////////////////////////////////////////////////////////
5006
wd_insert_patch_ticket(wd_part_t * part)5007 wd_memmap_item_t * wd_insert_patch_ticket
5008 (
5009 wd_part_t * part // valid pointer to a disc partition
5010 )
5011 {
5012 if (!wd_part_has_ticket(part))
5013 return 0;
5014
5015 wd_memmap_item_t * item
5016 = wd_insert_memmap( &part->disc->patch, WD_PAT_PART_TICKET,
5017 (u64)part->part_off4<<2, WII_TICKET_SIZE );
5018 DASSERT(item);
5019 item->index = part->index;
5020 item->data = &part->ph.ticket;
5021
5022 snprintf(item->info,sizeof(item->info),
5023 "ticket, id=%s, ckey=%u",
5024 wd_print_id(part->ph.ticket.title_id+4,4,0),
5025 part->ph.ticket.common_key_index );
5026
5027 part->sign_ticket = true;
5028 return item;
5029 }
5030
5031 ///////////////////////////////////////////////////////////////////////////////
5032
wd_insert_patch_tmd(wd_part_t * part)5033 wd_memmap_item_t * wd_insert_patch_tmd
5034 (
5035 wd_part_t * part // valid pointer to a disc partition
5036 )
5037 {
5038 if (!wd_part_has_tmd(part))
5039 return 0;
5040
5041 wd_memmap_item_t * item
5042 = wd_insert_memmap( &part->disc->patch, WD_PAT_PART_TMD,
5043 (u64)( part->part_off4 + part->ph.tmd_off4 ) << 2,
5044 part->ph.tmd_size );
5045 DASSERT(item);
5046 item->index = part->index;
5047 item->data = part->tmd;
5048
5049 u32 high = be32((ccp)&part->tmd->sys_version);
5050 u32 low = be32(((ccp)&part->tmd->sys_version)+4);
5051 if ( high == 1 && low < 0x100 )
5052 snprintf(item->info,sizeof(item->info),
5053 "tmd, id=%s, ios=%u",
5054 wd_print_id(part->tmd->title_id+4,4,0), low );
5055 else
5056 snprintf(item->info,sizeof(item->info),
5057 "tmd, id=%s, sys=%x-%x",
5058 wd_print_id(part->tmd->title_id+4,4,0), high, low );
5059
5060 part->sign_tmd = true;
5061 return item;
5062 }
5063
5064 ///////////////////////////////////////////////////////////////////////////////
5065
wd_insert_patch_fst(wd_part_t * part)5066 wd_memmap_item_t * wd_insert_patch_fst
5067 (
5068 wd_part_t * part // valid pointer to a disc partition
5069 )
5070 {
5071 wd_insert_patch_tmd(part);
5072 wd_memmap_item_t * item
5073 = wd_insert_memmap( &part->patch, WD_PAT_DATA,
5074 (u64)part->boot.fst_off4 << 2,
5075 (u64)part->boot.fst_size4 << 2 );
5076 DASSERT(item);
5077 item->index = part->index;
5078 item->data = part->fst;
5079 part->sign_tmd = true;
5080
5081 snprintf(item->info,sizeof(item->info),
5082 "fst.bin, N=%u", ntohl(part->fst->size) );
5083
5084 return item;
5085 }
5086
5087 ///////////////////////////////////////////////////////////////////////////////
5088
wd_print_disc_patch(FILE * f,int indent,wd_disc_t * disc,bool print_title,bool print_part)5089 void wd_print_disc_patch
5090 (
5091 FILE * f, // valid output file
5092 int indent, // indention of the output
5093 wd_disc_t * disc, // valid pointer to a disc
5094 bool print_title, // true: print table titles
5095 bool print_part // true: print partitions too
5096 )
5097 {
5098 ASSERT(disc);
5099 if (!f)
5100 return;
5101 indent = wd_normalize_indent(indent);
5102
5103 if (disc->patch.used)
5104 {
5105 if (print_title)
5106 {
5107 fprintf(f,"\n%*sPatching table of the disc:\n",indent,"");
5108 indent += 2;
5109 }
5110 wd_print_memmap(f,indent,&disc->patch);
5111 }
5112
5113 if (print_part)
5114 {
5115 wd_part_t *part, *part_end = disc->part + disc->n_part;
5116 for ( part = disc->part; part < part_end; part++ )
5117 {
5118 if (part->patch.used)
5119 {
5120 if (print_title)
5121 {
5122 char buf[50];
5123 fprintf(f,"\n%*sPatching table of partition %s:\n",
5124 indent-2, "",
5125 wd_print_part_name(buf,sizeof(buf),part->part_type,WD_PNAME_NUM_INFO));
5126 }
5127 wd_print_memmap(f,indent,&part->patch);
5128 }
5129 }
5130 }
5131 }
5132
5133 //
5134 ///////////////////////////////////////////////////////////////////////////////
5135 /////////////// read and patch ///////////////
5136 ///////////////////////////////////////////////////////////////////////////////
5137
wd_calc_group_hashes(const u8 * group_data,u8 * group_hash,u8 * h3,const u8 dirty[WII_GROUP_SECTORS])5138 void wd_calc_group_hashes
5139 (
5140 const u8 * group_data, // group data space
5141 u8 * group_hash, // group hash space
5142 u8 * h3, // NULL or H3 element to change
5143 const u8 dirty[WII_GROUP_SECTORS]
5144 // NULL or 'dirty sector' flags
5145 )
5146 {
5147 int d;
5148 for ( d = 0; d < WII_GROUP_SECTORS; d++ )
5149 {
5150 if ( dirty && !dirty[d] )
5151 continue;
5152
5153 const u8 * data = group_data + d * WII_SECTOR_DATA_SIZE;
5154 u8 * h0 = group_hash + d * WII_SECTOR_HASH_SIZE;
5155 int d0;
5156 for ( d0 = 0; d0 < WII_N_ELEMENTS_H0; d0++ )
5157 {
5158 SHA1(data,WII_H0_DATA_SIZE,h0);
5159 data += WII_H0_DATA_SIZE;
5160 h0 += WII_HASH_SIZE;
5161 }
5162
5163 h0 = group_hash + d * WII_SECTOR_HASH_SIZE;
5164 u8 * h1 = group_hash
5165 + (d/WII_N_ELEMENTS_H1) * WII_N_ELEMENTS_H1 * WII_SECTOR_HASH_SIZE
5166 + (d%WII_N_ELEMENTS_H1) * WII_HASH_SIZE
5167 + 0x280;
5168 SHA1(h0,WII_N_ELEMENTS_H0*WII_HASH_SIZE,h1);
5169 }
5170
5171 u8 * h1 = group_hash + 0x280;
5172 u8 * h2 = group_hash + 0x340;
5173 int d1, d2;
5174 for ( d2 = 0; d2 < WII_N_ELEMENTS_H2; d2++ )
5175 {
5176 for ( d1 = 1; d1 < WII_N_ELEMENTS_H1; d1++ )
5177 memcpy( h1 + d1 * WII_SECTOR_HASH_SIZE, h1, WII_N_ELEMENTS_H1*WII_HASH_SIZE );
5178
5179 SHA1(h1,WII_N_ELEMENTS_H1*WII_HASH_SIZE,h2);
5180 h1 += WII_N_ELEMENTS_H1 * WII_SECTOR_HASH_SIZE;
5181 h2 += WII_HASH_SIZE;
5182 }
5183
5184 h2 = group_hash + 0x340;
5185 for ( d1 = 1; d1 < WII_GROUP_SECTORS; d1++ )
5186 memcpy( h2 + d1 * WII_SECTOR_HASH_SIZE, h2, WII_N_ELEMENTS_H2*WII_HASH_SIZE );
5187
5188 if (h3)
5189 SHA1(h2,WII_N_ELEMENTS_H2*WII_HASH_SIZE,h3);
5190 }
5191
5192 ///////////////////////////////////////////////////////////////////////////////
5193
wd_rap_part_sectors(wd_part_t * part,u32 sector,u32 end_sector)5194 static enumError wd_rap_part_sectors
5195 (
5196 wd_part_t * part, // valid partition pointer
5197 u32 sector, // index of first sector to read
5198 u32 end_sector // index of last sector +1 to read
5199 )
5200 {
5201 DASSERT(part);
5202 DASSERT(part->disc);
5203 DASSERT( sector >= part->data_sector);
5204 DASSERT( end_sector <= part->end_sector );
5205 DASSERT( end_sector <= sector + WII_GROUP_SECTORS );
5206
5207 //----- cache setup
5208
5209 wd_disc_t * disc = part->disc;
5210 if (!disc->group_cache)
5211 {
5212 // space for cache and an extra sector (no inplace decryption possible)
5213 disc->group_cache = MALLOC(WII_GROUP_SIZE+WII_SECTOR_SIZE);
5214 }
5215 else if ( disc->group_cache_sector == sector )
5216 return ERR_OK;
5217 disc->group_cache_sector = sector;
5218 u8 * buf = disc->group_cache;
5219
5220 //----- reloc check
5221
5222 wd_reloc_t * reloc = disc->reloc;
5223 DASSERT(reloc);
5224
5225 u32 or_rel = 0, sect = sector;
5226 while ( sect < end_sector )
5227 or_rel |= reloc[sect++];
5228
5229 // delta must be same for all
5230 DASSERT( ( reloc[sector] & WD_RELOC_M_DELTA ) == ( or_rel & WD_RELOC_M_DELTA ) );
5231 DASSERT( ( reloc[sect-1] & WD_RELOC_M_DELTA ) == ( or_rel & WD_RELOC_M_DELTA ) );
5232
5233 const wd_reloc_t decrypt = or_rel & (WD_RELOC_F_PATCH|WD_RELOC_F_DECRYPT);
5234 const wd_reloc_t patch = or_rel & WD_RELOC_F_PATCH;
5235 const wd_reloc_t encrypt = or_rel & WD_RELOC_F_ENCRYPT;
5236
5237
5238 //----- load data
5239
5240 const u64 size = ( end_sector - sector ) * (u64)WII_SECTOR_SIZE;
5241 u32 src = sector + ( reloc[sector] & WD_RELOC_M_DELTA );
5242 if ( src > WII_MAX_SECTORS )
5243 src -= WII_MAX_SECTORS;
5244
5245 bool is_encrypted;
5246 if ( or_rel & WD_RELOC_F_COPY )
5247 {
5248 TRACE("READ(%x->%x..%x)\n",src,sector,end_sector);
5249 is_encrypted = part->is_encrypted;
5250 if ( is_encrypted && decrypt )
5251 buf += WII_SECTOR_SIZE;
5252 const enumError err
5253 = wd_read_raw( disc,
5254 src * WII_SECTOR_SIZE4,
5255 buf,
5256 size,
5257 0 );
5258 if (err)
5259 return err;
5260 }
5261 else
5262 {
5263 TRACE("ZERO(%x->%x..%x)\n",src,sector,end_sector);
5264 is_encrypted = false;
5265 memset( buf, 0, size );
5266 }
5267
5268
5269 //----- decrypt
5270
5271 bool is_splitted = false;
5272 DASSERT( sizeof(disc->temp_buf) >= WII_GROUP_SECTORS * WII_SECTOR_HASH_SIZE );
5273 u8 * hash_buf = patch ? disc->temp_buf : 0;
5274
5275 if ( is_encrypted && decrypt )
5276 {
5277 WDPRINT("DECRYPT(%x->%x..%x) split=%d\n",src,sector,end_sector,hash_buf!=0);
5278 is_encrypted = false;
5279
5280 wd_decrypt_sectors(part,0,buf,disc->group_cache,hash_buf,end_sector-sector);
5281 buf = disc->group_cache;
5282 is_splitted = hash_buf != 0;
5283 }
5284
5285
5286 //----- patching
5287
5288 if (patch)
5289 {
5290 WDPRINT("PATCH-P(%x->%x..%x) split=%d\n",src,sector,end_sector,!is_splitted);
5291 DASSERT(!is_encrypted);
5292 DASSERT(hash_buf);
5293
5294 if (!is_splitted)
5295 {
5296 is_splitted = true;
5297 wd_split_sectors(buf,buf,hash_buf,end_sector-sector);
5298 }
5299
5300 //--- patch data
5301
5302 u64 off1 = ( sector - part->data_sector ) * (u64)WII_SECTOR_DATA_SIZE;
5303 u64 off2 = ( end_sector - part->data_sector ) * (u64)WII_SECTOR_DATA_SIZE;
5304 const wd_memmap_item_t *item = part->patch.item;
5305 const wd_memmap_item_t *end_item = item + part->patch.used;
5306 noTRACE("> off=%llx..%llx, n-item=%u\n", off1, off2, part->patch.used );
5307
5308 u8 dirty[WII_GROUP_SECTORS];
5309 memset(dirty,0,sizeof(dirty));
5310
5311 for ( ; item < end_item && item->offset < off2; item++ )
5312 {
5313 const u64 end = item->offset + item->size;
5314 noTRACE("> off=%llx..%llx, item=%llx..%llx\n", off1, off2, item->offset, end );
5315 noTRACE("> data=%p, fst=%p\n",item->data,part->fst);
5316 if ( item->offset < off2 && end > off1 )
5317 {
5318 const u64 overlap1 = item->offset > off1 ? item->offset : off1;
5319 const u64 overlap2 = end < off2 ? end : off2;
5320 u32 dirty1 = (overlap1-off1) / WII_SECTOR_DATA_SIZE;
5321 u32 dirty2 = (overlap2-off1 + WII_SECTOR_DATA_SIZE - 1) / WII_SECTOR_DATA_SIZE;
5322 noTRACE(" -> %llx .. %llx, dirty=%u..%u\n",overlap1,overlap2,dirty1,dirty2);
5323 memset(dirty+dirty1,1,dirty2-dirty1);
5324
5325 switch (item->mode)
5326 {
5327 case WD_PAT_ZERO:
5328 memset( buf + (overlap1-off1), 0, overlap2-overlap1 );
5329 break;
5330
5331 case WD_PAT_DATA:
5332 memcpy( buf + (overlap1-off1),
5333 (u8*)item->data + (overlap1-item->offset),
5334 overlap2-overlap1 );
5335 break;
5336
5337 default:
5338 ASSERT(0);
5339 }
5340 }
5341 }
5342
5343 //--- calc hashes
5344
5345 u8 * h3 = part->h3;
5346 if (h3)
5347 {
5348 h3 += ( sector - part->data_sector ) / WII_GROUP_SECTORS * WII_HASH_SIZE;
5349 part->h3_dirty = true;
5350 }
5351 wd_calc_group_hashes(buf,disc->temp_buf,h3,dirty);
5352 }
5353
5354
5355 //----- encrypt
5356
5357 if ( !is_encrypted && encrypt )
5358 {
5359 WDPRINT("ENCRYPT(%x->%x..%x) compose=%d\n",src,sector,end_sector,hash_buf!=0);
5360 wd_encrypt_sectors(part,0,buf,hash_buf,buf,end_sector-sector);
5361 }
5362 else if (is_splitted)
5363 {
5364 WDPRINT("COMPOSE(%x->%x..%x)\n",src,sector,end_sector);
5365 wd_join_sectors(buf,hash_buf,buf,end_sector-sector);
5366 }
5367
5368 DASSERT( buf == disc->group_cache );
5369 return ERR_OK;
5370 }
5371
5372 ///////////////////////////////////////////////////////////////////////////////
5373
wd_rap_sectors(wd_disc_t * disc,u8 * buf,u32 sector,u32 n_sectors)5374 static enumError wd_rap_sectors
5375 (
5376 wd_disc_t * disc, // valid disc pointer
5377 u8 * buf, // destination buffer
5378 u32 sector, // index of first sector to read
5379 u32 n_sectors // number of sectors to read
5380 )
5381 {
5382 TRACE("wd_rap_sectors(%p,%p,%x,%x)\n",disc,buf,sector,n_sectors);
5383 DASSERT(disc);
5384 DASSERT(buf);
5385
5386 u32 end_sector = sector + n_sectors;
5387 if ( end_sector > WII_MAX_SECTORS )
5388 {
5389 // outside of manged area -> just fill with zeros
5390 u32 first_sector = sector;
5391 if ( first_sector < WII_MAX_SECTORS )
5392 first_sector = WII_MAX_SECTORS;
5393
5394 TRACE("ZERO(%x,+%x,%x)\n",first_sector,first_sector-sector,end_sector-first_sector);
5395 memset( buf + ( first_sector - sector ) * WII_SECTOR_SIZE, 0,
5396 ( end_sector - first_sector ) * WII_SECTOR_SIZE );
5397 if ( sector >= WII_MAX_SECTORS )
5398 return ERR_OK;
5399
5400 end_sector = WII_MAX_SECTORS;
5401 n_sectors = end_sector - sector;
5402 }
5403
5404 ASSERT( sector < end_sector );
5405 ASSERT( sector + n_sectors == end_sector );
5406 ASSERT( end_sector <= WII_MAX_SECTORS );
5407
5408 wd_reloc_t * reloc = disc->reloc;
5409 DASSERT(reloc);
5410
5411 while ( sector < end_sector )
5412 {
5413 wd_reloc_t rel = reloc[sector];
5414 if ( rel & WD_RELOC_M_CRYPT )
5415 {
5416 u32 pidx = ( rel & WD_RELOC_M_PART ) >> WD_RELOC_S_PART;
5417 if ( pidx < disc->n_part )
5418 {
5419 // partition data is read in blocks of 64 sectors
5420
5421 wd_part_t * part = disc->part + pidx;
5422 DASSERT(part->is_enabled);
5423 DASSERT(part->is_valid);
5424 u32 sect1 = ( sector - part->data_sector )
5425 / WII_GROUP_SECTORS * WII_GROUP_SECTORS
5426 + part->data_sector;
5427 u32 sect2 = sect1 + WII_GROUP_SECTORS;
5428 if ( sect2 > part->end_sector )
5429 sect2 = part->end_sector;
5430 const enumError err = wd_rap_part_sectors(part,sect1,sect2);
5431 if (err)
5432 return err;
5433
5434 if ( sect2 > end_sector )
5435 sect2 = end_sector;
5436 const u32 size = ( sect2 - sector ) * WII_SECTOR_SIZE;
5437 TRACE("COPY(sect=%x, delta=%x, cnt=%x, size=%x\n",
5438 sector, sector - sect1, sect2 - sector, size );
5439 memcpy( buf, disc->group_cache + (sector-sect1) * WII_SECTOR_SIZE, size );
5440 buf += size;
5441 sector = sect2;
5442 continue;
5443 }
5444 }
5445
5446 const u32 start = sector;
5447 rel &= WD_RELOC_M_DISCLOAD;
5448 u32 or_rel = 0;
5449 while ( sector < end_sector && (reloc[sector] & WD_RELOC_M_DISCLOAD) == rel )
5450 or_rel |= reloc[sector++];
5451
5452 const u32 count = sector - start;
5453 const u64 size = count * (u64)WII_SECTOR_SIZE;
5454 DASSERT(count);
5455
5456 u32 src = start + ( rel & WD_RELOC_M_DELTA );
5457 if ( src > WII_MAX_SECTORS )
5458 src -= WII_MAX_SECTORS;
5459
5460 if ( or_rel & WD_RELOC_F_COPY )
5461 {
5462 TRACE("READ(%x->%x,%x)\n",src,start,count);
5463 const enumError err
5464 = wd_read_raw( disc,
5465 src * WII_SECTOR_SIZE4,
5466 buf,
5467 size,
5468 0 );
5469 if (err)
5470 return err;
5471 }
5472 else
5473 {
5474 TRACE("ZERO(%x->%x,%x)\n",src,start,sector-start);
5475 memset( buf, 0, size );
5476 }
5477
5478 if ( or_rel & WD_RELOC_F_PATCH )
5479 {
5480 DASSERT( !( or_rel & WD_RELOC_M_CRYPT ) );
5481 TRACE("PATCH-D(%x->%x,%x)\n",src,start,sector-start);
5482 u64 off1 = start * (u64)WII_SECTOR_SIZE;
5483 u64 off2 = sector * (u64)WII_SECTOR_SIZE;
5484 const wd_memmap_item_t *item = disc->patch.item;
5485 const wd_memmap_item_t *end_item = item + disc->patch.used;
5486
5487 // skip unneeded items
5488 while ( item < end_item && item->offset + item->size <= off1 )
5489 item++;
5490
5491 for ( ; item < end_item && item->offset < off2; item++ )
5492 {
5493 const u64 end = item->offset + item->size;
5494 if ( item->offset < off2 && end > off1 )
5495 {
5496 const u64 overlap1 = item->offset > off1 ? item->offset : off1;
5497 const u64 overlap2 = end < off2 ? end : off2;
5498 TRACE(" -> %llx .. %llx\n",overlap1,overlap2);
5499 DASSERT( item->index < disc->n_part );
5500 wd_part_t * part = disc->part + item->index;
5501 switch (item->mode)
5502 {
5503 case WD_PAT_ZERO:
5504 memset( buf + (overlap1-off1), 0, overlap2-overlap1 );
5505 break;
5506
5507 case WD_PAT_DATA:
5508 memcpy( buf + (overlap1-off1),
5509 (u8*)item->data + (overlap1-item->offset),
5510 overlap2-overlap1 );
5511 break;
5512
5513 case WD_PAT_PART_TICKET:
5514 DASSERT(part);
5515 if (part->sign_ticket)
5516 {
5517 PRINT("SIGN TICKET\n");
5518 wd_encrypt_title_key(&part->ph.ticket,part->key);
5519 ticket_fake_sign(&part->ph.ticket,sizeof(part->ph.ticket));
5520 part->sign_ticket = false;
5521 }
5522 memcpy( buf + (overlap1-off1),
5523 (u8*)item->data + (overlap1-item->offset),
5524 overlap2-overlap1 );
5525 break;
5526
5527 case WD_PAT_PART_TMD:
5528 DASSERT(part);
5529 DASSERT(part->tmd);
5530 if ( part->h3_dirty && part->h3 )
5531 {
5532 PRINT("CALC H4\n");
5533 SHA1( part->h3, WII_H3_SIZE, part->tmd->content[0].hash );
5534 part->h3_dirty = false;
5535 part->sign_tmd = true;
5536 }
5537
5538 if (part->sign_tmd)
5539 {
5540 PRINT("SIGN TMD\n");
5541 tmd_fake_sign(part->tmd,part->ph.tmd_size);
5542 part->sign_tmd = false;
5543 }
5544 memcpy( buf + (overlap1-off1),
5545 (u8*)item->data + (overlap1-item->offset),
5546 overlap2-overlap1 );
5547 break;
5548
5549 default:
5550 ASSERT(0);
5551 }
5552 }
5553 }
5554 }
5555 buf += size;
5556 }
5557
5558 return ERR_OK;
5559 }
5560
5561 ///////////////////////////////////////////////////////////////////////////////
5562
wd_read_and_patch(wd_disc_t * disc,u64 offset,void * dest_buf,u32 count)5563 enumError wd_read_and_patch
5564 (
5565 wd_disc_t * disc, // valid disc pointer
5566 u64 offset, // offset to read
5567 void * dest_buf, // destination buffer
5568 u32 count // number of bytes to read
5569 )
5570 {
5571 DASSERT(disc);
5572 DASSERT(dest_buf);
5573
5574 //----- read first sector if unaligned
5575
5576 u8 *dest = dest_buf;
5577 u32 sector = offset / WII_SECTOR_SIZE;
5578 u32 skip = offset % WII_SECTOR_SIZE;
5579 noTRACE("sector=%x, skip=%x\n",sector,skip);
5580
5581 if ( count > 0 && skip )
5582 {
5583 // read and cache whole sector
5584 noTRACE("READ %x, cache=%x\n",sector,disc->cache_sector);
5585 if ( disc->cache_sector != sector )
5586 {
5587 const enumError err = wd_rap_sectors(disc,disc->cache,sector,1);
5588 if (err)
5589 {
5590 disc->cache_sector = ~(u32)0;
5591 return err;
5592 }
5593 disc->cache_sector = sector;
5594 }
5595
5596 u32 copy_len = WII_SECTOR_SIZE - skip;
5597 if ( copy_len > count )
5598 copy_len = count;
5599
5600 memcpy(dest,disc->cache+skip,copy_len);
5601 dest += copy_len;
5602 count -= copy_len;
5603 sector += 1;
5604 }
5605
5606 //----- read continous and well aligned sectors
5607
5608 if ( count > WII_SECTOR_SIZE )
5609 {
5610 const u32 n_sectors = count / WII_SECTOR_SIZE;
5611 const enumError err = wd_rap_sectors(disc,dest,sector,n_sectors);
5612 if (err)
5613 return err;
5614
5615 const u32 read_size = n_sectors * WII_SECTOR_SIZE;
5616 dest += read_size;
5617 count -= read_size;
5618 sector += n_sectors;
5619 }
5620
5621 //----- read last sector with cache
5622
5623 if (count)
5624 {
5625 // read and cache whole sector
5626 noTRACE("READ %x, cache=%x\n",sector,disc->cache_sector);
5627 if ( disc->cache_sector != sector )
5628 {
5629 const enumError err = wd_rap_sectors(disc,disc->cache,sector,1);
5630 if (err)
5631 {
5632 disc->cache_sector = ~(u32)0;
5633 return err;
5634 }
5635 disc->cache_sector = sector;
5636 }
5637 memcpy(dest,disc->cache,count);
5638 }
5639
5640 return ERR_OK;
5641 }
5642
5643 //
5644 ///////////////////////////////////////////////////////////////////////////////
5645 /////////////// patching ///////////////
5646 ///////////////////////////////////////////////////////////////////////////////
5647
wd_get_ptab_sector(wd_disc_t * disc)5648 u32 wd_get_ptab_sector
5649 (
5650 wd_disc_t * disc // valid disc pointer
5651 )
5652 {
5653 DASSERT(disc);
5654 return disc->disc_type == WD_DT_GAMECUBE
5655 ? 0
5656 : WII_PTAB_SECTOR;
5657 }
5658
5659 ///////////////////////////////////////////////////////////////////////////////
5660
wd_patch_ptab(wd_disc_t * disc,void * sector_data,bool force_patch)5661 bool wd_patch_ptab // result = true if something changed
5662 (
5663 wd_disc_t * disc, // valid disc pointer
5664 void * sector_data, // valid pointer to sector data
5665 // GC: GC_MULTIBOOT_PTAB_OFF + GC_MULTIBOOT_PTAB_SIZE
5666 // Wii: WII_MAX_PTAB_SIZE
5667 bool force_patch // false: patch only if needed
5668 )
5669 {
5670 DASSERT(disc);
5671 DASSERT(sector_data);
5672
5673 wd_load_all_part(disc,false,false,false);
5674
5675 if (!force_patch)
5676 {
5677 // test if any partitions is invalid or disabled
5678 int ip;
5679 for ( ip = 0; ip < disc->n_part; ip++ )
5680 {
5681 wd_part_t * part = disc->part + ip;
5682 if ( !part->is_valid || !part->is_enabled )
5683 {
5684 force_patch = true;
5685 break;
5686 }
5687 }
5688 if (!force_patch)
5689 return false;
5690 }
5691
5692 if ( disc->disc_type == WD_DT_GAMECUBE )
5693 {
5694 memcpy( sector_data, &disc->dhead,
5695 GC_MULTIBOOT_PTAB_OFF + GC_MULTIBOOT_PTAB_SIZE );
5696 if ( !( disc->disc_attrib & WD_DA_GC_MULTIBOOT ) )
5697 return true;
5698
5699 // check dvd9
5700 u32 max_part_off4 = 0;
5701 int n_part = 0, ip;
5702 for ( ip = 0; ip < disc->n_part; ip++ )
5703 {
5704 wd_part_t * part = disc->part + ip;
5705 if ( part->is_valid && part->is_enabled && part->part_off4 )
5706 {
5707 n_part++;
5708 if ( max_part_off4 < part->part_off4 )
5709 max_part_off4 = part->part_off4;
5710 }
5711 }
5712
5713 const bool is_dvd9 = max_part_off4 >= 0x40000000;
5714 if ( n_part > GC_MULTIBOOT_PTAB_SIZE/sizeof(u32) )
5715 n_part = GC_MULTIBOOT_PTAB_SIZE/sizeof(u32);
5716 WDPRINT("PATCH-PTAB: np=%u, max_part_off4=%x, is_dvd9=%d\n",
5717 n_part, max_part_off4, is_dvd9 );
5718
5719 u32 * ptab = (u32*)( (char*)sector_data + GC_MULTIBOOT_PTAB_OFF );
5720 u32 * ptab_end = ptab + n_part;
5721
5722 memcpy(sector_data, is_dvd9 ? "GCOPDVD9" : "GCOPDVD", 8 );
5723 memset(ptab,0,GC_MULTIBOOT_PTAB_SIZE);
5724
5725 for ( ip = 0; ip < disc->n_part && ptab < ptab_end; ip++ )
5726 {
5727 wd_part_t * part = disc->part + ip;
5728 if ( part->is_valid && part->is_enabled && part->part_off4 )
5729 {
5730 u32 off = part->part_off4;
5731 if (!is_dvd9)
5732 off <<= 2;
5733 *ptab++ = htonl(off);
5734 }
5735 }
5736 }
5737 else
5738 {
5739 memset(sector_data,0,WII_MAX_PTAB_SIZE);
5740 wd_ptab_t * ptab = sector_data;
5741 wd_ptab_info_t * info = ptab->info;
5742 wd_ptab_entry_t * entry = ptab->entry;
5743 u32 off4 = (ccp)entry - (ccp)info + WII_PTAB_REF_OFF >> 2;
5744
5745 int it;
5746 for ( it = 0; it < WII_MAX_PTAB; it++, info++ )
5747 {
5748 int n_part = 0, ip;
5749 for ( ip = 0; ip < disc->n_part; ip++ )
5750 {
5751 wd_part_t * part = disc->part + ip;
5752 if ( !part->is_valid || !part->is_enabled || part->ptab_index != it )
5753 continue;
5754
5755 n_part++;
5756 entry->off4 = htonl(part->part_off4);
5757 entry->ptype = htonl(part->part_type);
5758 entry++;
5759 }
5760
5761 if (n_part)
5762 {
5763 info->n_part = htonl(n_part);
5764 info->off4 = htonl(off4);
5765 off4 += n_part * sizeof(*entry) >> 2;
5766 }
5767 }
5768 }
5769
5770 return true;
5771 }
5772
5773 ///////////////////////////////////////////////////////////////////////////////
5774
wd_patch_id(void * dest_id,const void * source_id,const void * new_id,u32 id_len)5775 bool wd_patch_id
5776 (
5777 void * dest_id, // destination, size=id_len, not 0 term
5778 const void * source_id, // NULL or source id, length=id_len
5779 const void * new_id, // NULL or new ID / 0 term / '.': don't change
5780 u32 id_len // max length of id
5781 )
5782 {
5783 DASSERT(dest_id);
5784
5785 if (!source_id)
5786 source_id = dest_id;
5787 else if ( dest_id != source_id )
5788 memcpy(dest_id,source_id,id_len);
5789 if (!new_id)
5790 return false;
5791
5792 char * dest = dest_id;
5793 ccp src = new_id;
5794
5795 int i;
5796 for ( i = 0; i < id_len && src[i]; i++ )
5797 if ( src[i] != '.' )
5798 dest[i] = src[i];
5799 return memcmp(dest,source_id,id_len) != 0;
5800 }
5801
5802 ///////////////////////////////////////////////////////////////////////////////
5803
wd_patch_disc_header(wd_disc_t * disc,ccp new_id,ccp new_name)5804 bool wd_patch_disc_header // result = true if something changed
5805 (
5806 wd_disc_t * disc, // valid pointer to a disc
5807 ccp new_id, // NULL or new ID / 0 term / '.': don't change
5808 ccp new_name // NULL or new disc name
5809 )
5810 {
5811 DASSERT(disc);
5812 bool stat = false;
5813
5814 if ( new_id && !( disc->disc_attrib & WD_DA_GC_MULTIBOOT ) )
5815 {
5816 char id6[6];
5817 ccp src = &disc->dhead.disc_id;
5818 if (wd_patch_id(id6,src,new_id,6))
5819 {
5820 stat = true;
5821 wd_memmap_item_t * item
5822 = wd_insert_memmap_alloc( &disc->patch, WD_PAT_DATA, 0, 6 );
5823 DASSERT(item);
5824 snprintf(item->info,sizeof(item->info),
5825 "disc id: %s -> %s",
5826 wd_print_id(src,6,0), wd_print_id(id6,6,0) );
5827 memcpy(item->data,id6,6);
5828 }
5829 }
5830
5831 if (new_name)
5832 {
5833 char name[WII_TITLE_SIZE];
5834 strncpy(name,new_name,sizeof(name));
5835 name[sizeof(name)-1] = 0;
5836
5837 if (memcmp(name,disc->dhead.disc_title,sizeof(name)))
5838 {
5839 stat = true;
5840 wd_memmap_item_t * item
5841 = wd_insert_memmap_alloc( &disc->patch, WD_PAT_DATA,
5842 WII_TITLE_OFF, WII_TITLE_SIZE );
5843 DASSERT(item);
5844 snprintf(item->info,sizeof(item->info),"disc name: %s",name);
5845 memcpy(item->data,name,WII_TITLE_SIZE);
5846 }
5847 }
5848
5849 return stat;
5850 }
5851
5852 ///////////////////////////////////////////////////////////////////////////////
5853
wd_patch_region(wd_disc_t * disc,u32 new_region)5854 bool wd_patch_region // result = true if something changed
5855 (
5856 wd_disc_t * disc, // valid pointer to a disc
5857 u32 new_region // new region id
5858 )
5859 {
5860 DASSERT(disc);
5861
5862 if ( disc->disc_type == WD_DT_GAMECUBE )
5863 {
5864 const u32 be_region = htonl(new_region);
5865 if ( be_region == disc->region.region )
5866 return false;
5867
5868 wd_memmap_item_t * item
5869 = wd_insert_memmap_alloc( &disc->patch, WD_PAT_DATA,
5870 WII_BI2_OFF + WII_BI2_REGION_OFF,
5871 sizeof(u32) );
5872 snprintf(item->info,sizeof(item->info),"region: %x",new_region);
5873 *(u32*)item->data = be_region;
5874 return true;
5875 }
5876
5877 if (!wd_disc_has_region_setting(disc))
5878 return false;
5879
5880 wd_memmap_item_t * item
5881 = wd_insert_memmap_alloc( &disc->patch, WD_PAT_DATA,
5882 WII_REGION_OFF, WII_REGION_SIZE );
5883 snprintf(item->info,sizeof(item->info),"region: %x",new_region);
5884 wd_region_t * reg = item->data;
5885 reg->region = htonl(new_region);
5886 return true;
5887 }
5888
5889 ///////////////////////////////////////////////////////////////////////////////
5890
wd_patch_common_key(wd_part_t * part,wd_ckey_index_t ckey_index)5891 bool wd_patch_common_key // result = true if something changed
5892 (
5893 wd_part_t * part, // valid pointer to a disc partition
5894 wd_ckey_index_t ckey_index // new common key index
5895 )
5896 {
5897 DASSERT(part);
5898
5899 if (!wd_part_has_ticket(part))
5900 return 0;
5901
5902 bool stat = false;
5903
5904 if ( (unsigned)ckey_index < WD_CKEY__N
5905 && ckey_index != part->ph.ticket.common_key_index )
5906 {
5907 part->ph.ticket.common_key_index = ckey_index;
5908 wd_insert_patch_ticket(part);
5909 stat = true;
5910 }
5911
5912 return stat;
5913 }
5914
5915 ///////////////////////////////////////////////////////////////////////////////
5916
wd_patch_part_id(wd_part_t * part,wd_modify_t modify,ccp new_disc_id,ccp new_boot_id,ccp new_ticket_id,ccp new_tmd_id)5917 bool wd_patch_part_id // result = true if something changed
5918 (
5919 wd_part_t * part, // valid pointer to a disc partition
5920 wd_modify_t modify, // objects to modify
5921 ccp new_disc_id, // NULL or new disc ID / '.': don't change
5922 ccp new_boot_id, // NULL or new boot ID / '.': don't change
5923 ccp new_ticket_id, // NULL or new ticket ID / '.': don't change
5924 ccp new_tmd_id // NULL or new tmd ID / '.': don't change
5925 )
5926 {
5927 DASSERT(part);
5928 DASSERT(part->disc);
5929
5930 if (part->is_gc)
5931 modify = modify & (WD_MODIFY_DISC|WD_MODIFY_BOOT) ? WD_MODIFY_DISC : 0;
5932
5933 bool stat = false;
5934
5935 if ( new_disc_id && modify & WD_MODIFY_DISC )
5936 stat = wd_patch_disc_header(part->disc,new_disc_id,0);
5937
5938 char id6[6];
5939 if ( new_ticket_id && modify & WD_MODIFY_TICKET && wd_part_has_ticket(part) )
5940 {
5941 u8 * src = part->ph.ticket.title_id+4;
5942 if (wd_patch_id(id6,src,new_ticket_id,4))
5943 {
5944 memcpy(src,id6,4);
5945 wd_insert_patch_ticket(part);
5946 stat = true;
5947 }
5948 }
5949
5950 if ( new_tmd_id && modify & WD_MODIFY_TMD && part->tmd && wd_part_has_tmd(part) )
5951 {
5952 u8 * src = part->tmd->title_id+4;
5953 if (wd_patch_id(id6,src,new_tmd_id,4))
5954 {
5955 memcpy(src,id6,4);
5956 wd_insert_patch_tmd(part);
5957 stat = true;
5958 }
5959 }
5960
5961 if ( new_boot_id && modify & WD_MODIFY_BOOT )
5962 {
5963 ccp src = &part->boot.dhead.disc_id;
5964 if (wd_patch_id(id6,src,new_boot_id,6))
5965 {
5966 wd_memmap_item_t * item
5967 = wd_insert_memmap_alloc( &part->patch, WD_PAT_DATA, WII_BOOT_OFF, 6 );
5968 DASSERT(item);
5969 item->index = part->index;
5970 snprintf(item->info,sizeof(item->info),
5971 "boot id: %s -> %s",
5972 wd_print_id(src,6,0), wd_print_id(id6,6,0) );
5973 memcpy(item->data,id6,6);
5974 stat = true;
5975 }
5976 }
5977
5978 return stat;
5979 }
5980
5981 ///////////////////////////////////////////////////////////////////////////////
5982
wd_patch_part_name(wd_part_t * part,ccp new_name,wd_modify_t modify)5983 bool wd_patch_part_name // result = true if something changed
5984 (
5985 wd_part_t * part, // valid pointer to a disc partition
5986 ccp new_name, // NULL or new disc name
5987 wd_modify_t modify // objects to modify
5988 )
5989 {
5990 DASSERT(part);
5991
5992 if (part->is_gc)
5993 modify = modify & (WD_MODIFY_DISC|WD_MODIFY_BOOT) ? WD_MODIFY_DISC : 0;
5994
5995 bool stat = false;
5996
5997 if ( modify & WD_MODIFY_DISC )
5998 stat = wd_patch_disc_header(part->disc,0,new_name);
5999
6000 if ( modify & WD_MODIFY_BOOT )
6001 {
6002 char name[WII_TITLE_SIZE];
6003 strncpy(name,new_name,sizeof(name));
6004 name[sizeof(name)-1] = 0;
6005
6006 if (memcmp(name,part->boot.dhead.disc_title,sizeof(name)))
6007 {
6008 wd_memmap_item_t * item
6009 = wd_insert_memmap_alloc( &part->patch, WD_PAT_DATA,
6010 WII_BOOT_OFF + WII_TITLE_OFF, WII_TITLE_SIZE );
6011 DASSERT(item);
6012 snprintf(item->info,sizeof(item->info),"boot name: %s",name);
6013 memcpy(item->data,name,WII_TITLE_SIZE);
6014 stat = true;
6015 }
6016 }
6017
6018 return stat;
6019 }
6020
6021 ///////////////////////////////////////////////////////////////////////////////
6022
wd_patch_part_system(wd_part_t * part,u64 system)6023 bool wd_patch_part_system // result = true if something changed
6024 (
6025 wd_part_t * part, // valid pointer to a disc partition
6026 u64 system // new system id (IOS)
6027 )
6028 {
6029 DASSERT(part);
6030
6031 bool stat = false;
6032 system = hton64(system);
6033 if ( wd_part_has_tmd(part) && part->tmd->sys_version != system )
6034 {
6035 part->tmd->sys_version = system;
6036 wd_insert_patch_tmd(part);
6037 }
6038
6039 return stat;
6040 }
6041
6042 //
6043 ///////////////////////////////////////////////////////////////////////////////
6044 /////////////// relocation ///////////////
6045 ///////////////////////////////////////////////////////////////////////////////
6046
wd_mark_disc_reloc(wd_reloc_t * reloc,u64 off,u64 size,wd_reloc_t flags)6047 static void wd_mark_disc_reloc
6048 (
6049 wd_reloc_t * reloc, // valid pointer to relocation table
6050 u64 off, // offset of data
6051 u64 size, // size of data
6052 wd_reloc_t flags // flags to set
6053 )
6054 {
6055 u64 end = off + size;
6056 u32 idx = off / WII_SECTOR_SIZE;
6057 if ( off > idx * (u64)WII_SECTOR_SIZE )
6058 {
6059 DASSERT( idx < WII_MAX_SECTORS );
6060 reloc[idx] |= flags;
6061 off = ++idx * (u64)WII_SECTOR_SIZE;
6062 }
6063
6064 while ( off < end )
6065 {
6066 DASSERT( idx < WII_MAX_SECTORS );
6067 off += WII_SECTOR_SIZE;
6068 if ( off <= end && flags & WD_RELOC_F_PATCH )
6069 reloc[idx] &= ~WD_RELOC_F_COPY;
6070 reloc[idx++] |= flags;
6071 }
6072 }
6073
6074 ///////////////////////////////////////////////////////////////////////////////
6075
wd_mark_part_reloc(wd_part_t * part,wd_reloc_t * reloc,u64 off,u64 size,wd_reloc_t flags)6076 static void wd_mark_part_reloc
6077 (
6078 wd_part_t * part, // NULL (=disc) or pointer to partition
6079 wd_reloc_t * reloc, // valid pointer to relocation table
6080 u64 off, // offset of data
6081 u64 size, // size of data
6082 wd_reloc_t flags // flags to set
6083 )
6084 {
6085 u64 end = off + size;
6086 u32 idx = off / WII_SECTOR_DATA_SIZE + part->data_off4 / WII_SECTOR_SIZE4;
6087
6088 if ( off % WII_SECTOR_DATA_SIZE )
6089 {
6090 DASSERT( idx < WII_MAX_SECTORS );
6091 reloc[idx] |= flags;
6092 off = ++idx * (u64)WII_SECTOR_DATA_SIZE;
6093 }
6094
6095 while ( off < end )
6096 {
6097 DASSERT( idx < WII_MAX_SECTORS );
6098 off += WII_SECTOR_DATA_SIZE;
6099 if ( off <= end && flags & WD_RELOC_F_PATCH )
6100 reloc[idx] &= ~WD_RELOC_F_COPY;
6101 reloc[idx++] |= flags;
6102 }
6103 }
6104
6105 ///////////////////////////////////////////////////////////////////////////////
6106
wd_calc_relocation(wd_disc_t * disc,bool encrypt,wd_trim_mode_t trim_mode,u32 trim_align,const wd_select_t * select,bool force)6107 wd_reloc_t * wd_calc_relocation
6108 (
6109 wd_disc_t * disc, // valid disc pointer
6110 bool encrypt, // true: encrypt partition data
6111 wd_trim_mode_t trim_mode, // trim mode
6112 u32 trim_align, // alignment value for trimming
6113 const wd_select_t * select, // NULL or a new selector
6114 bool force // true: force new calculation
6115 )
6116 {
6117 DASSERT(disc);
6118 TRACE("wd_calc_relocation(%p,%d,%x,%x,%p,%d)\n",
6119 disc, encrypt, trim_mode, trim_align, select, force );
6120
6121 trim_mode = wd_get_relococation_trim(trim_mode,&trim_align,disc->disc_type);
6122 if ( disc->trim_mode != trim_mode || disc->trim_align != trim_align )
6123 {
6124 PRINT("TRIM: %x/%x -> %x/%x\n",
6125 disc->trim_mode, disc->trim_align, trim_mode, trim_align );
6126 disc->trim_mode = trim_mode;
6127 disc->trim_align = trim_align;
6128 force = true;
6129 // [trim] -> support for disc/part/file trimming -> below
6130 }
6131
6132 if (select)
6133 {
6134 wd_select(disc,select);
6135 force = true;
6136 }
6137
6138 const size_t reloc_size = WII_MAX_SECTORS * sizeof(*disc->reloc);
6139 if (!disc->reloc)
6140 disc->reloc = MALLOC(reloc_size);
6141 else if (!force)
6142 return disc->reloc;
6143
6144
6145 //----- check partition tables
6146
6147 wd_part_t *part, *part_end = disc->part + disc->n_part;
6148 int enable_count = 0;
6149 for ( part = disc->part; part < part_end; part++ )
6150 if ( part->is_enabled && part->is_valid )
6151 enable_count++;
6152
6153 if ( enable_count < disc->n_part )
6154 {
6155 wd_memmap_item_t * item
6156 = disc->disc_type == WD_DT_GAMECUBE
6157 ? wd_insert_memmap( &disc->patch,
6158 WD_PAT_DATA,
6159 0,
6160 GC_MULTIBOOT_PTAB_OFF + GC_MULTIBOOT_PTAB_SIZE )
6161 : wd_insert_memmap( &disc->patch,
6162 WD_PAT_DATA,
6163 WII_PTAB_REF_OFF,
6164 WII_MAX_PTAB_SIZE );
6165 DASSERT(item);
6166 snprintf(item->info,sizeof(item->info),
6167 "use %u of %u partitions", enable_count, disc->n_part );
6168 item->data = &disc->ptab;
6169 wd_patch_ptab(disc,&disc->ptab,true);
6170
6171 if ( disc->disc_attrib & WD_DA_GC_MULTIBOOT )
6172 {
6173 wd_part_t * part = disc->part;
6174 DASSERT(part);
6175 if ( ( !part->is_valid || !part->is_enabled ) && !part->data_off4 )
6176 {
6177 WDPRINT("REMOVE GC BOOT PARTITION\n");
6178 item = wd_insert_memmap( &disc->patch,
6179 WD_PAT_ZERO,
6180 sizeof(disc->dhead),
6181 WII_SECTOR_SIZE - sizeof(disc->dhead) );
6182 DASSERT(item);
6183 snprintf(item->info,sizeof(item->info),
6184 "remove boot partition (zero data)" );
6185 }
6186 }
6187 }
6188
6189
6190 //---- setup tables
6191
6192 u8 usage_table[WII_MAX_SECTORS];
6193 wd_filter_usage_table(disc,usage_table,0);
6194
6195 wd_reloc_t * reloc = disc->reloc;
6196 memset(reloc,0,reloc_size);
6197
6198
6199 //----- base setup
6200
6201 int idx;
6202 const wd_reloc_t f_part = encrypt ? WD_RELOC_F_ENCRYPT : WD_RELOC_F_DECRYPT;
6203 for ( idx = 0; idx < WII_MAX_SECTORS; idx++ )
6204 {
6205 const u32 uval = usage_table[idx];
6206 if ( uval >= WD_USAGE_PART_0 && !disc->whole_disc )
6207 {
6208 reloc[idx] = ( (uval&WD_USAGE__MASK) - WD_USAGE_PART_0 ) << WD_RELOC_S_PART
6209 | WD_RELOC_F_COPY;
6210 if ( uval & WD_USAGE_F_CRYPT )
6211 reloc[idx] |= f_part;
6212 }
6213 else if ( uval )
6214 reloc[idx] = WD_RELOC_F_COPY | WD_RELOC_M_PART;
6215 }
6216
6217 if (disc->whole_disc)
6218 return reloc;
6219
6220
6221 //----- partition patching
6222
6223 for ( part = disc->part; part < part_end; part++ )
6224 {
6225 if (part->patch.used)
6226 {
6227 //--- tmd
6228
6229 wd_load_part(part,false,true,false);
6230 wd_memmap_item_t * item = wd_insert_patch_tmd(part);
6231 wd_mark_disc_reloc(reloc,item->offset,item->size,WD_RELOC_F_LAST);
6232
6233 //--- h3
6234
6235 if (part->h3)
6236 {
6237 item = wd_insert_memmap( &part->disc->patch, WD_PAT_DATA,
6238 (u64)( part->part_off4 + part->ph.h3_off4 ) << 2,
6239 WII_H3_SIZE );
6240 DASSERT(item);
6241 item->index = part->index;
6242 item->data = part->h3;
6243 snprintf(item->info,sizeof(item->info),"h3");
6244 wd_mark_disc_reloc(reloc,item->offset,item->size,
6245 WD_RELOC_F_PATCH|WD_RELOC_F_LAST);
6246 }
6247
6248 //--- items
6249
6250 const wd_memmap_t *patch = &part->patch;
6251 const wd_memmap_item_t *last = patch->item + patch->used;
6252 for ( item = patch->item; item < last; item++ )
6253 wd_mark_part_reloc(part,reloc,item->offset,item->size,WD_RELOC_F_PATCH);
6254
6255
6256 //--- calc WD_RELOC_F_HASH
6257
6258 u32 sect = part->data_sector;
6259 while ( sect < part->end_sector )
6260 {
6261 u32 start = sect;
6262 u32 end = start + WII_GROUP_SECTORS;
6263 if ( end > part->end_sector )
6264 end = part->end_sector;
6265 for ( ; sect < end; sect++ )
6266 if ( reloc[sect] & WD_RELOC_F_PATCH )
6267 {
6268 while ( start < end )
6269 reloc[start++] |= WD_RELOC_F_HASH;
6270 break;
6271 }
6272 sect = end;
6273 }
6274 }
6275 }
6276
6277
6278 //----- disc patching
6279
6280 const wd_memmap_t * patch = &disc->patch;
6281 const wd_memmap_item_t *item, *last = patch->item + patch->used;
6282 for ( item = patch->item; item < last; item++ )
6283 wd_mark_disc_reloc(reloc,item->offset,item->size,WD_RELOC_F_PATCH);
6284
6285
6286 //----- terminate
6287
6288 wd_patch_ptab(disc,&disc->ptab,true);
6289 return reloc;
6290 }
6291
6292 ///////////////////////////////////////////////////////////////////////////////
6293
wd_print_relocation(FILE * f,int indent,const wd_reloc_t * reloc,bool print_title)6294 void wd_print_relocation
6295 (
6296 FILE * f, // valid output file
6297 int indent, // indention of the output
6298 const wd_reloc_t * reloc, // valid pointer to relocation table
6299 bool print_title // true: print table titles
6300 )
6301 {
6302 DASSERT(f);
6303 DASSERT(reloc);
6304
6305 indent = wd_normalize_indent(indent);
6306 if (print_title)
6307 {
6308 fprintf(f,"\n%*sRelocation table:\n",indent,"");
6309 indent += 2;
6310 }
6311
6312 fprintf(f,
6313 "\n"
6314 "%*s offset dest blocks : n(b) : source blocks : partition and flags\n"
6315 "%*s%.79s\n",
6316 indent, "", indent, "", wd_sep_200 );
6317
6318 const wd_reloc_t *rel = reloc, *end = reloc + WII_MAX_SECTORS;
6319 while ( rel < end )
6320 {
6321 //--- skip unused blocks
6322
6323 while ( rel < end && !*rel )
6324 rel++;
6325
6326 if ( rel == end )
6327 break;
6328
6329 //--- store values
6330
6331 const wd_reloc_t * start = rel;
6332 const u32 val = *rel;
6333 while ( rel < end && val == *rel )
6334 rel++;
6335
6336 const int dest = start - reloc;
6337 int src = dest + ( val & WD_RELOC_M_DELTA );
6338 if ( src > WII_MAX_SECTORS )
6339 src -= WII_MAX_SECTORS;
6340 const int count = rel - start;
6341
6342 if ( count > 1 )
6343 fprintf(f,"%*s%9llx %5x .. %5x :%5x : %5x .. %5x :",
6344 indent, "",
6345 (u64)WII_SECTOR_SIZE * dest,
6346 dest, dest + count-1, count, src, src + count-1 );
6347 else
6348 fprintf(f,"%*s%9llx %14x : 1 : %14x :",
6349 indent, "",
6350 (u64)WII_SECTOR_SIZE * dest, dest, src );
6351
6352 const wd_reloc_t pidx = val & WD_RELOC_M_PART;
6353 if ( pidx == WD_RELOC_M_PART )
6354 fprintf(f," -");
6355 else
6356 fprintf(f,"%3u", pidx >> WD_RELOC_S_PART );
6357
6358 fprintf(f," %s %s %s %s %s\n",
6359 val & WD_RELOC_F_ENCRYPT ? "enc"
6360 : val & WD_RELOC_F_DECRYPT ? "dec" : " - ",
6361 val & WD_RELOC_F_COPY ? "copy" : " - ",
6362 val & WD_RELOC_F_PATCH ? "patch" : " - ",
6363 val & WD_RELOC_F_HASH ? "hash" : " - ",
6364 val & WD_RELOC_F_LAST ? "last" : " -" );
6365 }
6366 }
6367
6368 ///////////////////////////////////////////////////////////////////////////////
6369
wd_print_disc_relocation(FILE * f,int indent,wd_disc_t * disc,bool print_title)6370 void wd_print_disc_relocation
6371 (
6372 FILE * f, // valid output file
6373 int indent, // indention of the output
6374 wd_disc_t * disc, // valid pointer to a disc
6375 bool print_title // true: print table titles
6376 )
6377 {
6378 DASSERT(disc);
6379 wd_print_relocation(f,indent,
6380 wd_calc_relocation(disc,true,disc->trim_mode,disc->trim_align,0,false),
6381 print_title );
6382 }
6383
6384 ///////////////////////////////////////////////////////////////////////////////
6385
wd_get_relococation_trim(wd_trim_mode_t trim_mode,u32 * trim_align,wd_disc_type_t disc_type)6386 wd_trim_mode_t wd_get_relococation_trim
6387 (
6388 wd_trim_mode_t trim_mode, // trim mode to check
6389 u32 * trim_align, // NULL or trim alignment (modify)
6390 wd_disc_type_t disc_type // type of disc for align calculation
6391 )
6392 {
6393 wd_trim_mode_t res = trim_mode & (WD_TRIM_DISC|WD_TRIM_PART);
6394 if ( res & WD_TRIM_DISC )
6395 res |= trim_mode & WD_TRIM_F_END;
6396
6397 if ( trim_align )
6398 {
6399 if (!res)
6400 *trim_align = 0;
6401 else
6402 {
6403 const u32 align0
6404 = *trim_align > WII_GROUP_SIZE
6405 ? WII_GROUP_SIZE
6406 : *trim_align
6407 ? *trim_align
6408 : disc_type == WD_DT_GAMECUBE
6409 ? GC_GOOD_PART_ALIGN
6410 : WII_SECTOR_SIZE;
6411 u32 align = WII_SECTOR_SIZE;
6412 while ( align < align0 )
6413 align <<= 1;
6414 *trim_align = align;
6415 }
6416 }
6417 return res;
6418 }
6419
6420 //
6421 ///////////////////////////////////////////////////////////////////////////////
6422 /////////////// file relocation ///////////////
6423 ///////////////////////////////////////////////////////////////////////////////
6424
wd_initialize_file_list(wd_file_list_t * fl)6425 wd_file_list_t * wd_initialize_file_list
6426 (
6427 wd_file_list_t * fl // NULL or working file list
6428 // If NULL: allocate a file list
6429 )
6430 {
6431 if (!fl)
6432 fl = MALLOC(sizeof(*fl));
6433
6434 memset(fl,0,sizeof(*fl));
6435 return fl;
6436 }
6437
6438 //-----------------------------------------------------------------------------
6439
wd_reset_file_list(wd_file_list_t * fl,bool free_fl)6440 void wd_reset_file_list
6441 (
6442 wd_file_list_t * fl, // NULL or working file list to reset (free data)
6443 bool free_fl // true: call 'FREE(fl)'
6444 )
6445 {
6446 if (fl)
6447 {
6448 DASSERT( fl->used <= fl->size );
6449 wd_file_t * file = fl->file;
6450 wd_file_t * file_end = file + fl->used;
6451 for ( ; file < file_end; file++ )
6452 {
6453 FREE((char*)file->iso_path);
6454 FREE((char*)file->file_path);
6455 }
6456
6457 FREE(fl->file);
6458 if (free_fl)
6459 FREE(fl);
6460 else
6461 memset(fl,0,sizeof(*fl));
6462 }
6463 }
6464
6465 //-----------------------------------------------------------------------------
6466
wd_create_file_list(wd_file_list_t * fl,wd_fst_item_t * fst,bool fst_is_gc)6467 wd_file_list_t * wd_create_file_list
6468 (
6469 wd_file_list_t * fl, // NULL or working file list
6470 // If NULL: allocate a file list
6471 wd_fst_item_t * fst, // valid fst data structure
6472 bool fst_is_gc // true: 'fst' is a GameCube source
6473 )
6474 {
6475 if (fl)
6476 wd_reset_file_list(fl,false);
6477 else
6478 fl = wd_initialize_file_list(0);
6479
6480 // [[2do]]
6481
6482 return fl;
6483 }
6484
6485 //-----------------------------------------------------------------------------
6486
wd_insert_file(wd_file_list_t * fl,u32 src_off4,u32 size,ccp file_path)6487 wd_file_t * wd_insert_file
6488 (
6489 wd_file_list_t * fl, // working file list
6490 u32 src_off4, // source offset
6491 u32 size, // size of file
6492 ccp file_path // valid filename
6493 )
6494 {
6495 // [[2do]]
6496 return 0;
6497 }
6498
6499 //-----------------------------------------------------------------------------
6500
wd_insert_directory(wd_file_list_t * fl,ccp dir_path)6501 wd_file_t * wd_insert_directory
6502 (
6503 wd_file_list_t * fl, // working file list
6504 ccp dir_path // valid directory name
6505 )
6506 {
6507 // [[2do]]
6508 return 0;
6509 }
6510
6511 //-----------------------------------------------------------------------------
6512
wd_create_file_list_fst(wd_file_list_t * fl,u32 * n_fst,bool create_gc_fst,u32 align1,u32 align2,u32 align3)6513 wd_fst_item_t * wd_create_file_list_fst
6514 (
6515 wd_file_list_t * fl, // working file list
6516 u32 * n_fst, // not NULL: store number of created fst elements
6517 bool create_gc_fst, // true: create a GameCube fst
6518
6519 u32 align1, // minimal alignment
6520 u32 align2, // alignment for files with size >= align2
6521 u32 align3 // alignment for files with size >= align3
6522 // All aligment values are rounded
6523 // up to the next power of 2.
6524 // The values are normalized (increased)
6525 // to align1 <= align2 <= align3
6526 )
6527 {
6528 DASSERT(fl);
6529
6530 // [[2do]]
6531
6532 return 0;
6533 }
6534
6535 //-----------------------------------------------------------------------------
6536
wd_is_directory(wd_file_t * file)6537 bool wd_is_directory
6538 (
6539 wd_file_t * file // pointer to a file
6540 )
6541 {
6542 DASSERT(file);
6543 return !file->dest_off4;
6544 }
6545
6546 //
6547 ///////////////////////////////////////////////////////////////////////////////
6548 /////////////// dump data structure ///////////////
6549 ///////////////////////////////////////////////////////////////////////////////
6550
wd_print_disc(FILE * f,int indent,wd_disc_t * disc,int dump_level)6551 void wd_print_disc
6552 (
6553 FILE * f, // valid output file
6554 int indent, // indention of the output
6555 wd_disc_t * disc, // valid disc pointer
6556 int dump_level // dump level
6557 // >0: print extended part info
6558 // >1: print usage table
6559 )
6560 {
6561 TRACE("wd_print_disc()\n");
6562
6563 ASSERT(f);
6564 ASSERT(disc);
6565
6566 if ( indent < 0 )
6567 indent = 0;
6568 else if ( indent > 50 )
6569 indent = 50;
6570
6571 fprintf(f,"%*sRead func+data: %p %p\n", indent,"",
6572 disc->read_func, disc->read_data );
6573
6574 fprintf(f,"%*sFile size: %llx/hex = %llu\n", indent,"",
6575 disc->iso_size, disc->iso_size );
6576
6577 const u8 *m1 = (u8*)&disc->dhead.wii_magic;
6578 const u8 *m2 = (u8*)&disc->magic2;
6579 fprintf(f,"%*sMagics: %02x-%02x-%02x-%02x %02x-%02x-%02x-%02x\n",
6580 indent,"",
6581 m1[0], m1[1], m1[2], m1[3],
6582 m2[0], m2[1], m2[2], m2[3] );
6583
6584 fprintf(f,"%*sID and title: %.6s, %.64s\n", indent,"",
6585 &disc->dhead.disc_id, disc->dhead.disc_title );
6586
6587 fprintf(f,"%*sRegion setting: %d / %s\n",
6588 indent,"", ntohl(disc->region.region),
6589 wd_print_age_rating(0,0,disc->region.age_rating) );
6590
6591 int ipt;
6592 for ( ipt = 0; ipt < WII_MAX_PTAB; ipt++ )
6593 {
6594 wd_ptab_info_t * pt = disc->ptab_info + ipt;
6595 const int n = ntohl(pt->n_part);
6596 if (n)
6597 fprintf(f,"%*sPartition table #%u: off=%llx, N(part)=%u\n", indent,"",
6598 ipt, (u64)ntohl(pt->off4)<<2, n );
6599 }
6600
6601 const bool load_part = dump_level > 0;
6602 int ip;
6603 for ( ip = 0; ip < disc->n_part; ip++ )
6604 {
6605 wd_part_t * part = wd_get_part_by_index(disc,ip,load_part);
6606
6607 if (load_part)
6608 putchar('\n');
6609
6610 char pname[30];
6611 fprintf(f,"%*sPartition #%-2u [%u.%02u], "
6612 "loaded=%d, valid=%d, enabled=%d, enc=%d, type=%s\n",
6613 indent,"",
6614 part->index, part->ptab_index , part->ptab_part_index,
6615 part->is_loaded, part->is_valid, part->is_enabled,
6616 part->is_encrypted,
6617 wd_print_part_name(pname,sizeof(pname),
6618 part->part_type, WD_PNAME_NUM_INFO ));
6619
6620 if ( load_part && part->is_valid )
6621 {
6622 u64 off = part->part_off4 << 2;
6623 fprintf(f,"%*s TICKET: %9llx .. %9llx, %9zx\n", indent,"",
6624 off, off + sizeof(part->ph), sizeof(part->ph) );
6625
6626 if ( part->ph.tmd_off4 )
6627 {
6628 off = ( part->part_off4 + part->ph.tmd_off4 ) << 2;
6629 fprintf(f,"%*s TMD: %9llx .. %9llx, %9x\n", indent,"",
6630 off, off + part->ph.tmd_size, part->ph.tmd_size );
6631 }
6632
6633 if ( part->ph.cert_off4 )
6634 {
6635 off = ( part->part_off4 + part->ph.cert_off4 ) << 2;
6636 fprintf(f,"%*s CERT: %9llx .. %9llx, %9x, loaded=%d\n", indent,"",
6637 off, off + part->ph.cert_size, part->ph.cert_size,
6638 part->cert != 0 );
6639 }
6640
6641 if ( part->ph.h3_off4 )
6642 {
6643 off = ( part->part_off4 + part->ph.h3_off4 ) << 2;
6644 fprintf(f,"%*s H3: %9llx .. %9llx, %9x, loaded=%d\n", indent,"",
6645 off, off + WII_H3_SIZE, WII_H3_SIZE,
6646 part->h3 != 0 );
6647 }
6648
6649 if ( part->ph.data_off4 )
6650 {
6651 off = part->data_off4 << 2;
6652 u64 data_size = part->ph.data_size4 << 2;
6653 fprintf(f,"%*s DATA: %9llx .. %9llx, %9llx\n", indent,"",
6654 off, off + data_size, data_size );
6655 }
6656 }
6657 }
6658
6659 #if 0 // defined(DEBUG) && defined(TEST)
6660 for ( ip = 0; ip < disc->n_part; ip++ )
6661 {
6662 wd_part_t * part = wd_get_part_by_index(disc,ip,load_part);
6663
6664 fprintf(f,"%*s ID: |",indent,"");
6665 int pmode;
6666 for ( pmode = 0; pmode < WD_PNAME__N; pmode++ )
6667 {
6668 char pname[30];
6669 wd_print_part_name(pname,sizeof(pname),part->part_type,pmode);
6670 fprintf(f,"%s|",pname);
6671 }
6672 fputc('\n',f);
6673 }
6674 #endif
6675
6676 if ( dump_level > 1 )
6677 {
6678 fprintf(f,"\n%*sUsage map:\n",indent,"");
6679 wd_print_disc_usage_tab(f,indent+2,disc,false);
6680 }
6681 }
6682
6683 ///////////////////////////////////////////////////////////////////////////////
6684
wd_print_disc_usage_tab(FILE * f,int indent,wd_disc_t * disc,bool print_all)6685 void wd_print_disc_usage_tab
6686 (
6687 FILE * f, // valid output file
6688 int indent, // indention of the output
6689 wd_disc_t * disc, // valid disc pointer
6690 bool print_all // false: ignore const lines
6691 )
6692 {
6693 wd_print_usage_tab( f, indent, wd_calc_usage_table(disc), disc->iso_size, print_all );
6694 }
6695
6696 ///////////////////////////////////////////////////////////////////////////////
6697
wd_print_usage_tab(FILE * f,int indent,const u8 * usage_tab,u64 iso_size,bool print_all)6698 void wd_print_usage_tab
6699 (
6700 FILE * f, // valid output file
6701 int indent, // indention of the output
6702 const u8 * usage_tab, // valid pointer, size = WII_MAX_SECTORS
6703 u64 iso_size, // NULL or size of iso file
6704 bool print_all // false: ignore const lines
6705 )
6706 {
6707 wd_print_byte_tab( f, indent, usage_tab,
6708 ( iso_size + WII_SECTOR_SIZE - 1 ) / WII_SECTOR_SIZE,
6709 WII_MAX_SECTORS, WII_SECTOR_SIZE,
6710 wd_usage_name_tab, print_all );
6711 }
6712
6713 ///////////////////////////////////////////////////////////////////////////////
6714
wd_print_files(wd_part_t * part,wd_mem_func_t func,void * param,u64 off,u64 end,u32 fst_sect,ccp msg)6715 static void wd_print_files
6716 (
6717 wd_part_t * part, // pointer to partition
6718 wd_mem_func_t func, // valid function pointer
6719 void * param, // user defined parameter
6720 u64 off, // minimal offset
6721 u64 end, // maximal offset
6722 u32 fst_sect, // sector index of fst.bin
6723 ccp msg // base message
6724 )
6725 {
6726 char buf[80];
6727
6728 const u8 * utab = part->disc->usage_table;
6729 const u8 usage_id = part->usage_id;
6730 while ( off < end )
6731 {
6732 u32 sect = off / WII_SECTOR_SIZE;
6733 u32 start = sect;
6734 while ( sect < WII_MAX_SECTORS
6735 && ( utab[sect] & WD_USAGE__MASK ) != usage_id )
6736 sect++;
6737 if ( start < sect )
6738 {
6739 u64 off2 = sect * (u64)WII_SECTOR_SIZE;
6740 if ( off2 > end )
6741 off2 = end;
6742 if ( off < off2 )
6743 off = off2;
6744 start = sect;
6745 }
6746
6747 while ( sect < WII_MAX_SECTORS
6748 && ( utab[sect] & WD_USAGE__MASK ) == usage_id )
6749 sect++;
6750 if ( start < sect )
6751 {
6752 u64 off2 = sect * (u64)WII_SECTOR_SIZE;
6753 if ( off2 > end )
6754 off2 = end;
6755 if ( off < off2 )
6756 {
6757 if ( fst_sect >= start && fst_sect < sect )
6758 {
6759 snprintf(buf,sizeof(buf),"%s, N(fst)=%u", msg, part->fst_n );
6760 func(param,off,off2-off,buf);
6761 }
6762 else
6763 func(param,off,off2-off,msg);
6764 off = off2;
6765 }
6766 }
6767
6768 if ( sect >= WII_MAX_SECTORS )
6769 break;
6770 }
6771 }
6772
6773 ///////////////////////////////////////////////////////////////////////////////
6774
wd_print_gc_mem(wd_disc_t * disc,wd_mem_func_t func,void * param)6775 static void wd_print_gc_mem
6776 (
6777 wd_disc_t * disc, // valid disc pointer
6778 wd_mem_func_t func, // valid function pointer
6779 void * param // user defined parameter
6780 )
6781 {
6782 ASSERT(disc);
6783 ASSERT(disc->part);
6784 DASSERT(func);
6785 TRACE("wd_print_gc_mem(%p,%p,%p)\n",disc,func,param);
6786
6787 char msg[80];
6788 wd_part_t * part = disc->part, *part_end = part + disc->n_part;
6789 bool have_0_part = false; // partition at offset 0
6790
6791 for ( ; part < part_end; part++ )
6792 {
6793 if (!part->part_off4)
6794 have_0_part = true;
6795
6796 u64 base_off = (u64)part->part_off4 << 2;
6797 WDPRINT("PART #%u, off=%9llx, size=%9llx, doff4=%8x, sect=%5x..%5x\n",
6798 part->index, base_off, part->part_size,
6799 part->data_off4, part->data_sector, part->end_sector );
6800
6801 char * dest = msg;
6802 if ( disc->n_part || !have_0_part )
6803 dest += snprintf( msg, sizeof(msg), "%sP.%u: ",
6804 part->index & 1 ? "" : " ", part->index );
6805 size_t msgsize = msg + sizeof(msg) - dest;
6806
6807 //----- boot.bin
6808
6809 wd_boot_t * boot = &part->boot;
6810 const u8 *m = (u8*)&boot->dhead.gc_magic;
6811 snprintf( dest, msgsize,
6812 "boot.bin, magic=%02x-%02x-%02x-%02x, id=%s",
6813 m[0], m[1], m[2], m[3], wd_print_id(&boot->dhead.disc_id,6,0) );
6814 func(param,base_off+WII_BOOT_OFF,WII_BOOT_SIZE,msg);
6815
6816 //----- bi2.bin
6817
6818 snprintf( dest, msgsize, "%s", "bi2.bin" );
6819 func(param,base_off+WII_BI2_OFF,WII_BI2_SIZE,msg);
6820
6821 //----- apploader.img
6822
6823 snprintf( dest, msgsize, "%s", "apploader.img" );
6824 func(param,base_off+WII_APL_OFF,part->apl_size,msg);
6825
6826 //----- main.dol
6827
6828 snprintf( dest, msgsize, "%s", "main.dol" );
6829 func(param,base_off+boot->dol_off4,part->dol_size,msg);
6830
6831 //----- fst.bin
6832
6833 snprintf( dest, msgsize, "%s", "fst.bin" );
6834 func(param,base_off+boot->fst_off4,boot->fst_size4,msg);
6835
6836 //----- files
6837
6838 wd_fst_item_t * fst = part->fst;
6839 if (fst)
6840 {
6841 u32 min = ~(u32)0, max = 0;
6842 const wd_fst_item_t *fst_end = fst + ntohl(fst->size);
6843
6844 for ( fst++; fst < fst_end; fst++ )
6845 if (!fst->is_dir)
6846 {
6847 u32 off = ntohl(fst->offset4);
6848 if ( min > off )
6849 min = off;
6850 off += ntohl(fst->size);
6851 if ( max < off )
6852 max = off;
6853 }
6854
6855 if ( min < max )
6856 {
6857 min += base_off;
6858 max += base_off;
6859 snprintf( dest, msgsize, "%s", "files" );
6860 wd_print_files(part,func,param,min,max,min/WII_SECTOR_SIZE,msg);
6861 }
6862 }
6863 }
6864
6865 if (!have_0_part)
6866 {
6867 snprintf( msg, sizeof(msg),
6868 "GameCube multiboot header, id=%.6s%s",
6869 &disc->dhead.disc_id,
6870 memcmp(&disc->dhead.disc_id+4,"DVD9",4) ? "" : ", dvd9" );
6871 func(param,0,0x100,msg);
6872 }
6873
6874 if (disc->iso_size)
6875 func(param,disc->iso_size,0,"-- End of file --");
6876 }
6877
6878 ///////////////////////////////////////////////////////////////////////////////
6879
wd_print_mem(wd_disc_t * disc,wd_mem_func_t func,void * param)6880 void wd_print_mem
6881 (
6882 wd_disc_t * disc, // valid disc pointer
6883 wd_mem_func_t func, // valid function pointer
6884 void * param // user defined parameter
6885 )
6886 {
6887 ASSERT(disc);
6888 DASSERT(func);
6889
6890 if ( disc->disc_type == WD_DT_GAMECUBE )
6891 {
6892 wd_print_gc_mem(disc,func,param);
6893 return;
6894 }
6895
6896 TRACE("wd_print_mem(%p,%p,%p)\n",disc,func,param);
6897 char msg[80];
6898
6899 //----- header
6900
6901 const u8 *m = (u8*)&disc->dhead.wii_magic;
6902 snprintf( msg, sizeof(msg),
6903 "Header, magic=%02x-%02x-%02x-%02x, id=%s",
6904 m[0], m[1], m[2], m[3], wd_print_id(&disc->dhead.disc_id,6,0) );
6905 func(param,0,sizeof(disc->dhead),msg);
6906
6907
6908 //----- partition tables
6909
6910 snprintf( msg, sizeof(msg),
6911 "Partition address table, N=%u+%u+%u+%u",
6912 ntohl(disc->ptab_info[0].n_part),
6913 ntohl(disc->ptab_info[1].n_part),
6914 ntohl(disc->ptab_info[2].n_part),
6915 ntohl(disc->ptab_info[3].n_part) );
6916 func(param,WII_PTAB_REF_OFF,sizeof(disc->ptab_info),msg);
6917
6918 int ip;
6919 for ( ip = 0; ip < WII_MAX_PTAB; ip++ )
6920 {
6921 const u32 np = ntohl(disc->ptab_info[ip].n_part);
6922 if (np)
6923 {
6924 const u64 off = (u64)ntohl(disc->ptab_info[ip].off4) << 2;
6925 snprintf( msg, sizeof(msg),
6926 "Partition table #%u with %u partition%s",
6927 ip, np, np == 1 ? "" : "s" );
6928 func(param,off,np*sizeof(wd_ptab_entry_t),msg);
6929 }
6930 }
6931
6932
6933 //----- region settings
6934
6935 snprintf( msg, sizeof(msg),
6936 "Region settings, region=%x",
6937 ntohl(disc->region.region) );
6938 func(param,WII_REGION_OFF,sizeof(disc->region),msg);
6939
6940
6941 //----- magic2
6942
6943 m = (u8*)&disc->magic2;
6944 snprintf( msg, sizeof(msg),
6945 "Magic2: %02x-%02x-%02x-%02x",
6946 m[0], m[1], m[2], m[3] );
6947 func(param,WII_MAGIC2_OFF,sizeof(disc->magic2),msg);
6948
6949
6950 //----- iterate partitions
6951
6952 for ( ip = 0; ip < disc->n_part; ip++ )
6953 {
6954 wd_part_t * part = wd_get_part_by_index(disc,ip,1);
6955
6956 char pname_buf[20];
6957 char * pname = wd_print_part_name(pname_buf,sizeof(pname_buf),
6958 part->part_type, WD_PNAME_NAME );
6959 char * dest = msg + snprintf( msg, sizeof(msg),
6960 "P.%u.%u %s: ",
6961 part->ptab_index, part->ptab_part_index,
6962 pname );
6963 size_t msgsize = msg + sizeof(msg) - dest;
6964
6965 if (!part->is_valid)
6966 {
6967 snprintf( dest, msgsize, "** INVALID **" );
6968 func(param,(u64)part->part_off4<<2,0,msg);
6969 }
6970 else if (part->is_enabled)
6971 {
6972 const wd_part_header_t *ph = &part->ph;
6973
6974 //--- header + ticket
6975
6976 snprintf( dest, msgsize, "ticket, id=%s",
6977 wd_print_id(ph->ticket.title_id+4,4,0) );
6978 func(param,(u64)part->part_off4<<2,sizeof(*ph),msg);
6979
6980 //--- tmd
6981
6982 if (ph->tmd_off4)
6983 {
6984 const u64 sys_version = ntoh64(part->tmd->sys_version);
6985 const u32 hi = sys_version >> 32;
6986 const u32 lo = (u32)sys_version;
6987 if ( hi == 1 && lo < 0x100 )
6988 snprintf( dest, msgsize, "tmd, ios=%u, id=%s",
6989 lo, wd_print_id(part->tmd->title_id+4,4,0) );
6990 else
6991 snprintf( dest, msgsize, "tmd, sys=%08x-%08x, id=%s",
6992 hi, lo, wd_print_id(part->tmd->title_id+4,4,0) );
6993 func(param,(u64)(part->part_off4+ph->tmd_off4)<<2,ph->tmd_size,msg);
6994 }
6995
6996 //--- cert
6997
6998 if (ph->cert_off4)
6999 {
7000 snprintf( dest, msgsize, "cert" );
7001 func(param,(u64)(part->part_off4+ph->cert_off4)<<2,ph->cert_size,msg);
7002 }
7003
7004 //--- h3
7005
7006 if (ph->h3_off4)
7007 {
7008 snprintf( dest, msgsize, "h3" );
7009 func(param,(u64)(part->part_off4+ph->h3_off4)<<2,WII_H3_SIZE,msg);
7010 }
7011
7012 //--- data
7013
7014 if (ph->data_off4)
7015 {
7016 u64 off = (u64)part->data_off4<<2;
7017 u64 end = off + ((u64)ph->data_size4<<2);
7018 const u32 fst_sect = part->fst_n
7019 ? off/WII_SECTOR_SIZE + part->boot.fst_off4/WII_SECTOR_DATA_SIZE4
7020 : 0;
7021
7022 snprintf(dest,msgsize,"data+fst");
7023 wd_print_files(part,func,param,off,end,fst_sect,msg);
7024 }
7025 }
7026 }
7027
7028 //----- end of wii disc
7029
7030 if (disc->iso_size)
7031 func(param,disc->iso_size,0,
7032 "-- End of file/disc --");
7033 }
7034
7035 //
7036 ///////////////////////////////////////////////////////////////////////////////
7037 /////////////// E N D ///////////////
7038 ///////////////////////////////////////////////////////////////////////////////
7039