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 <unistd.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <limits.h>
46 #include <ctype.h>
47 #include <errno.h>
48 #include <dirent.h>
49 #include <time.h>
50
51 #include "debug.h"
52 #include "wbfs-interface.h"
53 #include "titles.h"
54
55 ///////////////////////////////////////////////////////////////////////////////
56
57 #ifndef ENABLE_PARTITIONS_WORKAROUND
58 #if defined(__CYGWIN__)
59 // work around for a bug in /proc/partitions since cygwin 1.7.6
60 #define ENABLE_PARTITIONS_WORKAROUND 1
61 #else
62 #define ENABLE_PARTITIONS_WORKAROUND 0
63 #endif
64 #endif
65
66 //
67 ///////////////////////////////////////////////////////////////////////////////
68 /////////////// partitions ///////////////
69 ///////////////////////////////////////////////////////////////////////////////
70
71 PartitionInfo_t * first_partition_info = 0;
72 PartitionInfo_t ** append_partition_info = &first_partition_info;
73
74 int pi_count = 0;
75 PartitionInfo_t * pi_list[MAX_WBFS+1];
76 WDiscList_t pi_wlist = {0,0,0,0};
77 u32 pi_free_mib = 0;
78
79 int opt_part = 0;
80 int opt_auto = 0;
81 int opt_all = 0;
82
83 ///////////////////////////////////////////////////////////////////////////////
84
CreatePartitionInfo(ccp path,enumPartSource source)85 PartitionInfo_t * CreatePartitionInfo ( ccp path, enumPartSource source )
86 {
87 char real_path_buf[PATH_MAX];
88 ccp real_path = real_path_buf;
89 if (!realpath(path,real_path_buf))
90 {
91 TRACE("CAN'T DETERMINE REAL PATH: %s\n",path);
92 real_path = path;
93 }
94
95 PartitionInfo_t * info = first_partition_info;
96 while ( info && strcmp(info->real_path,real_path) )
97 info = info->next;
98
99 if (!info)
100 {
101 // new entry
102
103 PartitionInfo_t * info = MALLOC(sizeof(PartitionInfo_t));
104 memset(info,0,sizeof(PartitionInfo_t));
105 info->path = STRDUP(path);
106 info->real_path = STRDUP(real_path);
107 info->part_mode = PM_UNKNOWN;
108 info->source = source;
109 *append_partition_info = info;
110 append_partition_info = &info->next;
111 TRACE("PARTITION inserted: %s\n",real_path);
112 }
113 else if ( source == PS_PARAM )
114 {
115 // overrides previous definition
116
117 info->source = source;
118 FREE((char*)info->path);
119 info->path = STRDUP(path);
120 TRACE("PARTITION redefined: %s\n",real_path);
121 }
122 return info;
123 }
124
125 ///////////////////////////////////////////////////////////////////////////////
126
AddPartition(ccp arg,int unused)127 int AddPartition ( ccp arg, int unused )
128 {
129 if (opt_part++)
130 opt_all++;
131 CreatePartitionInfo(arg,PS_PARAM);
132 return 0;
133 }
134
135 ///////////////////////////////////////////////////////////////////////////////
136
ScanDevForPartitions(ccp dev_prefix)137 int ScanDevForPartitions ( ccp dev_prefix )
138 {
139 TRACE("ScanDevForPartitions(%s)\n",dev_prefix);
140 size_t len_prefix = strlen(dev_prefix);
141
142 static char prefix[] = "/dev/";
143 const int bufsize = 100;
144 char buf[bufsize+1+sizeof(prefix)];
145 strcpy(buf,prefix);
146
147 int count = 0;
148
149 DIR * dir = opendir("/dev");
150 if (dir)
151 {
152 for (;;)
153 {
154 struct dirent * dent = readdir(dir);
155 if (!dent)
156 break;
157 #ifdef _DIRENT_HAVE_D_TYPE
158 if ( dent->d_type == DT_BLK || dent->d_type == DT_CHR )
159 {
160 #endif
161 if (!memcmp(dent->d_name,dev_prefix,len_prefix))
162 {
163 StringCopyE(buf+sizeof(prefix)-1,buf+sizeof(buf),dent->d_name);
164 TRACE(" - part found: %s\n",buf);
165 CreatePartitionInfo(buf,PS_AUTO);
166 count++;
167 }
168 #ifdef _DIRENT_HAVE_D_TYPE
169 }
170 #endif
171 }
172 closedir(dir);
173 }
174
175 return count;
176 }
177
178 ///////////////////////////////////////////////////////////////////////////////
179 #if ENABLE_PARTITIONS_WORKAROUND
180
iterate_dev(ccp base)181 static void iterate_dev ( ccp base )
182 {
183 TRACE("ITERATE %s\n",base);
184
185 char dev[100];
186 int i;
187 for ( i = 1; i <= 15; i++ )
188 {
189 snprintf(dev,sizeof(dev),"%s%u",base,i);
190 CreatePartitionInfo(dev,PS_AUTO_IGNORE);
191 }
192 }
193
194 #endif // ENABLE_PARTITIONS_WORKAROUND
195 ///////////////////////////////////////////////////////////////////////////////
196
ScanPartitions(bool all)197 int ScanPartitions ( bool all )
198 {
199 opt_auto++;
200 opt_all += all;
201
202 int count = 0;
203
204 static char prefix[] = "/dev/";
205 enum { bufsize = 100 };
206 char buf[bufsize+1];
207 char buf2[bufsize+1+sizeof(prefix)];
208 strcpy(buf2,prefix);
209
210 #if ENABLE_PARTITIONS_WORKAROUND
211 PRINT("PARTITIONS WORKAROUND ENABLED\n");
212 char prev_disc[sizeof(buf2)] = {0};
213 int prev_disc_len = 0;
214 #endif
215
216 FILE * f = fopen("/proc/partitions","r");
217 if (f)
218 {
219 TRACE("SCAN /proc/partitions\n");
220
221 // skip first line
222 fgets(buf,bufsize,f);
223
224 while (fgets(buf,bufsize,f))
225 {
226 char * ptr = buf;
227 while (*ptr)
228 ptr++;
229 if ( ptr > buf )
230 {
231 ptr--;
232 while ( ptr > buf && (u8)*ptr <= ' ' )
233 ptr--;
234 ptr[1] = 0;
235 while ( ptr > buf && isalnum((int)*ptr) )
236 ptr--;
237 if (*++ptr)
238 {
239 strcpy(buf2+sizeof(prefix)-1,ptr);
240
241 #if ENABLE_PARTITIONS_WORKAROUND
242
243 if ( prev_disc_len && memcmp(prev_disc,buf2,prev_disc_len) )
244 iterate_dev(prev_disc);
245
246 prev_disc_len = strlen(buf2);
247 const int ch = buf2[prev_disc_len-1];
248 if ( ch >= '0' && ch <= '9' )
249 prev_disc_len = 0;
250 else
251 memcpy(prev_disc,buf2,sizeof(prev_disc));
252 TRACE("STORE: %d %s\n",prev_disc_len,prev_disc);
253 #endif
254
255 CreatePartitionInfo(buf2,PS_AUTO);
256 }
257 }
258 }
259 fclose(f);
260
261 #if ENABLE_PARTITIONS_WORKAROUND
262 if (prev_disc_len)
263 iterate_dev(prev_disc);
264 #endif
265 }
266 else
267 {
268 ScanDevForPartitions("sd");
269 if (!ScanDevForPartitions("rdisk"))
270 ScanDevForPartitions("disk");
271 }
272 return count;
273 }
274
275 ///////////////////////////////////////////////////////////////////////////////
276 ///////////////////////////////////////////////////////////////////////////////
277
AddEnvPartitions()278 void AddEnvPartitions()
279 {
280 TRACE("AddEnvPartitions() PART1=%d, AUTO=%d, all=%d, first=%p\n",
281 opt_part, opt_auto, opt_all, first_partition_info );
282
283 if ( !first_partition_info && !opt_part && !opt_auto )
284 {
285 TRACE("lookup environment var 'WWT_WBFS'\n");
286 char * env = getenv("WWT_WBFS");
287 if ( env && *env )
288 {
289 char * ptr = env;
290 for(;;)
291 {
292 env = ptr;
293 while ( *ptr && *ptr != ';' )
294 ptr++;
295 if ( ptr > env )
296 {
297 char ch = *ptr;
298 *ptr = 0;
299 CreatePartitionInfo(env,PS_ENV);
300 opt_all++;
301 *ptr = ch;
302 }
303 if (!*ptr)
304 break;
305 ptr++;
306 }
307 }
308 }
309 }
310
311 ///////////////////////////////////////////////////////////////////////////////
312 ///////////////////////////////////////////////////////////////////////////////
313
314 int wbfs_count = 0; // number of wbfs partitions
315
AnalyzePartitions(FILE * outfile,bool non_found_is_ok,bool scan_wbfs)316 enumError AnalyzePartitions ( FILE * outfile, bool non_found_is_ok, bool scan_wbfs )
317 {
318 TRACE("AnalyzePartitions(,%d,%d) PART1=%d, AUTO=%d, all=%d, first=%p\n",
319 non_found_is_ok, scan_wbfs,
320 opt_part, opt_auto, opt_all, first_partition_info );
321
322 AddEnvPartitions();
323
324 // standalone --all enables --auto
325 if ( opt_all && !opt_part && !opt_auto )
326 ScanPartitions(false);
327
328 wbfs_count = 0; // number of wbfs partitions
329
330 WBFS_t wbfs;
331 InitializeWBFS(&wbfs);
332 PartitionInfo_t * info;
333 for ( info = first_partition_info; info; info = info->next )
334 {
335 TRACE("Analyze partition %s, mode=%d, source=%d\n",
336 info->path, info->part_mode, info->source);
337 TRACE(" - realpath: %s\n",info->real_path);
338
339 if ( info->part_mode == PM_UNKNOWN )
340 {
341 ccp read_error = 0;
342 File_t F;
343 InitializeFile(&F);
344 F.disable_errors = info->source != PS_PARAM || !outfile;
345 enumError stat = OpenFile(&F,info->real_path,IOM_IS_WBFS_PART);
346 if (stat)
347 {
348 if ( info->source == PS_AUTO_IGNORE )
349 info->part_mode = PM_IGNORE;
350 read_error = ""; // message already printed
351 goto _done;
352 }
353
354 TRACE(" - st_mode=%x reg=%d dir=%d chr=%d blk=%d fifo=%d link=%d sock=%d\n",
355 F.st.st_mode,
356 S_ISREG(F.st.st_mode),
357 S_ISDIR(F.st.st_mode),
358 S_ISCHR(F.st.st_mode),
359 S_ISBLK(F.st.st_mode),
360 S_ISFIFO(F.st.st_mode),
361 S_ISLNK(F.st.st_mode),
362 S_ISSOCK(F.st.st_mode) );
363
364 info->filemode = GetFileMode(F.st.st_mode);
365 TRACE(" -> filemode= %x -> %d\n",F.st.st_mode,info->filemode);
366 if ( info->filemode == FM_OTHER )
367 {
368 info->part_mode = PM_WRONG_TYPE;
369 read_error = "Neither regular file nor char or block device: %s\n";
370 goto _done;
371 }
372
373 TRACE("sizeof: st_size=%zd st_blksize=%zd st_blocks=%zd\n",
374 sizeof(F.st.st_size),
375 sizeof(F.st.st_blksize), sizeof(F.st.st_blocks) );
376 TRACE("st_blksize=%lld st_blocks=%lld\n",
377 (u64)F.st.st_blksize, (u64)F.st.st_blocks );
378 info->file_size = F.st.st_size;
379 info->hss = GetHSS(F.fd,HD_SECTOR_SIZE);
380 info->disk_usage = info->hss * (u64)F.st.st_blocks;
381 TRACE(" - hss: %13d\n",info->hss);
382 TRACE(" - file-size: %13lld = %5lld GiB\n",info->file_size,info->file_size/GiB);
383 TRACE(" - disk-usage: %13lld = %5lld GiB\n",info->disk_usage,info->disk_usage/GiB);
384
385 wbfs_head_t whead;
386 stat = F.st.st_size < sizeof(whead)
387 ? ERR_WARNING
388 : ReadF(&F,&whead,sizeof(whead));
389 if (stat)
390 {
391 read_error = "Can't read WBFS header: %s\n";
392 goto _done;
393 }
394
395 if (memcmp(&whead.magic,"WBFS",sizeof(whead.magic)))
396 {
397 info->part_mode = PM_NO_WBFS_MAGIC;
398 read_error = "No WBFS magic found: %s\n";
399 goto _done;
400 }
401 info->wbfs_hss = 1 << whead.hd_sec_sz_s;
402 info->wbfs_wss = 1 << whead.wbfs_sec_sz_s;
403 info->wbfs_size = (u64)whead.n_hd_sec * info->wbfs_hss;
404
405 info->part_mode = PM_WBFS_MAGIC_FOUND;
406 wbfs_count++;
407
408 if (!info->file_size)
409 {
410 // second try: use lseek() (needed for block devices)
411 info->file_size = lseek(F.fd,0,SEEK_END);
412 if ( info->file_size == (off_t)-1 )
413 info->file_size = 0;
414 TRACE(" - file-size: %13lld = %5lld GiB\n",info->file_size,info->file_size/GiB);
415 }
416
417 if (scan_wbfs)
418 {
419 OpenPartWBFS(&wbfs,info,false);
420 ResetWBFS(&wbfs);
421 }
422
423 _done:;
424 int syserr = errno;
425 ClearFile(&F,false);
426
427 if ( read_error && info->part_mode != PM_IGNORE )
428 {
429 TRACE(read_error," -> ",info->real_path);
430 if ( info->part_mode < PM_CANT_READ )
431 info->part_mode = PM_CANT_READ;
432 if ( *read_error && info->source == PS_PARAM && outfile )
433 ERROR(syserr,ERR_READ_FAILED,read_error,info->real_path);
434 }
435 }
436 }
437 ASSERT( !wbfs.wbfs && !wbfs.sf ); // wbfs is closed!
438 TRACE("*** %d WBFS partition(s) found\n",wbfs_count);
439
440 if (!outfile)
441 return wbfs_count ? ERR_OK : ERR_NO_WBFS_FOUND;
442
443 enumError return_stat = ERR_OK;
444 if ( !wbfs_count )
445 {
446 if ( !non_found_is_ok || verbose >= 1 )
447 ERROR0(ERR_NO_WBFS_FOUND,"no WBFS partitions found -> abort\n");
448 if ( !non_found_is_ok && !return_stat )
449 return_stat = ERR_NO_WBFS_FOUND;
450 }
451 else if ( return_stat )
452 {
453 // [[2do]] ??? never reached
454 if (!print_sections)
455 fprintf(outfile,"%d WBFS partition%s found\n",
456 wbfs_count, wbfs_count == 1 ? "" : "s" );
457 ERROR0(ERR_WARNING,"Abort because of read errors while scanning\n");
458 }
459 else if ( wbfs_count > 1 )
460 {
461 if ( !opt_all )
462 return_stat = ERROR0(ERR_TO_MUCH_WBFS_FOUND,
463 "%d (more than 1) WBFS partitions found -> abort.\n",wbfs_count);
464 else if ( verbose >= 1 && !print_sections )
465 fprintf(outfile,"%d WBFS partition%s found\n",
466 wbfs_count, wbfs_count == 1 ? "" : "s" );
467 }
468 else if ( verbose > 0 && !print_sections )
469 {
470 fprintf(outfile,"One WBFS partition found.\n");
471 }
472
473 return return_stat;
474 }
475
476 ///////////////////////////////////////////////////////////////////////////////
477 ///////////////////////////////////////////////////////////////////////////////
478
ScanPartitionGames()479 void ScanPartitionGames()
480 {
481 int pi_disc_count = 0;
482 pi_free_mib = 0;
483
484 WBFS_t wbfs;
485 InitializeWBFS(&wbfs);
486 PartitionInfo_t * info;
487 enumError stat;
488 for ( stat = GetFirstWBFS(&wbfs,&info,false);
489 !stat;
490 stat = GetNextWBFS(&wbfs,&info,false) )
491 {
492 if ( !info->part_index || !info->wlist )
493 {
494 if ( pi_count >= MAX_WBFS )
495 {
496 ERROR0(ERR_TO_MUCH_WBFS_FOUND,"Too much (>%d) WBFS partitions\n",MAX_WBFS);
497 break;
498 }
499
500 info->part_index = ++pi_count;
501 pi_list[pi_count] = info;
502 info->part_index = pi_count;
503 info->wlist = GenerateWDiscList(&wbfs,pi_count);
504 }
505 pi_disc_count += info->wlist->used;
506 pi_free_mib += wbfs.free_mib;
507 }
508
509 if ( pi_wlist.used != pi_disc_count )
510 {
511 ResetWDiscList(&pi_wlist);
512 pi_wlist.sort_mode = SORT_NONE;
513 pi_wlist.used = pi_wlist.size = pi_disc_count;
514 pi_wlist.first_disc = CALLOC(pi_disc_count,sizeof(WDiscListItem_t));
515
516 WDiscListItem_t * dest = pi_wlist.first_disc;
517 int i;
518 for ( i = 1; i <= pi_count; i++ )
519 {
520 WDiscList_t * wlist = pi_list[i]->wlist;
521 ASSERT(wlist);
522 ASSERT( dest-pi_wlist.first_disc + wlist->used <= pi_disc_count );
523 memcpy(dest,wlist->first_disc,wlist->used*sizeof(*dest));
524 dest += wlist->used;
525 pi_wlist.total_size_mib += wlist->total_size_mib;
526 }
527 ASSERT ( dest == pi_wlist.first_disc + pi_disc_count );
528 }
529 }
530
531 //
532 ///////////////////////////////////////////////////////////////////////////////
533 ///////////////////////////////////////////////////////////////////////////////
534
ScanParamID6(StringField_t * select_list,const ParamList_t * param)535 enumError ScanParamID6
536 (
537 StringField_t * select_list, // append all results to this list
538 const ParamList_t * param // first param of a list to check
539 )
540 {
541 DASSERT(select_list);
542
543 char rule[8]; //, *rule_end = rule + 7;
544
545 for ( ; param; param = param->next )
546 {
547 if (!param->arg)
548 continue;
549 ccp arg = param->arg;
550 for(;;)
551 {
552 while ( *arg > 0 && *arg <= ' ' || *arg == ',' )
553 arg++;
554 if (!*arg)
555 break;
556
557 switch(*arg)
558 {
559 case '+': *rule = '+'; arg++; break;
560 case '/':
561 case '-': *rule = '-'; arg++; break;
562 default: *rule = '+';
563 }
564
565 // [[2do]] ScanArgID() verwenden!
566
567 ccp start = arg;
568 int err = 0, wildcards = 0;
569 while ( *arg > ' ' && *arg != ',' )
570 {
571 int ch = *arg++;
572 if ( ch == '+' || ch == '*' )
573 wildcards++;
574 else if (!isalnum(ch) && !strchr("_.",ch))
575 err++;
576 }
577 const int arglen = arg - start;
578 if ( err || wildcards > 1 || arglen > 6 )
579 return ERROR0(ERR_SEMANTIC,
580 "Illegal ID selector: %.*s\n", (int)(arg-start), start );
581
582 char * dest = rule+1;
583 for ( ; start < arg; start++ )
584 {
585 if ( *start == '+' || *start == '*' )
586 {
587 int count = 7 - arglen;
588 while ( count-- > 0 )
589 *dest++ = '.';
590 }
591 else
592 *dest++ = toupper((int)*start);
593 DASSERT( dest < rule + 8 );
594 }
595 while ( dest[-1] == '.' )
596 dest--;
597 *dest = 0;
598 AppendStringField(select_list,rule,false);
599 }
600 }
601 return ERR_OK;
602 }
603
604 ///////////////////////////////////////////////////////////////////////////////
605
AppendListID6(StringField_t * id6_list,const StringField_t * select_list,WBFS_t * wbfs)606 int AppendListID6 // returns number of inserted ids
607 (
608 StringField_t * id6_list, // append all selected IDs in this list
609 const StringField_t * select_list, // selector list
610 WBFS_t * wbfs // open WBFS file
611 )
612 {
613 DASSERT(id6_list);
614 DASSERT(select_list);
615 DASSERT(wbfs);
616
617 const int count = id6_list->used;
618 wbfs_t * w = wbfs->wbfs;
619 if (w)
620 {
621 id6_t *id_list = wbfs_load_id_list(w,false);
622 DASSERT(id_list);
623 for ( ; **id_list; id_list++ )
624 if (MatchRulesetID(select_list,*id_list))
625 InsertStringField(id6_list,*id_list,false);
626 }
627
628 return id6_list->used - count;
629 }
630
631 ///////////////////////////////////////////////////////////////////////////////
632
AppendWListID6(StringField_t * id6_list,const StringField_t * select_list,WDiscList_t * wlist,bool add_to_title_db)633 int AppendWListID6 // returns number of inserted ids
634 (
635 StringField_t * id6_list, // append all selected IDs in this list
636 const StringField_t * select_list, // selector list
637 WDiscList_t * wlist, // valid list
638 bool add_to_title_db // true: add to title DB if unkown
639 )
640 {
641 DASSERT(id6_list);
642 DASSERT(wlist);
643
644 const int count = id6_list->used;
645
646 WDiscListItem_t * ptr = wlist->first_disc;
647 WDiscListItem_t * end = ptr + wlist->used;
648 for ( ; ptr < end; ptr++ )
649 if ( !select_list || MatchRulesetID(select_list,ptr->id6) )
650 {
651 InsertStringField(id6_list,ptr->id6,false);
652 if ( add_to_title_db && !GetTitle(ptr->id6,0) )
653 InsertID(&title_db,ptr->id6,ptr->name64);
654 }
655
656 return id6_list->used - count;
657 }
658
659 ///////////////////////////////////////////////////////////////////////////////
660
MatchRulesetID(const StringField_t * select_list,ccp id)661 bool MatchRulesetID
662 (
663 const StringField_t * select_list, // selector list
664 ccp id // id to compare
665 )
666 {
667 DASSERT(select_list);
668 DASSERT(id);
669
670 ccp * pattern = select_list->field;
671 ccp * end_pattern = pattern + select_list->used;
672 for ( ; pattern < end_pattern; pattern++ )
673 if (MatchPatternID(*pattern+1,id))
674 return **pattern == '+';
675
676 return !select_list->used || end_pattern[-1][0] == '-';
677 }
678
679 ///////////////////////////////////////////////////////////////////////////////
680
MatchPatternID(ccp pattern,ccp id)681 bool MatchPatternID
682 (
683 ccp pattern, // pattern, '.' is a wildcard
684 ccp id // id to compare
685 )
686 {
687 DASSERT(pattern);
688 DASSERT(id);
689
690 noTRACE("MATCH |%s|%s|\n",pattern,id);
691 for(;;)
692 {
693 char pat = *pattern++;
694 if (!pat)
695 return true;
696
697 char ch = *id++;
698 if ( !ch || pat != '.' && pat != ch )
699 return false;
700 }
701 }
702
703 //
704 ///////////////////////////////////////////////////////////////////////////////
705 ///////////////////////////////////////////////////////////////////////////////
706
CheckParamRename(bool rename_id,bool allow_plus,bool allow_index)707 enumError CheckParamRename ( bool rename_id, bool allow_plus, bool allow_index )
708 {
709 int syntax_count = 0, semantic_count = 0;
710 ParamList_t * param;
711 for ( param = first_param; param; param = param->next )
712 {
713 memset(param->selector,0,sizeof(param->selector));
714 memset(param->id6,0,sizeof(param->id6));
715
716 char * arg = (char*)param->arg;
717 if (!arg)
718 continue;
719
720 while ( *arg > 0 &&*arg <= ' ' )
721 arg++;
722
723 long index = -1;
724 if ( allow_plus && ( *arg == '+' || *arg == '*' ) )
725 {
726 param->selector[0] = '+';
727 arg++;
728 }
729 else if ( CheckID(arg,true,false) == 6 )
730 {
731 // ID6 found
732 int i;
733 for ( i = 0; i < 6; i++ )
734 param->selector[i] = toupper((int)*arg++);
735 }
736 else if ( allow_index && *arg == '#' )
737 {
738 // a slot index;
739 index = strtoul(arg+1,&arg,0);
740 snprintf(param->selector,sizeof(param->selector),"#%lu",index);
741 }
742 else if ( allow_index )
743 {
744 char * start = arg;
745 index = strtoul(arg,&arg,0);
746 if ( arg == start )
747 {
748 ERROR0(ERR_SEMANTIC,
749 "ID6 or INDEX or #SLOT expected: %s\n", param->arg );
750 syntax_count++;
751 continue;
752 }
753 snprintf(param->selector,sizeof(param->selector),"$%lu",index);
754 }
755 else
756 {
757 ERROR0(ERR_SEMANTIC,
758 "ID6 expected: %s\n", param->arg );
759 syntax_count++;
760 continue;
761 }
762
763 if ( index >= 0 && wbfs_count != 1 )
764 {
765 ERROR0(ERR_SEMANTIC,
766 "Slot or disc index is only allowed if exact 1 WBFS is selected: %s\n",
767 param->arg );
768 semantic_count++;
769 continue;
770 }
771
772 if ( index > 99999 )
773 {
774 ERROR0(ERR_SEMANTIC,
775 "Slot or disc index to large: %s\n", param->arg );
776 semantic_count++;
777 continue;
778 }
779
780 while ( *arg > 0 &&*arg <= ' ' )
781 arg++;
782
783 if ( *arg != '=' )
784 {
785 ERROR0(ERR_SYNTAX,"Missing '=': %s -> %s\n", param->arg, arg );
786 syntax_count++;
787 continue;
788 }
789
790 arg++;
791 bool scan_title = !rename_id;
792 if (rename_id)
793 {
794 while ( *arg > 0 &&*arg <= ' ' )
795 arg++;
796
797 if ( *arg != ',' )
798 {
799 const int idlen = CountIDChars(arg,true,true);
800 if ( idlen < 1 || idlen > 6 )
801 {
802 ERROR0(ERR_SYNTAX,"Missing ID: %s -> %s\n", param->arg, arg );
803 syntax_count++;
804 continue;
805 }
806 memset(param->id6,'.',6);
807 int i;
808 for ( i = 0; i < idlen; i++ )
809 param->id6[i] = toupper((int)*arg++);
810 while ( *arg > 0 &&*arg <= ' ' )
811 arg++;
812 }
813
814 if ( *arg == ',' )
815 {
816 arg++;
817 scan_title = true;
818 }
819 }
820
821 if (scan_title)
822 {
823 if (!*arg)
824 {
825 ERROR0(ERR_SYNTAX,"Missing title: %s -> %s\n", param->arg, arg );
826 syntax_count++;
827 continue;
828 }
829 param->arg = arg;
830 }
831 else
832 param->arg = 0;
833 }
834
835 return syntax_count ? ERR_SYNTAX : semantic_count ? ERR_SEMANTIC : ERR_OK;
836 }
837
838 //
839 ///////////////////////////////////////////////////////////////////////////////
840 /////////////// access WBFS partitions ///////////////
841 ///////////////////////////////////////////////////////////////////////////////
842
843 static WBFS_t wbfs_cache;
844 static bool wbfs_cache_valid = false;
845 bool wbfs_cache_enabled = true; // [[2do]] is 'wbfs_cache_enabled' [[obsolete]] ?
846
847 wbfs_balloc_mode_t opt_wbfs_alloc = WBFS_BA_AUTO;
848
849 //-----------------------------------------------------------------------------
850
CloseWBFSCache()851 enumError CloseWBFSCache()
852 {
853 enumError err = ERR_OK;
854 if (wbfs_cache_valid)
855 {
856 TRACE("WBFS: CLOSE CACHE: %s\n",wbfs_cache.sf->f.fname);
857 wbfs_cache_valid = false;
858 wbfs_cache.cache_candidate = false;
859 err = ResetWBFS(&wbfs_cache);
860 }
861 return err;
862 }
863
864 ///////////////////////////////////////////////////////////////////////////////
865
InitializeWBFS(WBFS_t * w)866 void InitializeWBFS ( WBFS_t * w )
867 {
868 ASSERT(w);
869 memset(w,0,sizeof(*w));
870 w->disc_slot = -1;
871 }
872
873 ///////////////////////////////////////////////////////////////////////////////
874
ResetWBFS(WBFS_t * w)875 enumError ResetWBFS ( WBFS_t * w )
876 {
877 ASSERT(w);
878 TRACE("ResetWBFS() fd=%d, alloced=%d\n", w->sf ? GetFD(&w->sf->f) : -2, w->sf_alloced );
879
880 CloseWDisc(w);
881
882 enumError err = ERR_OK;
883 if ( wbfs_cache_enabled
884 && w->cache_candidate
885 && w->sf_alloced
886 && w->sf
887 && IsOpenSF(w->sf) )
888 {
889 CloseWBFSCache();
890 TRACE("WBFS: SETUP CACHE: %s\n",w->sf->f.fname);
891 DASSERT(!wbfs_cache_valid);
892 memcpy(&wbfs_cache,w,sizeof(wbfs_cache));
893 wbfs_cache_valid = true;
894 }
895 else
896 {
897 if (w->wbfs)
898 wbfs_close(w->wbfs);
899
900 if (w->sf)
901 {
902 w->sf->wbfs = 0;
903 if (w->sf_alloced)
904 {
905 err = ResetSF(w->sf,0);
906 FREE(w->sf);
907 }
908 }
909 }
910
911 InitializeWBFS(w);
912 return err;
913 }
914
915 ///////////////////////////////////////////////////////////////////////////////
916
OpenParWBFS(WBFS_t * w,SuperFile_t * sf,bool print_err,wbfs_param_t * par)917 enumError OpenParWBFS
918 ( WBFS_t * w, SuperFile_t * sf, bool print_err , wbfs_param_t * par )
919 {
920 ASSERT(w);
921 ASSERT(sf);
922 ASSERT(par);
923 TRACE("OpenParWBFS(%p,%p,%d,%p) fd=%d\n",
924 w, sf, print_err, par, GetFD(&sf->f) );
925
926 ResetWBFS(w);
927 w->sf = sf;
928
929 bool maybe_wbfs_file = false;
930 wbfs_balloc_mode_t balloc_mode = opt_wbfs_alloc;
931 if (S_ISREG(sf->f.st.st_mode))
932 {
933 maybe_wbfs_file = true;
934 char format[2*PATH_MAX], fname[PATH_MAX];
935 CalcSplitFilename(format,sizeof(format),sf->f.fname,OFT_WBFS);
936 snprintf(fname,sizeof(fname),format,1);
937 struct stat st;
938 if (!stat(fname,&st))
939 SetupSplitFile(&sf->f,OFT_WBFS,0);
940 if ( balloc_mode == WBFS_BA_AUTO )
941 balloc_mode = WBFS_BA_FIRST; // better because of sparse effect
942 }
943
944 TRACELINE;
945 if ( par->reset > 0 )
946 {
947 if (!par->hd_sector_size)
948 par->hd_sector_size = HD_SECTOR_SIZE;
949 if (!par->num_hd_sector)
950 par->num_hd_sector = (u32)( sf->f.st.st_size / par->hd_sector_size );
951 }
952 else
953 {
954 TRACELINE;
955 char buf[HD_SECTOR_SIZE];
956 enumError err = ReadAtF(&sf->f,0,&buf,sizeof(buf));
957 if (err)
958 return err;
959
960 TRACELINE;
961 wbfs_head_t * whead = (wbfs_head_t*)buf;
962 par->hd_sector_size = 1 << whead->hd_sec_sz_s;
963 par->num_hd_sector = 0; // not 'whead->n_hd_sec'
964 }
965 sf->f.sector_size = par->hd_sector_size;
966
967 TRACELINE;
968 ASSERT(!w->wbfs);
969 TRACE("CALL wbfs_open_partition_param(ss=%u,ns=%u,reset=%d)\n",
970 par->hd_sector_size, par->num_hd_sector, par->reset );
971
972 par->read_hdsector = WrapperReadSector;
973 par->write_hdsector = WrapperWriteSector;
974 par->callback_data = sf;
975 par->part_lba = 0;
976 par->balloc_mode = balloc_mode;
977
978 w->wbfs = wbfs_open_partition_param(par);
979
980 TRACELINE;
981 if (!w->wbfs)
982 {
983 TRACE("!! can't open WBFS %s\n",sf->f.fname);
984 if (print_err)
985 {
986 if ( par->reset > 0 )
987 ERROR0(ERR_WBFS,"Can't format WBFS partition: %s\n",sf->f.fname);
988 else
989 ERROR0(ERR_WBFS_INVALID,"Invalid WBFS partition: %s\n",sf->f.fname);
990 }
991 return ERR_WBFS_INVALID;
992 }
993
994 TRACELINE;
995 id6_t *id_list = wbfs_load_id_list(w->wbfs,1);
996 CalcWBFSUsage(w);
997
998 if ( maybe_wbfs_file && id_list[0][0] && wbfs_count_discs(w->wbfs) == 1 )
999 {
1000 noPRINT("A WBFS FILE\n");
1001 w->is_wbfs_file = true;
1002 }
1003
1004 #ifdef DEBUG
1005 TRACE("WBFS %s\n\n",sf->f.fname);
1006 DumpWBFS(w,TRACE_FILE,15,SHOW_INTRO,0,0,0);
1007 #endif
1008
1009 return ERR_OK;
1010 }
1011
1012 ///////////////////////////////////////////////////////////////////////////////
1013
SetupWBFS(WBFS_t * w,SuperFile_t * sf,bool print_err,int sector_size,bool recover)1014 enumError SetupWBFS ( WBFS_t * w, SuperFile_t * sf,
1015 bool print_err, int sector_size, bool recover )
1016 {
1017 ASSERT(w);
1018 ASSERT(sf);
1019 TRACE("SetupWBFS(%p,%d,%d,%d) fd=%d\n",
1020 sf, print_err, sector_size, recover, GetFD(&sf->f) );
1021
1022
1023 wbfs_param_t par;
1024 memset(&par,0,sizeof(par));
1025
1026 par.hd_sector_size = sector_size;
1027 par.reset = sector_size > 0;
1028 par.clear_inodes = !recover;
1029 par.setup_iinfo = !recover && GetFileMode(sf->f.st.st_mode) > FM_PLAIN;
1030
1031 return OpenParWBFS(w,sf,print_err,&par);
1032 }
1033
1034 ///////////////////////////////////////////////////////////////////////////////
1035
CreateGrowingWBFS(WBFS_t * w,SuperFile_t * sf,off_t size,int sector_size)1036 enumError CreateGrowingWBFS ( WBFS_t * w, SuperFile_t * sf, off_t size, int sector_size )
1037 {
1038 ASSERT(w);
1039 ASSERT(sf);
1040 ASSERT(size);
1041 ASSERT(sector_size);
1042 TRACE("CreateGrowingWBFS(%p,%p,%d)\n",w,sf,sector_size);
1043
1044 if ( S_ISREG(sf->f.st.st_mode) && sf->src && prealloc_mode > PREALLOC_OFF )
1045 {
1046 const u32 bl_size = wbfs_calc_sect_size(size,sector_size);
1047 if ( prealloc_mode == PREALLOC_ALL )
1048 {
1049 wd_disc_t * disc = OpenDiscSF(sf->src,false,false);
1050 if (disc)
1051 {
1052 const u32 sect_per_block = bl_size / WII_SECTOR_SIZE;
1053 const u32 n_blocks = wd_count_used_disc_blocks(disc,sect_per_block,0);
1054 noPRINT("NB = ( %u + 1 ) * 0x%x == %llx\n",
1055 n_blocks, bl_size, (n_blocks+1) * (u64)bl_size );
1056 PreallocateF( &sf->f, 0, (n_blocks+1) * (u64)bl_size );
1057 }
1058 }
1059 else
1060 {
1061 PreallocateF(&sf->f,0,sector_size
1062 + ALIGN64( sizeof(wbfs_disc_info_t)
1063 + 2*WII_MAX_DISC_SIZE/bl_size,sector_size) );
1064 PreallocateSF(sf,bl_size,0,bl_size/WII_SECTOR_SIZE,1);
1065 }
1066 }
1067
1068 wbfs_param_t par;
1069 memset(&par,0,sizeof(par));
1070
1071 par.num_hd_sector = (u32)( size / sector_size );
1072 par.hd_sector_size = sector_size;
1073 par.reset = 1;
1074 par.iinfo.mtime = hton64(sf->f.fatt.mtime);
1075
1076 sf->f.read_behind_eof = 2;
1077
1078 const enumError err = OpenParWBFS(w,sf,true,&par);
1079 if ( !err && S_ISREG(sf->f.st.st_mode) )
1080 {
1081 TRACE("GROWING WBFS: %s\n",sf->f.fname);
1082 w->is_growing = true;
1083 }
1084 return err;
1085 }
1086
1087 ///////////////////////////////////////////////////////////////////////////////
1088
OpenWBFSHelper(WBFS_t * w,ccp filename,bool open_modify,bool print_err,wbfs_param_t * par,int sector_size,bool recover)1089 static enumError OpenWBFSHelper
1090 (
1091 WBFS_t * w,
1092 ccp filename,
1093 bool open_modify, // true: open read+write
1094 bool print_err,
1095 wbfs_param_t *par,
1096 int sector_size,
1097 bool recover
1098 )
1099 {
1100 ASSERT(w);
1101 PRINT("OpenWBFSHelper(%s,rw=%d,pe=%d,ss=%d,recover=%d)\n",
1102 filename, open_modify, print_err, sector_size, recover );
1103
1104 if ( wbfs_cache_valid
1105 && IsOpenSF(wbfs_cache.sf)
1106 && ( !open_modify || IsWritableSF(wbfs_cache.sf) )
1107 && !strcmp(wbfs_cache.sf->f.fname,filename) )
1108 {
1109 PRINT("WBFS: USE CACHE: %s\n",wbfs_cache.sf->f.fname);
1110 wbfs_cache_valid = false;
1111 ResetWBFS(w);
1112 memcpy(w,&wbfs_cache,sizeof(*w));
1113 return ERR_OK;
1114 }
1115 CloseWBFSCache();
1116
1117 SuperFile_t * sf = MALLOC(sizeof(SuperFile_t));
1118 InitializeSF(sf);
1119 sf->f.disable_errors = !print_err;
1120 enumError err = open_modify
1121 ? OpenFileModify(&sf->f,filename,IOM_IS_WBFS_PART)
1122 : OpenFile(&sf->f,filename,IOM_IS_WBFS_PART);
1123 if (err)
1124 goto abort;
1125 sf->f.disable_errors = false;
1126 SetupIOD(sf,OFT_PLAIN,OFT_PLAIN);
1127
1128 err = par ? OpenParWBFS(w,sf,print_err,par)
1129 : SetupWBFS(w,sf,print_err,sector_size,recover);
1130 if (err)
1131 goto abort;
1132
1133 w->sf_alloced = true;
1134 w->cache_candidate = true;
1135 return ERR_OK;
1136
1137 abort:
1138 ResetWBFS(w);
1139 ResetSF(sf,0);
1140 FREE(sf);
1141 return err;
1142 }
1143
1144 ///////////////////////////////////////////////////////////////////////////////
1145
OpenWBFS(WBFS_t * w,ccp filename,bool open_modify,bool print_err,wbfs_param_t * par)1146 enumError OpenWBFS
1147 (
1148 WBFS_t *w, // valid data structure
1149 ccp filename, // filename to open
1150 bool open_modify, // true: open read+write
1151 bool print_err, // true: pprint error messages
1152 wbfs_param_t *par // NULL or parameter record
1153 )
1154 {
1155 PRINT("OpenWBFS(rw=%d,pr-err=%d)\n",open_modify,print_err);
1156 return OpenWBFSHelper(w,filename,open_modify,print_err,par,0,false);
1157 }
1158
1159 ///////////////////////////////////////////////////////////////////////////////
1160
FormatWBFS(WBFS_t * w,ccp filename,bool print_err,wbfs_param_t * par,int sector_size,bool recover)1161 enumError FormatWBFS
1162 ( WBFS_t * w, ccp filename, bool print_err,
1163 wbfs_param_t * par, int sector_size, bool recover )
1164 {
1165 if ( sector_size < HD_SECTOR_SIZE )
1166 sector_size = HD_SECTOR_SIZE;
1167 return OpenWBFSHelper(w,filename,true,print_err,par,sector_size,recover);
1168 }
1169
1170 ///////////////////////////////////////////////////////////////////////////////
1171
RecoverWBFS(WBFS_t * wbfs,ccp fname,bool testmode)1172 enumError RecoverWBFS ( WBFS_t * wbfs, ccp fname, bool testmode )
1173 {
1174 ASSERT(wbfs);
1175 ASSERT(wbfs->wbfs);
1176 ASSERT(wbfs->sf);
1177
1178 wbfs_t * w = wbfs->wbfs;
1179 ASSERT(w);
1180 ASSERT(w->head);
1181 ASSERT(w->head->disc_table);
1182
1183 wbfs_load_freeblocks(w);
1184 ASSERT(w->freeblks);
1185
1186 enumError err = ERR_OK;
1187
1188 // load first wbfs sector
1189 u8 * first_sector = MALLOC(w->wbfs_sec_sz);
1190 DASSERT(wbfs->sf);
1191 err = ReadSF(wbfs->sf,0,first_sector,w->wbfs_sec_sz);
1192 if (!err)
1193 {
1194 bool inodes_dirty = false;
1195
1196 int slot;
1197 for ( slot = 0; slot < w->max_disc; slot++ )
1198 {
1199 if (!w->head->disc_table[slot])
1200 {
1201 w->head->disc_table[slot] = WBFS_SLOT_VALID|WBFS_SLOT__USER;
1202 wd_header_t * head
1203 = (wd_header_t*)( first_sector + w->hd_sec_sz + slot * w->disc_info_sz );
1204 if ( ntohl(head->wii_magic) == WII_MAGIC_DELETED )
1205 {
1206 head->wii_magic = htonl(WII_MAGIC);
1207 inodes_dirty = true;
1208 }
1209 }
1210 else
1211 w->head->disc_table[slot] &= ~WBFS_SLOT__USER;
1212 }
1213
1214 if (inodes_dirty)
1215 WriteSF(wbfs->sf,
1216 w->hd_sec_sz,
1217 first_sector + w->hd_sec_sz,
1218 w->max_disc * w->disc_info_sz );
1219
1220 memset(w->freeblks,0,w->freeblks_size4*4);
1221 SyncWBFS(wbfs,true);
1222
1223 CheckWBFS_t ck;
1224 InitializeCheckWBFS(&ck);
1225 TRACELINE;
1226 if (CheckWBFS(&ck,wbfs,-1,0,0))
1227 {
1228 err = ERR_DIFFER;
1229 ASSERT(ck.disc);
1230 bool dirty = false;
1231 int n_recoverd = 0;
1232
1233 for ( slot = 0; slot < w->max_disc; slot++ )
1234 if ( w->head->disc_table[slot] & WBFS_SLOT__USER )
1235 {
1236 CheckDisc_t * cd = ck.disc + slot;
1237 if ( cd->no_blocks
1238 || cd->bl_overlap
1239 || cd->bl_invalid )
1240 {
1241 w->head->disc_table[slot] = WBFS_SLOT_FREE;
1242 dirty = true;
1243 }
1244 else
1245 n_recoverd++;
1246 }
1247
1248 if (n_recoverd)
1249 {
1250 if (testmode)
1251 printf(" * WOULD recover %u disc%s:\n",
1252 n_recoverd, n_recoverd == 1 ? "" : "s" );
1253 else
1254 printf(" * %u disc%s recoverd\n",
1255 n_recoverd, n_recoverd == 1 ? "" : "s" );
1256
1257 for ( slot = 0; slot < w->max_disc; slot++ )
1258 if ( w->head->disc_table[slot] & WBFS_SLOT__USER )
1259 {
1260 w->head->disc_table[slot] &= ~WBFS_SLOT__USER;
1261 wd_header_t * inode
1262 = (wd_header_t*)( first_sector + w->hd_sec_sz
1263 + slot * w->disc_info_sz );
1264 ccp id6 = &inode->disc_id;
1265 printf(" - slot #%03u [%.6s] %s\n",
1266 slot, id6, GetTitle(id6,(ccp)inode->disc_title) );
1267 }
1268 }
1269
1270 if (!testmode)
1271 {
1272 if (dirty)
1273 {
1274 ResetCheckWBFS(&ck);
1275 SyncWBFS(wbfs,true);
1276 CheckWBFS(&ck,wbfs,-1,0,0);
1277 }
1278
1279 TRACELINE;
1280 RepairWBFS(&ck,0,REPAIR_FBT|REPAIR_RM_INVALID|REPAIR_RM_EMPTY,-1,0,0);
1281 TRACELINE;
1282 ResetCheckWBFS(&ck);
1283 SyncWBFS(wbfs,true);
1284 if (CheckWBFS(&ck,wbfs,1,stdout,1))
1285 printf(" *** Run REPAIR %s ***\n\n", fname ? fname : wbfs->sf->f.fname );
1286 else
1287 putchar('\n');
1288 }
1289 }
1290 ResetCheckWBFS(&ck);
1291
1292 if (testmode)
1293 {
1294 WriteSF(wbfs->sf,0,first_sector,w->wbfs_sec_sz);
1295 err = ReloadWBFS(wbfs);
1296 }
1297 }
1298
1299 FREE(first_sector);
1300 return err;
1301 }
1302
1303 ///////////////////////////////////////////////////////////////////////////////
1304
TruncateWBFS(WBFS_t * w)1305 enumError TruncateWBFS ( WBFS_t * w )
1306 {
1307 ASSERT(w);
1308 TRACE("TruncateWBFS() fd=%d fp=%p\n",
1309 w->sf ? GetFD(&w->sf->f) : -2,
1310 w->sf ? GetFP(&w->sf->f) : 0 );
1311
1312 enumError err = CloseWDisc(w);
1313 SyncWBFS(w,false);
1314 if ( w->wbfs && w->sf )
1315 {
1316 wbfs_trim(w->wbfs);
1317 const u64 cut = (u64)w->wbfs->n_hd_sec * w->wbfs->hd_sec_sz;
1318 TRACE(" - cut = %u * %u = %llu = %llx/hex\n",
1319 w->wbfs->n_hd_sec, w->wbfs->hd_sec_sz, cut, cut );
1320 SetSizeF(&w->sf->f,cut);
1321 }
1322 return err;
1323 }
1324
1325 ///////////////////////////////////////////////////////////////////////////////
1326
CalcWBFSUsage(WBFS_t * w)1327 enumError CalcWBFSUsage ( WBFS_t * w )
1328 {
1329 ASSERT(w);
1330 if (!w->wbfs)
1331 {
1332 w->used_discs = 0;
1333 w->total_discs = 0;
1334 w->free_discs = 0;
1335 w->free_mib = 0;
1336 w->total_mib = 0;
1337 w->used_mib = 0;
1338 return ERR_NO_WBFS_FOUND;
1339 }
1340
1341 w->used_discs = wbfs_count_discs(w->wbfs);
1342 w->total_discs = w->wbfs->max_disc;
1343 w->free_discs = w->total_discs - w->used_discs;
1344
1345 u32 free_count = wbfs_get_free_block_count(w->wbfs);
1346 w->free_blocks = free_count;
1347 w->free_mib = ( (u64)w->wbfs->wbfs_sec_sz * free_count ) / MiB; // round down!
1348 w->total_mib = ( (u64)w->wbfs->wbfs_sec_sz * w->wbfs->n_wbfs_sec + MiB/2 ) / MiB;
1349 w->used_mib = w->total_mib - w->free_mib;
1350
1351 return ERR_OK;
1352 }
1353
1354 ///////////////////////////////////////////////////////////////////////////////
1355
SyncWBFS(WBFS_t * w,bool force_sync)1356 enumError SyncWBFS ( WBFS_t * w, bool force_sync )
1357 {
1358 ASSERT(w);
1359 wbfs_t * wbfs = w->wbfs;
1360 if (!wbfs)
1361 return ERR_OK;
1362
1363 wbfs_disc_t * disc = w->disc;
1364 if ( disc && ( force_sync || disc->is_dirty ) )
1365 wbfs_sync_disc_header(disc);
1366
1367 if ( force_sync || wbfs->is_dirty )
1368 wbfs_sync(wbfs);
1369
1370 return CalcWBFSUsage(w);
1371 }
1372
1373 ///////////////////////////////////////////////////////////////////////////////
1374
ReloadWBFS(WBFS_t * wbfs)1375 enumError ReloadWBFS ( WBFS_t * wbfs )
1376 {
1377 ASSERT(wbfs);
1378 enumError err = ERR_OK;
1379
1380 wbfs_t * w = wbfs->wbfs;
1381 if (w)
1382 {
1383 FREE(w->freeblks);
1384 w->freeblks = 0;
1385 FREE(w->id_list);
1386 w->id_list = 0;
1387 if ( w->head && wbfs->sf )
1388 {
1389 err = ReadSF(wbfs->sf,0,w->head,w->hd_sec_sz);
1390 CalcWBFSUsage(wbfs);
1391 }
1392 }
1393
1394 return err;
1395 }
1396
1397 ///////////////////////////////////////////////////////////////////////////////
1398
OpenPartWBFS(WBFS_t * w,PartitionInfo_t * info,bool open_modify)1399 enumError OpenPartWBFS ( WBFS_t * w, PartitionInfo_t * info, bool open_modify )
1400 {
1401 ASSERT(info);
1402 if ( !info || info->part_mode < PM_WBFS_MAGIC_FOUND )
1403 {
1404 ResetWBFS(w);
1405 return ERR_NO_WBFS_FOUND;
1406 }
1407
1408 const enumError err = OpenWBFSHelper( w, info->real_path, open_modify,
1409 info->source==PS_PARAM, 0, 0, 0 );
1410 if (err)
1411 {
1412 info->part_mode = PM_WBFS_INVALID;
1413 info->ignore = true;
1414 return err;
1415 }
1416
1417 info->part_mode = PM_WBFS;
1418 return ERR_OK;
1419 }
1420
1421 ///////////////////////////////////////////////////////////////////////////////
1422
GetWBFSHelper(WBFS_t * w,PartitionInfo_t ** p_info,bool open_modify)1423 static enumError GetWBFSHelper
1424 ( WBFS_t * w, PartitionInfo_t ** p_info, bool open_modify )
1425 {
1426 ResetWBFS(w);
1427
1428 if (p_info)
1429 {
1430 PartitionInfo_t * info;
1431 for ( info = *p_info; info; info = info->next )
1432 if ( !info->ignore && OpenPartWBFS(w,info,open_modify) == ERR_OK )
1433 {
1434 *p_info = info;
1435 return ERR_OK;
1436 }
1437 *p_info = 0;
1438 }
1439 return ERR_NO_WBFS_FOUND;
1440 }
1441
1442 //-----------------------------------------------------------------------------
1443
GetFirstWBFS(WBFS_t * w,PartitionInfo_t ** info,bool open_modify)1444 enumError GetFirstWBFS ( WBFS_t * w, PartitionInfo_t ** info, bool open_modify )
1445 {
1446 if (info)
1447 *info = first_partition_info;
1448 return GetWBFSHelper(w,info,open_modify);
1449 }
1450
1451 //-----------------------------------------------------------------------------
1452
GetNextWBFS(WBFS_t * w,PartitionInfo_t ** info,bool open_modify)1453 enumError GetNextWBFS ( WBFS_t * w, PartitionInfo_t ** info, bool open_modify )
1454 {
1455 if ( info && *info )
1456 *info = (*info)->next;
1457 return GetWBFSHelper(w,info,open_modify);
1458 }
1459
1460 //-----------------------------------------------------------------------------
1461
print_wbfs_section(WBFS_t * w,int count,int total,ccp path,ccp sub_mode,ccp term_string)1462 static void print_wbfs_section
1463 (
1464 WBFS_t * w, // valid and opened WBFS
1465 int count, // wbfs counter, 1 based
1466 // if NULL: neither 'count' nor 'total' are printed
1467 int total, // total wbfs count to handle
1468 // if NULL: don't print info
1469 ccp path, // path of sourcefile
1470 // if NULL: use 'w->sf->f.fname' (real path)
1471 ccp sub_mode, // "open" | "close"
1472 ccp term_string // append this string
1473 )
1474 {
1475 DASSERT(w);
1476 DASSERT(w->wbfs);
1477 DASSERT(sub_mode);
1478 DASSERT(term_string);
1479
1480 if (!count)
1481 printf("[wbfs:%s]\n",sub_mode);
1482 else if (!total)
1483 printf("[wbfs:%s]\nwbfs-count=%u\n",sub_mode,count);
1484 else
1485 printf("[wbfs:%s]\nwbfs-count=%u\nwbfs-total=%u\n",sub_mode,count,total);
1486
1487 printf(
1488 "path=%s\n"
1489 "version=%u\n"
1490 "n-discs=%u\n"
1491 "max-discs=%u\n"
1492 "free-mib=%u\n"
1493 "used-mib=%u\n"
1494 "total-mib=%u\n"
1495 "%s"
1496 ,path ? path : w->sf ? w->sf->f.fname : ""
1497 ,w->wbfs->head->wbfs_version
1498 ,wbfs_count_discs(w->wbfs)
1499 ,w->wbfs->max_disc
1500 ,w->free_mib
1501 ,w->used_mib
1502 ,w->total_mib
1503 ,term_string
1504 );
1505 }
1506
1507 //-----------------------------------------------------------------------------
1508
LogOpenedWBFS(WBFS_t * w,int count,int total,ccp path)1509 void LogOpenedWBFS
1510 (
1511 WBFS_t * w, // valid and opened WBFS
1512 int count, // wbfs counter, 1 based
1513 // if NULL: neither 'count' nor 'total' are printed
1514 int total, // total wbfs count to handle
1515 // if NULL: don't print info
1516 ccp path // path of sourcefile
1517 // if NULL: use 'w->sf->f.fname' (real path)
1518 )
1519 {
1520 DASSERT(w);
1521 DASSERT(w->wbfs);
1522 if ( !w || !w->wbfs )
1523 return;
1524
1525 if (!path)
1526 path = w->sf ? w->sf->f.fname : "";
1527
1528 if (print_sections)
1529 print_wbfs_section(w,count,total,path,"open","\n");
1530 else if (!count)
1531 printf("%sWBFSv%u opened: %s\n",
1532 verbose>0 ? "\n" : "",
1533 w->wbfs->head->wbfs_version, path );
1534 else if (!total)
1535 printf("%sWBFSv%u #%u opened: %s\n",
1536 verbose>0 ? "\n" : "",
1537 w->wbfs->head->wbfs_version, count, path );
1538 else
1539 printf("%sWBFSv%u #%u/%u opened: %s\n",
1540 verbose>0 ? "\n" : "",
1541 w->wbfs->head->wbfs_version, count, total, path );
1542 }
1543
1544 //-----------------------------------------------------------------------------
1545
LogCloseWBFS(WBFS_t * w,int count,int total,ccp path)1546 void LogCloseWBFS
1547 (
1548 WBFS_t * w, // valid and opened WBFS
1549 int count, // wbfs counter, 1 based
1550 // if NULL: neither 'count' nor 'total' are printed
1551 int total, // total wbfs count to handle
1552 // if NULL: don't print info
1553 ccp path // path of sourcefile
1554 // if NULL: use 'w->sf->f.fname' (real path)
1555 )
1556 {
1557 if (print_sections)
1558 print_wbfs_section(w,count,total,path,"close","");
1559 }
1560
1561 //-----------------------------------------------------------------------------
1562
CountWBFS()1563 uint CountWBFS()
1564 {
1565 uint count = 0;
1566 PartitionInfo_t * info;
1567 for ( info = first_partition_info; info; info = info->next )
1568 if ( !info->ignore && info->part_mode >= PM_WBFS_MAGIC_FOUND )
1569 count++;
1570 TRACE("CountWBFS(), N=%u\n",count);
1571 return count;
1572 }
1573
1574 //-----------------------------------------------------------------------------
1575
GetIdWBFS(WBFS_t * wbfs,IdField_t * idf)1576 uint GetIdWBFS ( WBFS_t * wbfs, IdField_t * idf )
1577 {
1578 DASSERT(wbfs);
1579 DASSERT(idf);
1580 uint count = 0;
1581
1582 wbfs_t *w = wbfs->wbfs;
1583 if (w)
1584 {
1585 int slot;
1586 wd_header_t head;
1587 char slot_name[20];
1588 const int slot_fw = snprintf(slot_name,sizeof(slot_name),"#%u",w->max_disc-1);
1589
1590 for ( slot = 0; slot < w->max_disc; slot++ )
1591 if (!wbfs_get_disc_info_by_slot(w,slot,(u8*)&head,sizeof(head),0,0,0,0))
1592 {
1593 const time_t mtime = wbfs_is_inode_info_valid(w,&head.iinfo)
1594 ? ntoh64(head.iinfo.mtime) : 0;
1595 snprintf(slot_name,sizeof(slot_name),"#%0*u",slot_fw,slot);
1596 InsertIdField(idf,&head.disc_id,0,mtime,slot_name);
1597 }
1598 }
1599
1600 return count;
1601 }
1602
1603 ///////////////////////////////////////////////////////////////////////////////
1604
1605 StringField_t wbfs_part_list;
1606
FindWBFSPartitions()1607 u32 FindWBFSPartitions()
1608 {
1609 static bool scanned = false;
1610 if (!scanned)
1611 {
1612 scanned = true;
1613 noPRINT("SCAN FOR WBFS PARTITIONS\n");
1614 ScanPartitions(true);
1615 AnalyzePartitions(0,true,false);
1616
1617 WBFS_t wbfs;
1618 InitializeWBFS(&wbfs);
1619 enumError err = ERR_OK;
1620 InitializeStringField(&wbfs_part_list);
1621 PartitionInfo_t * info;
1622 for ( err = GetFirstWBFS(&wbfs,&info,false);
1623 !err && !SIGINT_level;
1624 err = GetNextWBFS(&wbfs,&info,false) )
1625 {
1626 noPRINT("ADD WBFS PARTITION: %s,%d\n",info->path,info->part_mode);
1627 AppendStringField(&wbfs_part_list,info->path,false);
1628 }
1629 ResetWBFS(&wbfs);
1630 }
1631
1632 return wbfs_part_list.used;
1633 }
1634
1635 ///////////////////////////////////////////////////////////////////////////////
1636
DumpWBFS(WBFS_t * wbfs,FILE * f,int indent,ShowMode show_mode,int dump_level,int view_invalid_discs,CheckWBFS_t * ck)1637 enumError DumpWBFS
1638 (
1639 WBFS_t * wbfs, // valid WBFS
1640 FILE * f, // valid output file
1641 int indent, // indention of output
1642 ShowMode show_mode, // what should be printed
1643 int dump_level, // dump level: 0..3, ignored if show_mode is set
1644 int view_invalid_discs, // !=0: view invalid discs too
1645 CheckWBFS_t * ck // not NULL: dump only discs with errors
1646 )
1647 {
1648 ASSERT(wbfs);
1649 char buf[100];
1650
1651 if ( !f || !wbfs )
1652 return ERROR0(ERR_INTERNAL,0);
1653
1654 wbfs_t * w = wbfs->wbfs;
1655 if (!w)
1656 return ERR_NO_WBFS_FOUND;
1657
1658
1659 //----- options --show and --long
1660
1661 if ( show_mode & SHOW__DEFAULT )
1662 {
1663 show_mode = SHOW__ALL;
1664 switch (dump_level)
1665 {
1666 case 0:
1667 show_mode &= ~SHOW_FILES;
1668 // fall through
1669
1670 case 1:
1671 show_mode &= ~SHOW_D_MAP;
1672 // fall through
1673
1674 case 2:
1675 show_mode &= ~SHOW_W_MAP;
1676 // fall through
1677
1678 case 3:
1679 show_mode &= ~SHOW_USAGE;
1680 break;
1681 }
1682 }
1683
1684 if ( show_mode & SHOW_INTRO )
1685 show_mode |= SHOW_FHEADER | SHOW_SLOT | SHOW_GEOMETRY;
1686
1687 if ( view_invalid_discs )
1688 show_mode |= SHOW_FILES;
1689
1690
1691 //--- print WBFS header
1692
1693 indent = NormalizeIndent(indent);
1694 wbfs_head_t * head = w->head;
1695 if ( head && show_mode&(SHOW_FHEADER|SHOW_SLOT) )
1696 {
1697 fprintf(f,"%*sWBFS-Header:\n", indent,"");
1698 if ( show_mode & SHOW_FHEADER )
1699 {
1700 ccp magic = (ccp)&head->magic;
1701 fprintf(f,"%*s WBFS MAGIC: %10x %02x %02x %02x = '%s'\n",
1702 indent,"",
1703 magic[0], magic[1], magic[2], magic[3],
1704 wd_print_id(magic,4,0) );
1705
1706 fprintf(f,"%*s WBFS VERSION: %#13x =%15u\n", indent,"",
1707 head->wbfs_version, head->wbfs_version );
1708
1709 fprintf(f,"%*s hd sectors: %#13x =%15u\n", indent,"",
1710 (u32)htonl(head->n_hd_sec), (u32)htonl(head->n_hd_sec) );
1711 u32 n = 1 << head->hd_sec_sz_s;
1712 fprintf(f,"%*s hd sector size: %#11x =%15u = 2^%u\n", indent,"",
1713 n, n, head->hd_sec_sz_s );
1714 n = 1 << head->wbfs_sec_sz_s;
1715 fprintf(f,"%*s WBFS sector size: %#11x =%15u = 2^%u\n\n", indent,"",
1716 n, n, head->wbfs_sec_sz_s );
1717 }
1718
1719 if ( show_mode & SHOW_SLOT )
1720 {
1721 DASSERT(sizeof(buf)>=60);
1722 fprintf(f,"%*s Disc table (slot usage):\n", indent,"" );
1723 u8 * dt = head->disc_table;
1724 int count = w->max_disc, idx = 0;
1725 while ( count > 0 )
1726 {
1727 const int max = 50;
1728 int i, n = count < max ? count : max;
1729 char * dest = buf;
1730 for ( i = 0; i < n; i++ )
1731 {
1732 if (!(i%10))
1733 *dest++ = ' ';
1734 *dest++ = wbfs_slot_mode_info[ *dt++ & WBFS_SLOT__MASK ];
1735 }
1736 *dest = 0;
1737 fprintf( f, "%*s %3d..%3d:%s\n", indent,"", idx, idx+n-1, buf );
1738 idx += n;
1739 count -= n;
1740 }
1741 }
1742 fputc('\n',f);
1743 }
1744 else if (!head)
1745 fprintf(f,"%*s!! NO WBFS HEADER DEFINED !!\n\n", indent,"");
1746
1747
1748 //--- some calculations
1749
1750 const u32 NSEC = w->n_wbfs_sec;
1751 const u32 NSEC2 = w->n_wbfs_sec / 2;
1752
1753 const u32 used_blocks = NSEC - wbfs->free_blocks;
1754 const u32 used_perc = NSEC ? ( 100 * used_blocks + NSEC2 ) / NSEC : 0;
1755 const u32 free_perc = NSEC ? ( 100 * wbfs->free_blocks + NSEC2 ) / NSEC : 0;
1756
1757 const u64 wbfs_used = (u64)w->wbfs_sec_sz * used_blocks;
1758 const u64 wbfs_free = (u64)w->wbfs_sec_sz * wbfs->free_blocks;
1759 const u64 wbfs_total = (u64)w->wbfs_sec_sz * NSEC;
1760
1761 const u32 used_mib = ( wbfs_used + MiB/2 ) / MiB;
1762 const u32 free_mib = ( wbfs_free + MiB/2 ) / MiB;
1763 const u32 total_mib = ( wbfs_total + MiB/2 ) / MiB;
1764
1765 const u64 hd_total = (u64)w->hd_sec_sz * w->n_hd_sec;
1766
1767
1768 //--- print WBFS geometry
1769
1770 if ( show_mode & SHOW_GEOMETRY )
1771 {
1772 fprintf(f,"%*shd sector size: %#11x =%15u = 2^%u\n", indent,"",
1773 w->hd_sec_sz, w->hd_sec_sz, w->hd_sec_sz_s );
1774 fprintf(f,"%*shd num of sectors: %#11x =%15u\n", indent,"",
1775 w->n_hd_sec, w->n_hd_sec );
1776 fprintf(f,"%*shd total size: %#15llx =%15llu =%8llu MiB\n\n", indent,"",
1777 hd_total, hd_total, ( hd_total + MiB/2 ) / MiB );
1778
1779 fprintf(f,"%*swii sector size: %#11x =%15u = 2^%u\n", indent,"",
1780 w->wii_sec_sz, w->wii_sec_sz, w->wii_sec_sz_s );
1781 fprintf(f,"%*swii sectors/disc: %#11x =%15u\n", indent,"",
1782 w->n_wii_sec_per_disc, w->n_wii_sec_per_disc );
1783 fprintf(f,"%*swii num of sectors: %#11x =%15u\n", indent,"",
1784 w->n_wii_sec, w->n_wii_sec );
1785 u64 wii_total =(u64)w->wii_sec_sz * w->n_wii_sec;
1786 fprintf(f,"%*swii total size: %#15llx =%15llu =%8llu MiB\n\n", indent,"",
1787 wii_total, wii_total, ( wii_total + MiB/2 ) / MiB );
1788
1789 fprintf(f,"%*swbfs block size: %#11x =%15u = 2^%u\n", indent,"",
1790 w->wbfs_sec_sz, w->wbfs_sec_sz, w->wbfs_sec_sz_s );
1791 fprintf(f,"%*swbfs blocks/disc: %#11x =%15u\n", indent,"",
1792 w->n_wbfs_sec_per_disc, w->n_wbfs_sec_per_disc );
1793 fprintf(f,"%*swbfs free blocks: %#11x =%15u =%8u MiB = %3u%%\n", indent,"",
1794 wbfs->free_blocks, wbfs->free_blocks, free_mib, free_perc );
1795 fprintf(f,"%*swbfs used blocks: %#11x =%15u =%8u MiB = %3u%%\n", indent,"",
1796 used_blocks, used_blocks, used_mib, used_perc );
1797 fprintf(f,"%*swbfs total blocks: %#11x =%15u =%8u MiB = 100%%\n\n", indent,"",
1798 w->n_wbfs_sec, w->n_wbfs_sec, total_mib );
1799
1800 const u64 wbfs_max = (u64)w->wbfs_sec_sz * WBFS_MAX_SECTORS;
1801 const u64 wbfs_trim = (u64)w->wbfs_sec_sz * (wbfs_find_last_used_block(w)+1);
1802 const u32 max_mib = ( wbfs_max + MiB/2 ) / MiB;
1803 const u32 trim_mib = ( wbfs_trim + MiB/2 ) / MiB;
1804 fprintf(f,"%*swbfs min possible:%#15llx =%15llu =%8u MiB =%4llu%%\n", indent,"",
1805 wbfs_used, wbfs_used, used_mib, 100*wbfs_used/wbfs_total );
1806 fprintf(f,"%*swbfs trimmed size:%#15llx =%15llu =%8u MiB =%4llu%%\n", indent,"",
1807 wbfs_trim, wbfs_trim, trim_mib, 100*wbfs_trim/wbfs_total );
1808 fprintf(f,"%*swbfs total size: %#15llx =%15llu =%8u MiB = 100%%\n", indent,"",
1809 wbfs_total, wbfs_total, total_mib );
1810 fprintf(f,"%*swbfs max possible:%#15llx =%15llu =%8u MiB =%4llu%%\n\n", indent,"",
1811 wbfs_max, wbfs_max, max_mib, 100*wbfs_max/wbfs_total );
1812
1813 fprintf(f,"%*spartition lba: %#11x =%15u\n", indent,"",
1814 w->part_lba, w->part_lba );
1815 fprintf(f,"%*sfree blocks lba: %#11x =%15u\n", indent,"",
1816 w->freeblks_lba, w->freeblks_lba );
1817 const u32 fb_lb_size = w->freeblks_lba_count * w->hd_sec_sz;
1818 fprintf(f,"%*sfree blocks lba size: %#11x =%15u =%8u block%s\n", indent,"",
1819 fb_lb_size, fb_lb_size,
1820 w->freeblks_lba_count, w->freeblks_lba_count == 1 ? "" : "s" );
1821 fprintf(f,"%*sfree blocks size: %#11x =%15u\n", indent,"",
1822 w->freeblks_size4 * 4, w->freeblks_size4 * 4 );
1823 fprintf(f,"%*sfb last u32 mask: %#11x =%15u\n", indent,"",
1824 w->freeblks_mask, w->freeblks_mask );
1825 fprintf(f,"%*sdisc info size: %#11x =%15u\n\n", indent,"",
1826 w->disc_info_sz, w->disc_info_sz );
1827
1828 fprintf(f,"%*sused slots (wii discs): %8u =%14u%%\n", indent,"",
1829 wbfs->used_discs, 100 * wbfs->used_discs / w->max_disc );
1830 fprintf(f,"%*stotal slots (wii discs): %8u =%14u%%\n", indent,"",
1831 w->max_disc, 100 );
1832 }
1833
1834
1835 //--- print disc list
1836
1837 MemMap_t mm;
1838 MemMapItem_t * mi;
1839 InitializeMemMap(&mm);
1840
1841 if ( show_mode & (SHOW_W_MAP|SHOW_D_MAP|SHOW_FILES) )
1842 {
1843 ASSERT(w);
1844 const u32 sec_per_disc = w->n_wbfs_sec_per_disc;
1845 const u32 wii_sec_per_wbfs_sect
1846 = 1 << ( w->wbfs_sec_sz_s - w->wii_sec_sz_s );
1847 const off_t sec_size = w->wbfs_sec_sz;
1848 const off_t sec_delta = w->part_lba * w->hd_sec_sz;
1849
1850 u32 slot;
1851 WDiscInfo_t dinfo;
1852 for ( slot = 0; slot < w->max_disc; slot++ )
1853 {
1854 noTRACE("---");
1855 noTRACE(">> SLOT = %d/%d\n",slot,w->max_disc);
1856 if ( ck && ck->disc && !ck->disc[slot].err_count )
1857 continue;
1858
1859 wbfs_disc_t * d = wbfs_open_disc_by_slot(w,slot,view_invalid_discs);
1860 if (!d)
1861 continue;
1862 wd_header_t ihead;
1863 LoadIsoHeader(wbfs,&ihead,d);
1864
1865 if (GetWDiscInfoBySlot(wbfs,&dinfo,slot))
1866 {
1867 if ( !view_invalid_discs
1868 || ( view_invalid_discs < 2 && !d->is_valid && !d->is_deleted ) )
1869 {
1870 if ( !view_invalid_discs && show_mode &SHOW_FILES )
1871 fprintf(f,"\n%*s!! NO INFO ABOUT DISC #%d AVAILABLE !!\n",indent,"",slot);
1872 wbfs_close_disc(d);
1873 continue;
1874 }
1875
1876 memset(&dinfo,0,sizeof(dinfo));
1877 memcpy(&dinfo.dhead,d->header->dhead,sizeof(dinfo.dhead));
1878 CalcWDiscInfo(&dinfo,0);
1879 }
1880
1881 if ( show_mode & (SHOW_FILES|SHOW_D_MAP) )
1882 {
1883 fprintf(f,"\n%*sDump of %sWii disc at slot #%d of %d:\n",
1884 indent,"", d->is_used ? "" : "*DELETED* ", slot, w->max_disc );
1885 if ( show_mode & SHOW_FILES )
1886 DumpWDiscInfo( &dinfo, d->is_used ? &ihead : 0, f, indent+2 );
1887 if ( w->head->disc_table[slot] & WBFS_SLOT_INVALID )
1888 fprintf(f,"%*s>>> DISC MARKED AS INVALID! <<<\n",indent,"");
1889 else
1890 {
1891 if ( w->head->disc_table[slot] & WBFS_SLOT_F_SHARED )
1892 fprintf(f,"%*s>>> DISC IS/WAS SHARING BLOCKS WITH OTHER DISCS! <<<\n",indent,"");
1893 if ( w->head->disc_table[slot] & WBFS_SLOT_F_FREED )
1894 fprintf(f,"%*s>>> DISC IS/WAS USING FREE BLOCKS! <<<\n",indent,"");
1895 }
1896 }
1897
1898 if ( show_mode & (SHOW_D_MAP|SHOW_W_MAP) )
1899 {
1900 int ind = indent + 3;
1901 if ( show_mode & SHOW_D_MAP )
1902 fprintf(f,"\n%*sWii disc memory mapping:\n\n"
1903 "%*s mapping index : wbfs blocks : disc offset range : size\n"
1904 "%*s----------------------------------------------------------------------\n",
1905 ind-1,"", ind,"", ind,"" );
1906 u16 * tab = d->header->wlba_table;
1907
1908 u8 used[WBFS_MAX_SECTORS];
1909 memset(used,0,sizeof(used));
1910 ASSERT( sec_per_disc < sizeof(used) );
1911
1912 int idx = 0;
1913 for (;;)
1914 {
1915 while ( idx < sec_per_disc && !tab[idx] )
1916 idx++;
1917 if ( idx == sec_per_disc )
1918 break;
1919 const u32 start = idx;
1920 int delta = ntohs(tab[idx])-idx;
1921 while ( idx < sec_per_disc )
1922 {
1923 u32 wlba = ntohs(tab[idx]);
1924 if (!wlba)
1925 break;
1926 if ( wlba < sizeof(used) )
1927 used[wlba] = 1;
1928 if ( wlba-idx != delta )
1929 break;
1930 idx++;
1931 }
1932
1933 if ( !( show_mode & SHOW_D_MAP ))
1934 continue;
1935
1936 off_t off = start * wii_sec_per_wbfs_sect * (u64)WII_SECTOR_SIZE;
1937 off_t size = (idx-start) * wii_sec_per_wbfs_sect * (u64)WII_SECTOR_SIZE;
1938
1939 if ( start == idx - 1 )
1940 fprintf(f,"%*s%11u : %9u :%10llx ..%10llx :%9llx\n",
1941 ind,"",
1942 start,
1943 start + delta,
1944 (u64)off,
1945 (u64)( off + size ),
1946 (u64)size );
1947 else
1948 fprintf(f,"%*s%7u ..%6u : %5u .. %5u :%10llx ..%10llx :%9llx\n",
1949 ind,"",
1950 start,
1951 idx - 1,
1952 start + delta,
1953 idx - 1 + delta,
1954 (u64)off,
1955 (u64)( off + size ),
1956 (u64)size );
1957 }
1958
1959 if ( show_mode & SHOW_W_MAP && d->is_used )
1960 {
1961 mi = InsertMemMap(&mm, w->hd_sec_sz+slot*w->disc_info_sz,
1962 sizeof(d->header->dhead)
1963 + sizeof(*d->header->wlba_table) * w->n_wbfs_sec_per_disc );
1964 snprintf(mi->info,sizeof(mi->info),
1965 "Inode of slot #%03u [%s]",slot,dinfo.id6);
1966
1967 int idx = 0, segment = 0;
1968 for ( ;; segment++ )
1969 {
1970 while ( idx < NSEC && !used[idx] )
1971 idx++;
1972 if ( idx == NSEC )
1973 break;
1974 const u32 start = idx;
1975 while ( idx < NSEC && used[idx] )
1976 idx++;
1977
1978 const int n = idx - start;
1979 mi = InsertMemMap(&mm, start*sec_size+sec_delta, n*sec_size );
1980 if (segment)
1981 snprintf(mi->info,sizeof(mi->info),
1982 "%4u block%s -> disc #%03u.%u [%s]",
1983 n, n == 1 ? " " : "s", slot, segment, dinfo.id6 );
1984 else
1985 snprintf(mi->info,sizeof(mi->info),
1986 "%4u block%s -> disc #%03u [%s]",
1987 n, n == 1 ? " " : "s", slot, dinfo.id6 );
1988 }
1989 }
1990 }
1991 wbfs_close_disc(d);
1992 }
1993 }
1994 fputc('\n',f);
1995
1996 if ( show_mode & SHOW_W_MAP )
1997 {
1998 mi = InsertMemMap(&mm,0,sizeof(wbfs_head_t));
1999 StringCopyS(mi->info,sizeof(mi->info),"WBFS header");
2000
2001 mi = InsertMemMap(&mm,sizeof(wbfs_head_t),w->max_disc);
2002 StringCopyS(mi->info,sizeof(mi->info),"Disc table");
2003
2004 int slot = 0;
2005 for (;;)
2006 {
2007 int start = slot;
2008 while ( slot < w->max_disc && !head->disc_table[slot] )
2009 slot++;
2010 int count = slot - start;
2011 if (count)
2012 {
2013 mi = InsertMemMap(&mm, w->hd_sec_sz + start * w->disc_info_sz,
2014 count * w->disc_info_sz );
2015 if ( count > 1 )
2016 snprintf(mi->info,sizeof(mi->info),
2017 "%d unused inodes, slots #%03u..#%03u",
2018 count, start, slot-1 );
2019 else
2020 snprintf(mi->info,sizeof(mi->info),
2021 "Unused inode, slot #%03u",start);
2022 }
2023 if ( slot == w->max_disc )
2024 break;
2025 slot++;
2026 }
2027
2028 const off_t fbt_off = ( w->part_lba + w->freeblks_lba ) * w->hd_sec_sz;
2029 mi = InsertMemMap(&mm,fbt_off,w->freeblks_size4*4);
2030 StringCopyS(mi->info,sizeof(mi->info),"Free blocks table");
2031
2032 mi = InsertMemMap(&mm,w->wbfs_sec_sz,0);
2033 StringCopyS(mi->info,sizeof(mi->info),"-- end of management block #0 --");
2034
2035 if ( wbfs_total < hd_total )
2036 {
2037 mi = InsertMemMap(&mm,wbfs_total,0);
2038 StringCopyS(mi->info,sizeof(mi->info),"-- end of WBFS data --");
2039 }
2040
2041 mi = InsertMemMap(&mm,hd_total,0);
2042 StringCopyS(mi->info,sizeof(mi->info),"-- end of WBFS device/file --");
2043
2044 fprintf(f,"\f\n%*sWBFS Memory Map:\n\n", indent,"" );
2045 PrintMemMap(&mm,f,indent+1,0);
2046
2047 fputc('\n',f);
2048 }
2049 ResetMemMap(&mm);
2050
2051 if ( show_mode & SHOW_USAGE )
2052 {
2053 fprintf(f,"\f\n%*sWBFS Memory Usage:\n\n", indent,"" );
2054 wbfs_print_block_usage(stdout,indent+1,w,false);
2055 fputc('\n',f);
2056 }
2057
2058 if ( show_mode & SHOW_CHECK ) // && isatty(fileno(f))
2059 {
2060 CheckWBFS_t ck;
2061 InitializeCheckWBFS(&ck);
2062 if (CheckWBFS(&ck,wbfs,0,f,1))
2063 fprintf(f," -> use command \"CHECK -vv\" for a verbose report!\n\n");
2064 ResetCheckWBFS(&ck);
2065 }
2066
2067 return ERR_OK;
2068 }
2069
2070 ///////////////////////////////////////////////////////////////////////////////
2071
ScanOptWbfsAlloc(ccp arg)2072 int ScanOptWbfsAlloc ( ccp arg )
2073 {
2074 static const CommandTab_t tab[] =
2075 {
2076 { WBFS_BA_AUTO, "AUTO", "DEFAULT", 0 },
2077 { WBFS_BA_FIRST, "FIRST", "FRAG", 0 },
2078 { WBFS_BA_AVOID_FRAG, "NO-FRAG", "NOFRAG", 0 },
2079 {0,0,0,0}
2080 };
2081
2082 const CommandTab_t * cmd = ScanCommand(0,arg,tab);
2083 if (cmd)
2084 {
2085 opt_wbfs_alloc = cmd->id;
2086 return 0;
2087 }
2088
2089 ERROR0(ERR_SYNTAX,"Illegal WBFS block allocation mode (option --wbfs-allocv): '%s'\n",arg);
2090 return 1;
2091 }
2092
2093 //
2094 ///////////////////////////////////////////////////////////////////////////////
2095 /////////////// Analyze WBFS ///////////////
2096 ///////////////////////////////////////////////////////////////////////////////
2097
2098 const size_t AW_BUF_SIZE = 16 * MiB;
2099
2100 ///////////////////////////////////////////////////////////////////////////////
2101
AW_get_record(AWData_t * awd)2102 AWRecord_t * AW_get_record ( AWData_t * awd )
2103 {
2104 ASSERT(awd);
2105
2106 static AWRecord_t dummy = {0};
2107
2108 AWRecord_t * r = awd && awd->n_record < AW_MAX_RECORD
2109 ? awd->rec + awd->n_record++
2110 : &dummy;
2111 memset(r,0,sizeof(*r));
2112 return r;
2113 }
2114
2115 ///////////////////////////////////////////////////////////////////////////////
2116
AW_header(AWData_t * awd,File_t * f)2117 static void AW_header ( AWData_t * awd, File_t * f )
2118 {
2119 TRACE("AW_header(%p,%p)\n",awd,f);
2120 ASSERT(awd);
2121 ASSERT(f);
2122
2123 if ( !f || f->st.st_size < sizeof(wbfs_head_t) )
2124 return;
2125
2126 wbfs_head_t wh;
2127 if ( ReadAtF(f,0,&wh,sizeof(wh)) == ERR_OK
2128 && !memcmp((ccp)&wh.magic,"WBFS",4) )
2129 {
2130 AWRecord_t * r = AW_get_record(awd);
2131 r->status = AW_HEADER;
2132 r->magic_found = true;
2133 r->wbfs_version = wh.wbfs_version;
2134 r->hd_sectors = ntohl(wh.n_hd_sec);
2135 r->hd_sector_size = 1 << wh.hd_sec_sz_s;
2136 r->wbfs_sector_size = 1 << wh.wbfs_sec_sz_s;
2137 StringCopyS(r->title,sizeof(r->title),"HEADER:");
2138 StringCopyS(r->info,sizeof(r->info),"WBFS header scanning");
2139 }
2140 }
2141
2142 ///////////////////////////////////////////////////////////////////////////////
2143
2144 typedef struct aw_inode_t
2145 {
2146 be32_t n_hd_sec;
2147 u8 hd_sec_sz_s;
2148 u8 wbfs_sec_sz_s;
2149 u8 wbfs_version;
2150 u16 count;
2151 time_t xtime;
2152
2153 } aw_inode_t;
2154
2155 //-----------------------------------------------------------------------------
2156
AW_insert_inode(AWData_t * awd,aw_inode_t * inode,ccp head)2157 static AWRecord_t * AW_insert_inode
2158 ( AWData_t * awd, aw_inode_t * inode, ccp head )
2159 {
2160 ASSERT(awd);
2161 ASSERT(inode);
2162 ASSERT(head);
2163
2164 if (!inode->count)
2165 return 0;
2166
2167 AWRecord_t * r = AW_get_record(awd);
2168
2169 r->status = AW_INODES;
2170 r->magic_found = true;
2171 r->wbfs_version = inode->wbfs_version;
2172 r->hd_sectors = ntohl(inode->n_hd_sec);
2173 r->hd_sector_size = 1 << inode->hd_sec_sz_s;
2174 r->wbfs_sector_size = 1 << inode->wbfs_sec_sz_s;
2175
2176 snprintf(r->title,sizeof(r->title),"INODE-%s:",head);
2177
2178 if (inode->xtime)
2179 {
2180 struct tm * tm = localtime(&inode->xtime);
2181 char timbuf[40];
2182 strftime(timbuf,sizeof(timbuf),"%F %T",tm);
2183 snprintf(r->info,sizeof(r->info),"%s n=%u", timbuf, inode->count );
2184 }
2185 else
2186 snprintf(r->info,sizeof(r->info),"n=%u", inode->count );
2187
2188 inode->count = 0;
2189 inode->xtime = 0;
2190
2191 return r;
2192 }
2193
2194 //-----------------------------------------------------------------------------
2195
AW_inodes(AWData_t * awd,File_t * f,ccp data)2196 static void AW_inodes ( AWData_t * awd, File_t * f, ccp data )
2197 {
2198 TRACE("AW_inodes(%p,%p,%p)\n",awd,f,data);
2199 ASSERT(awd);
2200 ASSERT(f);
2201 ASSERT(data);
2202
2203 //----- collect data
2204
2205 const int inode_size = 20;
2206 aw_inode_t inode_tab[inode_size];
2207 aw_inode_t *inode_end = inode_tab + inode_size, *iptr;
2208 memset(inode_tab,0,sizeof(inode_tab));
2209
2210 TRACE_SIZEOF(aw_inode_t);
2211 TRACE_SIZEOF(inode_tab);
2212
2213 int max_count = 0;
2214 time_t max_time = 0;
2215
2216 ccp ptr = data + 512 + WBFS_INODE_INFO_OFF;
2217 ccp end = data + AW_BUF_SIZE;
2218 for ( ; ptr < end; ptr += 512 )
2219 {
2220 TRACELINE;
2221 wbfs_inode_info_t * iinfo = (wbfs_inode_info_t*)ptr;
2222 if (!memcmp((ccp)&iinfo->magic,"WBFS",4))
2223 {
2224 const u32 hd_sec_size = 1 << iinfo->hd_sec_sz_s;
2225 const u32 wbfs_sec_size = 1 << iinfo->wbfs_sec_sz_s;
2226 if ( hd_sec_size >= HD_SECTOR_SIZE
2227 && hd_sec_size < wbfs_sec_size
2228 && ptr-data < wbfs_sec_size )
2229 {
2230 TRACE("INODE FOUND: %8zx\n",ptr-data);
2231 for ( iptr = inode_tab; iptr < inode_end; iptr++ )
2232 {
2233 if ( !iptr->count
2234 || iptr->n_hd_sec == iinfo->n_hd_sec
2235 && iptr->hd_sec_sz_s == iinfo->hd_sec_sz_s
2236 && iptr->wbfs_sec_sz_s == iinfo->wbfs_sec_sz_s )
2237 {
2238 iptr->count++;
2239 if ( max_count < iptr->count )
2240 max_count = iptr->count;
2241
2242 iptr->n_hd_sec = iinfo->n_hd_sec;
2243 iptr->hd_sec_sz_s = iinfo->hd_sec_sz_s;
2244 iptr->wbfs_sec_sz_s = iinfo->wbfs_sec_sz_s;
2245 if ( iptr->wbfs_version < iinfo->wbfs_version )
2246 iptr->wbfs_version = iinfo->wbfs_version;
2247
2248 time_t temp;
2249 temp = ntoh64(iinfo->itime);
2250 if ( iptr->xtime < temp )
2251 iptr->xtime = temp;
2252 temp = ntoh64(iinfo->mtime);
2253 if ( iptr->xtime < temp )
2254 iptr->xtime = temp;
2255 temp = ntoh64(iinfo->ctime);
2256 if ( iptr->xtime < temp )
2257 iptr->xtime = temp;
2258 temp = ntoh64(iinfo->atime);
2259 if ( iptr->xtime < temp )
2260 iptr->xtime = temp;
2261 temp = ntoh64(iinfo->dtime);
2262 if ( iptr->xtime < temp )
2263 iptr->xtime = temp;
2264 if ( max_time < iptr->xtime )
2265 max_time = iptr->xtime;
2266 break;
2267 }
2268 }
2269 }
2270 }
2271 }
2272
2273 //----- find newest inode
2274
2275 if (max_time)
2276 for ( iptr = inode_tab; iptr < inode_end; iptr++ )
2277 if ( iptr->xtime == max_time )
2278 AW_insert_inode(awd,iptr,"TIM");
2279
2280 //----- find most frequently inodes
2281
2282 if (max_count)
2283 {
2284 max_count /= 4;
2285 for ( iptr = inode_tab; iptr < inode_end; iptr++ )
2286 if ( iptr->count >= max_count )
2287 AW_insert_inode(awd,iptr,"CNT");
2288 }
2289
2290 //----- add very first inode
2291
2292 AW_insert_inode(awd,inode_tab,"1ST");
2293 }
2294
2295 ///////////////////////////////////////////////////////////////////////////////
2296
AW_discs(AWData_t * awd,File_t * f,ccp data)2297 static void AW_discs ( AWData_t * awd, File_t * f, ccp data )
2298 {
2299 TRACE("AW_discs(%p,%p,%p)\n",awd,f,data);
2300 ASSERT(awd);
2301 ASSERT(f);
2302 ASSERT(data);
2303
2304 const size_t SEC_SIZE = HD_SECTOR_SIZE;
2305 const size_t SEC_MAX = 501;
2306 const size_t SEC_LEVEL = 4;
2307 ASSERT( SEC_SIZE * SEC_MAX <= AW_BUF_SIZE );
2308
2309 const size_t WSS_MIN_LEVEL = 20;
2310 const size_t WSS_MAX_LEVEL = 28;
2311
2312 // helper vars
2313 int sec, level, total_count = 0;
2314
2315 // hss vars
2316 int first_sec_found = 0, max_level = 0;
2317 u32 sec_count[SEC_LEVEL];
2318 memset(sec_count,0,sizeof(sec_count));
2319
2320 // wss vars
2321 u32 wss = 0;
2322 int wss_count[WSS_MAX_LEVEL], max_wss_count = 0;
2323 memset(wss_count,0,sizeof(wss_count));
2324 char buf[SEC_SIZE];
2325
2326 for ( sec = 1; sec <= SEC_MAX; sec++ )
2327 {
2328 wd_header_t *wd = (wd_header_t *)( data + sec * SEC_SIZE );
2329 u32 magic = ntohl(wd->wii_magic);
2330 if ( ( magic == WII_MAGIC || magic == WII_MAGIC_DELETED )
2331 && CheckID6(wd,false,false) )
2332 {
2333 noTRACE(" - DISC found @ sector #%u\n",sec);
2334 for ( level = SEC_LEVEL-1; level >= 0; level-- )
2335 {
2336 const int hss = 1 << level;
2337 if ( sec >= hss && !(sec-hss & hss-1) )
2338 {
2339 total_count++;
2340 TRACE(" - DISC found @ sector #%u -> level=%u\n",sec,level);
2341 sec_count[level]++;
2342 if (!first_sec_found)
2343 {
2344 first_sec_found = sec;
2345 //first_iso_sect = ntohs(*(u16*)(wd+1));
2346 max_level = level;
2347 }
2348 break;
2349 }
2350 }
2351
2352 if (!wss)
2353 {
2354 // try to calculate wss
2355
2356 const size_t WSS_CMP_SIZE = 128;
2357 wd_header_t inode;
2358 memcpy( &inode, wd, WSS_CMP_SIZE );
2359 inode.wii_magic = htonl(WII_MAGIC);
2360 off_t iso_sect = ntohs(*(u16*)(wd+1));
2361
2362 for ( level = WSS_MIN_LEVEL; level < WSS_MAX_LEVEL; level++ )
2363 {
2364 const off_t off = iso_sect << level;
2365 if ( f->st.st_size < off + SEC_SIZE || ReadAtF(f,off,buf,sizeof(buf)) )
2366 break;
2367
2368 if (!memcmp(buf,&inode,WSS_CMP_SIZE))
2369 {
2370 wss_count[level]++;
2371 if ( max_wss_count < wss_count[level] )
2372 {
2373 max_wss_count = wss_count[level];
2374 if ( max_wss_count >= 3 )
2375 {
2376 wss = 1 << level;
2377 break;
2378 }
2379 }
2380 }
2381 }
2382 }
2383 }
2384 }
2385
2386 if (!total_count)
2387 return; // no disc found
2388
2389 if (!wss)
2390 {
2391 if (max_wss_count)
2392 for ( level = WSS_MIN_LEVEL; level < WSS_MAX_LEVEL; level++ )
2393 if ( wss_count[level] == max_wss_count )
2394 {
2395 wss = 1 << level;
2396 break;
2397 }
2398
2399 if (!wss)
2400 return;
2401 }
2402
2403 int hd_sec_level = 0;
2404 if ( max_level > 0 )
2405 {
2406 for ( level = 0; level < SEC_LEVEL-1; level++ )
2407 if ( sec_count[level] && sec_count[level] >= sec_count[level+1] )
2408 break;
2409 hd_sec_level = level;
2410 }
2411 const u32 hss = SEC_SIZE << hd_sec_level;
2412 TRACE(" - NH=%llx, HSS=%x, WSS=%x\n",(u64)f->st.st_size/hss,hss,wss);
2413
2414 AWRecord_t * r = AW_get_record(awd);
2415 r->status = AW_DISCS;
2416 r->magic_found = false;
2417 r->wbfs_version = 0;
2418 r->hd_sectors = f->st.st_size/hss;
2419 r->hd_sector_size = hss;
2420 r->wbfs_sector_size = wss;
2421 StringCopyS(r->title,sizeof(r->title),"DISCS:");
2422 snprintf(r->info,sizeof(r->info),"%u disc header found",total_count);
2423 }
2424
2425 ///////////////////////////////////////////////////////////////////////////////
2426
AW_calc(AWData_t * awd,File_t * f,u32 sec_size,bool old)2427 static void AW_calc ( AWData_t * awd, File_t * f, u32 sec_size, bool old )
2428 {
2429 TRACE("AW_calc(%p,%p,%u,%d)\n",awd,f,sec_size,old);
2430 ASSERT(awd);
2431 ASSERT(f);
2432
2433 if ( !f || f->st.st_size < sizeof(wbfs_head_t) )
2434 return;
2435
2436 const int version = old ? 0 : WBFS_VERSION;
2437 const u64 num_sec = f->st.st_size / sec_size;
2438 const u32 n_wii_sec = old
2439 ? ( num_sec / WII_SECTOR_SIZE ) * sec_size
2440 : num_sec / ( WII_SECTOR_SIZE / sec_size );
2441
2442 int sz_s;
2443 for ( sz_s = 6; sz_s < 12; sz_s++ )
2444 if ( n_wii_sec < (1<<16) * (1<<sz_s) )
2445 break;
2446 const u32 wbfs_sec_size = ( 1 << sz_s ) * WII_SECTOR_SIZE;
2447
2448 AWRecord_t * r;
2449 char title[sizeof(r->title)];
2450 ccp info;
2451
2452 if (old)
2453 {
2454 // search AW_CALC with same geometry, if found => abort
2455 int i;
2456 for ( i = 0; i < awd->n_record; i++ )
2457 {
2458 r = awd->rec + i;
2459 if ( r->status == AW_CALC
2460 && r->hd_sectors == num_sec
2461 && r->wbfs_sector_size == wbfs_sec_size )
2462 {
2463 return;
2464 }
2465 }
2466
2467 snprintf(title,sizeof(title),"OLD %4u:",sec_size);
2468 info = "old and buggy calculation";
2469 }
2470 else
2471 {
2472 snprintf(title,sizeof(title),"CALC %4u:",sec_size);
2473 info = "calculation of init function";
2474 }
2475
2476 r = AW_get_record(awd);
2477 r->status = old ? AW_OLD_CALC : AW_CALC;
2478 r->magic_found = false;
2479 r->wbfs_version = version;
2480 r->hd_sectors = num_sec;
2481 r->hd_sector_size = sec_size;
2482 r->wbfs_sector_size = wbfs_sec_size;
2483 StringCopyS(r->title,sizeof(r->title),title);
2484 StringCopyS(r->info,sizeof(r->info),info);
2485 }
2486
2487 ///////////////////////////////////////////////////////////////////////////////
2488
AnalyzeWBFS(AWData_t * awd,File_t * f)2489 int AnalyzeWBFS ( AWData_t * awd, File_t * f )
2490 {
2491 ASSERT(awd);
2492 ASSERT(f);
2493 ASSERT( AW_NONE == 0 );
2494
2495 const bool disable_errors = f->disable_errors;
2496 f->disable_errors = true;
2497
2498 memset(awd,0,sizeof(*awd));
2499 TRACE("AnalyzeWBFS(%p,%p)\n",awd,f);
2500
2501 //----- scan wbfs header
2502
2503 AW_header(awd,f);
2504
2505 //----- scan disc infos
2506
2507 if ( f->st.st_size >= AW_BUF_SIZE )
2508 {
2509 char * data = MALLOC(AW_BUF_SIZE);
2510 if ( ReadAtF(f,0,data,AW_BUF_SIZE) == ERR_OK )
2511 {
2512 AW_inodes(awd,f,data);
2513 AW_discs(awd,f,data);
2514 }
2515 FREE(data);
2516 }
2517
2518 //----- calculate geometry
2519
2520 int sec_size;
2521 for ( sec_size = 512; sec_size <= 4096; sec_size <<= 1 )
2522 AW_calc(awd,f,sec_size,false);
2523 for ( sec_size = 512; sec_size <= 4096; sec_size <<= 1 )
2524 AW_calc(awd,f,sec_size,true);
2525
2526
2527 //----- calculate dependencies and remove invalid data
2528
2529 int i;
2530 AWRecord_t * dest = awd->rec;
2531 for ( i = 0; i < awd->n_record; i++ )
2532 {
2533 AWRecord_t * r = awd->rec + i;
2534
2535 wbfs_t wbfs;
2536 memset(&wbfs,0,sizeof(wbfs));
2537 wbfs_calc_geometry( &wbfs,
2538 r->hd_sectors,
2539 r->hd_sector_size,
2540 r->wbfs_sector_size );
2541 if ( wbfs.n_wbfs_sec > 1 )
2542 {
2543 r->wbfs_sectors = wbfs.n_wbfs_sec;
2544 r->max_disc = wbfs.max_disc;
2545 r->disc_info_size = wbfs.disc_info_sz;
2546 memcpy(dest,r,sizeof(*dest));
2547 dest++;
2548 }
2549 }
2550 awd->n_record = dest - awd->rec;
2551
2552 //----- finish operation
2553
2554 #ifdef DEBUG
2555 PrintAnalyzeWBFS(TRACE_FILE,0,awd,2);
2556 #endif
2557
2558 f->disable_errors = disable_errors;
2559 return awd->n_record;
2560 }
2561
2562 ///////////////////////////////////////////////////////////////////////////////
2563 ///////////////////////////////////////////////////////////////////////////////
2564
PrintAnalyzeWBFS(FILE * out,int indent,AWData_t * awd,int print_calc)2565 int PrintAnalyzeWBFS
2566 (
2567 FILE * out, // valid output stream
2568 int indent, // indent of output
2569 AWData_t * awd, // valid pointer
2570 int print_calc // 0: suppress calculated values
2571 // 1: print calculated values if other values available
2572 // 2: print calculated values
2573 )
2574 {
2575 DASSERT(out);
2576 DASSERT(awd);
2577
2578 if ( !out || !awd->n_record )
2579 return 0;
2580 if ( print_calc < 2 && ( awd->rec->status == AW_CALC || awd->rec->status == AW_OLD_CALC ))
2581 return 0;
2582
2583 indent = NormalizeIndent(indent);
2584
2585 char buf[30];
2586 static const char sep[] =
2587 "------------------------------------------"
2588 "------------------------------------------";
2589
2590 fprintf(out,"%*s%s\n"
2591 "%*s HD SECTORS WBFS SECTORS DISCS (all values in hex)\n"
2592 "%*s WBFS total sec total sec max inode\n"
2593 "%*sNAME magic vrs num size num size num size ADDITIONAL INFORMATION\n"
2594 "%*s%s\n",
2595 indent,"", sep, indent,"", indent,"", indent,"", indent,"", sep );
2596
2597 int i;
2598 for ( i = 0; i < awd->n_record; i++ )
2599 {
2600 AWRecord_t * ar = awd->rec + i;
2601 const bool is_calc = ar->status == AW_CALC || ar->status == AW_OLD_CALC;
2602 if ( !print_calc && is_calc )
2603 continue;
2604
2605 ccp info = ar->info;
2606 if ( i > 0 && ar->status == ar[-1].status && is_calc )
2607 {
2608 snprintf(buf,sizeof(buf),
2609 " \" but sector-size=%u",ar->hd_sector_size);
2610 info = buf;
2611 }
2612
2613 fprintf(out,"%*s%-10s %s %2x %8x %4x %5x %7x %4x %4x %s\n",
2614 indent,"", ar->title,
2615 ar->magic_found ? "ok" : " -",
2616 ar->wbfs_version,
2617 ar->hd_sectors, ar->hd_sector_size,
2618 ar->wbfs_sectors, ar->wbfs_sector_size,
2619 ar->max_disc, ar->disc_info_size,
2620 info );
2621 }
2622
2623 fprintf(out,"%*s%s\n", indent,"",sep);
2624 return awd->n_record;
2625 }
2626
2627 //
2628 ///////////////////////////////////////////////////////////////////////////////
2629 /////////////// Check WBFS ///////////////
2630 ///////////////////////////////////////////////////////////////////////////////
2631
InitializeCheckWBFS(CheckWBFS_t * ck)2632 void InitializeCheckWBFS ( CheckWBFS_t * ck )
2633 {
2634 ASSERT(ck);
2635 memset(ck,0,sizeof(*ck));
2636 }
2637
2638 ///////////////////////////////////////////////////////////////////////////////
2639
ResetCheckWBFS(CheckWBFS_t * ck)2640 void ResetCheckWBFS ( CheckWBFS_t * ck )
2641 {
2642 ASSERT(ck);
2643 FREE(ck->cur_fbt);
2644 FREE(ck->good_fbt);
2645 FREE(ck->ubl);
2646 FREE(ck->blc);
2647 FREE(ck->disc);
2648 InitializeCheckWBFS(ck);
2649 }
2650
2651 ///////////////////////////////////////////////////////////////////////////////
2652
2653 #if defined(TEST)
2654
check_func(wbfs_t * p,wbfs_check_t check_mode,int slot,ccp id6,int block,uint count,ccp msg,uint msg_len,void * param)2655 static void check_func
2656 (
2657 wbfs_t * p, // valid WBFS descriptor
2658 wbfs_check_t check_mode, // modus
2659 int slot, // -1 or related slot index
2660 ccp id6, // NULL or pointer to disc ID6
2661 int block, // -1 or related block number
2662 uint count, // block usage count
2663 ccp msg, // clear text message
2664 uint msg_len, // strlen(msg)
2665 void * param // user defined paramater
2666 )
2667 {
2668 printf("%2u: %3d [%s] %d,%d %s\n",check_mode,slot,id6?id6:"-",block,count,msg);
2669 }
2670
2671 #endif
2672
2673 ///////////////////////////////////////////////////////////////////////////////
2674
CheckWBFS(CheckWBFS_t * ck,WBFS_t * wbfs,int verbose,FILE * f,int indent)2675 enumError CheckWBFS
2676 ( CheckWBFS_t * ck, WBFS_t * wbfs, int verbose, FILE * f, int indent )
2677 {
2678 ASSERT(ck);
2679 ASSERT(wbfs);
2680
2681 enum { SHOW_SUM, SHOW_DETAILS, PRINT_DUMP, PRINT_EXT_DUMP, PRINT_FULL_DUMP };
2682
2683 ResetCheckWBFS(ck);
2684 if ( !wbfs || !wbfs->wbfs || !wbfs->sf || !IsOpenSF(wbfs->sf) )
2685 return ERROR0(ERR_INTERNAL,0);
2686
2687 ck->wbfs = wbfs;
2688 wbfs_t * w = wbfs->wbfs;
2689
2690 if (!f)
2691 verbose = -1;
2692 indent = NormalizeIndent(indent);
2693
2694 ck->fbt_off = ( w->part_lba + w->freeblks_lba ) * w->hd_sec_sz;
2695 ck->fbt_size = w->freeblks_lba_count * w->hd_sec_sz;
2696
2697 #if 0 && defined(TEST) // [[2do]]
2698 if (logging)
2699 wbfs_calc_used_blocks(w,true,true,check_func,ck);
2700 #endif
2701
2702 //---------- calculate number of sectors
2703
2704 const u32 N_SEC = w->n_wbfs_sec;
2705
2706 u32 ALLOC_SEC = ck->fbt_size * 8;
2707 if ( ALLOC_SEC < N_SEC )
2708 ALLOC_SEC = N_SEC;
2709
2710 u32 WBFS0_SEC = N_SEC; // used sectors
2711
2712 if ( w->head->wbfs_version == 0 )
2713 {
2714 // the old wbfs versions have calculation errors
2715 // - because a rounding error 'n_wii_sec' is sometimes to short
2716 // => 'n_wbfs_sec' is to short
2717 // => free blocks table may be too small
2718 // - block search uses only (n_wbfs_sec/32)*32 blocks
2719
2720 // this is the old buggy calcualtion
2721 u32 n_wii_sec = ( w->n_hd_sec / w->wii_sec_sz ) * w->hd_sec_sz;
2722 WBFS0_SEC = n_wii_sec >> ( w->wbfs_sec_sz_s - w->wii_sec_sz_s );
2723
2724 WBFS0_SEC = ( WBFS0_SEC / 32 ) * 32 + 1;
2725
2726 if ( WBFS0_SEC > N_SEC )
2727 WBFS0_SEC = N_SEC;
2728 }
2729 TRACE("N-SEC=%u, WBFS0-SEC=%u, ALLOC-SEC=%u\n",N_SEC,WBFS0_SEC,ALLOC_SEC);
2730
2731 //---------- alloctate data
2732
2733 u32 * fbt = MALLOC(ck->fbt_size);
2734 u8 * ubl = CALLOC(ALLOC_SEC,1);
2735 u8 * blc = CALLOC(ALLOC_SEC,1);
2736 CheckDisc_t * disc = CALLOC(w->max_disc,sizeof(*disc));
2737
2738 ck->cur_fbt = fbt;
2739 ck->ubl = ubl;
2740 ck->blc = blc;
2741 ck->disc = disc;
2742
2743 //---------- load free blocks table
2744
2745 enumError err = ReadAtF(&wbfs->sf->f,ck->fbt_off,fbt,ck->fbt_size);
2746 if (err)
2747 return err;
2748
2749 int i, j, bl = 1;
2750 for ( i = 0; i < w->freeblks_size4; i++ )
2751 {
2752 u32 v = wbfs_ntohl(fbt[i]);
2753 if ( v == ~(u32)0 )
2754 bl += 32;
2755 else
2756 for ( j = 0; j < 32; j++, bl++ )
2757 if ( !(v & 1<<j) )
2758 ubl[bl] = 1;
2759 }
2760
2761 //---------- scan discs
2762
2763 if ( verbose >= SHOW_DETAILS )
2764 fprintf(f,"%*s* Scan %d discs in %d slots.\n",
2765 indent,"", wbfs->used_discs, w->max_disc );
2766 u32 slot;
2767 for ( slot = 0; slot < w->max_disc; slot++ )
2768 {
2769 wbfs_disc_t * d = wbfs_open_disc_by_slot(w,slot,0);
2770 if (!d)
2771 continue;
2772
2773 u16 * wlba_tab = d->header->wlba_table;
2774 ASSERT(wlba_tab);
2775
2776 int bl;
2777 for ( bl = 0; bl < w->n_wbfs_sec_per_disc; bl++ )
2778 {
2779 const u32 wlba = ntohs(wlba_tab[bl]);
2780 if ( wlba > 0 && wlba < N_SEC && blc[wlba] < 255 )
2781 blc[wlba]++;
2782 }
2783
2784 wbfs_close_disc(d);
2785 }
2786
2787 //---------- check for disc errors
2788
2789 if ( verbose >= SHOW_DETAILS )
2790 fprintf(f,"%*s* Check for disc block errors.\n", indent,"" );
2791
2792 u32 total_err_invalid = 0;
2793 u32 total_err_no_blocks = 0;
2794 u32 invalid_disc_count = 0;
2795 u32 no_iinfo_count = 0;
2796
2797 for ( slot = 0; slot < w->max_disc; slot++ )
2798 {
2799 wbfs_disc_t * d = wbfs_open_disc_by_slot(w,slot,0);
2800 if (!d)
2801 continue;
2802
2803 CheckDisc_t * g = disc + slot;
2804 memcpy(g->id6,d->header->dhead,6);
2805
2806 wbfs_inode_info_t * iinfo = wbfs_get_disc_inode_info(d,0);
2807 if (!wbfs_is_inode_info_valid(wbfs->wbfs,iinfo))
2808 {
2809 g->no_iinfo_count = 1;
2810 no_iinfo_count++;
2811 }
2812
2813 if (!d->is_valid)
2814 {
2815 wbfs_close_disc(d);
2816 continue;
2817 }
2818
2819 u16 * wlba_tab = d->header->wlba_table;
2820 ASSERT(wlba_tab);
2821
2822 int bl;
2823 u32 invalid_game = 0, block_count = 0;
2824 for ( bl = 0; bl < w->n_wbfs_sec_per_disc; bl++ )
2825 {
2826 const u32 wlba = ntohs(wlba_tab[bl]);
2827 if ( wlba >= N_SEC )
2828 {
2829 invalid_game = 1;
2830 g->err_count++;
2831 g->bl_invalid++;
2832 total_err_invalid++;
2833 if ( verbose >= SHOW_DETAILS )
2834 fprintf(f,"%*s - #%d=%s/%u: invalid WBFS block #%u for block!\n",
2835 indent,"", slot, g->id6, bl, wlba );
2836 }
2837 else if ( wlba )
2838 {
2839 block_count++;
2840 if ( !ubl[wlba] )
2841 {
2842 invalid_game = 1;
2843 g->err_count++;
2844 g->bl_fbt++;
2845 if ( verbose >= SHOW_DETAILS )
2846 fprintf(f,"%*s - #%d=%s/%u: WBFS block #%u marked as free!\n",
2847 indent,"", slot, g->id6, bl, wlba );
2848 }
2849
2850 if ( blc[wlba] > 1 )
2851 {
2852 invalid_game = 1;
2853 g->err_count++;
2854 g->bl_overlap++;
2855 if ( verbose >= SHOW_DETAILS )
2856 fprintf(f,"%*s - #%d=%s/%u: WBFS block #%u is used %u times!\n",
2857 indent,"", slot, g->id6, bl, wlba, blc[wlba] );
2858 }
2859 }
2860 }
2861
2862 if (!block_count)
2863 {
2864 invalid_game = 1;
2865 g->no_blocks = 1;
2866 g->err_count++;
2867 total_err_no_blocks++;
2868 if ( verbose >= SHOW_DETAILS )
2869 fprintf(f,"%*s - #%d=%s: no valid WBFS block!\n", indent,"", slot, g->id6 );
2870 }
2871
2872 if (invalid_game)
2873 {
2874 invalid_disc_count += invalid_game;
2875 ASSERT(w->head);
2876
2877 // we mark it, but don't set the dirty flag
2878 // ==> the marker is only written on an external sync()
2879 w->head->disc_table[slot] |= WBFS_SLOT_INVALID;
2880 }
2881
2882 wbfs_close_disc(d);
2883 }
2884
2885 //---------- check free blocks table.
2886
2887 if ( verbose >= SHOW_DETAILS )
2888 fprintf(f,"%*s* Check free blocks table.\n", indent,"" );
2889
2890 u32 total_err_overlap = 0;
2891 for ( bl = 0; bl < N_SEC; bl++ )
2892 if ( blc[bl] > 1 )
2893 total_err_overlap++;
2894
2895 u32 total_err_fbt_used = 0;
2896 u32 total_err_fbt_free = 0;
2897 u32 total_err_fbt_free_wbfs0 = 0;
2898
2899 for ( bl = 1; bl < N_SEC; )
2900 {
2901 if ( blc[bl] && !ubl[bl] )
2902 {
2903 const int start_bl = bl;
2904 while ( bl < N_SEC && blc[bl] && !ubl[bl] )
2905 bl++;
2906 const int count = bl - start_bl;
2907 total_err_fbt_used += count;
2908 if ( verbose >= SHOW_DETAILS )
2909 {
2910 if ( count > 1 )
2911 fprintf(f,"%*s - %d used WBFS sectors #%u .. #%u marked as 'free'!\n",
2912 indent,"", count, start_bl, bl-1 );
2913 else
2914 fprintf(f,"%*s - Used WBFS sector #%u marked as 'free'!\n",
2915 indent,"", bl );
2916 }
2917 }
2918 else if ( !blc[bl] && ubl[bl] )
2919 {
2920 const int start_bl = bl;
2921 while ( bl < N_SEC && !blc[bl] && ubl[bl] )
2922 bl++;
2923 const int count = bl - start_bl;
2924 total_err_fbt_free += count;
2925
2926 int wbfs0_count = 0;
2927 if ( bl > WBFS0_SEC )
2928 {
2929 const int max = bl - WBFS0_SEC;
2930 wbfs0_count = count < max ? count : max;
2931 }
2932
2933 if ( verbose >= SHOW_DETAILS )
2934 {
2935 if ( count > 1 )
2936 fprintf(f,"%*s - %d free WBFS sectors #%u .. #%u marked as 'used'!\n",
2937 indent,"", count, start_bl, bl-1 );
2938 else
2939 fprintf(f,"%*s - Free WBFS sector #%u marked as 'used'!\n",
2940 indent,"", bl );
2941 if ( wbfs0_count && !total_err_fbt_free_wbfs0 )
2942 fprintf(f,"%*sNote: Free sectors >= #%u are marked 'used'"
2943 " because a bug in libwbfs v0.\n",
2944 indent+6,"", WBFS0_SEC );
2945 }
2946
2947 total_err_fbt_free_wbfs0 += wbfs0_count;
2948 }
2949 else
2950 bl++;
2951 }
2952
2953
2954 //---------- summary
2955
2956 ck->err_fbt_used = total_err_fbt_used;
2957 ck->err_fbt_free = total_err_fbt_free;
2958 ck->err_fbt_free_wbfs0 = total_err_fbt_free_wbfs0;
2959 ck->err_no_blocks = total_err_no_blocks;
2960 ck->err_bl_overlap = total_err_overlap;
2961 ck->err_bl_invalid = total_err_invalid;
2962 ck->no_iinfo_count = no_iinfo_count;
2963 ck->invalid_disc_count = invalid_disc_count;
2964
2965 ck->err_total = total_err_fbt_used
2966 + total_err_fbt_free
2967 + total_err_no_blocks
2968 + total_err_overlap
2969 + total_err_invalid;
2970
2971 // with the new wbfs interface all errors are harmless
2972 ck->err = ck->err_total ? ERR_WARNING : ERR_OK;
2973
2974 if ( ck->err_total && verbose >= PRINT_DUMP )
2975 {
2976 printf("\f\nWBFS DUMP:\n\n");
2977 DumpWBFS(wbfs,f,indent,SHOW__DEFAULT,
2978 verbose >= PRINT_FULL_DUMP ? 3 : 2,
2979 verbose >= PRINT_FULL_DUMP,
2980 verbose >= PRINT_EXT_DUMP ? 0 : ck );
2981 }
2982
2983 if ( ck->err_total && verbose >= SHOW_SUM || verbose >= SHOW_DETAILS )
2984 {
2985 printf("\f\n");
2986 PrintCheckedWBFS(ck,f,indent);
2987 }
2988
2989 return ck->err;
2990 }
2991
2992 ///////////////////////////////////////////////////////////////////////////////
2993
AutoCheckWBFS(WBFS_t * wbfs,bool ignore_check,int indent)2994 enumError AutoCheckWBFS ( WBFS_t * wbfs, bool ignore_check, int indent )
2995 {
2996 ASSERT(wbfs);
2997 ASSERT(wbfs->wbfs);
2998
2999 CheckWBFS_t ck;
3000 InitializeCheckWBFS(&ck);
3001 enumError err = CheckWBFS(&ck,wbfs,-1,0,0);
3002 if (err)
3003 {
3004 PrintCheckedWBFS(&ck,stdout,indent);
3005 if ( !ignore_check && err > ERR_WARNING )
3006 printf("!>> To avoid this automatic check use the option --no-check.\n"
3007 "!>> To ignore the results of this check use option --force.\n"
3008 "\n" );
3009 }
3010 ResetCheckWBFS(&ck);
3011 return ignore_check ? ERR_OK : err;
3012 }
3013
3014 ///////////////////////////////////////////////////////////////////////////////
3015
PrintCheckedWBFS(CheckWBFS_t * ck,FILE * f,int indent)3016 enumError PrintCheckedWBFS ( CheckWBFS_t * ck, FILE * f, int indent )
3017 {
3018 ASSERT(ck);
3019 if ( !ck->wbfs || !ck->cur_fbt || !f )
3020 return ERR_OK;
3021
3022 indent = NormalizeIndent(indent);
3023
3024 if ( ck->err_total )
3025 {
3026 fprintf(f,"%*s* Summary of WBFS Check: %u error%s found:\n",
3027 indent,"", ck->err_total, ck->err_total == 1 ? "" : "s" );
3028 if (ck->err_fbt_used)
3029 fprintf(f,"%*s%5u used WBFS sector%s marked as free!\n",
3030 indent,"", ck->err_fbt_used, ck->err_fbt_used == 1 ? "" : "s" );
3031 if (ck->err_fbt_free)
3032 {
3033 fprintf(f,"%*s%5u free WBFS sector%s marked as used!\n",
3034 indent,"", ck->err_fbt_free, ck->err_fbt_free == 1 ? "" : "s" );
3035 if (ck->err_fbt_free_wbfs0)
3036 fprintf(f,"%*sNote: %u error%s are based on a bug in libwbfs v0.\n",
3037 indent+6,"", ck->err_fbt_free_wbfs0,
3038 ck->err_fbt_free_wbfs0 == 1 ? "" : "s" );
3039 }
3040 if (ck->err_bl_overlap)
3041 fprintf(f,"%*s%5u WBFS sector%s are used by 2 or more discs!\n",
3042 indent,"", ck->err_bl_overlap, ck->err_bl_overlap == 1 ? "" : "s" );
3043 if (ck->err_bl_invalid)
3044 fprintf(f,"%*s%5u invalid WBFS block reference%s found!\n",
3045 indent,"", ck->err_bl_invalid, ck->err_bl_invalid == 1 ? "" : "s" );
3046 if (ck->err_no_blocks)
3047 fprintf(f,"%*s%5u disc%s no valid WBFS blocks!\n",
3048 indent,"", ck->err_no_blocks, ck->err_no_blocks == 1 ? " has" : "s have" );
3049 if (ck->invalid_disc_count)
3050 fprintf(f,"%*s Total: %u disc%s invalid!\n",
3051 indent,"", ck->invalid_disc_count,
3052 ck->invalid_disc_count == 1 ? " is" : "s are" );
3053 fputc('\n',f);
3054 }
3055 else
3056 fprintf(f,"%*s* Summary of WBFS check: no errors found.\n", indent,"" );
3057
3058 return ck->err_total ? ERR_WBFS_INVALID : ERR_OK;
3059 }
3060
3061 ///////////////////////////////////////////////////////////////////////////////
3062
RepairWBFS(CheckWBFS_t * ck,int testmode,RepairMode rm,int verbose,FILE * f,int indent)3063 enumError RepairWBFS ( CheckWBFS_t * ck, int testmode,
3064 RepairMode rm, int verbose, FILE * f, int indent )
3065 {
3066 TRACE("RepairWBFS(%p,%d,%x,%d,%p,%d)\n",
3067 ck, testmode, rm, verbose, f, indent );
3068
3069 ASSERT(ck);
3070 ASSERT(ck->wbfs);
3071 ASSERT(ck->wbfs->sf);
3072 ASSERT(ck->cur_fbt);
3073
3074 wbfs_t * w = ck->wbfs->wbfs;
3075 ASSERT(w);
3076
3077 u32 repair_count = 0, sync = 0;
3078 if (!f)
3079 verbose = -1;
3080 else if ( testmode && verbose < 0 )
3081 verbose = 0;
3082
3083 TRACELINE;
3084 if ( rm & REPAIR_RM_ALL )
3085 {
3086 int slot;
3087 for ( slot = 0; slot < w->max_disc; slot++ )
3088 {
3089 CheckDisc_t * disc = ck->disc + slot;
3090 // [dt] norm with WBFS_SLOT__MASK
3091 if ( w->head->disc_table[slot]
3092 && ( rm & REPAIR_RM_INVALID && disc->bl_invalid
3093 || rm & REPAIR_RM_OVERLAP && disc->bl_overlap
3094 || rm & REPAIR_RM_FREE && disc->bl_fbt
3095 || rm & REPAIR_RM_EMPTY && disc->no_blocks
3096 ))
3097 {
3098 if ( verbose >= 0 )
3099 {
3100 ccp title = GetTitle(disc->id6,0);
3101 fprintf(f,"%*s* %sDrop disc at slot #%u, id=%s%s%s\n",
3102 indent,"", testmode ? "WOULD " : "", slot, disc->id6,
3103 title ? ", " : "", title ? title : "" );
3104 }
3105
3106 if (!testmode)
3107 {
3108 w->head->disc_table[slot] = WBFS_SLOT_FREE;
3109 sync++;
3110 }
3111 repair_count++;
3112 }
3113 }
3114 }
3115
3116 TRACELINE;
3117 if ( rm & REPAIR_FBT )
3118 {
3119 if ( CalcFBT(ck) )
3120 {
3121 TRACELINE;
3122 if ( verbose >= 0 )
3123 fprintf(f,"%*s* %sStore fixed 'free blocks table' (%zd bytes).\n",
3124 indent,"", testmode ? "WOULD " : "", ck->fbt_size );
3125
3126 if (!testmode)
3127 {
3128 TRACELINE;
3129 enumError err = WriteAtF( &ck->wbfs->sf->f,
3130 ck->fbt_off, ck->good_fbt, ck->fbt_size );
3131 if (err)
3132 return err;
3133 memcpy(ck->cur_fbt,ck->good_fbt,ck->fbt_size);
3134 sync++;
3135 }
3136 repair_count++;
3137 }
3138
3139 TRACELINE;
3140 if ( w->head->wbfs_version < WBFS_VERSION )
3141 {
3142 if ( verbose >= 0 )
3143 fprintf(f,"%*s* %sSet WBFS version from %u to %u.\n",
3144 indent,"", testmode ? "WOULD " : "",
3145 w->head->wbfs_version, WBFS_VERSION );
3146 if (!testmode)
3147 {
3148 w->head->wbfs_version = WBFS_VERSION;
3149 sync++;
3150 }
3151 repair_count++;
3152 }
3153 }
3154
3155 TRACELINE;
3156 if ( rm & REPAIR_INODES )
3157 {
3158 TRACELINE;
3159 int slot;
3160 for ( slot = 0; slot < w->max_disc; slot++ )
3161 {
3162 wbfs_disc_t * d = wbfs_open_disc_by_slot(w,slot,1);
3163 if (d)
3164 {
3165 wbfs_touch_disc(d,0,0,0,0);
3166 wbfs_close_disc(d);
3167 }
3168 }
3169 }
3170
3171 TRACELINE;
3172 if (repair_count)
3173 wbfs_calc_used_blocks(w,true,false,0,0);
3174 if (sync)
3175 wbfs_sync(w);
3176
3177 TRACELINE;
3178 if ( verbose >= 0 && repair_count )
3179 fputc('\n',f);
3180
3181 TRACELINE;
3182 return ERR_OK;
3183 }
3184
3185 ///////////////////////////////////////////////////////////////////////////////
3186
CheckRepairWBFS(WBFS_t * wbfs,int testmode,RepairMode rm,int verbose,FILE * f,int indent)3187 enumError CheckRepairWBFS ( WBFS_t * wbfs, int testmode,
3188 RepairMode rm, int verbose, FILE * f, int indent )
3189 {
3190 ASSERT(wbfs);
3191
3192 CheckWBFS_t ck;
3193 InitializeCheckWBFS(&ck);
3194 enumError err = CheckWBFS(&ck,wbfs,-1,0,0);
3195 if ( err == ERR_WARNING || err == ERR_WBFS_INVALID )
3196 err = RepairWBFS(&ck,testmode,rm,verbose,f,indent);
3197 ResetCheckWBFS(&ck);
3198 return err;
3199 }
3200
3201 ///////////////////////////////////////////////////////////////////////////////
3202
RepairFBT(WBFS_t * w,int testmode,FILE * f,int indent)3203 enumError RepairFBT ( WBFS_t * w, int testmode, FILE * f, int indent )
3204 {
3205 return CheckRepairWBFS(w,testmode,REPAIR_FBT,0,f,indent);
3206 }
3207
3208 ///////////////////////////////////////////////////////////////////////////////
3209
CalcFBT(CheckWBFS_t * ck)3210 bool CalcFBT ( CheckWBFS_t * ck )
3211 {
3212 ASSERT(ck);
3213 if ( !ck->wbfs || !ck->wbfs->wbfs || !ck->cur_fbt )
3214 return ERROR0(ERR_INTERNAL,0);
3215
3216 if (!ck->good_fbt)
3217 ck->good_fbt = MALLOC(ck->fbt_size);
3218 memset(ck->good_fbt,0,ck->fbt_size);
3219
3220 const u32 MAX_BL = ck->wbfs->wbfs->n_wbfs_sec - 2;
3221 u32 * fbt = ck->good_fbt;
3222 int bl;
3223 for ( bl = 0; bl <= MAX_BL; bl++ )
3224 if (!ck->blc[bl+1])
3225 fbt[bl/32] |= 1 << (bl&31);
3226
3227 for ( bl = 0; bl < ck->fbt_size/4; bl++ )
3228 fbt[bl] = htonl(fbt[bl]);
3229
3230 return memcmp(ck->cur_fbt,ck->good_fbt,ck->fbt_size);
3231 }
3232
3233 //
3234 ///////////////////////////////////////////////////////////////////////////////
3235 /////////////// WDiscInfo_t ///////////////
3236 ///////////////////////////////////////////////////////////////////////////////
3237 // is WDiscInfo_t obsolete? [wiidisc] [[obsolete]]
3238
InitializeWDiscInfo(WDiscInfo_t * dinfo)3239 void InitializeWDiscInfo ( WDiscInfo_t * dinfo )
3240 {
3241 memset(dinfo,0,sizeof(*dinfo));
3242 dinfo->slot = -1;
3243 }
3244
3245 ///////////////////////////////////////////////////////////////////////////////
3246
ResetWDiscInfo(WDiscInfo_t * dinfo)3247 enumError ResetWDiscInfo ( WDiscInfo_t * dinfo )
3248 {
3249 // nothing to do
3250
3251 InitializeWDiscInfo(dinfo);
3252 return ERR_OK;
3253 }
3254
3255 ///////////////////////////////////////////////////////////////////////////////
3256
GetWDiscInfo(WBFS_t * w,WDiscInfo_t * dinfo,int disc_index)3257 enumError GetWDiscInfo ( WBFS_t * w, WDiscInfo_t * dinfo, int disc_index )
3258 {
3259 ASSERT(w);
3260 if ( !w || !w->wbfs || !dinfo )
3261 return ERROR0(ERR_INTERNAL,0);
3262
3263 w->disc_slot = -1;
3264
3265 u32 slot, size4;
3266 const enumError err = wbfs_get_disc_info ( w->wbfs, disc_index,
3267 (u8*)&dinfo->dhead, sizeof(dinfo->dhead), &slot,
3268 &dinfo->disc_type, &dinfo->disc_attrib,
3269 &size4, &dinfo->wbfs_fragments );
3270
3271 if (err)
3272 {
3273 memset(dinfo,0,sizeof(*dinfo));
3274 return err;
3275 }
3276
3277 CalcWDiscInfo(dinfo,0);
3278 dinfo->disc_index = disc_index;
3279 dinfo->slot = slot;
3280 dinfo->size = (u64)size4 * 4;
3281 dinfo->used_blocks = dinfo->size / WII_SECTOR_SIZE; // [[2do]] not exact
3282
3283 return dinfo->disc_type == WD_DT_UNKNOWN ? ERR_WARNING : ERR_OK;
3284 }
3285
3286 ///////////////////////////////////////////////////////////////////////////////
3287
GetWDiscInfoBySlot(WBFS_t * w,WDiscInfo_t * dinfo,u32 disc_slot)3288 enumError GetWDiscInfoBySlot ( WBFS_t * w, WDiscInfo_t * dinfo, u32 disc_slot )
3289 {
3290 ASSERT(w);
3291 if ( !w || !w->wbfs || !dinfo )
3292 return ERROR0(ERR_INTERNAL,0);
3293
3294 w->disc_slot = -1;
3295
3296 memset(dinfo,0,sizeof(*dinfo));
3297
3298 u32 size4;
3299 const enumError err = wbfs_get_disc_info_by_slot ( w->wbfs, disc_slot,
3300 (u8*)&dinfo->dhead, sizeof(dinfo->dhead),
3301 &dinfo->disc_type, &dinfo->disc_attrib,
3302 &size4, &dinfo->wbfs_fragments );
3303
3304 if (err)
3305 {
3306 TRACE("GetWDiscInfoBySlot() err=%d, magic=%x\n",err,dinfo->dhead.wii_magic);
3307 return err;
3308 }
3309
3310 CalcWDiscInfo(dinfo,0);
3311 dinfo->disc_index = disc_slot;
3312 dinfo->slot = disc_slot;
3313 dinfo->size = (u64)size4 * 4;
3314 dinfo->used_blocks = dinfo->size / WII_SECTOR_SIZE; // [[2do]] not exact
3315
3316 w->disc_slot = disc_slot;
3317 return dinfo->disc_type == WD_DT_UNKNOWN ? ERR_WARNING : ERR_OK;
3318 }
3319
3320 ///////////////////////////////////////////////////////////////////////////////
3321
FindWDiscInfo(WBFS_t * w,WDiscInfo_t * dinfo,ccp id6)3322 enumError FindWDiscInfo ( WBFS_t * w, WDiscInfo_t * dinfo, ccp id6 )
3323 {
3324 // [[2do]] the wbfs subsystem can find ids!
3325
3326 ASSERT(w);
3327 ASSERT(dinfo);
3328 ASSERT(id6);
3329 if ( !w || !w->wbfs || !dinfo || !id6 )
3330 return ERROR0(ERR_INTERNAL,0);
3331
3332 w->disc_slot = -1;
3333
3334 int i;
3335 for ( i = 0; i < w->used_discs; i++ )
3336 {
3337 if ( GetWDiscInfo(w,dinfo,i) == ERR_OK
3338 && !memcmp(id6,&dinfo->dhead.disc_id,6) )
3339 return ERR_OK;
3340 }
3341 return ERR_WDISC_NOT_FOUND;
3342 }
3343
3344 ///////////////////////////////////////////////////////////////////////////////
3345
LoadIsoHeader(WBFS_t * w,wd_header_t * iso_header,wbfs_disc_t * disc)3346 enumError LoadIsoHeader ( WBFS_t * w, wd_header_t * iso_header, wbfs_disc_t * disc )
3347 {
3348 ASSERT(w);
3349 ASSERT(iso_header);
3350
3351 if ( !w || !w->sf || !w->wbfs || !iso_header )
3352 return ERROR0(ERR_INTERNAL,0);
3353
3354 memset(iso_header,0,sizeof(*iso_header));
3355 if (!disc)
3356 {
3357 disc = w->disc;
3358 if (!disc)
3359 return ERR_OK;
3360 }
3361
3362 u16 wlba = ntohs(disc->header->wlba_table[0]);
3363 if (!wlba)
3364 return ERR_OK;
3365
3366 char buf[HD_SECTOR_SIZE]; // read whole hd sector
3367 enumError err = ReadSF(w->sf, wlba * (off_t)w->wbfs->wbfs_sec_sz,
3368 buf, sizeof(buf) );
3369 if (!err)
3370 memcpy(iso_header,buf,sizeof(*iso_header));
3371 return err;
3372 }
3373
3374 ///////////////////////////////////////////////////////////////////////////////
3375
CalcWDiscInfo(WDiscInfo_t * dinfo,SuperFile_t * sf)3376 void CalcWDiscInfo ( WDiscInfo_t * dinfo, SuperFile_t * sf )
3377 {
3378 ASSERT(dinfo);
3379
3380 if (sf)
3381 {
3382 const wd_disc_t * disc = OpenDiscSF(sf,false,false);
3383 if (disc)
3384 {
3385 memcpy(&dinfo->dhead,&disc->dhead,sizeof(dinfo->dhead));
3386 dinfo->magic2 = disc->magic2;
3387 dinfo->n_part = disc->n_part;
3388 dinfo->used_blocks = CountUsedIsoBlocksSF(sf,&part_selector);
3389
3390 static char mask[] = "DUC?";
3391 strcpy(dinfo->part_info,"----");
3392 int ip;
3393 for ( ip = 0; ip < disc->n_part; ip++ )
3394 {
3395 u32 pt = disc->part[ip].part_type;
3396 if ( pt > sizeof(mask)-2 )
3397 pt = sizeof(mask)-2;
3398 dinfo->part_info[pt] = mask[pt];
3399 }
3400 }
3401 else
3402 ReadSF(sf,0,&dinfo->dhead,sizeof(dinfo->dhead));
3403 }
3404
3405 memcpy(dinfo->id6,&dinfo->dhead.disc_id,6);
3406 dinfo->id6[6] = 0;
3407 dinfo->used_blocks = 0;
3408 dinfo->disc_index = 0;
3409 dinfo->size = 0;
3410 dinfo->title = GetTitle(dinfo->id6,0);
3411 dinfo->disc_type = get_header_disc_type(&dinfo->dhead,&dinfo->disc_attrib);
3412 }
3413
3414 ///////////////////////////////////////////////////////////////////////////////
3415
CopyWDiscInfo(WDiscListItem_t * item,WDiscInfo_t * dinfo)3416 void CopyWDiscInfo ( WDiscListItem_t * item, WDiscInfo_t * dinfo )
3417 {
3418 ASSERT(item);
3419 ASSERT(dinfo);
3420
3421 memset(item,0,sizeof(*item));
3422
3423 memcpy(item->id6,&dinfo->dhead.disc_id,6);
3424 item->size_mib = (u32)(( dinfo->size + MiB/2 ) / MiB );
3425 memcpy(item->name64,dinfo->dhead.disc_title,64);
3426 memcpy(item->part_info,dinfo->part_info,sizeof(item->part_info));
3427 item->title = dinfo->title;
3428 item->n_part = dinfo->n_part;
3429 item->wbfs_slot = dinfo->disc_index;
3430 item->wbfs_fragments= dinfo->wbfs_fragments;
3431 item->ftype = FT_ID_WBFS | FT_A_WDISC;
3432 item->used_blocks = dinfo->used_blocks;
3433
3434 CopyFileAttribDiscInfo(&item->fatt,dinfo);
3435 }
3436
3437 ///////////////////////////////////////////////////////////////////////////////
3438
DumpTimestamp(FILE * f,int indent,ccp head,u64 xtime,ccp comment)3439 static void DumpTimestamp ( FILE * f, int indent, ccp head, u64 xtime, ccp comment )
3440 {
3441 time_t tim = ntoh64(xtime);
3442 if (tim)
3443 {
3444 struct tm * tm = localtime(&tim);
3445 char timbuf[40];
3446 strftime(timbuf,sizeof(timbuf),"%F %T",tm);
3447 fprintf(f,"%*s%-12s%s (%s)\n", indent, "", head, timbuf, comment );
3448 }
3449 }
3450
3451 //-----------------------------------------------------------------------------
3452
DumpWDiscInfo(WDiscInfo_t * di,wd_header_t * ih,FILE * f,int indent)3453 enumError DumpWDiscInfo
3454 ( WDiscInfo_t * di, wd_header_t * ih, FILE * f, int indent )
3455 {
3456 if ( !di || !f )
3457 return ERROR0(ERR_INTERNAL,0);
3458
3459 indent = NormalizeIndent(indent);
3460
3461 const u32 magic = ntohl(di->dhead.wii_magic);
3462 fprintf(f,"%*smagic: %08x, %.64s\n", indent, "", magic,
3463 magic == WII_MAGIC ? " (=WII-DISC)"
3464 : magic == WII_MAGIC_DELETED ? " (=DELETED)" : "" );
3465
3466 fprintf(f,"%*swbfs name: %6s, %.64s\n",
3467 indent, "", &di->dhead.disc_id, di->dhead.disc_title );
3468 if (ih)
3469 fprintf(f,"%*siso name: %6s, %.64s\n",
3470 indent, "", &ih->disc_id, ih->disc_title );
3471 if ( ih && strcmp(&di->dhead.disc_id,&ih->disc_id) )
3472 {
3473 if (di->title)
3474 fprintf(f,"%*swbfs title: %s\n", indent, "", (ccp)di->title );
3475 ccp title = GetTitle(&ih->disc_id,0);
3476 if (title)
3477 fprintf(f,"%*siso title: %s\n", indent, "", (ccp)di->title );
3478 }
3479 else if (di->title)
3480 fprintf(f,"%*stitle: %s\n", indent, "", (ccp)di->title );
3481
3482 const RegionInfo_t * rinfo = GetRegionInfo(di->dhead.region_code);
3483 fprintf(f,"%*sregion: %s [%s]\n", indent, "", rinfo->name, rinfo->name4 );
3484
3485 if (di->size)
3486 fprintf(f,"%*ssize: %lld MiB\n", indent, "", ( di->size + MiB/2 ) / MiB );
3487
3488 DumpTimestamp(f,indent,"i-time:",di->dhead.iinfo.itime,"insertion time");
3489 DumpTimestamp(f,indent,"m-time:",di->dhead.iinfo.mtime,"last modification time");
3490 DumpTimestamp(f,indent,"c-time:",di->dhead.iinfo.ctime,"last status change time");
3491 DumpTimestamp(f,indent,"a-time:",di->dhead.iinfo.atime,"last access time");
3492 DumpTimestamp(f,indent,"d-time:",di->dhead.iinfo.dtime,"deletion time");
3493
3494 return ERR_OK;
3495 }
3496
3497 ///////////////////////////////////////////////////////////////////////////////
3498
GenerateWDiscList(WBFS_t * w,int part_index)3499 WDiscList_t * GenerateWDiscList ( WBFS_t * w, int part_index )
3500 {
3501 ASSERT(w);
3502 if ( !w || !w->wbfs )
3503 {
3504 ERROR0(ERR_INTERNAL,0);
3505 return 0;
3506 }
3507
3508 WDiscList_t * wlist = MALLOC(sizeof(WDiscList_t));
3509 wlist->first_disc = CALLOC(w->used_discs,sizeof(WDiscListItem_t));
3510 wlist->total_size_mib = 0;
3511
3512 WDiscInfo_t dinfo;
3513 WDiscListItem_t * item = wlist->first_disc;
3514
3515 int slot;
3516 for ( slot = 0; slot < w->total_discs; slot++ )
3517 {
3518 if ( GetWDiscInfoBySlot(w,&dinfo,slot) == ERR_OK )
3519 {
3520 memcpy(item->id6,&dinfo.dhead.disc_id,6);
3521 if (!IsExcluded(item->id6))
3522 {
3523 CopyWDiscInfo(item,&dinfo);
3524 item->part_index = part_index;
3525 wlist->total_size_mib += item->size_mib;
3526 item++;
3527 }
3528 }
3529 }
3530
3531 wlist->used = item - wlist->first_disc;
3532 return wlist;
3533 }
3534
3535 ///////////////////////////////////////////////////////////////////////////////
3536
InitializeWDiscList(WDiscList_t * wlist)3537 void InitializeWDiscList ( WDiscList_t * wlist )
3538 {
3539 ASSERT(wlist);
3540 memset(wlist,0,sizeof(*wlist));
3541 }
3542
3543 ///////////////////////////////////////////////////////////////////////////////
3544
ResetWDiscList(WDiscList_t * wlist)3545 void ResetWDiscList ( WDiscList_t * wlist )
3546 {
3547 ASSERT(wlist);
3548
3549 if ( wlist->first_disc )
3550 {
3551 WDiscListItem_t * ptr = wlist->first_disc;
3552 WDiscListItem_t * end = ptr + wlist->used;
3553 for ( ; ptr < end; ptr++ )
3554 FREE((char*)ptr->fname);
3555 FREE(wlist->first_disc);
3556 }
3557 InitializeWDiscList(wlist);
3558 }
3559
3560 ///////////////////////////////////////////////////////////////////////////////
3561
AppendWDiscList(WDiscList_t * wlist,WDiscInfo_t * dinfo)3562 WDiscListItem_t * AppendWDiscList ( WDiscList_t * wlist, WDiscInfo_t * dinfo )
3563 {
3564 ASSERT(wlist);
3565 ASSERT( wlist->used <= wlist->size );
3566 if ( wlist->used == wlist->size )
3567 {
3568 wlist->size += 200;
3569 wlist->first_disc = REALLOC(wlist->first_disc,
3570 wlist->size*sizeof(*wlist->first_disc));
3571 }
3572 wlist->sort_mode = SORT_NONE;
3573 WDiscListItem_t * item = wlist->first_disc + wlist->used++;
3574 CopyWDiscInfo(item,dinfo);
3575 return item;
3576 }
3577
3578 ///////////////////////////////////////////////////////////////////////////////
3579
FreeWDiscList(WDiscList_t * wlist)3580 void FreeWDiscList ( WDiscList_t * wlist )
3581 {
3582 ASSERT(wlist);
3583 ResetWDiscList(wlist);
3584 FREE(wlist);
3585 }
3586
3587 //
3588 ///////////////////////////////////////////////////////////////////////////////
3589 /////////////// WDisc sorting ///////////////
3590 ///////////////////////////////////////////////////////////////////////////////
3591
3592 typedef int (*compare_func) (const void *, const void *);
3593
3594 //-----------------------------------------------------------------------------
3595
sort_by_title(const void * va,const void * vb)3596 static int sort_by_title ( const void * va, const void * vb )
3597 {
3598 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3599 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3600
3601 int stat = strcasecmp( a->title ? a->title : a->name64,
3602 b->title ? b->title : b->name64 );
3603 if (!stat)
3604 {
3605 stat = strcasecmp(a->name64,b->name64);
3606 if (!stat)
3607 {
3608 stat = strcmp(a->id6,b->id6);
3609 if (!stat)
3610 {
3611 stat = strcmp( a->fname ? a->fname : "" ,
3612 b->fname ? b->fname : "" );
3613 if (!stat)
3614 stat = a->part_index - b->part_index;
3615 }
3616 }
3617 }
3618 return stat;
3619 }
3620
3621 //-----------------------------------------------------------------------------
3622
sort_by_id(const void * va,const void * vb)3623 static int sort_by_id ( const void * va, const void * vb )
3624 {
3625 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3626 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3627
3628 const int stat = strcmp(a->id6,b->id6);
3629 return stat ? stat : sort_by_title(va,vb);
3630 }
3631
3632 //-----------------------------------------------------------------------------
3633
sort_by_name(const void * va,const void * vb)3634 static int sort_by_name ( const void * va, const void * vb )
3635 {
3636 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3637 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3638
3639 const int stat = strcasecmp(a->name64,b->name64);
3640 return stat ? stat : sort_by_title(va,vb);
3641 }
3642
3643 //-----------------------------------------------------------------------------
3644
sort_by_path(const void * va,const void * vb)3645 static int sort_by_path ( const void * va, const void * vb )
3646 {
3647 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3648 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3649
3650 const int stat = PathCMP(a->name64,b->name64);
3651 return stat ? stat : sort_by_title(va,vb);
3652 }
3653
3654 //-----------------------------------------------------------------------------
3655
sort_by_nintendo(const void * va,const void * vb)3656 static int sort_by_nintendo ( const void * va, const void * vb )
3657 {
3658 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3659 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3660
3661 const int stat = NintendoCMP(a->name64,b->name64);
3662 return stat ? stat : sort_by_title(va,vb);
3663 }
3664
3665 //-----------------------------------------------------------------------------
3666
sort_by_file(const void * va,const void * vb)3667 static int sort_by_file ( const void * va, const void * vb )
3668 {
3669 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3670 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3671
3672 const int stat = strcmp( a->fname ? a->fname : "" ,
3673 b->fname ? b->fname : "" );
3674 return stat ? stat : sort_by_title(va,vb);
3675 }
3676
3677 //-----------------------------------------------------------------------------
3678
sort_by_size(const void * va,const void * vb)3679 static int sort_by_size ( const void * va, const void * vb )
3680 {
3681 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3682 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3683
3684 const u32 ab = a->used_blocks ? a->used_blocks : a->size_mib * WII_SECTORS_PER_MIB;
3685 const u32 bb = b->used_blocks ? b->used_blocks : b->size_mib * WII_SECTORS_PER_MIB;
3686
3687 return ab < bb ? -1 : ab > bb ? +1 : sort_by_title(va,vb);
3688 }
3689
3690 //-----------------------------------------------------------------------------
3691
sort_by_offset(const void * va,const void * vb)3692 static int sort_by_offset ( const void * va, const void * vb )
3693 {
3694 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3695 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3696
3697 const int stat = (int)a->wbfs_slot - (int)b->wbfs_slot;
3698 return stat ? stat : sort_by_size(va,vb);
3699 }
3700
3701 //-----------------------------------------------------------------------------
3702
sort_by_mtime(const void * va,const void * vb)3703 static int sort_by_mtime ( const void * va, const void * vb )
3704 {
3705 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3706 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3707
3708 return a->fatt.mtime > b->fatt.mtime ? 1
3709 : a->fatt.mtime < b->fatt.mtime ? -1
3710 : a->fatt.itime > b->fatt.itime ? 1
3711 : a->fatt.itime < b->fatt.itime ? -1
3712 : a->fatt.ctime > b->fatt.ctime ? 1
3713 : a->fatt.ctime < b->fatt.ctime ? -1
3714 : a->fatt.atime > b->fatt.atime ? 1
3715 : a->fatt.atime < b->fatt.atime ? -1
3716 : sort_by_title(va,vb);
3717 }
3718
3719 //-----------------------------------------------------------------------------
3720
sort_by_itime(const void * va,const void * vb)3721 static int sort_by_itime ( const void * va, const void * vb )
3722 {
3723 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3724 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3725
3726 return a->fatt.itime > b->fatt.itime ? 1
3727 : a->fatt.itime < b->fatt.itime ? -1
3728 : a->fatt.ctime > b->fatt.ctime ? 1
3729 : a->fatt.ctime < b->fatt.ctime ? -1
3730 : sort_by_mtime(va,vb);
3731 }
3732
3733 //-----------------------------------------------------------------------------
3734
sort_by_ctime(const void * va,const void * vb)3735 static int sort_by_ctime ( const void * va, const void * vb )
3736 {
3737 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3738 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3739
3740 return a->fatt.ctime > b->fatt.ctime ? 1
3741 : a->fatt.ctime < b->fatt.ctime ? -1
3742 : sort_by_mtime(va,vb);
3743 }
3744
3745 //-----------------------------------------------------------------------------
3746
sort_by_atime(const void * va,const void * vb)3747 static int sort_by_atime ( const void * va, const void * vb )
3748 {
3749 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3750 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3751
3752 return a->fatt.atime > b->fatt.atime ? 1
3753 : a->fatt.atime < b->fatt.atime ? -1
3754 : sort_by_mtime(va,vb);
3755 }
3756
3757 //-----------------------------------------------------------------------------
3758
sort_by_region(const void * va,const void * vb)3759 static int sort_by_region ( const void * va, const void * vb )
3760 {
3761 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3762 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3763
3764 const int stat = a->id6[3] - b->id6[3];
3765 return stat ? stat : sort_by_id(va,vb);
3766 }
3767
3768 //-----------------------------------------------------------------------------
3769
sort_by_wbfs(const void * va,const void * vb)3770 static int sort_by_wbfs ( const void * va, const void * vb )
3771 {
3772 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3773 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3774
3775 const int stat = a->part_index - b->part_index;
3776 return stat ? stat : sort_by_title(va,vb);
3777 }
3778
3779 //-----------------------------------------------------------------------------
3780
sort_by_npart(const void * va,const void * vb)3781 static int sort_by_npart ( const void * va, const void * vb )
3782 {
3783 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3784 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3785
3786 const int stat = (int)a->n_part - (int)b->n_part;
3787 return stat ? stat : sort_by_title(va,vb);
3788 }
3789
3790 //-----------------------------------------------------------------------------
3791
sort_by_frag(const void * va,const void * vb)3792 static int sort_by_frag ( const void * va, const void * vb )
3793 {
3794 const WDiscListItem_t * a = (const WDiscListItem_t *)va;
3795 const WDiscListItem_t * b = (const WDiscListItem_t *)vb;
3796
3797 const int stat = (int)a->wbfs_fragments - (int)b->wbfs_fragments;
3798 return stat ? stat : sort_by_title(va,vb);
3799 }
3800
3801 ///////////////////////////////////////////////////////////////////////////////
3802
ReverseWDiscList(WDiscList_t * wlist)3803 void ReverseWDiscList ( WDiscList_t * wlist )
3804 {
3805 if (! wlist || wlist->used < 2 )
3806 return;
3807
3808 ASSERT(wlist->first_disc);
3809
3810 WDiscListItem_t *a, *b, temp;
3811 for ( a = wlist->first_disc, b = a + wlist->used-1; a < b; a++, b-- )
3812 {
3813 memcpy( &temp, a, sizeof(WDiscListItem_t) );
3814 memcpy( a, b, sizeof(WDiscListItem_t) );
3815 memcpy( b, &temp, sizeof(WDiscListItem_t) );
3816 }
3817 }
3818
3819 ///////////////////////////////////////////////////////////////////////////////
3820
SortWDiscList(WDiscList_t * wlist,SortMode sort_mode,SortMode default_sort_mode,int unique)3821 void SortWDiscList ( WDiscList_t * wlist,
3822 SortMode sort_mode, SortMode default_sort_mode, int unique )
3823 {
3824 if (!wlist)
3825 return;
3826
3827 TRACE("SortWDiscList(%p, s=%d,%d, u=%d) prev=%d\n",
3828 wlist, sort_mode, default_sort_mode, unique, wlist->sort_mode );
3829
3830 if ( (uint)(sort_mode&SORT__MODE_MASK) >= SORT_DEFAULT )
3831 {
3832 sort_mode = (uint)default_sort_mode >= SORT_DEFAULT
3833 ? wlist->sort_mode
3834 : default_sort_mode | sort_mode & SORT_REVERSE;
3835 }
3836
3837 if ( wlist->used < 2 )
3838 {
3839 // zero or one element is sorted!
3840 wlist->sort_mode = sort_mode;
3841 return;
3842 }
3843
3844 compare_func func = 0;
3845 SortMode umode = SORT_ID;
3846 SortMode smode = sort_mode & SORT__MODE_MASK;
3847 if ( smode == SORT_TIME )
3848 smode = SelectSortMode(opt_print_time);
3849 switch(smode)
3850 {
3851 case SORT_ID: func = sort_by_id; break;
3852 case SORT_NAME: func = sort_by_name; umode = SORT_NAME; break;
3853 case SORT_TITLE: func = sort_by_title; umode = SORT_TITLE; break;
3854 case SORT_PATH: func = sort_by_path; umode = SORT_PATH; break;
3855 case SORT_NINTENDO: func = sort_by_nintendo;umode = SORT_NINTENDO; break;
3856 case SORT_FILE: func = sort_by_file; break;
3857 case SORT_SIZE: func = sort_by_size; umode = SORT_SIZE; break;
3858 case SORT_OFFSET: func = sort_by_offset; break;
3859 case SORT_REGION: func = sort_by_region; umode = SORT_REGION; break;
3860 case SORT_WBFS: func = sort_by_wbfs; break;
3861 case SORT_NPART: func = sort_by_npart; break;
3862 case SORT_FRAG: func = sort_by_frag; break;
3863 case SORT_ITIME: func = sort_by_itime; break;
3864 case SORT_MTIME: func = sort_by_mtime; break;
3865 case SORT_CTIME: func = sort_by_ctime; break;
3866 case SORT_ATIME: func = sort_by_atime; break;
3867 default: break;
3868 }
3869
3870 if (unique)
3871 {
3872 SortWDiscList(wlist,umode,umode,false);
3873
3874 WDiscListItem_t *src, *dest, *end, *prev = 0;
3875 src = dest = wlist->first_disc;
3876 end = src + wlist->used;
3877 wlist->total_size_mib = 0;
3878 for ( ; src < end; src++ )
3879 {
3880 if ( !prev
3881 || memcmp(src->id6,prev->id6,6)
3882 || unique < 2 &&
3883 ( memcmp(src->name64,prev->name64,64)
3884 || src->size_mib != prev->size_mib ))
3885 {
3886 if ( dest != src )
3887 memcpy(dest,src,sizeof(*dest));
3888 wlist->total_size_mib += dest->size_mib;
3889 prev = dest++;
3890 }
3891 else
3892 FREE((char*)src->fname);
3893 }
3894 wlist->used = dest - wlist->first_disc;
3895 }
3896
3897 if ( func && wlist->sort_mode != sort_mode )
3898 {
3899 wlist->sort_mode = sort_mode;
3900 qsort(wlist->first_disc,wlist->used,sizeof(*wlist->first_disc),func);
3901 if ( sort_mode & SORT_REVERSE )
3902 ReverseWDiscList(wlist);
3903 }
3904 }
3905
3906 ///////////////////////////////////////////////////////////////////////////////
3907
print_sect_time(FILE * f,char name,time_t tim)3908 static void print_sect_time ( FILE *f, char name, time_t tim )
3909 {
3910 ASSERT(f);
3911 if (tim)
3912 {
3913 struct tm * tm = localtime(&tim);
3914 char timbuf[40];
3915 strftime(timbuf,sizeof(timbuf),"%F %T",tm);
3916 fprintf(f,"%ctime=%llu %s\n", name, (u64)tim, timbuf );
3917 }
3918 else
3919 fprintf(f,"%ctime=\n",name);
3920 }
3921
3922 //-----------------------------------------------------------------------------
3923
PrintSectWDiscListItem(FILE * f,WDiscListItem_t * witem,ccp def_fname)3924 void PrintSectWDiscListItem ( FILE * f, WDiscListItem_t * witem, ccp def_fname )
3925 {
3926 ASSERT(f);
3927 ASSERT(witem);
3928
3929 fprintf(f,"id=%s\n",witem->id6);
3930 fprintf(f,"name=%s\n",witem->name64);
3931 fprintf(f,"title=%s\n", witem->title ? witem->title : "" );
3932 fprintf(f,"region=%s\n",GetRegionInfo(witem->id6[3])->name4);
3933 fprintf(f,"size=%llu\n",(u64)witem->fatt.size
3934 ? (u64)witem->fatt.size : (u64)witem->size_mib*MiB );
3935 //fprintf(f,"size_mib=%u\n",witem->size_mib);
3936 if (witem->used_blocks)
3937 fprintf(f,"used-blocks=%u\n",witem->used_blocks);
3938
3939 print_sect_time(f,'i',witem->fatt.itime);
3940 print_sect_time(f,'m',witem->fatt.mtime);
3941 print_sect_time(f,'c',witem->fatt.ctime);
3942 print_sect_time(f,'a',witem->fatt.atime);
3943 //fprintf(f,"part_index=%u\n",witem->part_index);
3944 //fprintf(f,"n_part=%u\n",witem->n_part);
3945
3946 fprintf(f,"filetype=%s\n",GetNameFT(witem->ftype,0));
3947 fprintf(f,"container=%s\n",GetContainerNameFT(witem->ftype,"-"));
3948 const wd_disc_type_t dt = FileType2DiscType(witem->ftype);
3949 fprintf(f,"disctype=%d %s\n",dt,wd_get_disc_type_name(dt,"?"));
3950
3951 if ( witem->n_part > 0 || *witem->part_info )
3952 {
3953 fprintf(f,"n-partitions=%d\n",witem->n_part);
3954 fprintf(f,"partition-info=%s\n",witem->part_info);
3955 }
3956
3957 fprintf(f,"wbfs_slot=%d\n",witem->wbfs_slot);
3958 if (witem->wbfs_fragments)
3959 fprintf(f,"wbfs-fragments=%u\n",witem->wbfs_fragments);
3960
3961 ccp fname = witem->fname ? witem->fname : def_fname ? def_fname : "";
3962 fprintf(f,"filename=%s\n",fname);
3963 if ( *fname && witem->wbfs_slot >= 0 )
3964 fprintf(f,"source=%s/#%u\n",fname,witem->wbfs_slot);
3965 else
3966 fprintf(f,"source=%s\n",fname);
3967 }
3968
3969 //
3970 ///////////////////////////////////////////////////////////////////////////////
3971 /////////////// access to WBFS dics ///////////////
3972 ///////////////////////////////////////////////////////////////////////////////
3973
OpenWDiscID6(WBFS_t * w,ccp id6)3974 enumError OpenWDiscID6 ( WBFS_t * w, ccp id6 )
3975 {
3976 ASSERT(w);
3977 CloseWDisc(w);
3978
3979 if ( !w || !w->wbfs || !id6 )
3980 return ERROR0(ERR_INTERNAL,0);
3981
3982 w->disc = wbfs_open_disc_by_id6(w->wbfs,(u8*)id6);
3983 w->disc_slot = w->disc ? w->disc->slot : -1;
3984 return w->disc ? ERR_OK : ERR_WDISC_NOT_FOUND;
3985 }
3986
3987 ///////////////////////////////////////////////////////////////////////////////
3988
OpenWDiscIndex(WBFS_t * wbfs,u32 index)3989 enumError OpenWDiscIndex ( WBFS_t * wbfs, u32 index )
3990 {
3991 ASSERT(wbfs);
3992
3993 if ( !wbfs || !wbfs->wbfs )
3994 return ERROR0(ERR_INTERNAL,0);
3995
3996 wbfs_t *w = wbfs->wbfs;
3997 wbfs->disc_slot = -1;
3998
3999 u32 slot;
4000 for ( slot = 0; slot < w->max_disc; slot++ )
4001 if ( w->head->disc_table[slot] && !index-- )
4002 return OpenWDiscSlot(wbfs,slot,0);
4003
4004 CloseWDisc(wbfs);
4005 return ERR_WDISC_NOT_FOUND;
4006 }
4007
4008 ///////////////////////////////////////////////////////////////////////////////
4009
OpenWDiscSlot(WBFS_t * w,u32 slot,bool force_open)4010 enumError OpenWDiscSlot ( WBFS_t * w, u32 slot, bool force_open )
4011 {
4012 ASSERT(w);
4013 CloseWDisc(w);
4014
4015 if ( !w || !w->wbfs )
4016 return ERROR0(ERR_INTERNAL,0);
4017
4018 w->disc = wbfs_open_disc_by_slot(w->wbfs,slot,force_open);
4019 w->disc_slot = w->disc ? w->disc->slot : -1;
4020 return w->disc ? ERR_OK : ERR_WDISC_NOT_FOUND;
4021 }
4022
4023 ///////////////////////////////////////////////////////////////////////////////
4024
OpenWDiscSF(WBFS_t * w)4025 enumError OpenWDiscSF ( WBFS_t * w )
4026 {
4027 DASSERT(w);
4028 CloseWDiscSF(w);
4029 if ( !w->sf || !w->disc )
4030 return ERROR0(ERR_INTERNAL,0);
4031
4032 SuperFile_t * sf = w->sf;
4033 sf->wbfs = w;
4034 SetupIOD(sf,OFT_WBFS,OFT_WBFS);
4035 SetPatchFileID(&sf->f,w->disc->header,6); // [[2do]] SetFileID() ?
4036 w->disc_sf_opened = true;
4037
4038 CopyFileAttribStat( &sf->f.fatt, &sf->f.st, false );
4039 if ( w->disc->header && w->disc->header->dhead )
4040 {
4041 const wbfs_inode_info_t * ii
4042 = (wbfs_inode_info_t*) ( w->disc->header->dhead + WBFS_INODE_INFO_OFF );
4043 CopyFileAttribInode( &sf->f.fatt, ii, 0 );
4044 }
4045
4046 return ERR_OK;
4047 }
4048
4049 ///////////////////////////////////////////////////////////////////////////////
4050
CloseWDiscSF(WBFS_t * w)4051 enumError CloseWDiscSF ( WBFS_t * w )
4052 {
4053 DASSERT(w);
4054 if ( w->disc_sf_opened )
4055 {
4056 w->disc_sf_opened = false;
4057
4058 SuperFile_t * sf = w->sf;
4059 if ( sf && sf->iod.oft == OFT_WBFS )
4060 {
4061 CloseDiscSF(sf);
4062 ClearFileID(&sf->f);
4063 SetupIOD(sf,OFT_PLAIN,OFT_PLAIN);
4064 sf->wbfs = 0;
4065 }
4066
4067 CopyFileAttribStat(&sf->f.fatt,&sf->f.st,false);
4068 }
4069 return ERR_OK;
4070 }
4071
4072 ///////////////////////////////////////////////////////////////////////////////
4073
CloseWDisc(WBFS_t * w)4074 enumError CloseWDisc ( WBFS_t * w )
4075 {
4076 DASSERT(w);
4077
4078 CloseWDiscSF(w);
4079 SuperFile_t *sf = w->sf;
4080
4081 if (w->disc)
4082 {
4083 if ( !sf || !IsOpenSF(sf) )
4084 w->disc->is_dirty = false;
4085
4086 if ( sf
4087 && sf->f.is_writing
4088 && w->disc->header
4089 && w->is_wbfs_file
4090 && CopyPatchWbfsId( (char*)w->disc->header->dhead,
4091 *sf->wbfs_id6
4092 ? sf->wbfs_id6
4093 : (ccp)w->disc->header->dhead )
4094 ) // [[id+]]
4095 {
4096 w->disc->is_dirty = true;
4097 }
4098
4099 wbfs_close_disc(w->disc);
4100 w->disc = 0;
4101 }
4102
4103 if (sf)
4104 CloseDiscSF(sf);
4105
4106 return ERR_OK;
4107 }
4108
4109 ///////////////////////////////////////////////////////////////////////////////
4110
ExistsWDisc(WBFS_t * w,ccp id6)4111 enumError ExistsWDisc ( WBFS_t * w, ccp id6 )
4112 {
4113 ASSERT(w);
4114
4115 if ( !w || !w->wbfs || !id6 )
4116 return ERROR0(ERR_INTERNAL,0);
4117
4118 char patched_id6[7];
4119 CopyPatchWbfsId(patched_id6,id6);
4120
4121 return wbfs_find_slot(w->wbfs,(u8*)patched_id6) < 0
4122 ? ERR_WDISC_NOT_FOUND
4123 : ERR_OK;
4124 }
4125
4126 ///////////////////////////////////////////////////////////////////////////////
4127
GetWDiscHeader(WBFS_t * w)4128 wd_header_t * GetWDiscHeader ( WBFS_t * w )
4129 {
4130 return w && w->disc && w->disc->header
4131 ? (wd_header_t*)w->disc->header
4132 : 0;
4133 }
4134
4135 //
4136 ///////////////////////////////////////////////////////////////////////////////
4137 /////////////// AddWDisc() ///////////////
4138 ///////////////////////////////////////////////////////////////////////////////
4139
AddWDisc(WBFS_t * w,SuperFile_t * sf,const wd_select_t * psel)4140 enumError AddWDisc ( WBFS_t * w, SuperFile_t * sf, const wd_select_t * psel )
4141 {
4142 if ( !w || !w->wbfs || !w->sf || !sf )
4143 return ERROR0(ERR_INTERNAL,0);
4144
4145 TRACE("AddWDisc(w=%p,sf=%p) progress=%d,%d\n",
4146 w, sf, sf->show_progress, sf->show_summary );
4147
4148 CloseWDisc(w);
4149
4150 // this is needed for detailed error messages
4151 const enumError saved_max_error = max_error;
4152 max_error = 0;
4153
4154 wbfs_param_t par;
4155 memset(&par,0,sizeof(par));
4156 par.read_src_wii_disc = WrapperReadSF; // [[2do]] [[obsolete]]? (both: param and func)
4157 par.callback_data = sf;
4158 par.spinner = sf->show_progress ? PrintProgressSF : 0;
4159 par.psel = psel;
4160 par.iinfo.mtime = hton64(sf->f.fatt.mtime);
4161 par.iso_size = sf->file_size;
4162 par.wd_disc = OpenDiscSF(sf,false,true);
4163
4164 PRINT("iso_size=%llu\n",par.iso_size);
4165
4166 // try to copy mtime from WBFS source disc
4167 if ( sf->wbfs && sf->wbfs->disc )
4168 {
4169 const wbfs_inode_info_t * iinfo = wbfs_get_disc_inode_info(sf->wbfs->disc,0);
4170 if (ntoh64(iinfo->mtime))
4171 par.iinfo.mtime = iinfo->mtime;
4172 }
4173
4174 if (*sf->wbfs_id6)
4175 CopyPatchWbfsId( par.wbfs_id6, sf->wbfs_id6 );
4176 else if (par.wd_disc)
4177 CopyPatchWbfsId( par.wbfs_id6, &par.wd_disc->dhead.disc_id );
4178
4179 const int wbfs_stat = wbfs_add_disc_param(w->wbfs,&par);
4180
4181 // transfer results
4182 w->disc = par.open_disc;
4183 w->disc_slot = par.slot;
4184
4185 enumError err = ERR_OK;
4186 if (wbfs_stat)
4187 {
4188 err = ERR_WBFS;
4189 if (!w->sf->f.disable_errors)
4190 ERROR0(err,"Error while adding disc [%s]: %s\n",
4191 sf->f.id6_dest, w->sf->f.fname );
4192 }
4193 else
4194 {
4195 ASSERT(w->sf);
4196 TRACE("AddWDisc/stat: w=%p, slot=%d, w->sf=%p, oft=%d\n",
4197 w, w->disc_slot, w->sf, w->sf->iod.oft );
4198 err = RewriteModifiedSF(sf,0,w,0);
4199 }
4200
4201 // catch read/write errors
4202 err = max_error = max_error > err ? max_error : err;
4203 if ( max_error < saved_max_error )
4204 max_error = saved_max_error;
4205
4206 PrintSummarySF(sf);
4207
4208 // calculate the wbfs usage again
4209 CalcWBFSUsage(w);
4210
4211 TRACE("AddWDisc() returns err=%d [%s]\n",err,GetErrorName(err));
4212 return err;
4213 }
4214
4215 //
4216 ///////////////////////////////////////////////////////////////////////////////
4217 /////////////// RemoveWDisc() ///////////////
4218 ///////////////////////////////////////////////////////////////////////////////
4219
RemoveWDisc(WBFS_t * w,ccp id6,int slot,int free_slot_only)4220 enumError RemoveWDisc
4221 (
4222 WBFS_t * w, // valid WBFS descriptor
4223 ccp id6, // id6 to remove. If NULL: remove 'slot'
4224 int slot, // slot index, only used if 'discid==NULL'
4225 int free_slot_only // true: do not free blocks
4226 )
4227 {
4228 TRACE("RemoveWDisc(%p,%s,%d,%d)\n",w,id6?id6:"-",slot,free_slot_only);
4229 if ( !w || !w->wbfs || !w->sf )
4230 return ERROR0(ERR_INTERNAL,0);
4231
4232 // this is needed for detailed error messages
4233 const enumError saved_max_error = max_error;
4234 max_error = 0;
4235
4236 // remove the disc
4237 enumError err = ERR_OK;
4238 if (wbfs_rm_disc(w->wbfs,(u8*)id6,slot,free_slot_only))
4239 {
4240 err = ERR_WDISC_NOT_FOUND;
4241 if (!w->sf->f.disable_errors)
4242 ERROR0(err,"Can't remove disc non existing [%s]: %s\n",
4243 id6, w->sf->f.fname );
4244 }
4245
4246 #if defined(TEST) && defined(DEBUG)
4247 if (logging)
4248 DumpWBFS(w,stdout,3,SHOW_USAGE,0,0,0);
4249 #endif
4250
4251 #ifdef DEBUG
4252 DumpWBFS(w,TRACE_FILE,3,SHOW__DEFAULT,0,0,0);
4253 #endif
4254
4255 // check if the disc is really removed
4256 if ( id6 && !ExistsWDisc(w,id6) )
4257 {
4258 err = ERR_REMOVE_FAILED;
4259 if (!w->sf->f.disable_errors)
4260 ERROR0(err,"Can't remove disc [%s]: %s\n",
4261 id6, w->sf->f.fname );
4262 }
4263
4264 // catch read/write errors
4265 err = max_error = max_error > err ? max_error : err;
4266 if ( max_error < saved_max_error )
4267 max_error = saved_max_error;
4268
4269 // calculate the wbfs usage again
4270 CalcWBFSUsage(w);
4271
4272 TRACE("RemoveWDisc(%s) returns err=%d [%s]\n",id6,err,GetErrorName(err));
4273 return err;
4274 }
4275
4276 //
4277 ///////////////////////////////////////////////////////////////////////////////
4278 /////////////// RenameWDisc() ///////////////
4279 ///////////////////////////////////////////////////////////////////////////////
4280
RenameWDisc(WBFS_t * wbfs,ccp set_id6,ccp set_title,bool change_wbfs_head,bool change_iso_head,int verbose,int testmode)4281 enumError RenameWDisc
4282 ( WBFS_t * wbfs, ccp set_id6, ccp set_title,
4283 bool change_wbfs_head, bool change_iso_head,
4284 int verbose, int testmode )
4285 {
4286 ASSERT(wbfs);
4287 ASSERT(wbfs->wbfs);
4288 ASSERT(wbfs->disc);
4289 ASSERT(wbfs->disc->header);
4290
4291 TRACE("RenameWDisc(%p,%.6s,%s,%d,%d,v=%d,tm=%d)\n",
4292 wbfs, set_id6 ? set_id6 : "-",
4293 set_title ? set_title : "-",
4294 change_wbfs_head, change_iso_head, verbose, testmode );
4295
4296 if ( !wbfs || !wbfs->wbfs || !wbfs->sf )
4297 return ERROR0(ERR_INTERNAL,0);
4298
4299 if ( !set_id6 || !*set_id6 || strlen(set_id6) > 6 )
4300 set_id6 = 0; // invalid id6
4301
4302 if ( !set_title || !*set_title )
4303 set_title = 0; // invalid title
4304
4305 if ( !set_id6 && !set_title )
4306 return ERR_OK; // nothing to do
4307
4308 if ( !change_wbfs_head && !change_iso_head )
4309 change_wbfs_head = change_iso_head = true;
4310
4311 wd_header_t * whead = (wd_header_t*)wbfs->disc->header;
4312 char w_id6[7], n_id6[7];
4313 memset(w_id6,0,sizeof(w_id6));
4314 StringCopyS(w_id6,sizeof(w_id6),&whead->disc_id);
4315 memcpy(n_id6,w_id6,sizeof(n_id6));
4316
4317 if ( testmode || verbose >= 0 )
4318 {
4319 ccp mode = !change_wbfs_head
4320 ? "(ISO header only)"
4321 : !change_iso_head
4322 ? "(WBFS header only)"
4323 : "(WBFS+ISO header)";
4324 printf(" - %sModify %s [%s] %s\n",
4325 testmode ? "WOULD " : "", mode, w_id6, wbfs->sf->f.fname );
4326 }
4327
4328 if (set_id6)
4329 {
4330 memcpy(n_id6,set_id6,6);
4331 set_id6 = n_id6;
4332 if ( testmode || verbose >= 0 )
4333 printf(" - %sRename ID to `%s'\n", testmode ? "WOULD " : "", set_id6 );
4334 }
4335
4336 if (set_title)
4337 {
4338 wd_header_t ihead;
4339 LoadIsoHeader(wbfs,&ihead,0);
4340
4341 char w_name[0x40], i_id6[7], i_name[0x40];
4342 StringCopyS(i_id6,sizeof(i_id6),&ihead.disc_id);
4343 StringCopyS(w_name,sizeof(w_name),whead->disc_title);
4344 StringCopyS(i_name,sizeof(i_name),ihead.disc_title);
4345
4346 ccp w_title = GetTitle(w_id6,w_name);
4347 ccp i_title = GetTitle(i_id6,i_name);
4348 ccp n_title = GetTitle(n_id6,w_name);
4349
4350 TRACE("W-TITLE: %s, %s\n",w_id6,w_title);
4351 TRACE("I-TITLE: %s, %s\n",i_id6,i_title);
4352 TRACE("N-TITLE: %s, %s\n",n_id6,n_title);
4353
4354 // and now the destination filename
4355 SubstString_t subst_tab[] =
4356 {
4357 { 'j', 0, 0, w_id6 },
4358 { 'J', 0, 0, i_id6 },
4359 { 'i', 'I', 0, n_id6 },
4360
4361 { 'n', 0, 0, w_name },
4362 { 'N', 0, 0, i_name },
4363
4364 { 'p', 0, 0, w_title },
4365 { 'P', 0, 0, i_title },
4366 { 't', 'T', 0, n_title },
4367
4368 {0,0,0,0}
4369 };
4370
4371 char title[PATH_MAX];
4372 SubstString(title,sizeof(title),subst_tab,set_title,0);
4373 set_title = title;
4374
4375 if ( testmode || verbose >= 0 )
4376 printf(" - %sSet title to `%s'\n",
4377 testmode ? "WOULD " : "", set_title );
4378 }
4379
4380 if (!testmode
4381 && wbfs_rename_disc( wbfs->disc, set_id6, set_title,
4382 change_wbfs_head, change_iso_head ) )
4383 return ERROR0(ERR_WBFS,"Renaming of disc failed: %s\n",
4384 wbfs->sf->f.fname );
4385 return ERR_OK;
4386 }
4387
4388 //
4389 ///////////////////////////////////////////////////////////////////////////////
4390 /////////////// END ///////////////
4391 ///////////////////////////////////////////////////////////////////////////////
4392
4393