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