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 #define _GNU_SOURCE 1
38 
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <fcntl.h>
42 #include <errno.h>
43 #include <ctype.h>
44 
45 #include "patch.h"
46 #include "version.h"
47 #include "lib-std.h"
48 #include "lib-sf.h"
49 #include "wbfs-interface.h"
50 
51 //
52 ///////////////////////////////////////////////////////////////////////////////
53 ///////////////			--enc				///////////////
54 ///////////////////////////////////////////////////////////////////////////////
55 
56 int opt_hook = 0; // <0: disabled, =0: auto, >0: enabled
57 
58 ///////////////////////////////////////////////////////////////////////////////
59 
ScanEncoding(ccp arg)60 enumEncoding ScanEncoding ( ccp arg )
61 {
62     static const CommandTab_t tab[] =
63     {
64 	{ 0,			"AUTO",		0,		ENCODE_MASK },
65 
66 	{ ENCODE_CLEAR_HASH,	"NO-HASH",	"NOHASH",	ENCODE_CALC_HASH },
67 	{ ENCODE_CALC_HASH,	"HASHONLY",	0,		ENCODE_CLEAR_HASH },
68 
69 	{ ENCODE_DECRYPT,	"DECRYPT",	0,		ENCODE_ENCRYPT },
70 	{ ENCODE_ENCRYPT,	"ENCRYPT",	0,		ENCODE_DECRYPT },
71 
72 	{ ENCODE_NO_SIGN,	"NO-SIGN",	"NOSIGN",	ENCODE_SIGN },
73 	{ ENCODE_SIGN,		"SIGN",		"TRUCHA",	ENCODE_NO_SIGN },
74 
75 	{ 0,0,0,0 }
76     };
77 
78     const int stat = ScanCommandListMask(arg,tab);
79     if ( stat >= 0 )
80 	return stat & ENCODE_MASK;
81 
82     ERROR0(ERR_SYNTAX,"Illegal encoding mode (option --enc): '%s'\n",arg);
83     return -1;
84 }
85 
86 //-----------------------------------------------------------------------------
87 
88 enumEncoding encoding = ENCODE_DEFAULT;
89 
ScanOptEncoding(ccp arg)90 int ScanOptEncoding ( ccp arg )
91 {
92     const int new_encoding = ScanEncoding(arg);
93     if ( new_encoding == -1 )
94 	return 1;
95     encoding = new_encoding;
96     return 0;
97 }
98 
99 //-----------------------------------------------------------------------------
100 
101 static const enumEncoding encoding_m_tab[]
102 	= { ENCODE_M_HASH, ENCODE_M_CRYPT, ENCODE_M_SIGN, 0 };
103 
SetEncoding(enumEncoding val,enumEncoding set_mask,enumEncoding default_mask)104 enumEncoding SetEncoding
105 	( enumEncoding val, enumEncoding set_mask, enumEncoding default_mask )
106 {
107     TRACE("SetEncoding(%04x,%04x,%04x)\n",val,set_mask,default_mask);
108 
109     const enumEncoding * tab;
110     for ( tab = encoding_m_tab; *tab; tab++ )
111     {
112 	// set values
113 	if ( set_mask & *tab )
114 	    val = val & ~*tab | set_mask & *tab;
115 
116 	// if more than 1 bit set: clear it
117 	if ( Count1Bits32( val & *tab ) > 1 )
118 	    val &= ~*tab;
119 
120 	// if no bits are set: use default
121 	if ( !(val & *tab) )
122 	    val |= default_mask & *tab;
123     }
124 
125     TRACE(" -> %04x\n", val & ENCODE_MASK );
126     return val & ENCODE_MASK;
127 }
128 
129 //
130 ///////////////////////////////////////////////////////////////////////////////
131 ///////////////			--region			///////////////
132 ///////////////////////////////////////////////////////////////////////////////
133 
ScanRegion(ccp arg)134 enumRegion ScanRegion ( ccp arg )
135 {
136     static const CommandTab_t tab[] =
137     {
138 	{ REGION_JAP,		"JAPAN",	"JAP",		0 },
139 	{ REGION_USA,		"USA",		0,		0 },
140 	{ REGION_EUR,		"EUROPE",	"EUR",		0 },
141 	{ REGION_KOR,		"KOREA",	"KOR",		0 },
142 
143 	{ REGION__AUTO,		"AUTO",		0,		0 },
144 	{ REGION__FILE,		"FILE",		0,		0 },
145 
146 	{ 0,0,0,0 }
147     };
148 
149     const int stat = ScanCommandListMask(arg,tab);
150     if ( stat >= 0 )
151 	return stat;
152 
153     // try if arg is a number
154     char * end;
155     ulong num = strtoul(arg,&end,10);
156     if ( end != arg && !*end )
157 	return num;
158 
159     ERROR0(ERR_SYNTAX,"Illegal region mode (option --region): '%s'\n",arg);
160     return REGION__ERROR;
161 }
162 
163 //-----------------------------------------------------------------------------
164 
165 enumRegion opt_region = REGION__AUTO;
166 
ScanOptRegion(ccp arg)167 int ScanOptRegion ( ccp arg )
168 {
169     const int new_region = ScanRegion(arg);
170     if ( new_region == REGION__ERROR )
171 	return 1;
172     opt_region = new_region;
173     return 0;
174 }
175 
176 //-----------------------------------------------------------------------------
177 
GetRegionName(enumRegion region,ccp unkown_value)178 ccp GetRegionName ( enumRegion region, ccp unkown_value )
179 {
180     static ccp tab[] =
181     {
182 	"Japan",
183 	"USA",
184 	"Europe",
185 	"Korea"
186     };
187 
188     return (unsigned)region < sizeof(tab)/sizeof(*tab)
189 		? tab[region]
190 		: unkown_value;
191 }
192 
193 ///////////////////////////////////////////////////////////////////////////////
194 ///////////////////////////////////////////////////////////////////////////////
195 
196 static const RegionInfo_t RegionTable[] =
197 {
198 	// -> http://www.wiibrew.org/wiki/Title_Database#Region_Codes
199 
200 	/*A*/ { REGION_EUR,  0, "ALL ", "All" },
201 	/*B*/ { REGION_EUR,  0, "-?- ", "-?-" },
202 	/*C*/ { REGION_EUR,  0, "-?- ", "-?-" },
203 	/*D*/ { REGION_EUR, 1,  "GERM", "German" },
204 	/*E*/ { REGION_USA, 1,  "NTSC", "NTSC" },
205 	/*F*/ { REGION_EUR, 1,  "FREN", "French" },
206 	/*G*/ { REGION_EUR,  0, "-?- ", "-?-" },
207 	/*H*/ { REGION_EUR,  0, "NL  ", "Netherlands" },	// ??
208 	/*I*/ { REGION_EUR, 1,  "ITAL", "Italian" },
209 	/*J*/ { REGION_JAP, 1,  "JAPA", "Japan" },
210 	/*K*/ { REGION_KOR, 1,  "KORE", "Korea" },
211 	/*L*/ { REGION_JAP, 1,  "J>PL", "Japan->PAL" },
212 	/*M*/ { REGION_USA, 1,  "A>PL", "America->PAL" },
213 	/*N*/ { REGION_JAP, 1,  "J>US", "Japan->NTSC" },
214 	/*O*/ { REGION_EUR,  0, "-?- ", "-?-" },
215 	/*P*/ { REGION_EUR, 1,  "PAL ", "PAL" },
216 	/*Q*/ { REGION_KOR, 1,  "KO/J", "Korea (japanese)" },
217 	/*R*/ { REGION_EUR,  0, "RUS ", "Russia" },		// ??
218 	/*S*/ { REGION_EUR, 1,  "SPAN", "Spanish" },
219 	/*T*/ { REGION_KOR, 1,  "KO/E", "Korea (english)" },
220 	/*U*/ { REGION_EUR,  0, "AUS ", "Australia" },		// ??
221 	/*V*/ { REGION_EUR,  0, "SCAN", "Scandinavian" },	// ??
222 	/*W*/ { REGION_EUR,  0, "CHIN", "China" },		// ??
223 	/*X*/ { REGION_EUR, 1,  "RF  ", "Region free" },
224 	/*Y*/ { REGION_EUR,  0, "-?- ", "-?-" },
225 	/*Z*/ { REGION_EUR,  0, "-?- ", "-?-" },
226 
227 	/*?*/ { REGION_EUR,  0, "-?- ", "-?-" } // illegal region_code
228 };
229 
230 //-----------------------------------------------------------------------------
231 
GetRegionInfo(char region_code)232 const RegionInfo_t * GetRegionInfo ( char region_code )
233 {
234     region_code = toupper((int)region_code);
235     if ( region_code < 'A' || region_code > 'Z' )
236 	region_code = 'Z' + 1;
237     return RegionTable + (region_code-'A');
238 }
239 
240 //
241 ///////////////////////////////////////////////////////////////////////////////
242 ///////////////			--common-key			///////////////
243 ///////////////////////////////////////////////////////////////////////////////
244 
ScanCommonKey(ccp arg)245 wd_ckey_index_t ScanCommonKey ( ccp arg )
246 {
247     static const CommandTab_t tab[] =
248     {
249 	{ WD_CKEY_STANDARD,	"STANDARD",	0,		0 },
250 	{ WD_CKEY_KOREA,	"KOREAN",	0,		0 },
251 	{ WD_CKEY__N,		"AUTO",		0,		0 },
252 
253 	{ 0,0,0,0 }
254     };
255 
256     const int stat = ScanCommandListMask(arg,tab);
257     if ( stat >= 0 )
258 	return stat;
259 
260     // try if arg is a number
261     char * end;
262     ulong num = strtoul(arg,&end,10);
263     if ( end != arg && !*end && num < WD_CKEY__N )
264 	return num;
265 
266     ERROR0(ERR_SYNTAX,"Illegal common key index (option --common-key): '%s'\n",arg);
267     return -1;
268 }
269 
270 //-----------------------------------------------------------------------------
271 
272 enumRegion opt_common_key = WD_CKEY__N;
273 
ScanOptCommonKey(ccp arg)274 int ScanOptCommonKey ( ccp arg )
275 {
276     const wd_ckey_index_t new_common_key = ScanCommonKey(arg);
277     if ( new_common_key == -1 )
278 	return 1;
279     opt_common_key = new_common_key;
280     return 0;
281 }
282 
283 //
284 ///////////////////////////////////////////////////////////////////////////////
285 ///////////////			--ios				///////////////
286 ///////////////////////////////////////////////////////////////////////////////
287 
288 u64 opt_ios = 0;
289 bool opt_ios_valid = false;
290 
291 //-----------------------------------------------------------------------------
292 
ScanSysVersion(u64 * ios,ccp arg)293 bool ScanSysVersion ( u64 * ios, ccp arg )
294 {
295     u32 stat, lo, hi = 1;
296 
297     arg = ScanNumU32(arg,&stat,&lo,0,~(u32)0);
298     if (!stat)
299 	return false;
300 
301     if ( *arg == ':' || *arg == '-' )
302     {
303 	arg++;
304 	hi = lo;
305 	arg = ScanNumU32(arg,&stat,&lo,0,~(u32)0);
306 	if (!stat)
307 	    return false;
308     }
309 
310     if (ios)
311 	*ios = (u64)hi << 32 | lo;
312 
313     return !*arg;
314 }
315 
316 //-----------------------------------------------------------------------------
317 
ScanOptIOS(ccp arg)318 int ScanOptIOS ( ccp arg )
319 {
320     opt_ios = 0;
321     opt_ios_valid = false;
322 
323     if ( !arg || !*arg )
324 	return 0;
325 
326     opt_ios_valid = ScanSysVersion(&opt_ios,arg);
327     if (opt_ios_valid)
328 	return 0;
329 
330     ERROR0(ERR_SYNTAX,"Illegal system version (option --ios): %s\n",arg);
331     return 1;
332 }
333 
334 //
335 ///////////////////////////////////////////////////////////////////////////////
336 ///////////////			--modify			///////////////
337 ///////////////////////////////////////////////////////////////////////////////
338 
ScanModify(ccp arg)339 wd_modify_t ScanModify ( ccp arg )
340 {
341     static const CommandTab_t tab[] =
342     {
343 	{ WD_MODIFY__NONE,	"NONE",		"-",	WD_MODIFY__ALL },
344 	{ WD_MODIFY__ALL,	"ALL",		0,	WD_MODIFY__ALL },
345 	{ WD_MODIFY__AUTO,	"AUTO",		0,	WD_MODIFY__ALL },
346 
347 	{ WD_MODIFY_DISC,	"DISC",		0,	0 },
348 	{ WD_MODIFY_BOOT,	"BOOT",		0,	0 },
349 	{ WD_MODIFY_TICKET,	"TICKET",	"TIK",	0 },
350 	{ WD_MODIFY_TMD,	"TMD",		0,	0 },
351 	{ WD_MODIFY_WBFS,	"WBFS",		0,	0 },
352 
353 	{ 0,0,0,0 }
354     };
355 
356     const int stat = ScanCommandList(arg,tab,0,true,0,0);
357     if ( stat >= 0 )
358 	return ( stat & WD_MODIFY__ALL ? stat & WD_MODIFY__ALL : stat ) | WD_MODIFY__ALWAYS;
359 
360     ERROR0(ERR_SYNTAX,"Illegal modify mode (option --modify): '%s'\n",arg);
361     return -1;
362 }
363 
364 //-----------------------------------------------------------------------------
365 
366 wd_modify_t opt_modify = WD_MODIFY__AUTO | WD_MODIFY__ALWAYS;
367 
ScanOptModify(ccp arg)368 int ScanOptModify ( ccp arg )
369 {
370     const int new_modify = ScanModify(arg);
371     if ( new_modify == -1 )
372 	return 1;
373     ASSERT( new_modify & WD_MODIFY__ALWAYS );
374     opt_modify = new_modify;
375     return 0;
376 }
377 
378 //
379 ///////////////////////////////////////////////////////////////////////////////
380 ///////////////			    --name			///////////////
381 ///////////////////////////////////////////////////////////////////////////////
382 
383 ccp modify_name = 0;
384 static char modify_name_buf[WII_TITLE_SIZE];
385 
386 //-----------------------------------------------------------------------------
387 
ScanOptName(ccp arg)388 int ScanOptName ( ccp arg )
389 {
390     if ( !arg || !*arg )
391     {
392 	modify_name = 0;
393 	return 0;
394     }
395 
396     const size_t max_len = sizeof(modify_name_buf) - 1;
397     size_t len = strlen(arg);
398     if ( len > max_len )
399     {
400 	ERROR0(ERR_WARNING,"option --name: name is %zu characters to long:\n!\t-> %s\n",
401 		len - max_len, arg);
402 	len = max_len;
403     }
404 
405     ASSERT( len < sizeof(modify_name_buf) );
406     memset(modify_name_buf,0,sizeof(modify_name_buf));
407     memcpy(modify_name_buf,arg,len);
408     modify_name = modify_name_buf;
409 
410     return 0;
411 }
412 
413 //
414 ///////////////////////////////////////////////////////////////////////////////
415 ///////////////			--id  &  --xxx-id		///////////////
416 ///////////////////////////////////////////////////////////////////////////////
417 
418 ccp modify_id		= 0;
419 ccp modify_disc_id	= 0;
420 ccp modify_boot_id	= 0;
421 ccp modify_ticket_id	= 0;
422 ccp modify_tmd_id	= 0;
423 ccp modify_wbfs_id	= 0;
424 
425 static char modify_id_buf	[7];
426 static char modify_disc_id_buf	[7];
427 static char modify_boot_id_buf	[7];
428 static char modify_ticket_id_buf[5];
429 static char modify_tmd_id_buf	[5];
430 static char modify_wbfs_id_buf	[7];
431 
432 //-----------------------------------------------------------------------------
433 
ScanOptIdHelper(ccp p_arg,ccp opt_name,ccp * id_ptr,char * buf,int max_len)434 static int ScanOptIdHelper
435 	( ccp p_arg, ccp opt_name, ccp *id_ptr, char *buf, int max_len )
436 {
437     DASSERT(opt_name);
438     DASSERT(id_ptr);
439     DASSERT(buf);
440     DASSERT( max_len == 4 || max_len == 6 ); // not really needed
441 
442     *id_ptr = 0;
443     memset(buf,0,max_len+1);
444     if ( !p_arg || !*p_arg )
445 	return 0;
446 
447     ccp plus = 0, arg = p_arg;
448     char *dest = buf;
449     while ( *arg )
450     {
451 	if ( *arg == '+' && !plus )
452 	{
453 	    plus = dest;
454 	    arg++;
455 	    continue;
456 	}
457 
458 	if ( dest >= buf + max_len )
459 	{
460 	    ERROR0(ERR_SYNTAX,"Option --%s: ID is %zu characters to long: %s\n",
461 		    opt_name, strlen(arg), p_arg );
462 	    return 1;
463 	}
464 
465 	if ( *arg == '.'  || *arg == '_' )
466 	{
467 	    *dest++ = *arg++;
468 	}
469 	else if ( isalnum((int)*arg))
470 	{
471 	    *dest++ = toupper((int)*arg++);
472 	}
473 	else
474 	{
475 	    ERROR0(ERR_SYNTAX,"Option --%s: Illegal character '%c' at index #%u: %s\n",
476 			opt_name, *arg, (int)(arg-p_arg), p_arg );
477 	    return 1;
478 	}
479     }
480 
481     if (plus)
482     {
483 	char *dest2 = buf + max_len;
484 	while ( dest > plus )
485 	    *--dest2 = *--dest;
486 	while ( dest2 > plus )
487 	    *--dest2 = '.';
488     }
489 
490     *id_ptr = buf;
491     return 0;
492 }
493 
494 //-----------------------------------------------------------------------------
495 
ScanOptId(ccp arg)496 int ScanOptId ( ccp arg )
497 {
498     return ScanOptIdHelper( arg, "id", &modify_id,
499 			modify_id_buf, sizeof(modify_id_buf)-1 );
500 }
501 
ScanOptDiscId(ccp arg)502 int ScanOptDiscId ( ccp arg )
503 {
504     return ScanOptIdHelper( arg, "disc-id", &modify_disc_id,
505 			modify_disc_id_buf, sizeof(modify_disc_id_buf)-1 );
506 }
507 
ScanOptBootId(ccp arg)508 int ScanOptBootId ( ccp arg )
509 {
510     return ScanOptIdHelper( arg, "boot-id", &modify_boot_id,
511 			modify_boot_id_buf, sizeof(modify_boot_id_buf)-1 );
512 }
513 
ScanOptTicketId(ccp arg)514 int ScanOptTicketId ( ccp arg )
515 {
516     return ScanOptIdHelper( arg, "ticket-id", &modify_ticket_id,
517 			modify_ticket_id_buf, sizeof(modify_ticket_id_buf)-1 );
518 }
519 
ScanOptTmdId(ccp arg)520 int ScanOptTmdId ( ccp arg )
521 {
522     return ScanOptIdHelper( arg, "tmd-id", &modify_tmd_id,
523 			modify_tmd_id_buf, sizeof(modify_tmd_id_buf)-1 );
524 }
525 
ScanOptWbfsId(ccp arg)526 int ScanOptWbfsId ( ccp arg )
527 {
528     return ScanOptIdHelper( arg, "wbfs-id", &modify_wbfs_id,
529 			modify_wbfs_id_buf, sizeof(modify_wbfs_id_buf)-1 );
530 }
531 
532 //-----------------------------------------------------------------------------
533 
NormID(wd_modify_t condition,ccp * id_ptr,char * buf,int max_len)534 static void NormID
535 	( wd_modify_t condition, ccp *id_ptr, char *buf, int max_len )
536 {
537     noPRINT("NormID() cond=%03x, len=%u, src=%s\n",condition,max_len,modify_id);
538     DASSERT(id_ptr);
539 
540     ccp src = modify_id;
541     char *dest = buf;
542 
543     if ( !*id_ptr && src && condition & opt_modify )
544     {
545 	for ( ; max_len > 0 && *src && *dest; src++, dest++, max_len-- )
546 	    if ( *dest == '.' )
547 		*dest = *src;
548 
549 	for ( ; max_len > 0 && *src; max_len-- )
550 	    *dest++ = *src++;
551     }
552 
553     while ( max_len-- > 0 && *dest )
554 	dest++;
555 
556     while ( dest > buf && dest[-1] == '.' )
557 	dest--;
558     *dest = 0;
559     *id_ptr = dest == buf ? 0 : buf;
560     noPRINT(" -> new-id=%s\n",*id_ptr);
561 }
562 
NormalizeIdOptions()563 void NormalizeIdOptions()
564 {
565     NormID( WD_MODIFY__AUTO|WD_MODIFY_DISC, &modify_disc_id,
566 		modify_disc_id_buf, sizeof(modify_disc_id_buf)-1 );
567     NormID( WD_MODIFY__AUTO|WD_MODIFY_BOOT, &modify_boot_id,
568 		modify_boot_id_buf, sizeof(modify_boot_id_buf)-1 );
569     NormID( WD_MODIFY__AUTO|WD_MODIFY_TICKET, &modify_ticket_id,
570 		modify_ticket_id_buf, sizeof(modify_ticket_id_buf)-1 );
571     NormID( WD_MODIFY__AUTO|WD_MODIFY_TMD,  &modify_tmd_id,
572 		modify_tmd_id_buf,  sizeof(modify_tmd_id_buf)-1 );
573     NormID( WD_MODIFY__AUTO|WD_MODIFY_WBFS, &modify_wbfs_id,
574 		modify_wbfs_id_buf, sizeof(modify_wbfs_id_buf)-1 );
575 }
576 
577 //-----------------------------------------------------------------------------
578 
PatchId(void * dest_id,ccp patch_id,int skip,int maxlen)579 bool PatchId
580 (
581     void	*dest_id,	// destination with 'maxlen' byte
582     ccp		patch_id,	// NULL or patch string
583     int		skip,		// 'patch_id' starts at index 'skip'
584     int		maxlen		// length of destination ID
585 )
586 {
587     ASSERT(dest_id);
588     if ( !patch_id || maxlen < 1 )
589 	return false;
590 
591     PRINT("PATCH ID: %.*s -> %.*s\n",maxlen,(ccp)dest_id,maxlen,patch_id);
592 
593     while ( skip-- > 0 && *patch_id )
594 	patch_id++;
595 
596     char *dest;
597     for ( dest = dest_id; *patch_id && maxlen > 0; patch_id++, dest++, maxlen-- )
598     {
599 	if ( *patch_id != '.' )
600 	    *dest = *patch_id;
601     }
602     return true;
603 }
604 
605 //-----------------------------------------------------------------------------
606 
CopyPatchId(void * dest,const void * src,ccp patch_id,int maxlen,bool null_term)607 bool CopyPatchId
608 (
609     void	*dest,		// destination with 'maxlen' byte
610     const void	*src,		// source of ID. If NULL or empty: clear dest
611     ccp		patch_id,	// NULL or patch string
612     int		maxlen,		// length of destination ID
613     bool	null_term	// true: Add an additional 0 byte to end of dest
614 )
615 {
616     DASSERT(dest);
617     DASSERT( maxlen>0 && maxlen <= 6 );
618 
619     if ( src && *(u8*)src )
620     {
621 	memcpy(dest,src,maxlen);
622 	if (null_term)
623 	    ((u8*)dest)[maxlen] = 0;
624 	return PatchId(dest,patch_id,0,maxlen);
625     }
626 
627     if (null_term)
628 	maxlen++;
629     memset(dest,0,maxlen);
630     return false;
631 }
632 
633 //-----------------------------------------------------------------------------
634 
CopyPatchWbfsId(char * dest_id6,const void * source_id6)635 bool CopyPatchWbfsId ( char *dest_id6, const void * source_id6 )
636 {
637     return CopyPatchId(dest_id6,source_id6,modify_wbfs_id,6,true);
638 }
639 
640 //-----------------------------------------------------------------------------
641 
CopyPatchDiscId(char * dest_id6,const void * source_id6)642 bool CopyPatchDiscId ( char *dest_id6, const void * source_id6 )
643 {
644     return opt_hook >= 0
645 	&& CopyPatchId(dest_id6,source_id6,modify_disc_id,6,true);
646 }
647 
648 //-----------------------------------------------------------------------------
649 
PatchIdCond(void * id,int skip,int maxlen,wd_modify_t condition)650 bool PatchIdCond ( void * id, int skip, int maxlen, wd_modify_t condition )
651 {
652     return ( opt_modify & condition ) && PatchId(id,modify_id,skip,maxlen);
653 }
654 
655 //-----------------------------------------------------------------------------
656 
PatchName(void * name,wd_modify_t condition)657 bool PatchName ( void * name, wd_modify_t condition )
658 {
659     ASSERT(name);
660     if ( !modify_name || !(opt_modify & condition) )
661 	return false;
662 
663     ASSERT( strlen(modify_name) < WII_TITLE_SIZE );
664     memcpy(name,modify_name,WII_TITLE_SIZE);
665     return true;
666 }
667 
668 //-----------------------------------------------------------------------------
669 
PatchDiscHeader(void * dhead,const void * patch_id,const void * patch_name)670 bool PatchDiscHeader ( void * dhead, const void * patch_id, const void * patch_name )
671 {
672     DASSERT(dhead);
673     bool stat = false;
674 
675     if (patch_id)
676 	stat = wd_patch_id(dhead,dhead,patch_id,6);
677 
678     if (patch_name)
679     {
680 	char * title = (char*)dhead + WII_TITLE_OFF;
681 	if (memcmp(title,patch_name,WII_TITLE_SIZE))
682 	{
683 	    stat = true;
684 	    strncpy(title,patch_name,WII_TITLE_SIZE);
685 	    title[WII_TITLE_SIZE-1] = 0;
686 	}
687     }
688 
689     return stat;
690 }
691 
692 //
693 ///////////////////////////////////////////////////////////////////////////////
694 ///////////////			--trim				///////////////
695 ///////////////////////////////////////////////////////////////////////////////
696 
697 wd_trim_mode_t opt_trim = WD_TRIM_DEFAULT;
698 
699 //-----------------------------------------------------------------------------
700 
ScanTrim(ccp arg,ccp err_text_extend)701 wd_trim_mode_t ScanTrim
702 (
703     ccp arg,			// argument to scan
704     ccp err_text_extend		// error message extention
705 )
706 {
707     static const CommandTab_t tab[] =
708     {
709 	{ WD_TRIM_DEFAULT,	"DEFAULT",	0,	WD_TRIM_M_ALL },
710 
711 	{ WD_TRIM_NONE,		"NONE",		"-",	WD_TRIM_ALL },
712 	{ WD_TRIM_ALL,		"ALL",		0,	WD_TRIM_ALL },
713 	{ WD_TRIM_FAST,		"FAST",		0,	WD_TRIM_ALL },
714 
715 	{ WD_TRIM_DISC,		"DISC",		"D",	WD_TRIM_DEFAULT },
716 	{ WD_TRIM_PART,		"PARTITION",	"P",	WD_TRIM_DEFAULT },
717 	{ WD_TRIM_FST,		"FILESYSTEM",	"F",	WD_TRIM_DEFAULT },
718 
719 	{ 0,			"BEGIN",	0,	WD_TRIM_DEFAULT | WD_TRIM_F_END },
720 	{ WD_TRIM_F_END,	"END",		0,	WD_TRIM_DEFAULT | WD_TRIM_F_END },
721 
722 	{ 0,0,0,0 }
723     };
724 
725     const int stat = ScanCommandList(arg,tab,0,true,0,0);
726     if ( stat >= 0 )
727 	return stat;
728 
729     ERROR0(ERR_SYNTAX,"Illegal trim mode%s: '%s'\n",err_text_extend,arg);
730     return -1;
731 }
732 
733 //-----------------------------------------------------------------------------
734 
ScanOptTrim(ccp arg)735 int ScanOptTrim
736 (
737     ccp arg			// argument to scan
738 )
739 {
740     const int new_trim = ScanTrim(arg," (option --trim)");
741     if ( new_trim == -1 )
742 	return 1;
743     opt_trim = new_trim;
744     return 0;
745 }
746 
747 //
748 ///////////////////////////////////////////////////////////////////////////////
749 ///////////////	    --align & --align-part & --align-files	///////////////
750 ///////////////////////////////////////////////////////////////////////////////
751 
752 u32  opt_align1		= 0;
753 u32  opt_align2		= 0;
754 u32  opt_align3		= 0;
755 u32  opt_align_part	= 0;
756 bool opt_align_files	= false;
757 
758 //-----------------------------------------------------------------------------
759 
ScanOptAlign(ccp p_arg)760 int ScanOptAlign ( ccp p_arg )
761 {
762     opt_align1 = opt_align2 = opt_align3 = 0;
763 
764     static u32 * align_tab[] = { &opt_align1, &opt_align2, &opt_align3, 0 };
765     u32 ** align_ptr = align_tab;
766 
767     ccp arg = p_arg, prev_arg = 0;
768     char * dest_end = iobuf + sizeof(iobuf) - 1;
769 
770     while ( arg && *arg && *align_ptr )
771     {
772 	ccp save_arg = arg;
773 	char * dest = iobuf;
774 	while ( dest < dest_end && *arg && *arg != ',' )
775 	    *dest++ = *arg++;
776 	*dest = 0;
777 	if ( dest > iobuf && ScanSizeOptU32(
778 				*align_ptr,	// u32 * num
779 				iobuf,		// ccp source
780 				1,		// default_factor1
781 				0,		// int force_base
782 				"align",	// ccp opt_name
783 				4,		// u64 min
784 				WII_SECTOR_SIZE,// u64 max
785 				0,		// u32 multiple
786 				1,		// u32 pow2
787 				true		// bool print_err
788 				))
789 	    return 1;
790 
791 	if ( prev_arg && align_ptr[-1][0] >= align_ptr[0][0] )
792 	{
793 	    ERROR0(ERR_SEMANTIC,
794 			"Option --align: Ascending order expected: %.*s\n",
795 			(int)(arg - prev_arg), prev_arg );
796 	    return 1;
797 	}
798 
799 	align_ptr++;
800 	if ( !*align_ptr || !*arg )
801 	    break;
802 	arg++;
803 	prev_arg = save_arg;
804     }
805 
806     if (*arg)
807     {
808 	ERROR0(ERR_SEMANTIC,
809 		"Option --align: End of parameter expected: %.20s\n",arg);
810 	return 1;
811     }
812 
813     return 0;
814 }
815 
816 //-----------------------------------------------------------------------------
817 
ScanOptAlignPart(ccp arg)818 int ScanOptAlignPart ( ccp arg )
819 {
820     return ScanSizeOptU32(
821 		&opt_align_part,	// u32 * num
822 		arg,			// ccp source
823 		1,			// default_factor1
824 		0,			// int force_base
825 		"align-part",		// ccp opt_name
826 		WII_SECTOR_SIZE,	// u64 min
827 		WII_GROUP_SIZE,		// u64 max
828 		0,			// u32 multiple
829 		1,			// u32 pow2
830 		true			// bool print_err
831 		) != ERR_OK;
832 }
833 
834 //
835 ///////////////////////////////////////////////////////////////////////////////
836 ///////////////			--disc-size			///////////////
837 ///////////////////////////////////////////////////////////////////////////////
838 
839 u64 opt_disc_size = 0;
840 
841 //-----------------------------------------------------------------------------
842 
ScanOptDiscSize(ccp arg)843 int ScanOptDiscSize ( ccp arg )
844 {
845     return ScanSizeOptU64(&opt_disc_size,arg,GiB,0,"disc-size",
846 		0, WII_SECTOR_SIZE * (u64)WII_MAX_SECTORS,
847 		WII_SECTOR_SIZE, 0, true ) != ERR_OK;
848 }
849 
850 //
851 ///////////////////////////////////////////////////////////////////////////////
852 ///////////////		    --add-files & --rm-files		///////////////
853 ///////////////////////////////////////////////////////////////////////////////
854 
855 StringField_t add_file;
856 StringField_t repl_file;
857 
858 //-----------------------------------------------------------------------------
859 
ScanOptFile(ccp arg,bool add)860 int ScanOptFile ( ccp arg, bool add )
861 {
862     if ( arg && *arg )
863     {
864 	StringField_t * sf = add ? &add_file : &repl_file;
865 	InsertStringField(sf,arg,false);
866     }
867     return 0;
868 }
869 
870 //
871 ///////////////////////////////////////////////////////////////////////////////
872 ///////////////		      RewriteModifiedSF()		///////////////
873 ///////////////////////////////////////////////////////////////////////////////
874 
RewriteModifiedSF(SuperFile_t * fi,SuperFile_t * fo,struct WBFS_t * wbfs,u64 off)875 enumError RewriteModifiedSF
876 (
877     SuperFile_t		* fi,		// valid input file
878     SuperFile_t		* fo,		// NULL or output file
879     struct WBFS_t	* wbfs,		// NULL or output WBFS
880     u64			off		// offset: write_off := read_off + off
881 )
882 {
883     ASSERT(fi);
884     ASSERT(fi->f.is_reading);
885     if (wbfs)
886 	fo = wbfs->sf;
887     ASSERT(fo);
888     ASSERT(fo->f.is_writing);
889     TRACE("+++ RewriteModifiedSF(%p,%p,%p,%llx), oft=%d,%d\n",
890 		fi,fo,wbfs,off,fi->iod.oft,fo->iod.oft);
891 
892     wd_disc_t * disc = fi->disc1;
893     if ( !fi->modified_list.used && ( !disc || !disc->reloc ))
894     {
895 	TRACE("--- RewriteModifiedSF() ERR_OK: nothing to do\n");
896 	return ERR_OK;
897     }
898 
899     UpdateSignatureFST(fi->fst); // NULL allowed
900 
901     if ( logging > 2 )
902     {
903 	printf("\n Rewrite:\n\n");
904 	PrintMemMap(&fi->modified_list,stdout,3,0);
905 	if ( disc && disc->reloc )
906 	    wd_print_relocation(stdout,3,disc->reloc,true);
907 	putchar('\n');
908     }
909 
910  #ifdef DEBUG
911     fprintf(TRACE_FILE,"Rewrite:\n");
912     PrintMemMap(&fi->modified_list,TRACE_FILE,3,0);
913  #endif
914 
915     IOData_t saved_iod;
916     memcpy(&saved_iod,&fo->iod,sizeof(saved_iod));
917     WBFS_t * saved_wbfs = fo->wbfs;
918     bool close_disc = false;
919 
920     if (wbfs)
921     {
922 	TRACE(" - WBFS stat: w=%p, disc=#%d,%p, oft=%d\n",
923 		wbfs, wbfs->disc_slot, wbfs->disc, fo->iod.oft );
924 	if (!wbfs->disc)
925 	{
926 	    OpenWDiscSlot(wbfs,wbfs->disc_slot,0);
927 	    if (!wbfs->disc)
928 	    {
929 		TRACE("--- RewriteModifiedSF() ERR_CANT_OPEN: wbfs disc\n");
930 		return ERR_CANT_OPEN;
931 	    }
932 	    close_disc = true;
933 	}
934 	SetupIOD(fo,OFT_WBFS,OFT_WBFS);
935 	fo->wbfs = wbfs;
936     }
937 
938     int idx;
939     enumError err = ERR_OK;
940     for ( idx = 0; idx < fi->modified_list.used && !err; idx++ )
941     {
942 	const MemMapItem_t * mmi = fi->modified_list.field[idx];
943 	err = CopyRawData2(fi,mmi->off,fo,mmi->off+off,mmi->size);
944     }
945 
946     if ( disc && disc->reloc )
947     {
948 	const wd_reloc_t * reloc = disc->reloc;
949 	for ( idx = 0; idx < WII_MAX_SECTORS && !err; idx++, reloc++ )
950 	    if ( *reloc & WD_RELOC_F_LAST )
951 	    {
952 		u64 inoff = idx*WII_SECTOR_SIZE;
953 		err = CopyRawData2(fi,inoff,fo,inoff+off,WII_SECTOR_SIZE);
954 	    }
955     }
956 
957     if (close_disc)
958     {
959 	CloseWDisc(wbfs);
960 	wbfs->disc = 0;
961     }
962 
963     memcpy(&fo->iod,&saved_iod,sizeof(fo->iod));
964     fo->wbfs = saved_wbfs;
965     TRACE("--- RewriteModifiedSF() err=%u: END\n",err);
966     return err;
967 }
968 
969 //
970 ///////////////////////////////////////////////////////////////////////////////
971 ///////////////			write patch files		///////////////
972 ///////////////////////////////////////////////////////////////////////////////
973 
SetupWritePatch(WritePatch_t * pat)974 void SetupWritePatch
975 (
976     WritePatch_t	* pat		// patch data structure
977 )
978 {
979     DASSERT(pat);
980     memset(pat,0,sizeof(*pat));
981 }
982 
983 ///////////////////////////////////////////////////////////////////////////////
984 
CloseWritePatch(WritePatch_t * pat)985 enumError CloseWritePatch
986 (
987     WritePatch_t	* pat		// patch data structure
988 )
989 {
990     DASSERT(pat);
991 
992     enumError err = ERR_OK;
993     if (pat->file)
994     {
995 	wpat_toc_header_t thead;
996 	memset(&thead,0,sizeof(thead));
997 	thead.type_size = wpat_calc_size(WPAT_TOC_HEADER,sizeof(thead));
998 	thead.entry_offset4 = htonl(ftell(pat->file)>>2);
999 	memcpy(thead.magic,wpat_magic,sizeof(thead.magic));
1000 
1001 	int n_entires = 0;
1002 	// [[2do]] write toc files
1003 	thead.n_entires = htonl(n_entires);
1004 
1005 	if ( !err && fwrite(&thead,sizeof(thead),1,pat->file) != 1 )
1006 	    err = ERROR1(ERR_WRITE_FAILED,
1007 			"Writing patch toc header failed: %s",pat->fname);
1008 
1009 	fclose(pat->file);
1010 	pat->file = 0;
1011     }
1012 
1013     if (pat->fname)
1014     {
1015 	FreeString(pat->fname);
1016 	pat->fname = 0;
1017     }
1018 
1019     // [[2do]] clear dynamic data (toc)
1020 
1021     return err;
1022 }
1023 
1024 ///////////////////////////////////////////////////////////////////////////////
1025 
CreateWritePatch(WritePatch_t * pat,ccp filename)1026 enumError CreateWritePatch
1027 (
1028     WritePatch_t	* pat,		// patch data structure
1029     ccp			filename	// filename of output file
1030 )
1031 {
1032     DASSERT(pat);
1033     DASSERT(filename);
1034 
1035     CloseWritePatch(pat);
1036     FILE * f = fopen(filename,"wb");
1037     if (!f)
1038 	return ERROR1(ERR_CANT_CREATE,
1039 		"Can't create patch file: %s\n",filename);
1040 
1041     return CreateWritePatchF(pat,f,filename);
1042 }
1043 
1044 ///////////////////////////////////////////////////////////////////////////////
1045 
CreateWritePatchF(WritePatch_t * pat,FILE * file,ccp filename)1046 enumError CreateWritePatchF
1047 (
1048     WritePatch_t	* pat,		// patch data structure
1049     FILE		* file,		// open output file
1050     ccp			filename	// NULL or known filename
1051 )
1052 {
1053     DASSERT(pat);
1054     DASSERT(file);
1055 
1056     CloseWritePatch(pat);
1057     SetupWritePatch(pat);
1058     pat->file = file;
1059     pat->fname = STRDUP( filename ? filename : "?" );
1060 
1061     //--- write patch header & creator comment
1062 
1063     wpat_header_t hd;
1064     memcpy(hd.magic,wpat_magic,sizeof(hd.magic));
1065     hd.type_size	= wpat_calc_size(WPAT_HEADER,sizeof(hd)-sizeof(hd.magic));
1066     hd.version		= htonl(WIT_PATCH_VERSION);
1067     hd.compatible	= htonl(WIT_PATCH_COMPATIBLE);
1068 
1069     if ( fwrite(&hd,sizeof(hd),1,pat->file) != 1 )
1070 	return ERROR1(ERR_WRITE_FAILED,
1071 		"Writing patch header failed: %s",pat->fname);
1072 
1073     return WritePatchComment(pat,"%s",
1074 		"Creator: " TOOLSET_SHORT " v" VERSION " r" REVISION " " SYSTEM );
1075 }
1076 
1077 ///////////////////////////////////////////////////////////////////////////////
1078 ///////////////////////////////////////////////////////////////////////////////
1079 
WritePatchComment(WritePatch_t * pat,ccp format,...)1080 enumError WritePatchComment
1081 (
1082     WritePatch_t	* pat,		// patch data structure
1083     ccp			format,		// format string
1084     ...					// arguments for 'format'
1085 )
1086 {
1087     DASSERT(pat);
1088     DASSERT(pat->file);
1089 
1090     struct obj_t
1091     {
1092 	wpat_comment_t cm;
1093 	char comment[1000];
1094     } obj;
1095 
1096 
1097     va_list arg;
1098     va_start(arg,format);
1099     const int comment_len = vsnprintf(obj.comment,sizeof(obj.comment),format,arg);
1100     va_end(arg);
1101 
1102     obj.cm.type_size = wpat_calc_size(WPAT_COMMENT,sizeof(wpat_comment_t)+comment_len);
1103     if ( fwrite(&obj,wpat_get_size(obj.cm.type_size),1,pat->file) != 1 )
1104 	return ERROR1(ERR_WRITE_FAILED,
1105 		"Writing patch header failed: %s",pat->fname);
1106     return ERR_OK;
1107 }
1108 
1109 //
1110 ///////////////////////////////////////////////////////////////////////////////
1111 ///////////////			read patch files		///////////////
1112 ///////////////////////////////////////////////////////////////////////////////
1113 
SetupReadPatch(ReadPatch_t * pat)1114 void SetupReadPatch
1115 (
1116     ReadPatch_t		* pat		// patch data structure
1117 )
1118 {
1119     DASSERT(pat);
1120     memset(pat,0,sizeof(*pat));
1121 }
1122 
1123 ///////////////////////////////////////////////////////////////////////////////
1124 
CloseReadPatch(ReadPatch_t * pat)1125 enumError CloseReadPatch
1126 (
1127     ReadPatch_t		* pat		// patch data structure
1128 )
1129 {
1130     DASSERT(pat);
1131 
1132     enumError err = ERR_OK;
1133     if (pat->file)
1134     {
1135 	fclose(pat->file);
1136 	pat->file = 0;
1137     }
1138 
1139     if (pat->fname)
1140     {
1141 	FreeString(pat->fname);
1142 	pat->fname = 0;
1143     }
1144 
1145     // [[2do]] clear dynamic data (toc)
1146 
1147     return err;
1148 }
1149 
1150 ///////////////////////////////////////////////////////////////////////////////
1151 
OpenReadPatch(ReadPatch_t * pat,ccp filename)1152 enumError OpenReadPatch
1153 (
1154     ReadPatch_t		* pat,		// patch data structure
1155     ccp			filename	// filename of input file
1156 )
1157 {
1158     DASSERT(pat);
1159     DASSERT(filename);
1160 
1161     CloseReadPatch(pat);
1162     FILE * f = fopen(filename,"rb");
1163     if (!f)
1164 	return ERROR1(ERR_CANT_OPEN,
1165 		"Can't open patch file: %s\n",filename);
1166 
1167     return OpenReadPatchF(pat,f,filename);
1168 }
1169 
1170 ///////////////////////////////////////////////////////////////////////////////
1171 
OpenReadPatchF(ReadPatch_t * pat,FILE * file,ccp filename)1172 enumError OpenReadPatchF
1173 (
1174     ReadPatch_t		* pat,		// patch data structure
1175     FILE		* file,		// open input file
1176     ccp			filename	// NULL or known filename
1177 )
1178 {
1179     DASSERT(pat);
1180     DASSERT(file);
1181 
1182     CloseReadPatch(pat);
1183     SetupReadPatch(pat);
1184     pat->file = file;
1185     pat->fname = STRDUP( filename ? filename : "?" );
1186 
1187     int readlen = fread(pat->read_buf,1,sizeof(pat->read_buf),pat->file);
1188     if ( readlen < sizeof(wpat_header_t) )
1189 	goto invalid;
1190 
1191     const wpat_header_t * hd = (wpat_header_t*)pat->read_buf;
1192     if (memcmp(hd->magic,wpat_magic,sizeof(hd->magic)))
1193 	goto invalid;
1194 
1195     pat->cur_type   = hd->type_size.type;
1196     pat->cur_size   = wpat_get_size(hd->type_size);
1197     if ( pat->cur_type != WPAT_HEADER )
1198 	goto invalid;
1199 
1200     pat->is_valid   = true;
1201     pat->version    = ntohl(hd->version);
1202     pat->compatible = ntohl(hd->compatible);
1203     SupportedVersionReadPatch(pat,true);
1204     return ERR_OK;
1205 
1206  invalid:
1207     return ERROR0(ERR_INVALID_FILE,"Invalid patch file: %s\n",pat->fname);
1208 
1209 }
1210 
1211 ///////////////////////////////////////////////////////////////////////////////
1212 
SupportedVersionReadPatch(ReadPatch_t * pat,bool silent)1213 enumError SupportedVersionReadPatch
1214 (
1215     ReadPatch_t		* pat,		// patch data structure
1216     bool		silent		// true: suppress error message
1217 )
1218 {
1219     DASSERT(pat);
1220     pat->is_compatible = WIT_PATCH_VERSION >= pat->compatible
1221 			&& pat->version >= WIT_PATCH_READ_COMPATIBLE;
1222     if (!pat->is_compatible)
1223     {
1224 	if (silent)
1225 	    return ERR_INVALID_VERSION;
1226 
1227 	if ( WIT_PATCH_READ_COMPATIBLE < WIT_PATCH_VERSION )
1228 	    return ERROR0(ERR_INVALID_VERSION,
1229 		"Patch version %s not supported (compatible %s .. %s): %s\n",
1230 		PrintVersion(0,0,pat->version),
1231 		PrintVersion(0,0,WIT_PATCH_READ_COMPATIBLE),
1232 		PrintVersion(0,0,WIT_PATCH_VERSION),
1233 		pat->fname );
1234 
1235 	return ERROR0(ERR_INVALID_VERSION,
1236 		"Patch version %s not supported (%s expected): %s\n",
1237 		PrintVersion(0,0,pat->version),
1238 		PrintVersion(0,0,WIT_PATCH_VERSION),
1239 		pat->fname );
1240     }
1241 
1242     return ERR_OK;
1243 }
1244 
1245 ///////////////////////////////////////////////////////////////////////////////
1246 ///////////////////////////////////////////////////////////////////////////////
1247 
GetNextReadPatch(ReadPatch_t * pat)1248 enumError GetNextReadPatch
1249 (
1250     ReadPatch_t		* pat		// patch data structure
1251 )
1252 {
1253     DASSERT(pat);
1254 
1255     pat->cur_type = 0;
1256     pat->cur_size = 0;
1257     return ERR_OK;
1258 }
1259 
1260 //
1261 ///////////////////////////////////////////////////////////////////////////////
1262 ///////////////			     E N D			///////////////
1263 ///////////////////////////////////////////////////////////////////////////////
1264 
1265