2012-01-21 21:57:41 +01:00
|
|
|
// Copyright 2009 Kwiirk
|
|
|
|
// Licensed under the terms of the GNU GPL, version 2
|
|
|
|
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
|
|
|
|
|
|
|
// Modified by oggzee
|
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2012-01-21 21:57:41 +01:00
|
|
|
#include "libwbfs.h"
|
|
|
|
|
|
|
|
#define likely(x) __builtin_expect(!!(x), 1)
|
|
|
|
#define unlikely(x) __builtin_expect(!!(x), 0)
|
|
|
|
|
|
|
|
#define ERROR(x) do {wbfs_error(x);goto error;}while(0)
|
|
|
|
#define ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1)))
|
|
|
|
|
|
|
|
wbfs_t wbfs_iso_file;
|
|
|
|
|
|
|
|
static int force_mode=0;
|
|
|
|
|
|
|
|
void wbfs_set_force_mode(int force)
|
|
|
|
{
|
|
|
|
force_mode = force;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u8 size_to_shift(u32 size)
|
|
|
|
{
|
|
|
|
u8 ret = 0;
|
|
|
|
while(size)
|
|
|
|
{
|
|
|
|
ret++;
|
|
|
|
size >>= 1;
|
|
|
|
}
|
|
|
|
return ret - 1;
|
|
|
|
}
|
|
|
|
|
2012-07-26 00:12:17 +02:00
|
|
|
#define read_le32_unaligned(x) ((x)[0]|((x)[1]<<8)|((x)[2]<<16)|((x)[3]<<24))
|
|
|
|
|
2012-01-21 21:57:41 +01:00
|
|
|
wbfs_t *wbfs_open_hd(rw_sector_callback_t read_hdsector, rw_sector_callback_t write_hdsector, void *callback_data, int hd_sector_size, int num_hd_sector __attribute((unused)), int reset)
|
|
|
|
{
|
|
|
|
int i=num_hd_sector,ret;
|
2013-12-21 18:02:36 +01:00
|
|
|
u8 *ptr,*tmp_buffer = wbfs_malloc(hd_sector_size);
|
2012-01-21 21:57:41 +01:00
|
|
|
u8 part_table[16*4];
|
|
|
|
ret = read_hdsector(callback_data,0,1,tmp_buffer);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(ret)
|
|
|
|
return 0;
|
2012-01-21 21:57:41 +01:00
|
|
|
//find wbfs partition
|
|
|
|
wbfs_memcpy(part_table, tmp_buffer + 0x1be, 16 * 4);
|
|
|
|
ptr = part_table;
|
|
|
|
for(i = 0; i < 4; i++, ptr += 16)
|
|
|
|
{
|
|
|
|
u32 part_lba = read_le32_unaligned(ptr + 0x8);
|
|
|
|
wbfs_head_t *head = (wbfs_head_t *)tmp_buffer;
|
|
|
|
ret = read_hdsector(callback_data, part_lba, 1, tmp_buffer);
|
|
|
|
// verify there is the magic.
|
|
|
|
if (head->magic == wbfs_htonl(WBFS_MAGIC))
|
|
|
|
{
|
|
|
|
wbfs_t *p = wbfs_open_partition(read_hdsector, write_hdsector, callback_data, hd_sector_size, 0, part_lba,reset);
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(tmp_buffer);
|
2012-01-21 21:57:41 +01:00
|
|
|
return p;
|
|
|
|
}
|
|
|
|
}
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(tmp_buffer);
|
2012-01-21 21:57:41 +01:00
|
|
|
if(reset)// XXX make a empty hd partition..
|
|
|
|
{
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
wbfs_t *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)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
wbfs_t *p = wbfs_malloc(sizeof(wbfs_t));
|
2012-04-28 00:55:17 +02:00
|
|
|
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_head_t *head = wbfs_malloc( hd_sector_size ? hd_sector_size : 512 );
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
//constants, but put here for consistancy
|
|
|
|
p->wii_sec_sz = 0x8000;
|
|
|
|
p->wii_sec_sz_s = size_to_shift(0x8000);
|
|
|
|
p->n_wii_sec = (num_hd_sector / 0x8000) * hd_sector_size;
|
2012-04-28 00:55:17 +02:00
|
|
|
p->n_wii_sec_per_disc = 143432 * 2; //support for double layers discs..
|
2012-01-21 21:57:41 +01:00
|
|
|
p->head = head;
|
|
|
|
p->part_lba = part_lba;
|
|
|
|
// init the partition
|
|
|
|
if (reset)
|
|
|
|
{
|
|
|
|
u8 sz_s;
|
|
|
|
wbfs_memset(head, 0, hd_sector_size);
|
|
|
|
head->magic = wbfs_htonl(WBFS_MAGIC);
|
|
|
|
head->hd_sec_sz_s = size_to_shift(hd_sector_size);
|
|
|
|
head->n_hd_sec = wbfs_htonl(num_hd_sector);
|
|
|
|
// choose minimum wblk_sz that fits this partition size
|
|
|
|
for(sz_s = 6; sz_s < 11; sz_s++)
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
// ensure that wbfs_sec_sz is big enough to address every blocks using 16 bits
|
|
|
|
if(p->n_wii_sec < ((1U << 16) * (1 << sz_s)))
|
|
|
|
break;
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
head->wbfs_sec_sz_s = sz_s + p->wii_sec_sz_s;
|
|
|
|
}
|
2012-04-28 00:55:17 +02:00
|
|
|
else
|
|
|
|
read_hdsector(callback_data, p->part_lba, 1, head);
|
|
|
|
if (head->magic != wbfs_htonl( WBFS_MAGIC ))
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR( "bad magic\n" );
|
2012-04-28 00:55:17 +02:00
|
|
|
if (!force_mode && hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size))
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR( "hd sector size doesn't match\n" );
|
2012-04-28 00:55:17 +02:00
|
|
|
if (!force_mode && num_hd_sector && head->n_hd_sec != (u32) wbfs_htonl( num_hd_sector ))
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR( "hd num sector doesn't match\n" );
|
2012-01-21 21:57:41 +01:00
|
|
|
p->hd_sec_sz = 1 << head->hd_sec_sz_s;
|
|
|
|
p->hd_sec_sz_s = head->hd_sec_sz_s;
|
|
|
|
p->n_hd_sec = wbfs_ntohl(head->n_hd_sec);
|
|
|
|
|
|
|
|
p->n_wii_sec = (p->n_hd_sec / p->wii_sec_sz) * (p->hd_sec_sz);
|
2012-04-28 00:55:17 +02:00
|
|
|
|
2012-01-21 21:57:41 +01:00
|
|
|
p->wbfs_sec_sz_s = head->wbfs_sec_sz_s;
|
|
|
|
p->wbfs_sec_sz = 1 << p->wbfs_sec_sz_s;
|
|
|
|
p->n_wbfs_sec = p->n_wii_sec >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s);
|
|
|
|
p->n_wbfs_sec_per_disc = p->n_wii_sec_per_disc >> (p->wbfs_sec_sz_s - p->wii_sec_sz_s);
|
|
|
|
p->disc_info_sz = ALIGN_LBA(sizeof(wbfs_disc_info_t) + p->n_wbfs_sec_per_disc * 2);
|
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
//gprintf("hd_sector_size %X wii_sector size %X wbfs sector_size %X\n", p->hd_sec_sz, p->wii_sec_sz, p->wbfs_sec_sz);
|
2012-01-21 21:57:41 +01:00
|
|
|
p->read_hdsector = read_hdsector;
|
|
|
|
p->write_hdsector = write_hdsector;
|
|
|
|
p->callback_data = callback_data;
|
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec / 8) >> p->hd_sec_sz_s;
|
|
|
|
|
|
|
|
if (!reset)
|
|
|
|
p->freeblks = 0; // will alloc and read only if needed
|
2012-01-21 21:57:41 +01:00
|
|
|
else
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
// init with all free blocks
|
2013-12-21 18:02:36 +01:00
|
|
|
p->freeblks = wbfs_malloc(ALIGN_LBA( p->n_wbfs_sec / 8));
|
2012-04-28 00:55:17 +02:00
|
|
|
wbfs_memset(p->freeblks, 0xff, p->n_wbfs_sec / 8);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
p->max_disc = (p->freeblks_lba - 1) / (p->disc_info_sz >> p->hd_sec_sz_s);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(p->max_disc > p->hd_sec_sz - sizeof(wbfs_head_t))
|
|
|
|
p->max_disc = p->hd_sec_sz - sizeof(wbfs_head_t);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2013-12-21 18:02:36 +01:00
|
|
|
p->tmp_buffer = wbfs_malloc(p->hd_sec_sz);
|
2012-01-21 21:57:41 +01:00
|
|
|
p->n_disc_open = 0;
|
|
|
|
return p;
|
2012-04-28 00:55:17 +02:00
|
|
|
|
2012-01-21 21:57:41 +01:00
|
|
|
error:
|
|
|
|
wbfs_free(p);
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(head);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void wbfs_sync(wbfs_t *p)
|
|
|
|
{
|
|
|
|
// copy back descriptors
|
|
|
|
if(p->write_hdsector)
|
|
|
|
{
|
2012-05-12 18:03:14 +02:00
|
|
|
p->write_hdsector(p->callback_data, p->part_lba + 0, 1, p->head);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(p->freeblks)
|
|
|
|
p->write_hdsector(p->callback_data, p->part_lba + p->freeblks_lba, ALIGN_LBA(p->n_wbfs_sec / 8) >> p->hd_sec_sz_s, p->freeblks);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void wbfs_close(wbfs_t *p)
|
|
|
|
{
|
|
|
|
wbfs_sync(p);
|
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
if(p->n_disc_open)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("trying to close wbfs while discs still open\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(p->head);
|
|
|
|
wbfs_free(p->tmp_buffer);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(p->freeblks)
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(p->freeblks);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
wbfs_free(p);
|
|
|
|
|
|
|
|
error:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wbfs_disc_t *wbfs_open_disc(wbfs_t* p, const u8 *discid)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;
|
|
|
|
wbfs_disc_t *d = 0;
|
|
|
|
for(i = 0; i < p->max_disc; i++)
|
|
|
|
{
|
|
|
|
if (p->head->disc_table[i])
|
|
|
|
{
|
2012-05-12 18:03:14 +02:00
|
|
|
p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, 1, p->tmp_buffer);
|
2012-01-21 21:57:41 +01:00
|
|
|
if(wbfs_memcmp(discid,p->tmp_buffer,6)==0)
|
|
|
|
{
|
|
|
|
d = wbfs_malloc(sizeof(*d));
|
2012-04-28 00:55:17 +02:00
|
|
|
if(!d)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("allocating memory\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
d->p = p;
|
|
|
|
d->i = i;
|
2013-12-21 18:02:36 +01:00
|
|
|
d->header = wbfs_malloc(p->disc_info_sz);
|
2012-01-21 21:57:41 +01:00
|
|
|
if(!d->header)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("allocating memory\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, disc_info_sz_lba, d->header);
|
|
|
|
p->n_disc_open ++;
|
2012-04-28 00:55:17 +02:00
|
|
|
//for(i = 0; i < p->n_wbfs_sec_per_disc; i++)
|
|
|
|
//printf("%d,", wbfs_ntohs(d->header->wlba_table[i]));
|
2012-01-21 21:57:41 +01:00
|
|
|
return d;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
2012-04-28 00:55:17 +02:00
|
|
|
|
2012-01-21 21:57:41 +01:00
|
|
|
error:
|
2013-12-21 18:02:36 +01:00
|
|
|
if(d) wbfs_free(d);
|
2012-01-21 21:57:41 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
void wbfs_close_disc(wbfs_disc_t*d)
|
|
|
|
{
|
|
|
|
d->p->n_disc_open --;
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(d->header);
|
2012-01-21 21:57:41 +01:00
|
|
|
wbfs_free(d);
|
|
|
|
}
|
|
|
|
// offset is pointing 32bit words to address the whole dvd, although len is in bytes
|
|
|
|
int wbfs_disc_read(wbfs_disc_t *d, u32 offset, u32 len, u8 *data)
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
if (d->p == &wbfs_iso_file)
|
|
|
|
return wbfs_iso_file_read(d, offset, data, len);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
wbfs_t *p = d->p;
|
|
|
|
u16 wlba = offset>>(p->wbfs_sec_sz_s-2);
|
|
|
|
u32 iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s;
|
|
|
|
u32 lba_mask = (p->wbfs_sec_sz-1)>>(p->hd_sec_sz_s);
|
|
|
|
u32 lba = (offset>>(p->hd_sec_sz_s-2))&lba_mask;
|
|
|
|
u32 off = offset&((p->hd_sec_sz>>2)-1);
|
|
|
|
u16 iwlba = wbfs_ntohs(d->header->wlba_table[wlba]);
|
|
|
|
u32 len_copied;
|
|
|
|
int err = 0;
|
|
|
|
u8 *ptr = data;
|
2012-04-28 00:55:17 +02:00
|
|
|
if(unlikely(iwlba==0))
|
|
|
|
return 1;
|
2012-01-21 21:57:41 +01:00
|
|
|
if(unlikely(off))
|
|
|
|
{
|
|
|
|
off *= 4;
|
|
|
|
err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba<<iwlba_shift) + lba, 1, p->tmp_buffer);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(err)
|
|
|
|
return err;
|
2012-01-21 21:57:41 +01:00
|
|
|
len_copied = p->hd_sec_sz - off;
|
2012-04-28 00:55:17 +02:00
|
|
|
if(likely(len < len_copied))
|
|
|
|
len_copied = len;
|
2012-01-21 21:57:41 +01:00
|
|
|
wbfs_memcpy(ptr, p->tmp_buffer + off, len_copied);
|
|
|
|
len -= len_copied;
|
|
|
|
ptr += len_copied;
|
|
|
|
lba++;
|
|
|
|
if(unlikely(lba>lba_mask && len))
|
|
|
|
{
|
|
|
|
lba=0;
|
|
|
|
iwlba = wbfs_ntohs(d->header->wlba_table[++wlba]);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(unlikely(iwlba==0))
|
|
|
|
return 1;
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
while(likely(len>=p->hd_sec_sz))
|
|
|
|
{
|
|
|
|
u32 nlb = len>>(p->hd_sec_sz_s);
|
|
|
|
if(unlikely(lba + nlb > p->wbfs_sec_sz)) // dont cross wbfs sectors..
|
2012-04-28 00:55:17 +02:00
|
|
|
nlb = p->wbfs_sec_sz-lba;
|
2012-01-21 21:57:41 +01:00
|
|
|
err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba<<iwlba_shift) + lba, nlb, ptr);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(err)
|
|
|
|
return err;
|
2012-01-21 21:57:41 +01:00
|
|
|
len -= nlb << p->hd_sec_sz_s;
|
|
|
|
ptr += nlb << p->hd_sec_sz_s;
|
|
|
|
lba += nlb;
|
|
|
|
if(unlikely(lba > lba_mask && len))
|
|
|
|
{
|
|
|
|
lba = 0;
|
|
|
|
iwlba = wbfs_ntohs(d->header->wlba_table[++wlba]);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(unlikely(iwlba==0))
|
|
|
|
return 1;
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if(unlikely(len))
|
|
|
|
{
|
|
|
|
err = p->read_hdsector(p->callback_data, p->part_lba + (iwlba << iwlba_shift) + lba, 1, p->tmp_buffer);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(err)
|
|
|
|
return err;
|
2012-01-21 21:57:41 +01:00
|
|
|
wbfs_memcpy(ptr, p->tmp_buffer, len);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// disc listing
|
|
|
|
u32 wbfs_count_discs(wbfs_t *p)
|
|
|
|
{
|
|
|
|
u32 i,count=0;
|
|
|
|
for(i = 0; i < p->max_disc; i++)
|
2012-04-28 00:55:17 +02:00
|
|
|
{
|
|
|
|
if (p->head->disc_table[i])
|
|
|
|
count++;
|
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 wbfs_sector_used(wbfs_t *p, wbfs_disc_info_t *di)
|
|
|
|
{
|
|
|
|
u32 tot_blk = 0, j;
|
|
|
|
for(j = 0; j < p->n_wbfs_sec_per_disc; j++)
|
2012-04-28 00:55:17 +02:00
|
|
|
{
|
|
|
|
if(wbfs_ntohs(di->wlba_table[j]))
|
|
|
|
tot_blk++;
|
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
return tot_blk;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 wbfs_sector_used2(wbfs_t *p, wbfs_disc_info_t *di, u32 *last_blk)
|
|
|
|
{
|
|
|
|
u32 tot_blk = 0, j;
|
|
|
|
for(j = 0; j < p->n_wbfs_sec_per_disc; j++)
|
2012-04-28 00:55:17 +02:00
|
|
|
{
|
2012-01-21 21:57:41 +01:00
|
|
|
if(wbfs_ntohs(di->wlba_table[j]))
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
if (last_blk)
|
|
|
|
*last_blk = j;
|
2012-01-21 21:57:41 +01:00
|
|
|
tot_blk++;
|
|
|
|
}
|
2012-04-28 00:55:17 +02:00
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
return tot_blk;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 wbfs_get_disc_info(wbfs_t *p, u32 index,u8 *header,int header_size,u32 *size)//size in 32 bit
|
|
|
|
{
|
|
|
|
u32 i,count=0;
|
2012-04-28 00:55:17 +02:00
|
|
|
if (!p)
|
|
|
|
return 1;
|
2012-01-21 21:57:41 +01:00
|
|
|
int disc_info_sz_lba = p->disc_info_sz >> p->hd_sec_sz_s;
|
|
|
|
|
|
|
|
for(i = 0; i < p->max_disc; i++)
|
2012-04-28 00:55:17 +02:00
|
|
|
{
|
2012-01-21 21:57:41 +01:00
|
|
|
if (p->head->disc_table[i])
|
|
|
|
{
|
|
|
|
if(count++ == index)
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, 1, p->tmp_buffer);
|
2012-05-12 18:03:14 +02:00
|
|
|
if(header_size > (int)p->hd_sec_sz)
|
|
|
|
header_size = p->hd_sec_sz;
|
2012-01-21 21:57:41 +01:00
|
|
|
u32 magic = wbfs_ntohl(*(u32*)(p->tmp_buffer + 24));
|
2012-05-27 23:14:40 +02:00
|
|
|
if(magic != WII_MAGIC)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
p->head->disc_table[i]=0;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
memcpy(header, p->tmp_buffer, header_size);
|
|
|
|
if(size)
|
|
|
|
{
|
2013-12-21 18:02:36 +01:00
|
|
|
u8 *header = wbfs_malloc(p->disc_info_sz);
|
2012-01-21 21:57:41 +01:00
|
|
|
p->read_hdsector(p->callback_data, p->part_lba + 1 + i * disc_info_sz_lba, disc_info_sz_lba, header);
|
|
|
|
u32 sec_used = wbfs_sector_used(p,(wbfs_disc_info_t *)header);
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(header);
|
2012-01-21 21:57:41 +01:00
|
|
|
*size = sec_used<<(p->wbfs_sec_sz_s-2);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
2012-04-28 00:55:17 +02:00
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void load_freeblocks(wbfs_t *p)
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
if(p->freeblks)
|
|
|
|
return;
|
2012-01-21 21:57:41 +01:00
|
|
|
// XXX should handle malloc error..
|
2013-12-21 18:02:36 +01:00
|
|
|
p->freeblks = wbfs_malloc(ALIGN_LBA(p->n_wbfs_sec/8));
|
2012-01-21 21:57:41 +01:00
|
|
|
p->read_hdsector(p->callback_data, p->part_lba + p->freeblks_lba, ALIGN_LBA(p->n_wbfs_sec / 8) >> p->hd_sec_sz_s, p->freeblks);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 wbfs_count_usedblocks(wbfs_t *p)
|
|
|
|
{
|
|
|
|
u32 i, j, count = 0;
|
|
|
|
load_freeblocks(p);
|
|
|
|
for(i = 0; i < p->n_wbfs_sec / (8 * 4); i++)
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
u32 v = wbfs_ntohl(p->freeblks[i]);
|
|
|
|
if(v == ~0U)
|
|
|
|
count += 32;
|
|
|
|
else if(v != 0)
|
|
|
|
{
|
|
|
|
for(j = 0; j < 32; j++)
|
|
|
|
{
|
|
|
|
if (v & (1 << j))
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
// write access
|
|
|
|
// static
|
|
|
|
int block_used(u8 *used, u32 i, u32 wblk_sz)
|
|
|
|
{
|
|
|
|
u32 k;
|
|
|
|
i *= wblk_sz;
|
|
|
|
for(k = 0; k < wblk_sz; k++)
|
2012-04-28 00:55:17 +02:00
|
|
|
{
|
|
|
|
if(i + k < 143432 * 2 && used[i + k])
|
|
|
|
return 1;
|
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 alloc_block(wbfs_t *p)
|
|
|
|
{
|
|
|
|
u32 i,j;
|
|
|
|
for(i = 0; i < p->n_wbfs_sec / (8 * 4); i++)
|
|
|
|
{
|
|
|
|
u32 v = wbfs_ntohl(p->freeblks[i]);
|
|
|
|
if(v != 0)
|
|
|
|
{
|
|
|
|
for(j = 0; j < 32; j++)
|
2012-04-28 00:55:17 +02:00
|
|
|
{
|
2012-01-21 21:57:41 +01:00
|
|
|
if (v & (1 << j))
|
|
|
|
{
|
|
|
|
p->freeblks[i] = wbfs_htonl(v & ~(1<<j));
|
|
|
|
return (i * 32) + j + 1;
|
|
|
|
}
|
2012-04-28 00:55:17 +02:00
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ~0;
|
|
|
|
}
|
|
|
|
static void free_block(wbfs_t *p,int bl)
|
|
|
|
{
|
|
|
|
int i = (bl - 1) / (32);
|
|
|
|
int j = (bl - 1) & 31;
|
|
|
|
u32 v = wbfs_ntohl(p->freeblks[i]);
|
|
|
|
p->freeblks[i] = wbfs_htonl(v | 1 << j);
|
|
|
|
}
|
|
|
|
|
2016-12-01 01:05:39 +01:00
|
|
|
u32 wbfs_add_disc(wbfs_t*p, read_wiidisc_callback_t read_src_wii_disc, void *callback_data,
|
|
|
|
progress_callback_t spinner, void *spinner_data, partition_selector_t sel, int copy_1_1)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
2012-01-23 22:50:53 +01:00
|
|
|
int i,discn;
|
2012-01-21 21:57:41 +01:00
|
|
|
u32 tot,cur;
|
|
|
|
u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s - p->wii_sec_sz_s);
|
|
|
|
wiidisc_t *d = 0;
|
|
|
|
u8 *used = 0;
|
|
|
|
wbfs_disc_info_t *info = 0;
|
|
|
|
u8* copy_buffer = 0;
|
|
|
|
int retval = -1;
|
|
|
|
int num_wbfs_sect_to_copy;
|
|
|
|
u32 last_used;
|
|
|
|
used = wbfs_malloc(p->n_wii_sec_per_disc);
|
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
if(!used)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("unable to alloc memory\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
// copy_1_1 needs disk usage for layers detection
|
|
|
|
//if(!copy_1_1)
|
|
|
|
{
|
|
|
|
d = wd_open_disc(read_src_wii_disc, callback_data);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(!d)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("unable to open wii disc\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
wd_build_disc_usage(d, sel, used);
|
|
|
|
wd_close_disc(d);
|
|
|
|
d = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(i = 0; i < p->max_disc; i++)// find a free slot.
|
2012-04-28 00:55:17 +02:00
|
|
|
{
|
|
|
|
if(p->head->disc_table[i]==0)
|
|
|
|
break;
|
|
|
|
}
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
if(i == p->max_disc)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("no space left on device (table full)\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
p->head->disc_table[i] = 1;
|
|
|
|
discn = i;
|
|
|
|
load_freeblocks(p);
|
|
|
|
|
|
|
|
// build disc info
|
2013-12-21 18:02:36 +01:00
|
|
|
info = wbfs_malloc(p->disc_info_sz);
|
2012-01-21 21:57:41 +01:00
|
|
|
read_src_wii_disc(callback_data, 0, 0x100, info->disc_header_copy);
|
|
|
|
|
2013-12-21 18:02:36 +01:00
|
|
|
copy_buffer = wbfs_malloc(p->wii_sec_sz);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(!copy_buffer)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("alloc memory\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
tot = 0;
|
|
|
|
cur = 0;
|
|
|
|
num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc;
|
|
|
|
// count total number of sectors to write
|
|
|
|
last_used = 0;
|
|
|
|
for(i = 0; i < num_wbfs_sect_to_copy; i++)
|
|
|
|
{
|
|
|
|
if(block_used(used, i, wii_sec_per_wbfs_sect))
|
|
|
|
{
|
|
|
|
tot += wii_sec_per_wbfs_sect;
|
|
|
|
last_used = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (copy_1_1)
|
|
|
|
{
|
|
|
|
// detect single or dual layer
|
2012-04-28 00:55:17 +02:00
|
|
|
if((last_used + 1) > (p->n_wbfs_sec_per_disc / 2))
|
2012-01-21 21:57:41 +01:00
|
|
|
num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc;
|
2012-04-28 00:55:17 +02:00
|
|
|
else
|
|
|
|
num_wbfs_sect_to_copy = p->n_wbfs_sec_per_disc / 2;
|
2012-01-21 21:57:41 +01:00
|
|
|
tot = num_wbfs_sect_to_copy * wii_sec_per_wbfs_sect;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
// num of hd sectors to copy could be specified directly
|
|
|
|
if (copy_1_1 > 1) {
|
|
|
|
u32 hd_sec_per_wii_sec = p->wii_sec_sz / p->hd_sec_sz;
|
|
|
|
num_wbfs_sect_to_copy = copy_1_1 / hd_sec_per_wii_sec / wii_sec_per_wbfs_sect;
|
|
|
|
tot = num_wbfs_sect_to_copy * wii_sec_per_wbfs_sect;
|
|
|
|
}*/
|
2012-01-23 22:50:53 +01:00
|
|
|
int ret = 0;
|
2012-01-21 21:57:41 +01:00
|
|
|
if(spinner) spinner(0, tot, spinner_data);
|
|
|
|
for(i=0; i < num_wbfs_sect_to_copy; i++)
|
|
|
|
{
|
|
|
|
u16 bl = 0;
|
|
|
|
if(copy_1_1 || block_used(used,i, wii_sec_per_wbfs_sect))
|
|
|
|
{
|
|
|
|
u16 j;
|
|
|
|
|
|
|
|
bl = alloc_block(p);
|
|
|
|
if (bl==0xffff)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("no space left on device (disc full)\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
for(j = 0; j < wii_sec_per_wbfs_sect; j++)
|
|
|
|
{
|
|
|
|
u32 offset = (i * (p->wbfs_sec_sz >> 2)) + (j * (p->wii_sec_sz >> 2));
|
|
|
|
|
|
|
|
ret = read_src_wii_disc(callback_data, offset, p->wii_sec_sz, copy_buffer);
|
|
|
|
if (ret)
|
|
|
|
{
|
|
|
|
if (copy_1_1 && i > p->n_wbfs_sec_per_disc / 2)
|
|
|
|
{
|
|
|
|
// end of dual layer data
|
2012-04-28 00:55:17 +02:00
|
|
|
if(j > 0)
|
|
|
|
info->wlba_table[i] = wbfs_htons(bl);
|
2012-01-21 21:57:41 +01:00
|
|
|
spinner(tot,tot,spinner_data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
//ERROR("read error");
|
2012-05-12 18:03:14 +02:00
|
|
|
gprintf("\rWARNING: read (%u) error (%d)\n", offset, ret);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//fix the partition table
|
2012-04-28 00:55:17 +02:00
|
|
|
if(offset == (0x40000>>2))
|
2012-05-12 18:03:14 +02:00
|
|
|
wd_fix_partition_table(sel, copy_buffer);
|
2012-01-21 21:57:41 +01:00
|
|
|
p->write_hdsector(p->callback_data, p->part_lba + bl * (p->wbfs_sec_sz / p->hd_sec_sz) + j
|
|
|
|
* (p->wii_sec_sz / p->hd_sec_sz), p->wii_sec_sz / p->hd_sec_sz, copy_buffer);
|
|
|
|
cur++;
|
2012-04-28 00:55:17 +02:00
|
|
|
if(spinner)
|
|
|
|
spinner(cur,tot,spinner_data);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
}
|
2012-04-28 00:55:17 +02:00
|
|
|
if(ret)
|
|
|
|
break;
|
2012-01-21 21:57:41 +01:00
|
|
|
info->wlba_table[i] = wbfs_htons(bl);
|
|
|
|
}
|
|
|
|
// write disc info
|
|
|
|
int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;
|
2012-05-12 18:03:14 +02:00
|
|
|
p->write_hdsector(p->callback_data, p->part_lba+1+discn*disc_info_sz_lba,disc_info_sz_lba,info);
|
2012-01-21 21:57:41 +01:00
|
|
|
wbfs_sync(p);
|
|
|
|
retval = 0;
|
2012-04-28 00:55:17 +02:00
|
|
|
|
2012-01-21 21:57:41 +01:00
|
|
|
error:
|
2012-04-28 00:55:17 +02:00
|
|
|
if(d)
|
|
|
|
wd_close_disc(d);
|
|
|
|
if(used)
|
|
|
|
wbfs_free(used);
|
|
|
|
if(info)
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(info);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(copy_buffer)
|
2013-12-21 18:02:36 +01:00
|
|
|
wbfs_free(copy_buffer);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
// init with all free blocks
|
2012-01-21 21:57:41 +01:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 wbfs_rm_disc(wbfs_t *p, u8* discid)
|
|
|
|
{
|
|
|
|
wbfs_disc_t *d = wbfs_open_disc(p,discid);
|
|
|
|
int i;
|
|
|
|
int discn = 0;
|
|
|
|
int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;
|
2012-04-28 00:55:17 +02:00
|
|
|
if(!d)
|
|
|
|
return 1;
|
2012-01-21 21:57:41 +01:00
|
|
|
load_freeblocks(p);
|
|
|
|
discn = d->i;
|
|
|
|
for( i=0; i< p->n_wbfs_sec_per_disc; i++)
|
|
|
|
{
|
|
|
|
u32 iwlba = wbfs_ntohs(d->header->wlba_table[i]);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(iwlba)
|
|
|
|
free_block(p,iwlba);
|
2012-01-21 21:57:41 +01:00
|
|
|
}
|
|
|
|
memset(d->header,0,p->disc_info_sz);
|
2012-05-12 18:03:14 +02:00
|
|
|
p->write_hdsector(p->callback_data, p->part_lba+1+discn*disc_info_sz_lba,disc_info_sz_lba,d->header);
|
2012-01-21 21:57:41 +01:00
|
|
|
p->head->disc_table[discn] = 0;
|
|
|
|
wbfs_close_disc(d);
|
|
|
|
wbfs_sync(p);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
// trim the file-system to its minimum size
|
|
|
|
u32 wbfs_trim(wbfs_t *p)
|
|
|
|
{
|
|
|
|
u32 maxbl;
|
|
|
|
load_freeblocks(p);
|
|
|
|
maxbl = alloc_block(p);
|
|
|
|
p->n_hd_sec = maxbl << (p->wbfs_sec_sz_s - p->hd_sec_sz_s);
|
|
|
|
p->head->n_hd_sec = wbfs_htonl(p->n_hd_sec);
|
|
|
|
// make all block full
|
|
|
|
memset(p->freeblks, 0, p->n_wbfs_sec / 8);
|
|
|
|
wbfs_sync(p);
|
|
|
|
// os layer will truncate the file.
|
|
|
|
return maxbl;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
u32 wbfs_size_disc(wbfs_t *p,read_wiidisc_callback_t read_src_wii_disc,
|
|
|
|
void *callback_data,partition_selector_t sel, u32 *comp_size, u32 *real_size)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u32 tot = 0, last = 0;
|
|
|
|
u32 wii_sec_per_wbfs_sect = 1 << (p->wbfs_sec_sz_s - p->wii_sec_sz_s);
|
|
|
|
wiidisc_t *d = 0;
|
|
|
|
|
2013-12-21 18:02:36 +01:00
|
|
|
u8 *used = wbfs_malloc(p->n_wii_sec_per_disc);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(!used)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("unable to alloc memory\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
d = wd_open_disc(read_src_wii_disc,callback_data);
|
2012-04-28 00:55:17 +02:00
|
|
|
if(!d)
|
2012-05-12 18:03:14 +02:00
|
|
|
ERROR("unable to open wii disc\n");
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
wd_build_disc_usage(d, sel, used);
|
|
|
|
wd_close_disc(d);
|
|
|
|
d = 0;
|
|
|
|
|
|
|
|
// count total number to write for spinner
|
|
|
|
for (i = 0; i < p->n_wbfs_sec_per_disc; i++)
|
|
|
|
{
|
|
|
|
if (block_used(used, i, wii_sec_per_wbfs_sect))
|
|
|
|
{
|
|
|
|
tot += wii_sec_per_wbfs_sect;
|
|
|
|
last = i * wii_sec_per_wbfs_sect;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
2012-04-28 00:55:17 +02:00
|
|
|
if(d)
|
|
|
|
wd_close_disc(d);
|
|
|
|
if(used)
|
|
|
|
wbfs_free(used);
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
*comp_size = tot;
|
|
|
|
*real_size = last;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// offset is pointing 32bit words to address the whole dvd, although len is in bytes
|
|
|
|
//int wbfs_disc_read(wbfs_disc_t*d,u32 offset, u8 *data, u32 len)
|
|
|
|
|
|
|
|
// offset points 32bit words, count counts bytes
|
|
|
|
//int (*read_wiidisc_callback_t)(void*fp,u32 offset,u32 count,void*iobuf);
|
|
|
|
|
|
|
|
// connect wiidisc to wbfs_disc
|
|
|
|
int read_wiidisc_wbfsdisc(void *fp, u32 offset, u32 count, void *iobuf)
|
|
|
|
{
|
|
|
|
return wbfs_disc_read((wbfs_disc_t *)fp, offset, count, iobuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 wbfs_extract_file(wbfs_disc_t *d, char *path, void **data)
|
|
|
|
{
|
|
|
|
wiidisc_t *wd = 0;
|
|
|
|
u32 ret = 0;
|
|
|
|
|
|
|
|
wd = wd_open_disc(read_wiidisc_wbfsdisc, d);
|
|
|
|
if (!wd)
|
|
|
|
{
|
2012-05-12 18:03:14 +02:00
|
|
|
wbfs_error( "opening wbfs disc\n" );
|
2012-01-21 21:57:41 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*data = wd_extract_file(wd, &ret, ONLY_GAME_PARTITION, path);
|
|
|
|
if (!*data)
|
|
|
|
{
|
|
|
|
//ERROR("file not found");
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
wd_close_disc(wd);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-04-28 00:55:17 +02:00
|
|
|
int wbfs_get_fragments(wbfs_disc_t *d, _frag_append_t append_fragment,
|
|
|
|
void *callback_data, u32 hdd_sector_size)
|
2012-01-21 21:57:41 +01:00
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
if (!d)
|
|
|
|
return -1;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
wbfs_t *p = d->p;
|
|
|
|
int src_wbs_nlb = p->wbfs_sec_sz / hdd_sector_size;
|
|
|
|
int i, ret, last = 0;
|
|
|
|
for( i=0; i< p->n_wbfs_sec_per_disc; i++)
|
|
|
|
{
|
|
|
|
u32 iwlba = wbfs_ntohs(d->header->wlba_table[i]);
|
|
|
|
if (iwlba)
|
|
|
|
{
|
|
|
|
ret = append_fragment(callback_data,
|
|
|
|
i * src_wbs_nlb, // offset
|
|
|
|
p->part_lba + iwlba * src_wbs_nlb, // sector
|
|
|
|
src_wbs_nlb); // count
|
|
|
|
if (ret) return ret; // error
|
|
|
|
last = i;
|
|
|
|
}
|
|
|
|
}
|
2012-04-28 00:55:17 +02:00
|
|
|
if(last < p->n_wbfs_sec_per_disc / 2)
|
2012-01-21 21:57:41 +01:00
|
|
|
last = p->n_wbfs_sec_per_disc / 2;
|
|
|
|
|
|
|
|
u32 size = last * src_wbs_nlb;
|
|
|
|
append_fragment(callback_data, size, 0, 0); // set size
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// wrapper for reading .iso files using wbfs apis
|
|
|
|
|
|
|
|
// offset is pointing 32bit words to address the whole dvd, although len is in bytes
|
|
|
|
int wbfs_iso_file_read(wbfs_disc_t*d,u32 offset, u8 *data, u32 len)
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
if (!d || d->p != &wbfs_iso_file)
|
|
|
|
return -1;
|
2012-01-21 21:57:41 +01:00
|
|
|
int fd = (int)d->header; //HMM?
|
|
|
|
//int fd = d->i;
|
|
|
|
off_t off = ((u64)offset) << 2;
|
|
|
|
off_t ret_off;
|
|
|
|
ret_off = lseek(fd, off, SEEK_SET);
|
2012-04-28 00:55:17 +02:00
|
|
|
if (ret_off != off)
|
|
|
|
return -1;
|
2012-05-12 18:03:14 +02:00
|
|
|
u32 ret = read(fd, data, len);
|
2012-04-28 00:55:17 +02:00
|
|
|
if (ret != len)
|
|
|
|
return -2;
|
2012-01-21 21:57:41 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
u32 wbfs_disc_sector_used(wbfs_disc_t *d, u32 *num_blk)
|
|
|
|
{
|
2012-04-28 00:55:17 +02:00
|
|
|
if(!d)
|
|
|
|
return 0;
|
2012-01-21 21:57:41 +01:00
|
|
|
|
|
|
|
if (d->p == &wbfs_iso_file)
|
|
|
|
{
|
|
|
|
int fd = (int)d->header; //HMM?
|
|
|
|
//int fd = d->i;
|
|
|
|
struct stat st;
|
2012-04-28 00:55:17 +02:00
|
|
|
if (fstat(fd, &st) == -1)
|
|
|
|
return 0;
|
|
|
|
if (num_blk)
|
|
|
|
*num_blk = (st.st_size >> 9); // in 512 units
|
2012-01-21 21:57:41 +01:00
|
|
|
return st.st_blocks; // in 512 units (can be sparse)
|
|
|
|
}
|
|
|
|
u32 last_blk = 0;
|
|
|
|
u32 ret;
|
|
|
|
ret = wbfs_sector_used2(d->p, d->header, &last_blk);
|
2012-04-28 00:55:17 +02:00
|
|
|
if (num_blk)
|
|
|
|
*num_blk = last_blk + 1;
|
2012-01-21 21:57:41 +01:00
|
|
|
return ret;
|
|
|
|
}
|