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