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