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 Kwiirk                                             *
20  *   Copyright (c) 2009-2013 by Dirk Clemens <wiimm@wiimm.de>              *
21  *                                                                         *
22  ***************************************************************************
23  *                                                                         *
24  *   This program is free software; you can redistribute it and/or modify  *
25  *   it under the terms of the GNU General Public License as published by  *
26  *   the Free Software Foundation; either version 2 of the License, or     *
27  *   (at your option) any later version.                                   *
28  *                                                                         *
29  *   This program is distributed in the hope that it will be useful,       *
30  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
31  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
32  *   GNU General Public License for more details.                          *
33  *                                                                         *
34  *   See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt       *
35  *                                                                         *
36  ***************************************************************************/
37 
38 #include "libwbfs.h"
39 #include <errno.h>
40 
41 ///////////////////////////////////////////////////////////////////////////////
42 
43 #define WBFS_ERROR(x) do {wbfs_error(x);goto error;}while(0)
44 #define ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1)))
45 
46 ///////////////////////////////////////////////////////////////////////////////
47 // debugging macros
48 
49 #ifndef TRACE
50  #define TRACE(...)
51 #endif
52 
53 #ifndef PRINT
54  #define PRINT(...)
55 #endif
56 
57 #ifndef ASSERT
58  #define ASSERT(cond)
59 #endif
60 
61 ///////////////////////////////////////////////////////////////////////////////
62 
63 char wbfs_slot_mode_info[WBFS_SLOT__MASK+1] = ".#!!" "-?!!" "-?!!" "-?!!";
64 
65 ///////////////////////////////////////////////////////////////////////////////
66 ///////////////////////////////////////////////////////////////////////////////
67 
wbfs_setup_inode_info(wbfs_t * p,wbfs_inode_info_t * ii,bool is_valid,int is_changed)68 be64_t wbfs_setup_inode_info
69 	( wbfs_t * p, wbfs_inode_info_t * ii, bool is_valid, int is_changed )
70 {
71     ASSERT(p);
72     ASSERT(p->head);
73     ASSERT(ii);
74     ASSERT( sizeof(wbfs_inode_info_t) == WBFS_INODE_INFO_SIZE );
75 
76     memcpy(ii,p->head,WBFS_INODE_INFO_HEAD_SIZE);
77     ii->info_version = htonl(WBFS_INODE_INFO_VERSION);
78 
79     be64_t now = hton64(wbfs_time());
80 
81     if (is_changed)
82 	ii->ctime = ii->atime = now;
83 
84     if (is_valid)
85     {
86 	if ( !ntoh64(ii->itime) )
87 	    ii->itime = now;
88 	if ( !ntoh64(ii->mtime) )
89 	    ii->mtime = now;
90 	if ( !ntoh64(ii->ctime) )
91 	    ii->ctime = now;
92 	if ( !ntoh64(ii->atime) )
93 	    ii->atime = now;
94 	ii->dtime = 0;
95     }
96     else if ( !ntoh64(ii->dtime) )
97 	ii->dtime = now;
98 
99     return now;
100 }
101 
102 ///////////////////////////////////////////////////////////////////////////////
103 
wbfs_is_inode_info_valid(const wbfs_t * p,const wbfs_inode_info_t * ii)104 int wbfs_is_inode_info_valid
105 (
106     // if valid   -> return WBFS_INODE_INFO_VERSION
107     // if invalid -> return 0
108 
109     const wbfs_t		* p,	// NULL or WBFS
110     const wbfs_inode_info_t	* ii	// NULL or inode pointer
111 )
112 {
113     DASSERT( sizeof(wbfs_inode_info_t) == WBFS_INODE_INFO_SIZE );
114 
115     if (!ii)
116 	return 0;
117 
118     const u32 version = ntohl(ii->info_version);
119     if ( !version || version > WBFS_INODE_INFO_VERSION )
120 	return 0;
121 
122     if ( p && p->head )
123 	return memcmp(ii,p->head,WBFS_INODE_INFO_CMP_SIZE) ? 0 : version;
124 
125     return ii->magic == wbfs_htonl(WBFS_MAGIC) ? version : 0;
126 }
127 
128 ///////////////////////////////////////////////////////////////////////////////
129 ///////////////////////////////////////////////////////////////////////////////
130 
size_to_shift(u32 size)131 static u8 size_to_shift(u32 size)
132 {
133     u8 ret = 0;
134     while (size)
135     {
136 	ret++;
137 	size>>=1;
138     }
139     return ret-1;
140 }
141 
142 //
143 ///////////////////////////////////////////////////////////////////////////////
144 #ifndef WIT // not used in WiT
145 
wbfs_open_partition(rw_sector_callback_t read_hdsector,rw_sector_callback_t write_hdsector,void * callback_data,int hd_sector_size,int num_hd_sector,u32 part_lba,int reset)146 wbfs_t * wbfs_open_partition
147 (
148     rw_sector_callback_t	read_hdsector,
149     rw_sector_callback_t	write_hdsector,
150     void			* callback_data,
151     int				hd_sector_size,
152     int				num_hd_sector,
153     u32				part_lba,
154     int				reset
155 )
156 {
157     wbfs_param_t par;
158     memset(&par,0,sizeof(par));
159 
160     par.read_hdsector	= read_hdsector;
161     par.write_hdsector	= write_hdsector;
162     par.callback_data	= callback_data;
163     par.hd_sector_size	= hd_sector_size;
164     par.num_hd_sector	= num_hd_sector;
165     par.part_lba	= part_lba;
166     par.reset		= reset > 0;
167     par.clear_inodes	= reset > 1;
168     par.setup_iinfo	= reset > 2;
169     par.force_mode	= force_mode > 0;   // global parameter
170 
171     return wbfs_open_partition_param(&par);
172 }
173 
174 #endif // !WIT
175 ///////////////////////////////////////////////////////////////////////////////
176 
wbfs_setup_lists(wbfs_t * p)177 void wbfs_setup_lists
178 (
179     wbfs_t		* p		// valid WBFS descriptor
180 )
181 {
182     DASSERT(p);
183 
184     const size_t used_size = p->n_wbfs_sec + 32;
185     if (!p->used_block)
186 	p->used_block = MALLOC(used_size);
187     memset(p->used_block,0,used_size);
188     p->used_block[0] = 0xff;
189 
190     const size_t id_list_size = (p->max_disc+1) * sizeof(*p->id_list);
191     if (!p->id_list)
192 	p->id_list = MALLOC(id_list_size);
193     memset(p->id_list,0,id_list_size);
194 }
195 
196 ///////////////////////////////////////////////////////////////////////////////
197 
wbfs_open_partition_param(wbfs_param_t * par)198 wbfs_t * wbfs_open_partition_param ( wbfs_param_t * par )
199 {
200     TRACE("LIBWBFS: +wbfs_open_partition_param()\n");
201     ASSERT(par);
202 
203     //----- alloc mem
204 
205     int hd_sector_size	= par->hd_sector_size ? par->hd_sector_size : 512;
206     wbfs_head_t *head	= wbfs_ioalloc(hd_sector_size);
207     wbfs_t	*p	= wbfs_malloc(sizeof(wbfs_t));
208 
209     memset(p,0,sizeof(*p));
210 
211     //----- store head and parameters
212 
213     p->head		= head;
214     p->part_lba		= par->part_lba;
215     p->read_hdsector	= par->read_hdsector;
216     p->write_hdsector	= par->write_hdsector;
217     p->callback_data	= par->callback_data;
218     p->balloc_mode	= par->balloc_mode;
219 
220     //----- init/load partition header
221 
222     if ( par->reset > 0 )
223     {
224 	// if reset: make some calculations and store the result in the header
225 
226 	wbfs_memset(head,0,hd_sector_size);
227 	head->magic		= wbfs_htonl(WBFS_MAGIC);
228 	head->wbfs_version	= WBFS_VERSION;
229 	head->hd_sec_sz_s	= size_to_shift(hd_sector_size);
230 	hd_sector_size		= 1 << head->hd_sec_sz_s;
231 	head->n_hd_sec		= wbfs_htonl(par->num_hd_sector);
232 
233 	if ( par->old_wii_sector_calc > 0 )
234 	{
235 	    //*** this is the old calculation with rounding error
236 	    p->n_wii_sec = ( par->num_hd_sector / WII_SECTOR_SIZE) * hd_sector_size;
237 	}
238 	else
239 	{
240 	    //*** but *here* we can/should use the correct one
241 	    p->n_wii_sec = par->num_hd_sector / ( WII_SECTOR_SIZE / hd_sector_size );
242 	}
243 
244 	if ( par->wbfs_sector_size > 0 )
245 	    head->wbfs_sec_sz_s = size_to_shift(par->wbfs_sector_size);
246 	else
247 	    head->wbfs_sec_sz_s
248 		= wbfs_calc_size_shift( head->hd_sec_sz_s,
249 					par->num_hd_sector,
250 					par->old_wii_sector_calc );
251     }
252     else
253     {
254 	// no reset: just load header
255 
256 	p->read_hdsector(p->callback_data,p->part_lba,1,head);
257     }
258 
259     //----- validation tests
260 
261     if ( head->magic != wbfs_htonl(WBFS_MAGIC) )
262 	WBFS_ERROR("bad magic");
263 
264     if ( par->force_mode <= 0 )
265     {
266 	if ( hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size) )
267 	    WBFS_ERROR("hd sector size doesn't match");
268 
269 	if ( par->num_hd_sector && head->n_hd_sec != wbfs_htonl(par->num_hd_sector) )
270 	    WBFS_ERROR("hd num sector doesn't match");
271     }
272 
273 
274     //----- setup wbfs geometry
275 
276     wbfs_calc_geometry(p,
277 		wbfs_ntohl(head->n_hd_sec),
278 		1 << head->hd_sec_sz_s,
279 		1 << head->wbfs_sec_sz_s );
280 
281 
282     //----- etc
283 
284     p->tmp_buffer = wbfs_ioalloc(p->hd_sec_sz);
285 
286     if ( par->reset > 0 )
287     {
288 	if ( par->setup_iinfo > 0 )
289 	{
290 	    // build an empty inode with a valid inode_info and write it to all slots.
291 
292 	    const int disc_info_sz_lba =  p->disc_info_sz >> p->hd_sec_sz_s;
293 	    wbfs_disc_info_t * info = wbfs_ioalloc(p->disc_info_sz);
294 	    memset(info,0,p->disc_info_sz);
295 	    wbfs_inode_info_t * iinfo = wbfs_get_inode_info(p,info,0);
296 	    wbfs_setup_inode_info(p,&par->iinfo,0,0);
297 	    memcpy(iinfo,&par->iinfo,sizeof(*iinfo));
298 
299 	    int slot;
300 	    for ( slot = 0; slot < p->max_disc; slot++ )
301 	    {
302 		const int lba = p->part_lba + 1 + slot * disc_info_sz_lba;
303 		p->write_hdsector(p->callback_data,lba,disc_info_sz_lba,info);
304 	    }
305 
306 	    wbfs_iofree(info);
307 	}
308 	else if ( par->clear_inodes > 0 )
309 	{
310 	    // clear all disc header, but we are nice to sparse files
311 	    //  -> read the data first and only if not zeroed we write back
312 
313 	    const int disc_info_sz_lba =  p->disc_info_sz >> p->hd_sec_sz_s;
314 	    wbfs_disc_info_t * info = wbfs_ioalloc(p->disc_info_sz);
315 
316 	    int slot;
317 	    for ( slot = 0; slot < p->max_disc; slot++ )
318 	    {
319 		const int lba = p->part_lba + 1 + slot * disc_info_sz_lba;
320 		p->read_hdsector(p->callback_data,lba,disc_info_sz_lba,info);
321 
322 		const u32 * ptr = (u32*)info;
323 		const u32 * end = ptr + p->disc_info_sz/sizeof(*ptr);
324 		while ( ptr < end )
325 		{
326 		    if (*ptr++)
327 		    {
328 			memset(info,0,p->disc_info_sz);
329 			p->write_hdsector(p->callback_data,lba,disc_info_sz_lba,info);
330 			break;
331 		    }
332 		    //TRACE("%p %p %p\n",info,ptr,end);
333 		}
334 	    }
335 
336 	    wbfs_iofree(info);
337 	}
338     }
339 
340 
341     //----- all done, terminate
342 
343     if ( par->reset > 0 )
344     {
345 	wbfs_setup_lists(p);
346 	p->is_dirty = p->used_block_dirty = true;
347 	wbfs_sync(p);
348     }
349     else
350     {
351 	if ( wbfs_calc_used_blocks(p,true,false,0,0) )
352 	    goto error;
353 
354      #if 0 && defined(DEBUG) && defined(TEST)
355 	wbfs_print_block_usage(stdout,3,p,false);
356      #endif
357     }
358 
359     return p;
360 
361 
362     //----- error handling
363 
364 error:
365     p->is_dirty = false;
366     wbfs_close(p);
367     wbfs_iofree(head);
368     return 0;
369 }
370 
371 //
372 ///////////////////////////////////////////////////////////////////////////////
373 // public calculation helpers
374 
wbfs_calc_size_shift(u32 hd_sec_sz_s,u32 num_hd_sector,int old_wii_sector_calc)375 int wbfs_calc_size_shift
376 	( u32 hd_sec_sz_s, u32 num_hd_sector, int old_wii_sector_calc )
377 {
378     const u32 hd_sector_size = 1 << hd_sec_sz_s;
379 
380     const u32 n_wii_sec =  old_wii_sector_calc > 0
381 		//*** this is the old calculation with rounding error
382 		? ( num_hd_sector / WII_SECTOR_SIZE) * hd_sector_size
383 		//*** but *here* we can/should use the correct one
384 		: num_hd_sector / ( WII_SECTOR_SIZE / hd_sector_size );
385 
386     // ensure that wbfs_sec_sz is big enough to address every blocks using 16 bits
387     // choose minimum wblk_sz that fits this partition size
388     // the max value chooses the maximal supported partition size
389     //   - start_value < 6 ==> n_wbfs_sec_per_disc becomes to large
390 
391     DASSERT( ( 1 << WII_SECTOR_SIZE_SHIFT + WBFS_MIN_SECTOR_SHIFT ) == WBFS_MIN_SECTOR_SIZE );
392     DASSERT( ( 1 << WII_SECTOR_SIZE_SHIFT + WBFS_MAX_SECTOR_SHIFT ) == WBFS_MAX_SECTOR_SIZE );
393 
394     int shift_count;
395     for ( shift_count = WBFS_MIN_SECTOR_SHIFT;
396 	  shift_count <= WBFS_MAX_SECTOR_SHIFT;
397 	  shift_count++
398 	)
399     {
400 	// ensure that wbfs_sec_sz is big enough to address every blocks using 16 bits
401 	if ( n_wii_sec < (u32)WBFS_MAX_SECTORS << shift_count )
402 	    break;
403     }
404 
405     return shift_count + WII_SECTOR_SIZE_SHIFT;
406 }
407 
408 ///////////////////////////////////////////////////////////////////////////////
409 
wbfs_calc_sect_size(u64 total_size,u32 hd_sec_size)410 u32 wbfs_calc_sect_size ( u64 total_size, u32 hd_sec_size )
411 {
412     const u32 hd_sec_sz_s = size_to_shift(hd_sec_size);
413     hd_sec_size = (u32)1 << hd_sec_sz_s;
414     return (u32)1 << wbfs_calc_size_shift(hd_sec_sz_s,total_size/hd_sec_size,0);
415 }
416 
417 ///////////////////////////////////////////////////////////////////////////////
418 
wbfs_calc_geometry(wbfs_t * p,u32 n_hd_sec,u32 hd_sec_sz,u32 wbfs_sec_sz)419 void wbfs_calc_geometry
420 (
421     wbfs_t		* p,		// pointer to wbfs_t, p->head must be NULL or valid
422     u32			n_hd_sec,	// total number of hd_sec in partition
423     u32			hd_sec_sz,	// size of a hd/partition sector
424     u32			wbfs_sec_sz	// size of a wbfs sector
425 )
426 {
427     ASSERT(p);
428 
429     //----- parameter calculations & corrections
430 
431     const u32 hd_sec_sz_s	= size_to_shift(hd_sec_sz);
432     hd_sec_sz			= 1 << hd_sec_sz_s;
433 
434     const u32 wbfs_sec_sz_s	= size_to_shift(wbfs_sec_sz);
435     wbfs_sec_sz			= 1 << wbfs_sec_sz_s;
436 
437     //----- setup header if not NULL
438 
439     if (p->head)
440     {
441 	memset(p->head,0,sizeof(p->head));
442 	p->head->magic		= wbfs_htonl(WBFS_MAGIC);
443 	p->head->n_hd_sec	= wbfs_htonl(n_hd_sec);
444 	p->head->hd_sec_sz_s	= hd_sec_sz_s;
445 	p->head->wbfs_sec_sz_s	= wbfs_sec_sz_s;
446 	p->head->wbfs_version	= WBFS_VERSION;
447     }
448 
449     //----- setup some wii constants
450 
451     p->wii_sec_sz		= WII_SECTOR_SIZE;
452     p->wii_sec_sz_s		= WII_SECTOR_SIZE_SHIFT;
453     p->n_wii_sec_per_disc	= WII_MAX_SECTORS;
454 
455     //----- transfer parameters
456 
457     p->hd_sec_sz_s		= hd_sec_sz_s;
458     p->hd_sec_sz		= hd_sec_sz;
459     p->n_hd_sec			= n_hd_sec;
460 
461     p->wbfs_sec_sz_s		= wbfs_sec_sz_s;
462     p->wbfs_sec_sz		= wbfs_sec_sz;
463 
464     //----- base calculations
465 
466     // ************************************************************************
467     // ***  This calculation has a rounding bug. But we must leave it here  ***
468     // ***  because 'n_wbfs_sec' & 'freeblks_lba' are based in this value.  ***
469     // ************************************************************************
470 
471     p->n_wii_sec	= ( p->n_hd_sec / p->wii_sec_sz ) * p->hd_sec_sz;
472     p->n_wbfs_sec	= p->n_wii_sec >> ( p->wbfs_sec_sz_s - p->wii_sec_sz_s );
473     p->n_wbfs_sec_per_disc
474 			= p->n_wii_sec_per_disc >> ( p->wbfs_sec_sz_s - p->wii_sec_sz_s );
475     p->disc_info_sz	= ALIGN_LBA( sizeof(wbfs_disc_info_t) + p->n_wbfs_sec_per_disc*2 );
476 
477     //----- 'free blocks table' calculations
478 
479     const u32 fb_memsize= ALIGN_LBA(p->n_wbfs_sec/8);	// memory size of 'freeblks'
480     p->freeblks_lba	= ( p->wbfs_sec_sz - p->n_wbfs_sec / 8 ) >> p->hd_sec_sz_s;
481     p->freeblks_lba_count
482 			= fb_memsize >> p->hd_sec_sz_s;
483 
484     //----- here we correct the previous wrong calulations
485 
486     p->n_wii_sec	= p->n_hd_sec / ( p->wii_sec_sz / p->hd_sec_sz );
487     const u32 n_wbfs_sec= p->n_wii_sec >> ( p->wbfs_sec_sz_s - p->wii_sec_sz_s );
488     p->n_wbfs_sec	= n_wbfs_sec < WBFS_MAX_SECTORS
489 			? n_wbfs_sec : WBFS_MAX_SECTORS;
490 
491     //----- calculate and fix the needed space for free blocks table
492 
493     p->freeblks_size4	= ( p->n_wbfs_sec-1 + 31 ) / 32;
494     if ( p->freeblks_size4 > fb_memsize/4 )
495     {
496 	// not enough memory to store complete free blocks table :(
497 	p->freeblks_size4 = fb_memsize/4;
498 
499 	// fix the number of WBFS sectors too
500 	const u32 max_sec = p->freeblks_size4 * 32 + 1; // remember: 1 based
501 	if ( p->n_wbfs_sec > max_sec )
502 	     p->n_wbfs_sec = max_sec;
503     }
504     p->freeblks_mask	= ( 1ull << ( (p->n_wbfs_sec-1) & 31 )) - 1;
505     if (!p->freeblks_mask)
506 	p->freeblks_mask = ~(u32)0;
507 
508     //----- calculate max_disc
509 
510     p->max_disc = ( p->freeblks_lba - 1 ) / ( p->disc_info_sz >> p->hd_sec_sz_s );
511     if ( p->max_disc > p->hd_sec_sz - sizeof(wbfs_head_t) )
512 	 p->max_disc = p->hd_sec_sz - sizeof(wbfs_head_t);
513 }
514 
515 //
516 ///////////////////////////////////////////////////////////////////////////////
517 
wbfs_sync(wbfs_t * p)518 void wbfs_sync ( wbfs_t * p )
519 {
520     // writes wbfs header and free blocks to hardisk
521 
522     TRACE("LIBWBFS: +wbfs_sync(%p) wr_hds=%p, head=%p, freeblks=%p\n",
523 		p, p->write_hdsector, p->head, p->freeblks );
524 
525     if (p->write_hdsector)
526     {
527 	p->write_hdsector ( p->callback_data, p->part_lba, 1, p->head );
528 
529 	if ( p->used_block && p->used_block_dirty )
530 	{
531 	    wbfs_load_freeblocks(p);
532 	    if (p->freeblks)
533 		p->write_hdsector( p->callback_data,
534 				   p->part_lba + p->freeblks_lba,
535 				   p->freeblks_lba_count,
536 				   p->freeblks );
537 	    p->used_block_dirty = false;
538 	}
539 
540 	p->is_dirty = false;
541     }
542 }
543 
544 ///////////////////////////////////////////////////////////////////////////////
545 
wbfs_close(wbfs_t * p)546 void wbfs_close ( wbfs_t * p )
547 {
548     TRACE("LIBWBFS: +wbfs_close(%p) disc_open=%d, head=%p, freeblks=%p\n",
549 		p, p->n_disc_open, p->head, p->freeblks );
550 
551     if (p->n_disc_open)
552 	WBFS_ERROR("trying to close wbfs while discs still open");
553 
554     if (p->is_dirty)
555 	wbfs_sync(p);
556 
557     wbfs_free_freeblocks(p);
558     wbfs_iofree(p->block0);
559     wbfs_free(p->used_block);
560     wbfs_free(p->id_list);
561     wbfs_iofree(p->head);
562     wbfs_iofree(p->tmp_buffer);
563     wbfs_free(p);
564 
565  error:
566     return;
567 }
568 
569 //
570 ///////////////////////////////////////////////////////////////////////////////
571 ///////////////////////////////////////////////////////////////////////////////
572 
wbfs_open_disc_by_id6(wbfs_t * p,u8 * discid)573 wbfs_disc_t * wbfs_open_disc_by_id6 ( wbfs_t* p, u8 * discid )
574 {
575     ASSERT(p);
576     ASSERT(discid);
577 
578     const int slot = wbfs_find_slot(p,discid);
579     return slot < 0 ? 0 : wbfs_open_disc_by_slot(p,slot,0);
580 }
581 
582 ///////////////////////////////////////////////////////////////////////////////
583 
wbfs_open_disc_by_info(wbfs_t * p,u32 slot,wbfs_disc_info_t * info,int force_open)584 static wbfs_disc_t * wbfs_open_disc_by_info
585 	( wbfs_t * p, u32 slot, wbfs_disc_info_t * info, int force_open )
586 {
587     ASSERT(p);
588     ASSERT( slot >= 0 );
589     ASSERT(info);
590 
591     wbfs_disc_t * d = wbfs_calloc(1,sizeof(*d));
592     d->p = p;
593     d->slot = slot;
594     d->header = info;
595 
596     d->is_used = p->head->disc_table[slot] != WBFS_SLOT_FREE;
597     if ( *d->header->dhead )
598     {
599 	d->disc_type = get_header_disc_type((wd_header_t*)d->header->dhead,&d->disc_attrib);
600 	const u32 wii_magic = be32(d->header->dhead+WII_MAGIC_OFF);
601 	const u32 gc_magic  = be32(d->header->dhead+GC_MAGIC_OFF);
602 	noTRACE("MAGIC: %08x %08x => %d %d\n",
603 		wii_magic, gc_magic, wii_magic == WII_MAGIC, gc_magic == GC_MAGIC );
604 	d->is_valid   = wii_magic == WII_MAGIC || gc_magic == GC_MAGIC;
605 	d->is_deleted = wii_magic == WII_MAGIC_DELETED;
606     }
607     else
608 	d->is_valid = d->is_deleted = false;
609 
610     if ( !force_open && !d->is_valid )
611     {
612 	p->head->disc_table[slot] = WBFS_SLOT_FREE;
613 	wbfs_free(info);
614 	wbfs_free(d);
615 	return 0;
616     }
617 
618     wbfs_get_disc_inode_info(d,1);
619     p->n_disc_open++;
620     return d;
621 }
622 
623 ///////////////////////////////////////////////////////////////////////////////
624 
wbfs_open_disc_by_slot(wbfs_t * p,u32 slot,int force_open)625 wbfs_disc_t * wbfs_open_disc_by_slot ( wbfs_t * p, u32 slot, int force_open )
626 {
627     ASSERT(p);
628     TRACE("LIBWBFS: wbfs_open_disc_by_slot(%p,slot=%u,force=%d) max=%u, disctab=%u\n",
629 		p, slot, p->max_disc, p->head->disc_table[slot], force_open );
630 
631     if ( slot >= p->max_disc || !p->head->disc_table[slot] && !force_open )
632 	return 0;
633 
634     wbfs_disc_info_t * info = wbfs_ioalloc(p->disc_info_sz);
635 
636     const u32 disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
637     if ( p->read_hdsector (
638 			p->callback_data,
639 			p->part_lba + 1 + slot * disc_info_sz_lba,
640 			disc_info_sz_lba,
641 			info ) )
642     {
643 	wbfs_free(info);
644 	return 0;
645     }
646 
647     return wbfs_open_disc_by_info(p,slot,info,force_open);
648 }
649 
650 ///////////////////////////////////////////////////////////////////////////////
651 
wbfs_create_disc(wbfs_t * p,const void * disc_header,const void * disc_id)652 wbfs_disc_t * wbfs_create_disc
653 (
654     wbfs_t	* p,		// valid WBFS descriptor
655     const void	* disc_header,	// NULL or disc header to copy
656     const void	* disc_id	// NULL or ID6: check non existence
657 				// disc_id overwrites the id of disc_header
658 )
659 {
660     ASSERT(p);
661 
662     if ( !disc_id && disc_header )
663 	disc_id = disc_header;
664 
665     if (disc_id)
666     {
667 	int slot = wbfs_find_slot(p,disc_id);
668 	if ( slot >= 0 )
669 	{
670 	    wbfs_error("Disc with id '%s' already exists.",
671 		wd_print_id(disc_id,6,0));
672 	    return 0;
673 	}
674     }
675 
676     //----- find a free slot
677 
678     int slot;
679     for ( slot = 0; slot < p->max_disc; slot++ )
680 	if (!p->head->disc_table[slot])
681 	    break;
682 
683     if ( slot == p->max_disc )
684     {
685 	wbfs_error("Limit of %u discs alreday reached.",p->max_disc);
686 	return 0;
687     }
688 
689 
690     //----- open slot
691 
692     p->head->disc_table[slot] = WBFS_SLOT_VALID;
693     p->is_dirty = true;
694 
695     wbfs_disc_info_t * info = wbfs_ioalloc(p->disc_info_sz);
696     memset(info,0,p->disc_info_sz);
697     wd_header_t * dhead = (wd_header_t*)info->dhead;
698     dhead->wii_magic = htonl(WII_MAGIC);
699 
700     if (disc_header)
701 	memcpy(info->dhead,disc_header,sizeof(info->dhead));
702 
703     if (disc_id)
704 	wd_patch_id(info->dhead,info->dhead,disc_id,6);
705 
706     wbfs_disc_t * disc = wbfs_open_disc_by_info(p,slot,info,true);
707     if (disc)
708 	disc->is_creating = disc->is_dirty = true;
709     return disc;
710 }
711 
712 ///////////////////////////////////////////////////////////////////////////////
713 
wbfs_sync_disc_header(wbfs_disc_t * d)714 int wbfs_sync_disc_header ( wbfs_disc_t * d )
715 {
716     DASSERT(d);
717     if ( !d || !d->p || !d->header )
718 	 return 1;
719 
720     d->is_dirty = false;
721     wbfs_t * p = d->p;
722     const u32 disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
723     return p->write_hdsector (
724 			p->callback_data,
725 			p->part_lba + 1 + d->slot * disc_info_sz_lba,
726 			disc_info_sz_lba,
727 			d->header );
728 }
729 
730 ///////////////////////////////////////////////////////////////////////////////
731 
wbfs_close_disc(wbfs_disc_t * d)732 void wbfs_close_disc ( wbfs_disc_t * d )
733 {
734     ASSERT( d );
735     ASSERT( d->header );
736     ASSERT( d->p );
737     ASSERT( d->p->n_disc_open > 0 );
738 
739     if (d->is_dirty)
740 	wbfs_sync_disc_header(d);
741 
742     d->p->n_disc_open--;
743     wbfs_iofree(d->header);
744     wbfs_free(d);
745 }
746 
747 ///////////////////////////////////////////////////////////////////////////////
748 
wbfs_get_inode_info(wbfs_t * p,wbfs_disc_info_t * info,int clear_mode)749 wbfs_inode_info_t * wbfs_get_inode_info
750 	( wbfs_t *p, wbfs_disc_info_t * info, int clear_mode )
751 {
752     ASSERT(p);
753     ASSERT(info);
754     wbfs_inode_info_t * iinfo
755 	= (wbfs_inode_info_t*)(info->dhead + WBFS_INODE_INFO_OFF);
756 
757     if ( clear_mode>1 || clear_mode && !wbfs_is_inode_info_valid(p,iinfo) )
758 	memset(iinfo,0,sizeof(wbfs_inode_info_t));
759     return iinfo;
760 }
761 
762 ///////////////////////////////////////////////////////////////////////////////
763 
wbfs_get_disc_inode_info(wbfs_disc_t * d,int clear_mode)764 wbfs_inode_info_t * wbfs_get_disc_inode_info ( wbfs_disc_t * d, int clear_mode )
765 {
766     ASSERT(d);
767     wbfs_inode_info_t * iinfo =  wbfs_get_inode_info(d->p,d->header,clear_mode);
768     d->is_iinfo_valid = wbfs_is_inode_info_valid(d->p,iinfo) != 0;
769     return iinfo;
770 }
771 
772 ///////////////////////////////////////////////////////////////////////////////
773 
wbfs_get_fragments(const u16 * wlba_tab,uint tab_length,uint * disc_blocks)774 uint wbfs_get_fragments
775 (
776     const u16		* wlba_tab,	// valid wlba table in network byte order
777     uint		tab_length,	// length of 'wlba_tab'
778     uint		* disc_blocks	// not NULL: store number of disc blocks
779 )
780 {
781     int bl, next_block = -1, last_bl = 0, n_frag = 0;
782     for ( bl = 0; bl < tab_length; bl++ )
783     {
784 	int block = ntohs(wlba_tab[bl]);
785 	if (block)
786 	{
787 	    if ( block != next_block )
788 		n_frag++;
789 	    next_block = block + 1;
790 	    last_bl = bl;
791 	}
792     }
793     TRACE("WBFS-CALC-FRAG: %u, blocks = %u/%u\n", n_frag, last_bl+1, tab_length );
794 
795     if (disc_blocks)
796 	*disc_blocks = last_bl + 1;
797     return n_frag;
798 }
799 
800 ///////////////////////////////////////////////////////////////////////////////
801 
wbfs_get_disc_fragments(wbfs_disc_t * d,uint * disc_blocks)802 uint wbfs_get_disc_fragments
803 (
804     wbfs_disc_t		*d,		// valid wbfs disc
805     uint		* disc_blocks	// not NULL: store number of disc blocks
806 )
807 {
808     DASSERT(d);
809     DASSERT(d->p);
810     if ( !d->n_fragments && d->header )
811     {
812 	d->n_fragments
813 	    = wbfs_get_fragments( d->header->wlba_table,
814 				d->p->n_wbfs_sec_per_disc, &d->disc_blocks );
815     }
816     if (disc_blocks)
817 	*disc_blocks = d->disc_blocks;
818     return d->n_fragments;
819 }
820 
821 ///////////////////////////////////////////////////////////////////////////////
822 // rename a disc
823 
wbfs_rename_disc(wbfs_disc_t * d,const char * new_id,const char * new_title,int chg_wbfs_head,int chg_iso_head)824 int wbfs_rename_disc
825 (
826     wbfs_disc_t		* d,		// pointer to an open disc
827     const char		* new_id,	// if !NULL: take the first 6 chars as ID
828     const char		* new_title,	// if !NULL: take the first 0x39 chars as title
829     int			chg_wbfs_head,	// if !0: change ID/title of WBFS header
830     int			chg_iso_head	// if !0: change ID/title of ISO header
831 )
832 {
833     ASSERT(d);
834     ASSERT(d->p);
835     ASSERT(d->header);
836 
837     wbfs_t * p = d->p;
838 
839     wbfs_inode_info_t * iinfo = wbfs_get_disc_inode_info(d,1);
840     const be64_t now = wbfs_setup_inode_info(p,iinfo,d->is_valid,0);
841 
842     int do_sync = 0;
843     if ( chg_wbfs_head
844 	&& wd_rename(d->header->dhead,new_id,new_title) )
845     {
846 	iinfo->ctime = iinfo->atime = now;
847 	do_sync++;
848     }
849 
850     if ( chg_iso_head )
851     {
852 	u16 wlba = ntohs(d->header->wlba_table[0]);
853 	if (wlba)
854 	{
855 	    u8 * tmpbuf = p->tmp_buffer;
856 	    ASSERT(tmpbuf);
857 	    const u32 lba = wlba << ( p->wbfs_sec_sz_s - p->hd_sec_sz_s );
858 	    int err = p->read_hdsector( p->callback_data, lba, 1, tmpbuf );
859 	    if (err)
860 		return err;
861 	    if (wd_rename(tmpbuf,new_id,new_title))
862 	    {
863 		iinfo->mtime = iinfo->ctime = iinfo->atime = now;
864 		err = p->write_hdsector( p->callback_data, lba, 1, tmpbuf );
865 		if (err)
866 		    return err;
867 		do_sync++;
868 	    }
869 	}
870     }
871 
872     if (do_sync)
873     {
874 	TRACE("wbfs_rename_disc() now=%llu i=%llu m=%llu c=%llu a=%llu d=%llu\n",
875 		ntoh64(now),
876 		ntoh64(iinfo->itime),
877 		ntoh64(iinfo->mtime),
878 		ntoh64(iinfo->ctime),
879 		ntoh64(iinfo->atime),
880 		ntoh64(iinfo->dtime));
881 
882 	const int err = wbfs_sync_disc_header(d);
883 	if (err)
884 	    return err;
885     }
886 
887     return 0;
888 }
889 
890 ///////////////////////////////////////////////////////////////////////////////
891 
wbfs_touch_disc(wbfs_disc_t * d,u64 itime,u64 mtime,u64 ctime,u64 atime)892 int wbfs_touch_disc
893 (
894     wbfs_disc_t		* d,		// pointer to an open disc
895     u64			itime,		// if != 0: new itime
896     u64			mtime,		// if != 0: new mtime
897     u64			ctime,		// if != 0: new ctime
898     u64			atime		// if != 0: new atime
899 )
900 {
901     ASSERT(d);
902     ASSERT(d->p);
903     ASSERT(d->header);
904 
905     TRACE("wbfs_touch_disc(%p,%llu,%llu,%llu,%llu)\n",d,itime,mtime,ctime,atime);
906 
907     wbfs_inode_info_t * iinfo = wbfs_get_disc_inode_info(d,1);
908     wbfs_setup_inode_info(d->p,iinfo,d->is_valid,0);
909 
910     if (itime)
911 	iinfo->itime = hton64(itime);
912     if (mtime)
913 	iinfo->mtime = hton64(mtime);
914     if (ctime)
915 	iinfo->ctime = hton64(ctime);
916     if (atime)
917 	iinfo->atime = hton64(atime);
918 
919     return wbfs_sync_disc_header(d);
920 }
921 
922 ///////////////////////////////////////////////////////////////////////////////
923 
wbfs_print_block_usage(FILE * f,int indent,const wbfs_t * p,bool print_all)924 void wbfs_print_block_usage
925 (
926     FILE		* f,		// valid output file
927     int			indent,		// indention of the output
928     const wbfs_t	* p,		// valid WBFS descriptor
929     bool		print_all	// false: ignore const lines
930 )
931 {
932     DASSERT(p);
933     DASSERT(p->used_block);
934     wbfs_print_usage_tab( f, indent, p->used_block, p->n_wbfs_sec,
935 				p->wbfs_sec_sz, print_all );
936 }
937 
938 ///////////////////////////////////////////////////////////////////////////////
939 
940 const char wbfs_usage_name_tab[256] =
941 {
942 	".x23456789ABCDEFGHIJKLMNOPQRSTUV"
943 	"WXYZ++++++++++++++++++++++++++++"
944 	"++++++++++++++++++++++++++++++++"
945 	"+++++++++++++++++++++++++++++++*"
946 
947 	"0123456789ABCDEFGHIJKLMNOPQRSTUV"
948 	"WXYZ++++++++++++++++++++++++++++"
949 	"++++++++++++++++++++++++++++++++"
950 	"+++++++++++++++++++++++++++++++h"
951 };
952 
953 //-----------------------------------------------------------------------------
954 
wbfs_print_usage_tab(FILE * f,int indent,const u8 * used_block,u32 block_used_sz,u32 sector_size,bool print_all)955 void wbfs_print_usage_tab
956 (
957     FILE		* f,		// valid output file
958     int			indent,		// indention of the output
959     const u8		* used_block,	// valid pointer to usage table
960     u32			block_used_sz,	// size of 'used_block'
961     u32			sector_size,	// wbfs sector size
962     bool		print_all	// false: ignore const lines
963 )
964 {
965     wd_print_byte_tab( f, indent, used_block, block_used_sz, block_used_sz,
966 			sector_size, wbfs_usage_name_tab, print_all );
967 }
968 
969 ///////////////////////////////////////////////////////////////////////////////
970 
wbfs_count_discs(wbfs_t * p)971 u32 wbfs_count_discs ( wbfs_t * p )
972 {
973     u32 i,count=0;
974     for ( i = 0; i < p->max_disc; i++ )
975 	if (p->head->disc_table[i])
976 	    count++;
977     return count;
978 }
979 
980 ///////////////////////////////////////////////////////////////////////////////
981 
wbfs_sector_used(wbfs_t * p,wbfs_disc_info_t * di)982 static u32 wbfs_sector_used ( wbfs_t * p, wbfs_disc_info_t * di )
983 {
984     u32 tot_blk = 0, j;
985     for ( j = 0; j < p->n_wbfs_sec_per_disc; j++ )
986 	if (wbfs_ntohs(di->wlba_table[j]))
987 	    tot_blk++;
988     return tot_blk;
989 }
990 
991 ///////////////////////////////////////////////////////////////////////////////
992 
wbfs_get_disc_info(wbfs_t * p,u32 index,u8 * header,int header_size,u32 * slot_found,wd_disc_type_t * disc_type,wd_disc_attrib_t * disc_attrib,u32 * size4,u32 * n_fragments)993 enumError wbfs_get_disc_info
994 (
995     wbfs_t		* p,		// valid wbfs descriptor
996     u32			index,		// disc index: 0 .. num_dics-1
997     u8			* header,	// header to store data
998     int			header_size,	// size of 'header'
999     u32			* slot_found,	// not NULL: store slot of found disc
1000     wd_disc_type_t	* disc_type,	// not NULL: store disc type
1001     wd_disc_attrib_t	* disc_attrib,	// not NULL: store disc attrib
1002     u32			* size4,	// not NULL: store 'size>>2' of found disc
1003     u32			* n_fragments	// number of wbfs fragments
1004 )
1005 {
1006     u32 slot, count = 0;
1007     for( slot = 0; slot < p->max_disc; slot++ )
1008 	if (p->head->disc_table[slot])
1009 	    if ( count++ == index )
1010 	    {
1011 		if (slot_found)
1012 		    *slot_found = slot;
1013 		return wbfs_get_disc_info_by_slot(p,slot,header,header_size,
1014 							disc_type,disc_attrib,
1015 							size4,n_fragments);
1016 	    }
1017     return ERR_WDISC_NOT_FOUND;
1018 }
1019 
1020 ///////////////////////////////////////////////////////////////////////////////
1021 
wbfs_get_disc_info_by_slot(wbfs_t * p,u32 slot,u8 * header,int header_size,wd_disc_type_t * disc_type,wd_disc_attrib_t * disc_attrib,u32 * size4,u32 * n_fragments)1022 enumError wbfs_get_disc_info_by_slot
1023 (
1024     wbfs_t		* p,		// valid wbfs descriptor
1025     u32			slot,		// disc index: 0 .. num_dics-1
1026     u8			* header,	// not NULL: header to store data
1027     int			header_size,	// size of 'header'
1028     wd_disc_type_t	* disc_type,	// not NULL: store disc type
1029     wd_disc_attrib_t	* disc_attrib,	// not NULL: store disc attrib
1030     u32			* size4,	// not NULL: store 'size>>2' of found disc
1031     u32			* n_fragments	// number of wbfs fragments
1032 )
1033 {
1034     ASSERT(p);
1035     if ( slot >= p->max_disc || !p->head->disc_table[slot] )
1036 	return ERR_WDISC_NOT_FOUND;
1037 
1038     const u32 disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
1039     p->read_hdsector(	p->callback_data,
1040 			p->part_lba + 1 + slot * disc_info_sz_lba,
1041 			1,
1042 			p->tmp_buffer );
1043 
1044 
1045     const u32 wii_magic = be32(p->tmp_buffer+WII_MAGIC_OFF);
1046     const u32 gc_magic  = be32(p->tmp_buffer+GC_MAGIC_OFF);
1047     TRACE("MAGIC: %08x %08x => %d %d\n",
1048 		wii_magic, gc_magic, wii_magic == WII_MAGIC, gc_magic == GC_MAGIC );
1049 
1050     if ( wii_magic != WII_MAGIC && gc_magic != GC_MAGIC )
1051     {
1052 	p->head->disc_table[slot] = WBFS_SLOT_FREE;
1053 	return ERR_WARNING;
1054     }
1055 
1056     if (header)
1057     {
1058 	if (header_size > (int)p->hd_sec_sz)
1059 	    header_size = p->hd_sec_sz;
1060 	memcpy( header, p->tmp_buffer, header_size );
1061     }
1062 
1063     if ( disc_type || disc_attrib )
1064     {
1065 	const wd_disc_type_t dt
1066 	    = get_header_disc_type((wd_header_t*)p->tmp_buffer,disc_attrib);
1067 	if (disc_type)
1068 	    *disc_type = dt;
1069     }
1070 
1071     if ( size4 || n_fragments )
1072     {
1073 	u32 sec_used;
1074 	wbfs_disc_info_t *header = wbfs_ioalloc(p->disc_info_sz);
1075 
1076 	p->read_hdsector (  p->callback_data,
1077 			    p->part_lba + 1 + slot * disc_info_sz_lba,
1078 			    disc_info_sz_lba,
1079 			    header );
1080 
1081 	sec_used = wbfs_sector_used(p,header);
1082 	if (size4)
1083 	    *size4 = sec_used << (p->wbfs_sec_sz_s-2);
1084 	if (n_fragments)
1085 	    *n_fragments = wbfs_get_fragments( header->wlba_table,
1086 						p->n_wbfs_sec_per_disc, 0 );
1087 	wbfs_iofree(header);
1088     }
1089     return ERR_OK;
1090 }
1091 
1092 ///////////////////////////////////////////////////////////////////////////////
1093 
wbfs_load_id_list(wbfs_t * p,int force_reload)1094 id6_t * wbfs_load_id_list ( wbfs_t * p, int force_reload )
1095 {
1096     ASSERT(p);
1097     ASSERT(p->head);
1098     ASSERT(p->tmp_buffer);
1099     if ( p->id_list && !force_reload )
1100 	return p->id_list;
1101 
1102     const int id_item_size = sizeof(*p->id_list);
1103     const int id_list_size = (p->max_disc+1) * id_item_size;
1104     const u32 disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
1105 
1106     TRACE("LIBWBFS: +wbfs_load_id_list(%p,%d) id_list_size=%u*%u=%d\n",
1107 		p, force_reload, p->max_disc, id_item_size, id_list_size );
1108 
1109     if (!p->id_list)
1110     {
1111 	TRACE("MALLOC id_list = %d * (%d+1) = %d\n",
1112 		id_item_size, p->max_disc, id_list_size );
1113 	p->id_list = wbfs_malloc(id_list_size);
1114     }
1115     memset(p->id_list,0,id_list_size);
1116 
1117     u32 slot;
1118     for ( slot = 0; slot < p->max_disc; slot++ )
1119 	if (p->head->disc_table[slot])
1120 	{
1121 	    p->read_hdsector(p->callback_data,
1122 			     p->part_lba + 1 + slot * disc_info_sz_lba,
1123 			     1, p->tmp_buffer );
1124 	    TRACE(" - slot #%03u: %.6s\n",slot,p->tmp_buffer);
1125 	    memcpy(p->id_list[slot],p->tmp_buffer,id_item_size);
1126 	}
1127 
1128     return p->id_list;
1129 }
1130 
1131 //
1132 ///////////////////////////////////////////////////////////////////////////////
1133 
wbfs_find_slot(wbfs_t * p,const u8 * disc_id)1134 int wbfs_find_slot ( wbfs_t * p, const u8 * disc_id )
1135 {
1136     ASSERT(p);
1137     TRACE("LIBWBFS: +wbfs_find_slot(%p,%.6s)\n",p,disc_id);
1138 
1139     wbfs_load_id_list(p,0);
1140     ASSERT(p->id_list);
1141 
1142     const int id_item_size = sizeof(*p->id_list);
1143 
1144     u32 slot;
1145     for ( slot = 0; slot < p->max_disc; slot++ )
1146 	if ( p->head->disc_table[slot]
1147 		&& !memcmp(p->id_list[slot],disc_id,id_item_size) )
1148     {
1149 	TRACE("LIBWBFS: -wbfs_find_slot() slot=%u\n",slot);
1150 	return slot;
1151     }
1152 
1153     TRACE("LIBWBFS: -wbfs_find_slot() ID_NOT_FOUND\n");
1154     return -1;
1155 }
1156 
1157 ///////////////////////////////////////////////////////////////////////////////
1158 
wbfs_free_freeblocks(wbfs_t * p)1159 u32 * wbfs_free_freeblocks ( wbfs_t * p )
1160 {
1161     DASSERT(p);
1162 
1163     if (p->block0)
1164     {
1165 	if ( p->freeblks && ( (u8*)p->freeblks < p->block0
1166 			   || (u8*)p->freeblks > p->block0 + p->wbfs_sec_sz ))
1167 	{
1168 	    wbfs_iofree(p->freeblks);
1169 	}
1170 	p->freeblks = (u32*)( p->block0
1171 				+ ( p->part_lba + p->freeblks_lba ) * p->hd_sec_sz );
1172 	return p->freeblks;
1173     }
1174 
1175     if (p->freeblks)
1176     {
1177 	wbfs_iofree(p->freeblks);
1178 	p->freeblks = 0;
1179     }
1180 
1181     return 0;
1182 }
1183 
1184 ///////////////////////////////////////////////////////////////////////////////
1185 
wbfs_load_freeblocks(wbfs_t * p)1186 u32 * wbfs_load_freeblocks ( wbfs_t * p )
1187 {
1188     DASSERT(p);
1189 
1190     if (p->block0)
1191 	wbfs_free_freeblocks(p); // setup 'freeblks' pointer from block0
1192 
1193     bool dirty = p->used_block_dirty;
1194     const size_t fb_memsize = p->freeblks_lba_count * p->hd_sec_sz;
1195     if ( !p->freeblks && fb_memsize )
1196     {
1197 	p->freeblks = wbfs_ioalloc(fb_memsize);
1198 	dirty = true;
1199     }
1200 
1201     if ( p->freeblks && dirty )
1202     {
1203 	TRACE("WBFS: CALC FBT\n");
1204 
1205 	// fill complete array with zeros == mark all blocks as used
1206 	wbfs_memset(p->freeblks,0,fb_memsize);
1207 
1208 	u32 idx = 0, mask = 0;
1209 	u8 *ptr, *end = p->used_block + p->n_wbfs_sec;
1210 	for ( ptr = p->used_block + 1; ptr < end; ptr++ )
1211 	    if (!*ptr)
1212 	    {
1213 		const u32 bl = ptr - p->used_block - 1;
1214 		const u32 new_idx = bl / 32;
1215 		if ( idx != new_idx )
1216 		{
1217 		    if ( idx >= p->freeblks_size4 )
1218 			break; // end of free blocks table reached
1219 		    p->freeblks[idx] = htonl(mask);
1220 		    idx = new_idx;
1221 		    mask = 0;
1222 		}
1223 		mask |= (u32)1 << ( bl & 31 );
1224 	    }
1225 	if ( idx < p->freeblks_size4 )
1226 	    p->freeblks[idx] = htonl(mask);
1227     }
1228 
1229     return p->freeblks;
1230 }
1231 
1232 ///////////////////////////////////////////////////////////////////////////////
1233 
wbfs_calc_used_blocks(wbfs_t * p,bool force_reload,bool store_block0,wbfs_check_func func,void * param)1234 int wbfs_calc_used_blocks
1235 (
1236     wbfs_t		* p,		// valid WBFS descriptor
1237     bool		force_reload,	// true: definitely reload block #0
1238     bool		store_block0,	// true: don't free block0
1239     wbfs_check_func	func,		// call back function for errors
1240     void		* param		// user defined parameter
1241 )
1242 {
1243     DASSERT(p);
1244     char msg[100];
1245 
1246     //------ setup
1247 
1248     u8 * block0;
1249     if (p->block0)
1250     {
1251 	block0 = p->block0;
1252 	store_block0 = true;
1253     }
1254     else
1255     {
1256 	block0 = wbfs_ioalloc(p->wbfs_sec_sz);
1257 	force_reload = true;
1258     }
1259 
1260     wbfs_setup_lists(p);
1261     u8 *used = p->used_block;
1262     DASSERT(used);
1263 
1264     p->new_slot_err = p->all_slot_err = 0;
1265 
1266 
1267     //----- read block #0
1268 
1269     if (force_reload)
1270     {
1271 	TRACE("READ BLOCK0\n");
1272 	int read_stat = p->read_hdsector( p->callback_data,
1273 					p->part_lba,
1274 					p->wbfs_sec_sz / p->hd_sec_sz,
1275 					block0 );
1276 	if (read_stat)
1277 	{
1278 	    if (!store_block0)
1279 		wbfs_iofree(block0);
1280 	    return read_stat;
1281 	}
1282     }
1283 
1284 
1285     //----- scan free blocks table and mark free blocks with 0x80
1286 
1287     const u8 FREE_MARK = 0x80;
1288 
1289     u32 * fbt0 = (u32*)( block0 + ( p->part_lba + p->freeblks_lba ) * p->hd_sec_sz );
1290     u32 * fbt = fbt0;
1291     TRACE("block0=%p..%p, fbt=%p [%zx]\n",
1292 		block0,block0+p->wbfs_sec_sz,fbt,(ccp)fbt-(ccp)block0);
1293     u8 * dest = used + 1;
1294     u32 i;
1295     for ( i = 0; i < p->freeblks_size4; i++ )
1296     {
1297 	DASSERT( dest - used <= p->n_wbfs_sec );
1298 	u32 v = wbfs_ntohl(*fbt++);
1299 	noPRINT("  %05x,%08x -> %04zx\n",i,v,dest-used);
1300 	if ( v == 0 )
1301 	{
1302 	    // 32 used blocks
1303 	    dest += 32;
1304 	}
1305 	else if ( v == ~(u32)0 )
1306 	{
1307 	    // 32 free blocks
1308 	    memset(dest,FREE_MARK,32);
1309 	    dest += 32;
1310 	}
1311 	else
1312 	{
1313 	    u32 j;
1314 	    for ( j = 0; j < 32; j++, v >>= 1 )
1315 		*dest++ = (v&1) ? FREE_MARK : 0x00;
1316 	}
1317     }
1318     //HEXDUMP16(4,0,used,16);
1319     //HEXDUMP16(4,0x400,used+0x400,16);
1320 
1321 
1322     //----- scan discs, pass 1/2
1323 
1324     const bool valid_slot_info = p->head->wbfs_version > 1;
1325     p->head->wbfs_version = WBFS_VERSION;
1326 
1327     int slot;
1328     wbfs_disc_info_t * info = (wbfs_disc_info_t*)( block0 + p->hd_sec_sz );
1329 
1330     for ( slot = 0; slot < p->max_disc; slot++ )
1331     {
1332 	DASSERT( (u8*)info + p->disc_info_sz < block0 + p->wbfs_sec_sz );
1333 	u8 slot_info = p->head->disc_table[slot]; // [dt]
1334 	if (slot_info)
1335 	{
1336 	    if (valid_slot_info)
1337 		p->all_slot_err |= slot_info & ~WBFS_SLOT_VALID;
1338 	    else
1339 		slot_info = WBFS_SLOT_VALID;
1340 
1341 	    char * id6 = p->id_list[slot];
1342 	    memcpy(id6,info,6);
1343 
1344 	    noPRINT("NEW WBFS INTERFACE: check slot %u: stat=%x, off=%zx, id=%s\n",
1345 			slot, slot_info, (u8*)info - block0,
1346 			wd_print_id(info,6,0) );
1347 
1348 	    u16 * wlba_tab = info->wlba_table;
1349 	    int bl, bl_count = 0;
1350 	    for ( bl = 0; bl < p->n_wbfs_sec_per_disc; bl++ )
1351 	    {
1352 		const u32 wlba = wbfs_ntohs(wlba_tab[bl]);
1353 		if ( wlba >= p->n_wbfs_sec )
1354 		{
1355 		    PRINT_IF(!func,"!!! NEW WBFS INTERFACE: invalid block %u.%u\n",slot,bl);
1356 		    slot_info = slot_info & ~WBFS_SLOT_VALID | WBFS_SLOT_INVALID;
1357 		    p->new_slot_err |= WBFS_SLOT_INVALID;
1358 		    if (func)
1359 		    {
1360 			const uint msg_len = snprintf(msg,sizeof(msg),
1361 				"Invalid WBFS block (%u>%u) at slot #%u [%s].",
1362 				wlba, p->n_wbfs_sec-1, slot, id6 );
1363 			func(p,WBFS_CHK_INVALID_BLOCK,slot,id6,wlba,0,msg,msg_len,param);
1364 		    }
1365 		}
1366 		else if ( wlba > 0 )
1367 		{
1368 		    bl_count++;
1369 
1370 		    if ( used[wlba] & FREE_MARK )
1371 		    {
1372 			PRINT_IF(!func,
1373 				"!!! NEW WBFS INTERFACE: slot %u.%x used free block #%x [%02x]\n",
1374 				slot, bl, wlba, used[wlba] );
1375 			slot_info |= WBFS_SLOT_F_FREED;
1376 			p->new_slot_err |= WBFS_SLOT_F_FREED;
1377 			if (func)
1378 			{
1379 			    const uint msg_len = snprintf(msg,sizeof(msg),
1380 				"WBFS block #%u marked free, but used by slot #%u [%s].",
1381 				wlba, slot, id6 );
1382 			    func(p,WBFS_CHK_FREE_BLOCK_USED,slot,id6,wlba,0,msg,msg_len,param);
1383 			}
1384 		    }
1385 
1386 		    if ( ( used[wlba] & 0x7f ) < 0x7f )
1387 			used[wlba]++;
1388 		}
1389 	    }
1390 
1391 	    if (!bl_count)
1392 	    {
1393 		PRINT_IF(!func,
1394 		    "!!! NEW WBFS INTERFACE: disc @ slot %u does'n have any block\n",slot);
1395 		slot_info |= WBFS_SLOT_INVALID;
1396 		p->new_slot_err |= WBFS_SLOT_INVALID;
1397 	    }
1398 
1399 	    p->head->disc_table[slot] = slot_info;
1400 	}
1401 	info = (wbfs_disc_info_t*)( (u8*)info + p->disc_info_sz );
1402     }
1403 
1404 
1405     //----- normalize 'used'
1406 
1407     fbt = fbt0;
1408     u32 *fbt_end = fbt + p->freeblks_size4;
1409     memset(fbt,0,p->freeblks_size4*4);
1410     u32 v = 0;
1411     int count = 32;
1412     bool pass2_needed = false;
1413 
1414     //HEXDUMP16(4,0,used,16);
1415     //HEXDUMP16(4,0x400,used+0x400,16);
1416 
1417     for ( i = 1; i < p->n_wbfs_sec; i++ )
1418     {
1419 	const u8 ucnt = used[i] & ~FREE_MARK;
1420 	if (!ucnt)
1421 	{
1422 	    v |= 1 << (i-1&31);
1423 	    if ( func && !used[i] )
1424 	    {
1425 		const uint msg_len = snprintf(msg,sizeof(msg),
1426 			"WBFS block #%u marked used, but is not used by any slot.",i);
1427 		func(p,WBFS_CHK_UNUSED_BLOCK,-1,0,i,0,msg,msg_len,param);
1428 	    }
1429 	}
1430 	else if ( ucnt > 1 )
1431 	{
1432 	    PRINT_IF(!func,
1433 		"!!! NEW WBFS INTERFACE: block %u* used: #%x [%02x]\n", ucnt, i, used[i] );
1434 	    pass2_needed = true;
1435 	    if (func)
1436 	    {
1437 		const uint msg_len = snprintf(msg,sizeof(msg),
1438 			"WBFS block #%u used %u times.",i,ucnt);
1439 		func(p,WBFS_CHK_MULTIUSED_BLOCK,-1,0,i,ucnt,msg,msg_len,param);
1440 	    }
1441 	}
1442 	used[i] = ucnt;
1443 
1444 	if ( !--count && fbt < fbt_end )
1445 	{
1446 	    noPRINT("FBT[%04x] = %08x\n",4*(int)(fbt-fbt0),v);
1447 	    *fbt++ = wbfs_htonl(v);
1448 	    v = 0;
1449 	    count = 32;
1450 	}
1451     }
1452 
1453     if ( fbt < fbt_end )
1454     {
1455 	noPRINT("FBT[%04x] = %08x [END]\n",4*(int)(fbt-fbt0),v);
1456 	*fbt = wbfs_htonl(v);
1457     }
1458 
1459     used[0] = 0xff;
1460     //HEXDUMP16(0,0,fbt0,p->freeblks_size4*4);
1461 
1462 
1463     //----- scan discs, pass 2/2
1464 
1465     if ( pass2_needed )
1466     {
1467 	info = (wbfs_disc_info_t*)( block0 + p->hd_sec_sz );
1468 	for ( slot = 0; slot < p->max_disc; slot++ )
1469 	{
1470 	    DASSERT( (u8*)info + p->disc_info_sz < block0 + p->wbfs_sec_sz );
1471 	    u8 slot_info = p->head->disc_table[slot];
1472 	    if (slot_info)
1473 	    {
1474 		u16 * wlba_tab = info->wlba_table;
1475 		int bl;
1476 		for ( bl = 0; bl < p->n_wbfs_sec_per_disc; bl++ )
1477 		{
1478 		    const u32 wlba = wbfs_ntohs(wlba_tab[bl]);
1479 		    if ( wlba > 0 && wlba < p->n_wbfs_sec && used[wlba] > 1 )
1480 		    {
1481 			PRINT_IF(!func,
1482 			    "!!! NEW WBFS INTERFACE: slot %u.%x shares block #%x [%02x]\n",
1483 			    slot, bl, wlba, used[wlba] );
1484 			slot_info |= WBFS_SLOT_F_SHARED;
1485 			p->new_slot_err |= WBFS_SLOT_F_SHARED;
1486 			if (func)
1487 			{
1488 			    const uint msg_len = snprintf(msg,sizeof(msg),
1489 				"Disc at slot #%u [%s] use shared block %u (used %u* total).",
1490 				slot, p->id_list[slot], wlba, used[wlba] );
1491 			    func(p, WBFS_CHK_SHARED_BLOCK, slot, p->id_list[slot],
1492 						bl, used[wlba], msg, msg_len, param );
1493 			}
1494 		    }
1495 		}
1496 	    }
1497 	    info = (wbfs_disc_info_t*)( (u8*)info + p->disc_info_sz );
1498 	}
1499     }
1500 
1501 
1502     //----- terminate
1503 
1504 
1505     if (store_block0)
1506     {
1507 	p->block0 = block0;
1508 	wbfs_load_freeblocks(p);
1509     }
1510     else
1511 	wbfs_iofree(block0);
1512 
1513     p->all_slot_err |= p->new_slot_err;
1514     TRACE("SLOT-STAT: %02x/%02x\n",p->new_slot_err,p->all_slot_err);
1515     return 0;
1516 }
1517 
1518 ///////////////////////////////////////////////////////////////////////////////
1519 
wbfs_find_free_blocks(wbfs_t * p,u32 n_needed)1520 u32 wbfs_find_free_blocks
1521 (
1522     // returns index of first free block or WBFS_NO_BLOCK if not enough blocks free
1523 
1524     wbfs_t	* p,		// valid WBFS descriptor
1525     u32		n_needed	// number of needed blocks
1526 )
1527 {
1528     DASSERT(p);
1529     DASSERT(p->used_block);
1530     DASSERT(n_needed);
1531 
1532     u8 *p1, *p2, *end = p->used_block + p->n_wbfs_sec;
1533     for ( p1 = p->used_block + 1; p1 < end && *p1; p1++ )
1534 	;
1535 
1536     int count = n_needed;
1537     for ( p2 = p1; p2 < end; p2++ )
1538 	if ( !*p2 && --count <= 0 )
1539 	    break;
1540 
1541     if ( count > 0 )
1542 	return WBFS_NO_BLOCK;
1543 
1544     TRACE("found: %5zd..%5zd [%5zd]\n",p1-p->used_block,p2-p->used_block,p2-p1);
1545     u8 *found = p1;
1546     u32 range = p2 - p1;
1547 
1548     while ( range >= n_needed )
1549     {
1550 	for ( p1++; p1 < end && *p1; p1++ )
1551 	    ;
1552 	for ( p2++; p2 < end && *p2; p2++ )
1553 	    ;
1554 	if ( p2 >= end )
1555 	    break;
1556 
1557 	if ( p2 - p1 < range )
1558 	{
1559 	    TRACE("found: %5zd..%5zd [%5zd]\n",p1-p->used_block,p2-p->used_block,p2-p1);
1560 	    found = p1;
1561 	    range = p2 - p1;
1562 	}
1563     }
1564 
1565     return found - p->used_block;
1566 }
1567 
1568 ///////////////////////////////////////////////////////////////////////////////
1569 
wbfs_get_free_block_count(wbfs_t * p)1570 u32 wbfs_get_free_block_count ( wbfs_t * p )
1571 {
1572     DASSERT(p);
1573     DASSERT(p->used_block);
1574 
1575     u32 count = 0;
1576     u8 *ptr, *end = p->used_block + p->n_wbfs_sec;
1577     for ( ptr = p->used_block + 1; ptr < end; ptr++ )
1578 	if (!*ptr)
1579 	    count++;
1580     return count;
1581 }
1582 
1583 ///////////////////////////////////////////////////////////////////////////////
1584 
wbfs_alloc_block(wbfs_t * p,u32 start_block)1585 u32 wbfs_alloc_block
1586 (
1587     wbfs_t		* p,		// valid WBFS descriptor
1588     u32			start_block	// >0: start search at this block
1589 )
1590 {
1591     DASSERT(p);
1592     DASSERT(p->used_block);
1593 
1594     if ( start_block < 1 || start_block >= p->n_wbfs_sec )
1595 	 start_block = 1;
1596 
1597     u32 bl = start_block;
1598     do
1599     {
1600 	if (!p->used_block[bl])
1601 	{
1602 	    p->used_block[bl] = 1;
1603 	    p->used_block_dirty = p->is_dirty = true;
1604 	    noPRINT("wbfs_alloc_block(%p,%u) -> %d\n",p,start_block,bl);
1605 	    return bl;
1606 	}
1607 
1608 	if ( ++bl >= p->n_wbfs_sec )
1609 	    bl = 1;
1610 
1611     } while ( bl != start_block );
1612 
1613     return WBFS_NO_BLOCK;
1614 }
1615 
1616 ///////////////////////////////////////////////////////////////////////////////
1617 
wbfs_find_last_used_block(wbfs_t * p)1618 u32 wbfs_find_last_used_block ( wbfs_t * p )
1619 {
1620     DASSERT(p);
1621     DASSERT(p->used_block);
1622     ASSERT( p->used_block[0] == 0xff );
1623 
1624     u8 * ptr = p->used_block + p->n_wbfs_sec - 1;
1625     while (!*ptr)
1626 	ptr--;
1627     return ptr - p->used_block;
1628 }
1629 
1630 ///////////////////////////////////////////////////////////////////////////////
1631 
wbfs_free_block(wbfs_t * p,u32 bl)1632 void wbfs_free_block ( wbfs_t *p, u32 bl )
1633 {
1634     DASSERT(p);
1635     DASSERT(p->used_block);
1636 
1637     if	(  bl > 0
1638 	&& bl < p->n_wbfs_sec
1639 	&& p->used_block[bl] > 0
1640 	&& p->used_block[bl] < 127
1641 	)
1642     {
1643 	if (!--p->used_block[bl])
1644 	    p->used_block_dirty = p->is_dirty = true;
1645     }
1646 }
1647 
1648 ///////////////////////////////////////////////////////////////////////////////
1649 
wbfs_use_block(wbfs_t * p,u32 bl)1650 void wbfs_use_block ( wbfs_t *p, u32 bl )
1651 {
1652     DASSERT(p);
1653     DASSERT(p->used_block);
1654 
1655     if ( bl > 0 && bl < p->n_wbfs_sec && !p->used_block[bl] )
1656     {
1657 	p->used_block[bl] = 1;
1658 	p->used_block_dirty = p->is_dirty = true;
1659     }
1660 }
1661 
1662 //
1663 ///////////////////////////////////////////////////////////////////////////////
1664 
wbfs_add_disc(wbfs_t * p,wd_read_func_t read_src_wii_disc,void * callback_data,progress_callback_t spinner,const wd_select_t * psel,int copy_1_1)1665 u32 wbfs_add_disc
1666 (
1667     wbfs_t			*p,
1668     wd_read_func_t		read_src_wii_disc,
1669     void			*callback_data,
1670     progress_callback_t		spinner,
1671     const wd_select_t		* psel,
1672     int				copy_1_1
1673 )
1674 {
1675     wbfs_param_t par;
1676     memset(&par,0,sizeof(par));
1677 
1678     par.read_src_wii_disc	= read_src_wii_disc;
1679     par.callback_data		= callback_data;
1680     par.spinner			= spinner;
1681     par.psel			= psel;
1682 
1683     wd_select_t select_whole;
1684     if (copy_1_1)
1685     {
1686 	wd_initialize_select(&select_whole);
1687 	select_whole.whole_disc = true;
1688 	par.psel = &select_whole;
1689     }
1690 
1691     const u32 stat = wbfs_add_disc_param(p,&par);
1692     if (par.open_disc)
1693 	wbfs_close_disc(par.open_disc);
1694     return stat;
1695 }
1696 
1697 ///////////////////////////////////////////////////////////////////////////////
1698 
wbfs_add_disc_param(wbfs_t * p,wbfs_param_t * par)1699 u32 wbfs_add_disc_param ( wbfs_t *p, wbfs_param_t * par )
1700 {
1701     ASSERT(p);
1702     ASSERT(par);
1703 
1704     par->slot = -1; // no slot assigned
1705     par->open_disc = 0;
1706 
1707     int i, slot;
1708     u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s-p->wii_sec_sz_s);
1709     wbfs_disc_info_t *info = 0;
1710     u8* copy_buffer = 0;
1711     int disc_info_sz_lba;
1712     u8 * used = wbfs_malloc(WII_MAX_SECTORS);
1713 
1714 
1715     //----- open source disc
1716 
1717     wd_disc_t * disc = wd_dup_disc(par->wd_disc);
1718     if (!disc)
1719     {
1720 	disc = wd_open_disc(	par->read_src_wii_disc,
1721 				par->callback_data,
1722 				par->iso_size,
1723 				0,
1724 				0,
1725 				0 );
1726 	if (!disc)
1727 	    WBFS_ERROR("unable to open wii disc");
1728     }
1729 
1730     wd_filter_usage_table(disc,used,par->psel);
1731 
1732     #if HAVE_PRINT0
1733 	//wd_print_usage_tab(stdout,2,used,disc->iso_size,false);
1734 	wd_print_usage_tab(stdout,2,used,WII_MAX_DISC_SIZE,false);
1735     #endif
1736 
1737     //----- count total number of blocks to write
1738 
1739     u32 current_block = 0;
1740     u32 total_blocks  = 0;
1741 
1742     for ( i = 0; i < p->n_wbfs_sec_per_disc; i++ )
1743 	if ( wd_is_block_used(used, i, wii_sec_per_wbfs_sect) )
1744 	    total_blocks++;
1745 
1746     PRINT("ADD, TOTAL BLOCKS= %u*%u*%u = %llu\n",
1747 		total_blocks, wii_sec_per_wbfs_sect, WII_SECTOR_SIZE,
1748 		(u64)total_blocks * wii_sec_per_wbfs_sect * WII_SECTOR_SIZE );
1749 
1750     const u32 free_blocks = wbfs_get_free_block_count(p);
1751     if ( total_blocks > free_blocks )
1752     {
1753 	wbfs_error("New discs needs %u wbfs blocks (%s)"
1754 		" but only %u blocks are available.",
1755 		total_blocks,
1756 		wd_print_size(0,0,p->wbfs_sec_sz,false,WD_SIZE_AUTO),
1757 		free_blocks );
1758 	goto error;
1759     }
1760 
1761     if (par->spinner)
1762 	par->spinner(0,total_blocks,par->callback_data);
1763 
1764  // [codeview]
1765 
1766     for ( i = 0; i < p->max_disc; i++) // find a free slot.
1767 	if (p->head->disc_table[i] == WBFS_SLOT_FREE)
1768 	    break;
1769 
1770     if (i == p->max_disc)
1771 	WBFS_ERROR("no space left on device (table full)");
1772 
1773     p->head->disc_table[i] = WBFS_SLOT_VALID;
1774     slot = i;
1775 
1776     // build disc info
1777     info = wbfs_ioalloc(p->disc_info_sz);
1778     memset(info,0,p->disc_info_sz);
1779     // [[2do]] use wd_read_and_patch()
1780     par->read_src_wii_disc(par->callback_data, 0, 0x100, info->dhead);
1781     if (par->wbfs_id6[0])
1782 	memcpy(info->dhead,par->wbfs_id6,6);
1783 
1784     copy_buffer = wbfs_ioalloc(p->wbfs_sec_sz);
1785     if (!copy_buffer)
1786 	WBFS_ERROR("alloc memory");
1787 
1788  #ifndef WIT // WIT does it in an other way (patching while reading)
1789     const u32 ptab_off   = wd_get_ptab_sector(disc) * WII_SECTOR_SIZE;
1790     const int ptab_index = ptab_off >> p->wbfs_sec_sz_s;
1791  #endif
1792 
1793     u32 bl = 0;
1794     if ( p->balloc_mode == WBFS_BA_AVOID_FRAG
1795 	|| p->balloc_mode == WBFS_BA_AUTO
1796 		&& p->hd_sec_sz * (u64)p->n_hd_sec >= 20*(u64)GiB )
1797     {
1798 	bl = wbfs_find_free_blocks(p,total_blocks);
1799 	TRACE("WBFS: AVOID FRAG, first block=%u\n",bl);
1800     }
1801 
1802     for ( i = 0; i < p->n_wbfs_sec_per_disc; i++ )
1803     {
1804 	info->wlba_table[i] = wbfs_htons(0);
1805 	if ( wd_is_block_used(used, i, wii_sec_per_wbfs_sect))
1806 	{
1807 	    bl = wbfs_alloc_block(p,bl);
1808 	    if ( bl == WBFS_NO_BLOCK )
1809 	    {
1810 		// free disc slot
1811 		p->head->disc_table[slot] = WBFS_SLOT_FREE;
1812 
1813 		// free already allocated blocks
1814 		int j;
1815 		for ( j = 0; j < i; j++ )
1816 		{
1817 		    bl = wbfs_ntohs(info->wlba_table[j]);
1818 		    if (bl)
1819 			wbfs_free_block(p,bl);
1820 		}
1821 		wbfs_sync(p);
1822 		WBFS_ERROR("No space left on device (WBFS runs full)");
1823 	    }
1824 	    info->wlba_table[i] = wbfs_htons(bl);
1825 
1826 	    u8 * dest = copy_buffer;
1827 	    const u32 wiimax = (i+1) * wii_sec_per_wbfs_sect;
1828 	    u32 subsec = 0;
1829 	    while ( subsec < wii_sec_per_wbfs_sect )
1830 	    {
1831 		const u32 wiisec = i * wii_sec_per_wbfs_sect + subsec;
1832 		if ( wiisec < WII_MAX_SECTORS && used[wiisec] )
1833 		{
1834 		    u32 wiiend = wiisec+1;
1835 		    while ( wiiend < wiimax && used[wiiend] )
1836 			wiiend++;
1837 		    const u32 size = ( wiiend - wiisec ) * p->wii_sec_sz;
1838 		    // [[2do]] use wd_read_and_patch()
1839 		    if (par->read_src_wii_disc(par->callback_data,
1840 				wiisec * (p->wii_sec_sz>>2), size, dest ))
1841 			WBFS_ERROR("error reading disc");
1842 
1843 		    dest += size;
1844 		    subsec += wiiend - wiisec;
1845 		}
1846 		else
1847 		{
1848 		    TRACE("LIBWBFS: FILL sec %u>%u -> %p\n",subsec,wiisec,dest);
1849 		    memset(dest,0,p->wii_sec_sz);
1850 		    dest += p->wii_sec_sz;
1851 		    subsec++;
1852 		}
1853 	    }
1854 
1855  #ifndef WIT //  WIT does it in an other way (patching while reading)
1856 	    // fix the partition table.
1857 	    if ( i == ptab_index )
1858 		wd_patch_ptab(	disc,
1859 				copy_buffer + ptab_off - i * p->wbfs_sec_sz,
1860 				false );
1861  #endif
1862 
1863 	    p->write_hdsector(	p->callback_data,
1864 				p->part_lba + bl * (p->wbfs_sec_sz / p->hd_sec_sz),
1865 				p->wbfs_sec_sz / p->hd_sec_sz,
1866 				copy_buffer );
1867 
1868 	    if (par->spinner)
1869 		par->spinner(++current_block,total_blocks,par->callback_data);
1870 
1871  	}
1872    }
1873 
1874     // inode info
1875     par->iinfo.itime = 0ull;
1876     wbfs_setup_inode_info(p,&par->iinfo,1,1);
1877     memcpy( info->dhead + WBFS_INODE_INFO_OFF,
1878 	    &par->iinfo,
1879 	    sizeof(par->iinfo) );
1880 
1881     // write disc info
1882     disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
1883     p->write_hdsector(	p->callback_data,
1884 			p->part_lba + 1 + slot * disc_info_sz_lba,
1885 			disc_info_sz_lba,
1886 			info );
1887     if (p->id_list)
1888 	memcpy(p->id_list[slot],info,sizeof(*p->id_list));
1889     wbfs_sync(p);
1890 
1891     par->slot = slot;
1892     par->open_disc = wbfs_open_disc_by_info(p,slot,info,0);
1893     info = 0;
1894 
1895 error:
1896     wd_close_disc(disc);
1897     if (used)
1898 	wbfs_free(used);
1899     if (info)
1900 	wbfs_iofree(info);
1901     if (copy_buffer)
1902 	wbfs_iofree(copy_buffer);
1903 
1904     return 0;
1905 }
1906 
1907 ///////////////////////////////////////////////////////////////////////////////
1908 
wbfs_add_phantom(wbfs_t * p,ccp phantom_id,u32 wii_sectors)1909 u32 wbfs_add_phantom ( wbfs_t *p, ccp phantom_id, u32 wii_sectors )
1910 {
1911     ASSERT(p);
1912     TRACE("LIBWBFS: +wbfs_add_phantom(%p,%s,%u)\n",
1913 	    p, phantom_id, wii_sectors );
1914 
1915     if ( !phantom_id || !*phantom_id || !wii_sectors )
1916 	return 1;
1917 
1918     if ( wii_sectors > WII_MAX_SECTORS )
1919 	wii_sectors = WII_MAX_SECTORS;
1920 
1921     const u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s-p->wii_sec_sz_s);
1922     wbfs_disc_info_t * info = 0;
1923 
1924     // find a free slot.
1925     int slot;
1926     for ( slot = 0; slot < p->max_disc; slot++ )
1927 	if (!p->head->disc_table[slot])
1928 	    break;
1929 
1930     u32 err = 0;
1931     if ( slot == p->max_disc )
1932     {
1933 	err++;
1934 	WBFS_ERROR("no space left on device (table full)");
1935     }
1936 
1937     p->head->disc_table[slot] = WBFS_SLOT_VALID;
1938 
1939     // build disc info
1940     info = wbfs_ioalloc(p->disc_info_sz);
1941     memset(info,0,p->disc_info_sz);
1942     memcpy(info->dhead,phantom_id,6);
1943     snprintf( (char*)info->dhead + WII_TITLE_OFF,
1944 	WII_TITLE_SIZE, "Phantom %.6s @ slot %u -> not a real disc, for tests only!",
1945 		phantom_id, slot );
1946 
1947     const u32 max_wbfs_sect = (wii_sectors-1) / wii_sec_per_wbfs_sect + 1;
1948     TRACE(" - add %u wbfs sectors to slot %u\n",max_wbfs_sect,slot);
1949 
1950     u32 bl = 0;
1951     if ( p->balloc_mode == WBFS_BA_AVOID_FRAG
1952 	|| p->balloc_mode == WBFS_BA_AUTO
1953 		&& p->hd_sec_sz * (u64)p->n_hd_sec >= 20*(u64)GiB )
1954     {
1955 	bl = wbfs_find_free_blocks(p,max_wbfs_sect);
1956 	TRACE("WBFS: AVOID FRAG, first block=%u\n",bl);
1957     }
1958 
1959     int i;
1960     for ( i = 0; i < max_wbfs_sect; i++)
1961     {
1962 	bl = wbfs_alloc_block(p,bl);
1963 	if ( bl == WBFS_NO_BLOCK )
1964 	{
1965 	    if (!i)
1966 		p->head->disc_table[slot] = WBFS_SLOT_FREE;
1967 	    err++;
1968 	    break; // use smaller phantom
1969 	}
1970 
1971 	if ( i == 0 )
1972 	    p->write_hdsector(	p->callback_data,
1973 				p->part_lba + bl * (p->wbfs_sec_sz / p->hd_sec_sz),
1974 				1,
1975 				info );
1976 
1977 	info->wlba_table[i] = wbfs_htons(bl);
1978     }
1979 
1980     // setup inode info
1981     wbfs_inode_info_t * iinfo = wbfs_get_inode_info(p,info,2);
1982     wbfs_setup_inode_info(p,iinfo,1,1);
1983 
1984     // write disc info
1985     *(u32*)(info->dhead+24) = wbfs_ntohl(0x5D1C9EA3);
1986     u32 disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
1987     p->write_hdsector(	p->callback_data,
1988 			p->part_lba + 1 + slot * disc_info_sz_lba,
1989 			disc_info_sz_lba,
1990 			info );
1991 
1992     if (p->id_list)
1993 	memcpy(p->id_list[slot],info,sizeof(*p->id_list));
1994     wbfs_sync(p);
1995 
1996 error:
1997     if (info)
1998 	wbfs_iofree(info);
1999     TRACE("LIBWBFS: -wbfs_add_phantom() return %u\n",err);
2000     return err;
2001 }
2002 
2003 ///////////////////////////////////////////////////////////////////////////////
2004 
wbfs_rm_disc(wbfs_t * p,u8 * discid,int slot,int free_slot_only)2005 u32 wbfs_rm_disc
2006 (
2007     wbfs_t		* p,		// valid WBFS descriptor
2008     u8			* discid,	// id6 to remove. If NULL: remove 'slot'
2009     int			slot,		// slot index, only used if 'discid==NULL'
2010     int			free_slot_only	// true: do not free blocks
2011 )
2012 {
2013     TRACE("LIBWBFS: +wbfs_rm_disc(%p,%.6s,%d,%d)\n",
2014 		p, discid ? (ccp)discid : "-", slot, free_slot_only );
2015     DASSERT(p);
2016     DASSERT(p->head);
2017 
2018     wbfs_disc_t *d = discid
2019 			? wbfs_open_disc_by_id6(p,discid)
2020 			: wbfs_open_disc_by_slot(p,slot,false);
2021     if (!d)
2022 	return 1;
2023     slot = d->slot;
2024 
2025     TRACE("LIBWBFS: disc_table[slot=%d]=%x\n", slot, p->head->disc_table[slot] );
2026 
2027     if (!free_slot_only)
2028     {
2029 	int i;
2030 	for ( i=0; i< p->n_wbfs_sec_per_disc; i++)
2031 	{
2032 	    u16 iwlba = wbfs_ntohs(d->header->wlba_table[i]);
2033 	    //TRACE("FREE %u\n",iwlba);
2034 	    if (iwlba)
2035 		wbfs_free_block(p,iwlba);
2036 	}
2037 
2038 	wbfs_inode_info_t * iinfo = wbfs_get_disc_inode_info(d,1);
2039 	ASSERT(iinfo);
2040 	wbfs_setup_inode_info(p,iinfo,0,1);
2041  #ifdef TEST
2042 	*(u32*)(d->header->dhead+WII_MAGIC_OFF) = wbfs_htonl(WII_MAGIC_DELETED);
2043  #endif
2044 	const u32 disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
2045 	p->write_hdsector(
2046 			p->callback_data,
2047 			p->part_lba + 1 + slot * disc_info_sz_lba,
2048 			disc_info_sz_lba,
2049 			d->header );
2050     }
2051     wbfs_close_disc(d);
2052 
2053     p->head->disc_table[slot] = WBFS_SLOT_FREE;
2054     if (p->id_list)
2055 	memset(p->id_list[slot],0,sizeof(*p->id_list));
2056     wbfs_sync(p);
2057 
2058     TRACE("LIBWBFS: -wbfs_rm_disc() return=0\n");
2059     return 0;
2060 }
2061 
2062 ///////////////////////////////////////////////////////////////////////////////
2063 
wbfs_trim(wbfs_t * p)2064 u32 wbfs_trim ( wbfs_t * p ) // trim the file-system to its minimum size
2065 {
2066     DASSERT(p);
2067     DASSERT(p->used_block);
2068     DASSERT( p->wbfs_sec_sz_s >= p->hd_sec_sz_s );
2069 
2070     const u32 max_block = wbfs_find_last_used_block(p) + 1;
2071     wbfs_calc_geometry( p, max_block << p->wbfs_sec_sz_s - p->hd_sec_sz_s,
2072 			p->hd_sec_sz, p->wbfs_sec_sz );
2073     p->used_block_dirty = p->is_dirty = true;
2074     wbfs_sync(p);
2075 
2076     // os layer will truncate the file.
2077     TRACE("LIBWBFS: -wbfs_trim() return=%u\n",max_block);
2078     return max_block;
2079 }
2080 
2081 //
2082 ///////////////////////////////////////////////////////////////////////////////
2083 ///////////////			    END				///////////////
2084 ///////////////////////////////////////////////////////////////////////////////
2085 
2086