code:
// Copyright 2009 Kwiirk
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

#include "libwbfs.h"
#include <errno.h>

#ifndef WIN32
#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)
#else
#define likely(x)		(x)
#define unlikely(x)		(x)
#endif

#define WBFS_GOTO_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)))

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;
}

#define read_le32_unaligned(x) ((x)[0]|((x)[1]<<8)|((x)[2]<<16)|((x)[3]<<24))

void wbfs_sync(wbfs_t*p);

wbfs_t*wbfs_open_hd(
					rw_sector_callback_t read_hdsector,
					rw_sector_callback_t write_hdsector,
#ifdef WIN32
					close_callback_t close_hd,
#endif
					void *callback_data,
		    int hd_sector_size,
#ifdef WIN32
					int num_hd_sector,
#else
		    int num_hd_sector __attribute((unused)),
#endif
		    int reset)
{
	int i
#ifdef UNUSED_STUFF
	= num_hd_sector
#endif
	, ret;
	u8 *ptr,*tmp_buffer = wbfs_ioalloc(hd_sector_size);
	u8 part_table[16*4];
	ret = read_hdsector(callback_data,0,1,tmp_buffer);
	if(ret)
		return 0;
	//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;
#ifdef UNUSED_STUFF
		ret =
#endif
				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,
#ifdef WIN32
														close_hd,
#endif
														callback_data,
														hd_sector_size,
														0,
														part_lba,reset
														);
					return p;
		}
	}
	if(reset)// XXX make a empty hd partition..
	{
	}
	return 0;
}
wbfs_t*wbfs_open_partition(rw_sector_callback_t read_hdsector,
							rw_sector_callback_t write_hdsector,
#ifdef WIN32
							close_callback_t close_hd,
#endif
							void *callback_data,
							int hd_sector_size, int num_hd_sector, u32 part_lba, int reset)
{
	wbfs_t *p = wbfs_malloc(sizeof(wbfs_t));

	wbfs_head_t *head = wbfs_ioalloc(hd_sector_size?hd_sector_size:512);

	//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;
	p->n_wii_sec_per_disc = 143432*2;//support for double layers discs..
	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++)
		{
			// 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;
		}
		head->wbfs_sec_sz_s = sz_s+p->wii_sec_sz_s;
	} else
		read_hdsector(callback_data,p->part_lba,1,head);
	if (head->magic != wbfs_htonl(WBFS_MAGIC))
		WBFS_GOTO_ERROR("bad magic");
	if(!force_mode && hd_sector_size && head->hd_sec_sz_s !=  size_to_shift(hd_sector_size))
		WBFS_GOTO_ERROR("hd sector size doesn't match");
	if(!force_mode && num_hd_sector && head->n_hd_sec != wbfs_htonl(num_hd_sector))
		WBFS_GOTO_ERROR("hd num sector doesn't match");
	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);

	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);

	//printf("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);
	p->read_hdsector = read_hdsector;
	p->write_hdsector = write_hdsector;
#ifdef WIN32
		p->close_hd = close_hd;
#endif
	p->callback_data = callback_data;

	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
	else
	{
		// init with all free blocks
		p->freeblks = wbfs_ioalloc(ALIGN_LBA(p->n_wbfs_sec/8));
		wbfs_memset(p->freeblks,0xff,p->n_wbfs_sec/8);
	}
	p->max_disc = (p->freeblks_lba-1)/(p->disc_info_sz>>p->hd_sec_sz_s);
	if(p->max_disc > p->hd_sec_sz - sizeof(wbfs_head_t))
		p->max_disc = p->hd_sec_sz - sizeof(wbfs_head_t);

	p->tmp_buffer = wbfs_ioalloc(p->hd_sec_sz);
	p->n_disc_open = 0;
	wbfs_sync(p);
	return p;
error:
	wbfs_free(p);
	wbfs_iofree(head);
	return 0;

}

void wbfs_sync(wbfs_t*p)
{
	// copy back descriptors
	if(p->write_hdsector){
		p->write_hdsector(p->callback_data,p->part_lba+0,1, p->head);

		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);
		}

	}
}
void wbfs_close(wbfs_t*p)
{
	wbfs_sync(p);

	if(p->n_disc_open)
		WBFS_GOTO_ERROR("trying to close wbfs while discs still open");

	wbfs_iofree(p->head);
	wbfs_iofree(p->tmp_buffer);
	if(p->freeblks)
		wbfs_iofree(p->freeblks);

#ifdef WIN32
		p->close_hd(p->callback_data);
#endif

	wbfs_free(p);

error:
	return;
}

wbfs_disc_t *wbfs_open_disc(wbfs_t* p, 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]){
			p->read_hdsector(p->callback_data,
					 p->part_lba+1+i*disc_info_sz_lba,1,p->tmp_buffer);
#ifdef WIN32
			if(_strnicmp(discid,p->tmp_buffer,6)==0){
#else
			if(wbfs_memcmp(discid,p->tmp_buffer,6)==0){
#endif
				d = wbfs_malloc(sizeof(*d));
				if(!d)
					WBFS_GOTO_ERROR("allocating memory");
				d->p = p;
				d->i = i;
				d->header = wbfs_ioalloc(p->disc_info_sz);
				if(!d->header)
					WBFS_GOTO_ERROR("allocating memory");
				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 ++;
//				  for(i=0;i<p->n_wbfs_sec_per_disc;i++)
//					  printf("%d,",wbfs_ntohs(d->header->wlba_table[i]));
				return d;
			}
		}
	}

#ifdef WIN32
		//_set_errno(ENOENT);
#endif
	return 0;
error:
	if (d) {
			wbfs_iofree(d);
		}
#ifdef WIN32
		//_set_errno(ENOENT);
#endif
	return 0;

}
void wbfs_close_disc(wbfs_disc_t*d)
{
	d->p->n_disc_open --;
	wbfs_iofree(d->header);
	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, u8 *data, u32 len)
{

	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;
	if(unlikely(iwlba==0))
		return 1;
	if(unlikely(off)){
		off*=4;
		err = p->read_hdsector(p->callback_data,
				       p->part_lba + (iwlba<<iwlba_shift) + lba, 1, p->tmp_buffer);
		if(err)
			return err;
		len_copied = p->hd_sec_sz - off;
		if(likely(len < len_copied))
			len_copied = len;
		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]);
			if(unlikely(iwlba==0))
				return 1;
		}
	}
	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..
			nlb = p->wbfs_sec_sz-lba;
		err = p->read_hdsector(p->callback_data,
				 p->part_lba + (iwlba<<iwlba_shift) + lba, nlb, ptr);
		if(err)
			return err;
		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]);
			if(unlikely(iwlba==0))
				return 1;
		}
	}
	if(unlikely(len)){
		err = p->read_hdsector(p->callback_data,
				 p->part_lba + (iwlba<<iwlba_shift) + lba, 1, p->tmp_buffer);
		if(err)
			return err;
		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++)
		if (p->head->disc_table[i])
			count++;
	return count;

}

static 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++)
		if(wbfs_ntohs(di->wlba_table[j]))
			tot_blk++;
	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;
	int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;
	for(i=0;i<p->max_disc;i++)
		if (p->head->disc_table[i]){
			if(count++==index)
			{
								u32 magic;

				p->read_hdsector(p->callback_data,
						 p->part_lba+1+i*disc_info_sz_lba,1,p->tmp_buffer);
				if(header_size > (int)p->hd_sec_sz)
					header_size = p->hd_sec_sz;
				magic = wbfs_ntohl(*(u32*)(p->tmp_buffer+24));
				if(magic!=0x5D1C9EA3){
					p->head->disc_table[i]=0;
					return 1;
				}
				memcpy(header,p->tmp_buffer,header_size);
				if(size)
				{
										u32 sec_used;
					u8 *header = wbfs_ioalloc(p->disc_info_sz);

					p->read_hdsector(p->callback_data,
							 p->part_lba+1+i*disc_info_sz_lba,disc_info_sz_lba,header);
					sec_used = wbfs_sector_used(p,(wbfs_disc_info_t *)header);
					wbfs_iofree(header);
					*size = sec_used<<(p->wbfs_sec_sz_s-2);
				}
				return 0;
			}
		}
	return 1;
}

static void load_freeblocks(wbfs_t*p)
{
	if(p->freeblks)
		return;
	// XXX should handle malloc error..
	p->freeblks = wbfs_ioalloc(ALIGN_LBA(p->n_wbfs_sec/8));
	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++)
	{
		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++;
	}
	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++)
		if(i+k<143432*2 && used[i+k])
			return 1;
	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++)
				if (v & (1<<j))
				{
					p->freeblks[i] = wbfs_htonl(v & ~(1<<j));
					return (i*32)+j+1;
				}
		}
	}
	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);
}

u32 wbfs_add_disc
	(
		wbfs_t *p,
		read_wiidisc_callback_t read_src_wii_disc,
		void *callback_data,
		progress_callback_t spinner,
		partition_selector_t sel,
		int copy_1_1
#ifdef WIN32
		, char *new_name
#endif
	)
{
	int i, discn;
	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;
	u8 *b;
	int disc_info_sz_lba;
	used = wbfs_malloc(p->n_wii_sec_per_disc);

	if (!used)
	{
			WBFS_GOTO_ERROR("unable to alloc memory");
	}

	if (!copy_1_1)
	{
		d = wd_open_disc(read_src_wii_disc, callback_data);
		if(!d)
		{
			WBFS_GOTO_ERROR("unable to open wii disc");
		}
		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.
	{
		if (p->head->disc_table[i] == 0)
		{
			break;
		}
	}

	if (i == p->max_disc)
	{
		WBFS_GOTO_ERROR("no space left on device (table full)");
	}

	p->head->disc_table[i] = 1;
	discn = i;
	load_freeblocks(p);

	// build disc info
	info = wbfs_ioalloc(p->disc_info_sz);
	b = (u8 *)info;
	read_src_wii_disc(callback_data, 0, 0x100, info->disc_header_copy);

#ifdef WIN32
	if (new_name)
	{
		wbfs_memset(b+0x20, 0, 0x40);
		if(strlen(new_name)>=0x40) new_name[0x39]=0;
		strcpy(b+0x20, new_name);
	}
#endif

	fprintf(stderr, "adding %c%c%c%c%c%c %s...\n",b[0], b[1], b[2], b[3], b[4], b[5], b + 0x20);

	copy_buffer = wbfs_ioalloc(p->wbfs_sec_sz);
	if (!copy_buffer)
	{
			WBFS_GOTO_ERROR("alloc memory");
	}

	tot = 0;
	cur = 0;

	if (spinner)
	{
		// count total number to write for spinner
		for (i = 0; i < p->n_wbfs_sec_per_disc; i++)
		{
			if (copy_1_1 || block_used(used, i, wii_sec_per_wbfs_sect))
			{
				tot++;
				spinner(0, tot);
			}
		}
	}

	for (i = 0; i < p->n_wbfs_sec_per_disc; i++)
	{
		u16 bl = 0;
		if (copy_1_1 || block_used(used, i, wii_sec_per_wbfs_sect))
		{
			bl = alloc_block(p);
			if (bl == 0xffff)
			{
				WBFS_GOTO_ERROR("no space left on device (disc full)");
			}

			if(read_src_wii_disc(callback_data, i * (p->wbfs_sec_sz >> 2), p->wbfs_sec_sz, copy_buffer))
                                WBFS_GOTO_ERROR("error reading disc 3");

			// fix the partition table.
			if (i == (0x40000 >> p->wbfs_sec_sz_s))
			{
				wd_fix_partition_table(d, sel, copy_buffer + (0x40000 & (p->wbfs_sec_sz - 1)));
			}

			p->write_hdsector(p->callback_data, p->part_lba + bl * (p->wbfs_sec_sz / p->hd_sec_sz),
								p->wbfs_sec_sz / p->hd_sec_sz, copy_buffer);

			if (spinner)
			{
				cur++;
				spinner(cur, tot);
			}
		}

		info->wlba_table[i] = wbfs_htons(bl);
	}

	// write disc info
	disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;
	p->write_hdsector(p->callback_data, p->part_lba + 1 + discn * disc_info_sz_lba,disc_info_sz_lba, info);
	wbfs_sync(p);

	if(d)
			wd_close_disc(d);
	if(used)
			wbfs_free(used);
	if(info)
			wbfs_iofree(info);
	if(copy_buffer)
			wbfs_iofree(copy_buffer);

	// init with all free blocks

	return 0;

error:
	if(d)
			wd_close_disc(d);
	if(used)
			wbfs_free(used);
	if(info)
			wbfs_iofree(info);
	if(copy_buffer)
			wbfs_iofree(copy_buffer);

	// init with all free blocks

	return 1;
}

u32 wbfs_ren_disc(wbfs_t*p, u8* discid, u8* newname)
{
	wbfs_disc_t *d = wbfs_open_disc(p, discid);
	int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;

	if(!d)
		return 1;

	memset(d->header->disc_header_copy+0x20, 0, 0x40);
	strncpy(d->header->disc_header_copy+0x20, (char*)newname, 0x39);
	d->header->disc_header_copy[0x20+0x39] = '\0'; //force last char to 0

	p->write_hdsector(p->callback_data,
					  p->part_lba+1+d->i*disc_info_sz_lba,
					  disc_info_sz_lba,
					  d->header);

	wbfs_close_disc(d);
	wbfs_sync(p);
	return 0;
}

u32 wbfs_nid_disc(wbfs_t*p, u8* discid, u8* newid)
{
	wbfs_disc_t *d = wbfs_open_disc(p, discid);
	int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;

	if(!d)
		return 1;

	if(strlen((const char *)newid) > 0x6)
		return 1;

	strcpy((char *)(d->header->disc_header_copy+0x0), (const char *)newid);

	p->write_hdsector(p->callback_data,
					  p->part_lba+1+d->i*disc_info_sz_lba,
					  disc_info_sz_lba,
					  d->header);

	wbfs_close_disc(d);
	wbfs_sync(p);
	return 0;
}

u32 wbfs_estimate_disc
	(
		wbfs_t *p, read_wiidisc_callback_t read_src_wii_disc,
		void *callback_data,
		partition_selector_t sel, u8* header
                )
{
	u8 *b;
	int i;
	u32 tot;
	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;

	tot = 0;

	used = wbfs_malloc(p->n_wii_sec_per_disc);
	if (!used)
	{
		WBFS_GOTO_ERROR("unable to alloc memory");
	}

	d = wd_open_disc(read_src_wii_disc, callback_data);
	if (!d)
	{
		WBFS_GOTO_ERROR("unable to open wii disc");
	}

	int result = wd_build_disc_usage(d,sel,used);
	wd_close_disc(d);
	d = 0;

	info = wbfs_ioalloc(p->disc_info_sz);
	b = (u8 *)info;
	read_src_wii_disc(callback_data, 0, 0x100, info->disc_header_copy);

	for (i = 0; i < p->n_wbfs_sec_per_disc; i++)
	{
		if (block_used(used, i, wii_sec_per_wbfs_sect))
		{
			tot++;
		}
	}

	if(result)
	{
		memcpy(header, b,0x100);
	}

error:
	if (d)
		wd_close_disc(d);

	if (used)
		wbfs_free(used);

	if (info)
		wbfs_iofree(info);

	return !result ? 0 : tot * ((p->wbfs_sec_sz / p->hd_sec_sz) * 512);
}

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;
	if(!d)
		return 1;

	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]);
		if (iwlba)
			free_block(p,iwlba);
	}
	memset(d->header,0,p->disc_info_sz);
	p->write_hdsector(p->callback_data,p->part_lba+1+discn*disc_info_sz_lba,disc_info_sz_lba,d->header);
	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;
}

// data extraction
u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector,void *callback_data,progress_callback_t spinner)
{
	wbfs_t *p = d->p;
	u8* copy_buffer = 0;
	int tot = 0, cur = 0;
	int i;
	int filling_info = 0;

	int src_wbs_nlb=p->wbfs_sec_sz/p->hd_sec_sz;
	int dst_wbs_nlb=p->wbfs_sec_sz/p->wii_sec_sz;

	copy_buffer = wbfs_ioalloc(p->wbfs_sec_sz);

	if (!copy_buffer)
		WBFS_GOTO_ERROR("alloc memory");

	if (spinner)
	{
		// count total number to write for spinner
		for (i = 0; i < p->n_wbfs_sec_per_disc; i++)
		{
			u32 iwlba = wbfs_ntohs(d->header->wlba_table[i]);
			if (iwlba)
			{
				tot++;
				spinner(0, tot);
			}
		}
	}

	for (i = 0; i < p->n_wbfs_sec_per_disc; i++)
	{
		u32 iwlba = wbfs_ntohs(d->header->wlba_table[i]);
		if (iwlba)
		{
			cur++;
			if (spinner)
				spinner(cur,tot);

			if(p->read_hdsector(p->callback_data, p->part_lba + iwlba*src_wbs_nlb, src_wbs_nlb, copy_buffer))
                                WBFS_GOTO_ERROR("reading disc");
			if(write_dst_wii_sector(callback_data, i*dst_wbs_nlb, dst_wbs_nlb, copy_buffer))
                                WBFS_GOTO_ERROR("writing disc");
		}
		else
		{
			switch (filling_info) {
				case 0:
					if (cur == tot)
						filling_info = 1;
					break;

				case 1:
					fprintf(stderr, "Filling empty space in extracted image. Please wait...\n");
					filling_info = 2;
					break;

				case 2:
				default:
					break;
			}
		}
	}
	wbfs_iofree(copy_buffer);
	return 0;
error:
	return 1;
}

u32 wbfs_extract_file(wbfs_disc_t* d, char *path);


code:
#ifndef LIBWBFS_H
#define LIBWBFS_H

#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs
#include "wiidisc.h"

#ifdef __cplusplus
   extern "C" {
#endif /* __cplusplus */

typedef u32 be32_t;
typedef u16 be16_t;

#ifdef WIN32
#pragma pack(1)
#endif

typedef struct wbfs_head
{
        be32_t magic;
        // parameters copied in the partition for easy dumping, and bug reports
        be32_t n_hd_sec;	       // total number of hd_sec in this partition
        u8  hd_sec_sz_s;       // sector size in this partition
        u8  wbfs_sec_sz_s;     // size of a wbfs sec
        u8  padding3[2];
        u8  disc_table[0];	// size depends on hd sector size
}
#ifndef WIN32
__attribute((packed)) wbfs_head_t;
#else
__attribute((packed)) wbfs_head_t;
#endif

#ifdef WIN32
#pragma pack()
#endif

typedef struct wbfs_disc_info
{
        u8 disc_header_copy[0x100];
        be16_t wlba_table[0];
}wbfs_disc_info_t;

//  WBFS first wbfs_sector structure:
//
//  -----------
// | wbfs_head |  (hd_sec_sz)
//  -----------
// |	       |
// | disc_info |
// |	       |
//  -----------
// |	       |
// | disc_info |
// |	       |
//  -----------
// |	       |
// | ...       |
// |	       |
//  -----------
// |	       |
// | disc_info |
// |	       |
//  -----------
// |	       |
// |freeblk_tbl|
// |	       |
//  -----------
//

// callback definition. Return 1 on fatal error (callback is supposed to make retries until no hopes..)
typedef int (*rw_sector_callback_t)(void*fp,u32 lba,u32 count,void*iobuf);
typedef void (*progress_callback_t)(int status,int total);
#ifdef WIN32
typedef void (*close_callback_t)(void*fp);
#endif

typedef struct wbfs_s
{
        wbfs_head_t *head;

        /* hdsectors, the size of the sector provided by the hosting hard drive */
        u32 hd_sec_sz;
        u8  hd_sec_sz_s; // the power of two of the last number
        u32 n_hd_sec;	 // the number of hd sector in the wbfs partition

        /* standard wii sector (0x8000 bytes) */
        u32 wii_sec_sz;
        u8  wii_sec_sz_s;
        u32 n_wii_sec;
        u32 n_wii_sec_per_disc;

        /* The size of a wbfs sector */
        u32 wbfs_sec_sz;
        u32 wbfs_sec_sz_s;
        u16 n_wbfs_sec;   // this must fit in 16 bit!
        u16 n_wbfs_sec_per_disc;   // size of the lookup table

        u32 part_lba;
        /* virtual methods to read write the partition */
        rw_sector_callback_t read_hdsector;
        rw_sector_callback_t write_hdsector;
#ifdef WIN32
		close_callback_t close_hd;
#endif

        void *callback_data;

        u16 max_disc;
        u32 freeblks_lba;
        u32 *freeblks;
        u16 disc_info_sz;

        u8  *tmp_buffer;  // pre-allocated buffer for unaligned read

        u32 n_disc_open;

}wbfs_t;

typedef struct wbfs_disc_s
{
        wbfs_t *p;
        wbfs_disc_info_t  *header;	  // pointer to wii header
        int i;		  		  // disc index in the wbfs header (disc_table)
}wbfs_disc_t;

#define WBFS_MAGIC (('W'<<24)|('B'<<16)|('F'<<8)|('S'))

/*! @brief open a MSDOS partitionned harddrive. This tries to find a wbfs partition into the harddrive
   @param read_hdsector,write_hdsector: accessors to a harddrive
   @hd_sector_size: size of the hd sector. Can be set to zero if the partition in already initialized
   @num_hd_sector:  number of sectors in this disc. Can be set to zero if the partition in already initialized
   @reset: not implemented, This will format the whole harddrive with one wbfs partition that fits the whole disk.
   calls wbfs_error() to have textual meaning of errors
   @return NULL in case of error
*/
wbfs_t * wbfs_open_hd(rw_sector_callback_t read_hdsector,
						rw_sector_callback_t write_hdsector,
#ifdef WIN32
						close_callback_t close_hd,
#endif
						void *callback_data,
						int hd_sector_size, int num_hd_sector, int reset);

/*! @brief open a wbfs partition
   @param read_hdsector,write_hdsector: accessors to the partition
   @hd_sector_size: size of the hd sector. Can be set to zero if the partition in already initialized
   @num_hd_sector:  number of sectors in this partition. Can be set to zero if the partition in already initialized
   @partition_lba:  The partitio offset if you provided accessors to the whole disc.
   @reset: initialize the partition with an empty wbfs.
   calls wbfs_error() to have textual meaning of errors
   @return NULL in case of error
*/
wbfs_t*wbfs_open_partition(rw_sector_callback_t read_hdsector,
                           rw_sector_callback_t write_hdsector,
#ifdef WIN32
                           close_callback_t close_hd,
#endif
                           void *callback_data,
                           int hd_sector_size, int num_hd_sector, u32 partition_lba, int reset);

/*! @brief close a wbfs partition, and sync the metadatas to the disc */
void wbfs_close(wbfs_t*);

/*! @brief open a disc inside a wbfs partition use a 6 char discid+vendorid
  @return NULL if discid is not present
*/
wbfs_disc_t *wbfs_open_disc(wbfs_t* p, u8 *diskid);

/*! @brief close a already open disc inside a wbfs partition */
void wbfs_close_disc(wbfs_disc_t*d);

/*! @brief accessor to the wii disc
  @param d: a pointer to already open disc
  @param offset: an offset inside the disc, *points 32bit words*, allowing to access 16GB data
  @param len: The length of the data to fetch, in *bytes*
 */
// 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);

/*! @return the number of discs inside the paritition */
u32 wbfs_count_discs(wbfs_t*p);
/*! get the disc info of ith disc inside the partition. It correspond to the first 0x100 bytes of the wiidvd
  http://www.wiibrew.org/wiki/Wiidisc#Header
  @param i: index of the disc inside the partition
  @param header: pointer to 0x100 bytes to write the header
  @size: optional pointer to a 32bit word that will get the size in 32bit words of the DVD taken on the partition.
*/
u32 wbfs_get_disc_info(wbfs_t*p, u32 i,u8 *header,int header_size,u32 *size);

/*! get the number of used block of the partition.
  to be multiplied by p->wbfs_sec_sz (use 64bit multiplication) to have the number in bytes
*/
u32 wbfs_count_usedblocks(wbfs_t*p);

/******************* write access  ******************/

/*! add a wii dvd inside the partition
  @param read_src_wii_disc: a callback to access the wii dvd. offsets are in 32bit, len in bytes!
  @callback_data: private data passed to the callback
  @spinner: a pointer to a function that is regulary called to update a progress bar.
  @sel: selects which partitions to copy.
  @copy_1_1: makes a 1:1 copy, whenever a game would not use the wii disc format, and some data is hidden outside the filesystem.
#ifdef WIN32
	// It's a bit silly to fidef this... - g3power
  @new_name: different name for imported ISO. NULL to use default name from ISO header
#endif
 */
u32 wbfs_add_disc(wbfs_t*p,read_wiidisc_callback_t read_src_wii_disc,
					void *callback_data,
					progress_callback_t spinner,
					partition_selector_t sel,
					int copy_1_1
#ifdef WIN32
					, char *new_name
#endif
					);

u32 wbfs_estimate_disc(wbfs_t*p,read_wiidisc_callback_t read_src_wii_disc, void *callback_data,
                  partition_selector_t sel, u8* header);

/*! remove a wiidvd inside a partition */
u32 wbfs_rm_disc(wbfs_t*p, u8* discid);

/*! rename a wiidvd inside a partition */
u32 wbfs_ren_disc(wbfs_t*p, u8* discid, u8* newname);

/*! edit a wiidvd diskid */
u32 wbfs_nid_disc(wbfs_t*p, u8* discid, u8* newid);

/*! trim the file-system to its minimum size
  This allows to use wbfs as a wiidisc container
 */
u32 wbfs_trim(wbfs_t*p);

/*! extract a disc from the wbfs, unused sectors are just untouched, allowing descent filesystem to only really usefull space to store the disc.
Even if the filesize is 4.7GB, the disc usage will be less.
 */
u32 wbfs_extract_disc(wbfs_disc_t*d, rw_sector_callback_t write_dst_wii_sector,void *callback_data,progress_callback_t spinner);

/*! extract a file from the wii disc filesystem.
  E.G. Allows to extract the opening.bnr to install a game as a system menu channel
 */
u32 wbfs_extract_file(wbfs_disc_t*d, char *path);

// remove some sanity checks
void wbfs_set_force_mode(int force);

/* OS specific functions provided by libwbfs_<os>.c */

wbfs_t *wbfs_try_open(char *disk, char *partition, int reset);
wbfs_t *wbfs_try_open_partition(char *fn, int reset);

void *wbfs_open_file_for_read(char*filename);
void *wbfs_open_file_for_write(char*filename);
int wbfs_read_file(void*handle, int len, void *buf);
int wbfs_close_file(void *handle);
int wbfs_file_reserve_space(void*handle,long long size);
void wbfs_file_truncate(void *handle,long long size);
int wbfs_read_wii_file(void *handle, u32 offset, u32 count, void *buf);
int wbfs_write_wii_file(void *handle, u32 lba, u32 count, void *buf);

#ifdef __cplusplus
   }
#endif /* __cplusplus */

#endif

repo_name: CYBERDEViL/qwbfsmanager

license: unknown


code:
###########################################################################################
##      Created using Monkey Studio v1.8.4.0 (1.8.4.0)
##
##  Author    : Filipe Azevedo aka Nox P@sNox <pasnox@gmail.com>
##  Project   : libwbfs
##  FileName  : libwbfs.pri
##  Date      : 2010-04-04T11:04:05
##  License   : GPL2
##  Comment   : Creating using Monkey Studio RAD
##  Home Page   : https://github.com/pasnox/qwbfsmanager
##
###########################################################################################

include($$PWD/shared.pri)
LIBS    *= -L$${LIB_WBFS_BUILD_PATH}

CONFIG(debug, debug|release) {
    #Debug
    unix:LIBS   *= -lwbfs_debug
    else:LIBS   *= -lwbfsd
} else {
    #Release
    LIBS    *= -lwbfs
}

win32:LIBS  *= -lws2_32 -leay32
else:LIBS   *=  -lcrypto


code:
###########################################################################################
##      Created using Monkey Studio v1.8.4.0 (1.8.4.0)
##
##  Author    : Filipe Azevedo aka Nox P@sNox <pasnox@gmail.com>
##  Project   : libwbfs
##  FileName  : libwbfs.pro
##  Date      : 2010-04-04T11:04:05
##  License   : GPL2
##  Comment   : Creating using Monkey Studio RAD
##  Home Page   : https://github.com/pasnox/qwbfsmanager
##
###########################################################################################

TEMPLATE    = lib
LANGUAGE    = C++/Qt4
CONFIG  -= debug_and_release release debug warn_off warn_on
CONFIG  *= staticlib release warn_on
include( shared.pri )
TARGET  = $${LIB_WBFS_TARGET}
BUILD_PATH  = $${LIB_WBFS_BUILD_PATH}
DESTDIR = $${BUILD_PATH}
win32:DEFINES -= UNICODE

include( ../shared.pri )

INCLUDEPATH *= libwbfs

HEADERS *= tools.h \
    wiidisc.h \
    libwbfs_os.h \
    libwbfs.h

SOURCES *= tools.c \
    ec.c \
    bn.c \
    rijndael.c \
    wiidisc.c \
    libwbfs.c \
    libwbfs_unix.c \
    libwbfs_win32.c


code:
#ifndef LIBWBFS_OS_H
#define LIBWBFS_OS_H

// this file abstract the os integration
// libwbfs_glue.h for segher tools env.

// standard u8, u32 and co types, + fatal
#include "tools.h"
#include <stdio.h>

#ifdef WIN32
#define wbfs_fatal non_fatal
#define wbfs_error non_fatal
#define wbfs_warning non_fatal
#else
#define wbfs_fatal non_fatal
#define wbfs_error non_fatal
#define wbfs_warning non_fatal
#endif

#include <stdlib.h>
#define wbfs_malloc(x) malloc(x)
#define wbfs_free(x) free(x)

// alloc memory space suitable for disk io
#define wbfs_ioalloc(x) malloc(x)
#define wbfs_iofree(x) free(x)

#ifdef WIN32
#include <winsock2.h>
#else
#include <arpa/inet.h>
#endif

// endianess tools
#define wbfs_ntohl(x) ntohl(x)
#define wbfs_ntohs(x) ntohs(x)
#define wbfs_htonl(x) htonl(x)
#define wbfs_htons(x) htons(x)

#include <string.h>
#define wbfs_memcmp(x,y,z) memcmp(x,y,z)
#define wbfs_memcpy(x,y,z) memcpy(x,y,z)
#define wbfs_memset(x,y,z) memset(x,y,z)

#endif


code:
#if defined( __linux__) || defined(__APPLE__) || defined(__CYGWIN__)
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#if defined(__linux__)
#include <linux/fs.h>
#elif defined(__CYGWIN__)
#include <cygwin/fs.h>
#else
#include <sys/disk.h>
#endif
#include <fcntl.h>
#include <unistd.h>

#include "libwbfs.h"

void *wbfs_open_file_for_read(char*filename)
{
        FILE*f = fopen(filename,"r");
	if (!f)
		wbfs_fatal("unable to open file\n");
        return (void*)f;
}
void *wbfs_open_file_for_write(char*filename)
{
        FILE*f = fopen(filename,"w");
	if (!f)
		wbfs_fatal("unable to open file\n");
        return (void*)f;
}
int wbfs_read_file(void*handle, int len, void *buf)
{
        return fread(buf,len,1,(FILE*)handle);
}
int wbfs_close_file(void *handle)
{
        return fclose((FILE*)handle) == 0 ? 0 : 1;
}
int wbfs_file_reserve_space(void*handle, long long size)
{
        FILE*f=(FILE*)handle;
		int result;
        fseeko(f, size-1ULL, SEEK_SET);
        result = fwrite("", 1, 1, f);
		return result == 1 ? 0 : 1;
}
void wbfs_file_truncate(void *handle,long long size)
{
        ftruncate(fileno((FILE*)handle),size);
}
int wbfs_read_wii_file(void*handle,u32 offset,u32 count,void*iobuf)
{
	FILE*fp =handle;
	u64 off = offset;
	off<<=2;

	if (fseeko(fp, off, SEEK_SET))
	{
		wbfs_error("error seeking in disc file (%ld)",off);
                return 1;
        }
        if (fread(iobuf, count, 1, fp) != 1){
                wbfs_error("error reading disc 2");
                return 1;
	}
	return 0;
}

int wbfs_write_wii_file(void*handle,u32 lba,u32 count,void*iobuf)
{
	FILE*fp=handle;
	u64 off = lba;
	off *=0x8000;

	if (fseeko(fp, off, SEEK_SET))
        {
		wbfs_error("error seeking in disc file (%lld)",off);
                return 1;
        }
        if (fwrite(iobuf, count*0x8000, 1, fp) != 1){
                wbfs_error("error writing disc file");
                return 1;
        }
        return 0;
}

static int wbfs_fread_sector(void *_fp,u32 lba,u32 count,void*buf)
{
	FILE*fp =_fp;
	u64 off = lba;
	off*=512ULL;
	if (fseeko(fp, off, SEEK_SET))
	{
		fprintf(stderr,"\n\n%lld %d %p\n",off,count,_fp);
		wbfs_error("error seeking in disc partition");
		return 1;
	}
	if (fread(buf, count*512ULL, 1, fp) != 1){
		wbfs_error("error reading disc 1");
		return 1;
	}
	return 0;

}
static int wbfs_fwrite_sector(void *_fp,u32 lba,u32 count,void*buf)
{
	FILE*fp =_fp;
	u64 off = lba;
	off*=512ULL;
	if (fseeko(fp, off, SEEK_SET))
	{
		wbfs_error("error seeking in disc file");
		return 1;
	}
	if (fwrite(buf, count*512ULL, 1, fp) != 1){
		wbfs_error("error writing disc");
		return 1;
	}
	return 0;

}
static int get_capacity(char *file,u32 *sector_size,u32 *n_sector)
{
	int fd = open(file,O_RDONLY);
	int ret;
	if(fd<0){
		return 0;
	}
#if defined(__linux__) || defined(__CYGWIN__)
        if (sizeof(void *) == 8) {
          unsigned long long sec_size;
          ret = ioctl(fd,BLKSSZGET,&sec_size);
          *sector_size = (unsigned int) sec_size;
        } else
          ret = ioctl(fd,BLKSSZGET,sector_size);
#else //__APPLE__
	ret = ioctl(fd,DKIOCGETBLOCKSIZE,sector_size);
#endif
	if(ret<0)
	{
		FILE *f;
		close(fd);
		f = fopen(file,"r");
		fseeko(f,0,SEEK_END);
		*n_sector = ftello(f)/512;
		*sector_size = 512;
		fclose(f);
		return 1;
	}
#if defined(__linux__) || defined(__CYGWIN__)
        if (sizeof(void *) == 8) {
          unsigned long long n_sec;
          ret = ioctl(fd,BLKGETSIZE,&n_sec);
          *n_sector = (unsigned int) n_sec;
        } else
          ret = ioctl(fd,BLKGETSIZE,n_sector);
#else //__APPLE__
	long long my_n_sector;
	ret = ioctl(fd,DKIOCGETBLOCKCOUNT,&my_n_sector);
	*n_sector = (long)my_n_sector;
#endif
	close(fd);
	if(*sector_size>512)
		*n_sector*=*sector_size/512;
	if(*sector_size<512)
		*n_sector/=512/ *sector_size;
	return 1;
}
wbfs_t *wbfs_try_open_hd(char *fn,int reset)
{
	u32 sector_size, n_sector;
	if(!get_capacity(fn,&sector_size,&n_sector))
		return NULL;
	FILE *f = fopen(fn,"r+");
	if (!f)
		return NULL;
	return wbfs_open_hd(wbfs_fread_sector,wbfs_fwrite_sector,f,
			    sector_size ,n_sector,reset);
}
wbfs_t *wbfs_try_open_partition(char *fn,int reset)
{
	u32 sector_size, n_sector;
	if(!get_capacity(fn,&sector_size,&n_sector))
		return NULL;
	FILE *f = fopen(fn,"r+");
	if (!f)
		return NULL;
	return wbfs_open_partition(wbfs_fread_sector,wbfs_fwrite_sector,f,
				   sector_size ,n_sector,0,reset);
}
wbfs_t *wbfs_try_open(char *disc,char *partition, int reset)
{
	wbfs_t *p = 0;
	if(partition)
		p = wbfs_try_open_partition(partition,reset);
	if (!p && !reset && disc)
		p = wbfs_try_open_hd(disc,0);
	else if(!p && !reset){
		char buffer[32];
		int i;
#if defined(__linux__) || defined(__CYGWIN__)
		for (i='b';i<'z';i++)
		{
			snprintf(buffer,32,"/dev/sd%c",i);
			p = wbfs_try_open_hd(buffer,0);
			if (p)
			{
				fprintf(stderr,"using %s\n",buffer);
				return p;
			}
			snprintf(buffer,32,"/dev/hd%c",i);
			p = wbfs_try_open_hd(buffer,0);
			if (p)
			{
				fprintf(stderr,"using %s\n",buffer);
				return p;
			}
		}
#else
		int j;
		for (i=0;i<10;i++)
			for (j=0;j<10;j++){
				snprintf(buffer,32,"/dev/disk%ds%d",i,j);
				p = wbfs_try_open_partition(buffer,0);
				if (p)
				{
					fprintf(stderr,"using %s\n",buffer);
					return p;
				}
			}
#endif
		wbfs_error("cannot find any wbfs partition (verify permissions))");
	}
	return p;

}

#endif //__linux__ or __APPLE__


code:
#ifdef WIN32

#include <windows.h>
#include <setupapi.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <winioctl.h>

#include "libwbfs.h"

void *wbfs_open_file_for_read(char*filename)
{
	HANDLE *handle = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);

	if (handle == INVALID_HANDLE_VALUE)
	{
		fprintf(stderr, "unable to open disc file\n");
		return 0;
	}
	return (void*)handle;
}

void *wbfs_open_file_for_write(char*filename)
{
	HANDLE *handle = CreateFile(filename, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
	if (handle == INVALID_HANDLE_VALUE)
	{
		fprintf(stderr, "unable to open file\n");
		return 0;
	}
	return (void*)handle;
}

int wbfs_read_file(void*handle, int len, void *buf)
{
	DWORD read;
	ReadFile((HANDLE)handle, buf, len, &read, NULL);
	return read;
}

int wbfs_close_file(void *handle)
{
	return CloseHandle((HANDLE)handle) == 0 ? 1 : 0;
}

int wbfs_file_reserve_space(void*handle,long long size)
{
	int result;
	LARGE_INTEGER large;
	large.QuadPart = size;
	result = SetFilePointerEx((HANDLE)handle, large, NULL, FILE_BEGIN);
	if ( result != 0 ) {
		result = SetEndOfFile((HANDLE)handle);
	}
	return result == 0 ? 1 : 0;
}

void wbfs_file_truncate(void *handle,long long size)
{
	_chsize(fileno((FILE*)handle),size);
}

int wbfs_read_wii_file(void *_handle, u32 _offset, u32 count, void *buf)
{
	HANDLE *handle = (HANDLE *)_handle;
	LARGE_INTEGER large;
	DWORD read;
	u64 offset = _offset;

	offset <<= 2;
	large.QuadPart = offset;

	if (SetFilePointerEx(handle, large, NULL, FILE_BEGIN) == FALSE)
	{
		wbfs_error("error seeking in disc file");
		return 1;
	}

	read = 0;
	if ((ReadFile(handle, buf, count, &read, NULL) == FALSE) || !read)
	{
		wbfs_error("error reading wii disc sector");
		return 1;
	}

	if (read < count)
	{
		wbfs_warning("warning: requested %d, but read only %d bytes (trimmed or bad padded ISO)", count, read);
		wbfs_memset((u8*)buf+read, 0, count-read);
	}

	return 0;
}

int wbfs_write_wii_file(void *_handle, u32 lba, u32 count, void *buf)
{
	HANDLE *handle = (HANDLE *)_handle;
	LARGE_INTEGER large;
	DWORD written;
	u64 offset = lba;

	offset *= 0x8000;
	large.QuadPart = offset;

	if (SetFilePointerEx(handle, large, NULL, FILE_BEGIN) == FALSE)
	{
		fprintf(stderr,"\n\n%lld %p\n", offset, handle);
		wbfs_error("error seeking in wii disc sector (write)");
		return 1;
	}

	written = 0;
	if (WriteFile(handle, buf, count * 0x8000, &written, NULL) == FALSE)
	{
		wbfs_error("error writing wii disc sector");
		return 1;
	}

	if (written != count * 0x8000)
	{
		wbfs_error("error writing wii disc sector (size mismatch)");
		return 1;
	}

	return 0;
}

static int read_sector(void *_handle, u32 lba, u32 count, void *buf)
{
	HANDLE *handle = (HANDLE *)_handle;
	LARGE_INTEGER large;
	DWORD read;
	u64 offset = lba;

	offset *= 512ULL;
	large.QuadPart = offset;

	if (SetFilePointerEx(handle, large, NULL, FILE_BEGIN) == FALSE)
	{
		fprintf(stderr, "\n\n%lld %d %p\n", offset, count, _handle);
		wbfs_error("error seeking in hd sector (read)");
		return 1;
	}

	read = 0;
	if (ReadFile(handle, buf, count * 512ULL, &read, NULL) == FALSE)
	{
		wbfs_error("error reading hd sector");
		return 1;
	}

	return 0;
}

static int write_sector(void *_handle, u32 lba, u32 count, void *buf)
{
	HANDLE *handle = (HANDLE *)_handle;
	LARGE_INTEGER large;
	DWORD written;
	u64 offset = lba;

	offset *= 512ULL;
	large.QuadPart = offset;

	if (SetFilePointerEx(handle, large, NULL, FILE_BEGIN) == FALSE)
	{
		wbfs_error("error seeking in hd sector (write)");
		return 1;
	}

	written = 0;
	if (WriteFile(handle, buf, count * 512ULL, &written, NULL) == FALSE)
	{
		wbfs_error("error writing hd sector");
		return 1;
	}

	return 0;

}

static void close_handle(void *handle)
{
	CloseHandle((HANDLE *)handle);
}

static int get_capacity(char *fileName, u32 *sector_size, u32 *sector_count)
{
	DISK_GEOMETRY dg;
	PARTITION_INFORMATION pi;

	DWORD bytes;
	HANDLE *handle = CreateFile(fileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);

	if (handle == INVALID_HANDLE_VALUE)
	{
		wbfs_error("could not open drive");
		return 0;
	}

	if (DeviceIoControl(handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &dg, sizeof(DISK_GEOMETRY), &bytes, NULL) == FALSE)
	{
		CloseHandle(handle);
		wbfs_error("could not get drive geometry");
		return 0;
	}

	*sector_size = dg.BytesPerSector;

	if (DeviceIoControl(handle, IOCTL_DISK_GET_PARTITION_INFO, NULL, 0, &pi, sizeof(PARTITION_INFORMATION), &bytes, NULL) == FALSE)
	{
		CloseHandle(handle);
		wbfs_error("could not get partition info");
		return 0;
	}

	*sector_count = (u32)(pi.PartitionLength.QuadPart / dg.BytesPerSector);

	CloseHandle(handle);
	return 1;
}

wbfs_t *wbfs_try_open_hd(char *driveName, int reset)
{
	wbfs_error("no direct harddrive support");
	return 0;
}

wbfs_t *wbfs_try_open_partition(char *partitionLetter, int reset)
{
	HANDLE *handle;
	char drivePath[8] = "\\\\?\\Z:";

	u32 sector_size, sector_count;

	if (strlen(partitionLetter) != 1)
	{
		wbfs_error("bad drive name");
		return NULL;
	}

	drivePath[4] = partitionLetter[0];

	if (!get_capacity(drivePath, &sector_size, &sector_count))
	{
		return NULL;
	}

	handle = CreateFile(drivePath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL);

	if (handle == INVALID_HANDLE_VALUE)
	{
		return NULL;
	}

	return wbfs_open_partition(read_sector, write_sector, close_handle, handle, sector_size, sector_count, 0, reset);
}

wbfs_t *wbfs_try_open(char *disc, char *partition, int reset)
{
	wbfs_t *p = 0;

	if (partition)
	{
		p = wbfs_try_open_partition(partition,reset);
	}

	if (!p && !reset && disc)
	{
		p = 0;
	}
	else if(!p && !reset)
	{
		p = 0;
	}

	return p;
}

#endif


code:
/* Rijndael Block Cipher - rijndael.c

   Written by Mike Scott 21st April 1999
   mike@compapp.dcu.ie

   Permission for free direct or derivative use is granted subject
   to compliance with any conditions that the originators of the
   algorithm place on its exploitation.

*/

#include <stdio.h>
#include <string.h>

#ifdef WIN32
#include <tools.h>
#else
#define u8 unsigned char       /* 8 bits  */
#define u32 unsigned long       /* 32 bits */
#define u64 unsigned long long
#endif

/* rotates x one bit to the left */

#define ROTL(x) (((x)>>7)|((x)<<1))

/* Rotates 32-bit word left by 1, 2 or 3 byte  */

#define ROTL8(x) (((x)<<8)|((x)>>24))
#define ROTL16(x) (((x)<<16)|((x)>>16))
#define ROTL24(x) (((x)<<24)|((x)>>8))

/* Fixed Data */

static u8 InCo[4]={0xB,0xD,0x9,0xE};  /* Inverse Coefficients */

static u8 fbsub[256];
static u8 rbsub[256];
static u8 ptab[256],ltab[256];
static u32 ftable[256];
static u32 rtable[256];
static u32 rco[30];

/* Parameter-dependent data */

int Nk,Nb,Nr;
u8 fi[24],ri[24];
u32 fkey[120];
u32 rkey[120];

static u32 pack(u8 *b)
{ /* pack bytes into a 32-bit Word */
    return ((u32)b[3]<<24)|((u32)b[2]<<16)|((u32)b[1]<<8)|(u32)b[0];
}

static void unpack(u32 a,u8 *b)
{ /* unpack bytes from a word */
    b[0]=(u8)a;
    b[1]=(u8)(a>>8);
    b[2]=(u8)(a>>16);
    b[3]=(u8)(a>>24);
}

static u8 xtime(u8 a)
{
    u8 b;
    if (a&0x80) b=0x1B;
    else        b=0;
    a<<=1;
    a^=b;
    return a;
}

static u8 bmul(u8 x,u8 y)
{ /* x.y= AntiLog(Log(x) + Log(y)) */
    if (x && y) return ptab[(ltab[x]+ltab[y])%255];
    else return 0;
}

static u32 SubByte(u32 a)
{
    u8 b[4];
    unpack(a,b);
    b[0]=fbsub[b[0]];
    b[1]=fbsub[b[1]];
    b[2]=fbsub[b[2]];
    b[3]=fbsub[b[3]];
    return pack(b);
}

static u8 product(u32 x,u32 y)
{ /* dot product of two 4-byte arrays */
    u8 xb[4],yb[4];
    unpack(x,xb);
    unpack(y,yb);
    return bmul(xb[0],yb[0])^bmul(xb[1],yb[1])^bmul(xb[2],yb[2])^bmul(xb[3],yb[3]);
}

static u32 InvMixCol(u32 x)
{ /* matrix Multiplication */
    u32 y,m;
    u8 b[4];

    m=pack(InCo);
    b[3]=product(m,x);
    m=ROTL24(m);
    b[2]=product(m,x);
    m=ROTL24(m);
    b[1]=product(m,x);
    m=ROTL24(m);
    b[0]=product(m,x);
    y=pack(b);
    return y;
}

u8 ByteSub(u8 x)
{
    u8 y=ptab[255-ltab[x]];  /* multiplicative inverse */
    x=y;  x=ROTL(x);
    y^=x; x=ROTL(x);
    y^=x; x=ROTL(x);
    y^=x; x=ROTL(x);
    y^=x; y^=0x63;
    return y;
}

void gentables(void)
{ /* generate tables */
    int i;
    u8 y,b[4];

  /* use 3 as primitive root to generate power and log tables */

    ltab[0]=0;
    ptab[0]=1;  ltab[1]=0;
    ptab[1]=3;  ltab[3]=1;
    for (i=2;i<256;i++)
    {
        ptab[i]=ptab[i-1]^xtime(ptab[i-1]);
        ltab[ptab[i]]=i;
    }

  /* affine transformation:- each bit is xored with itself shifted one bit */

    fbsub[0]=0x63;
    rbsub[0x63]=0;
    for (i=1;i<256;i++)
    {
        y=ByteSub((u8)i);
        fbsub[i]=y; rbsub[y]=i;
    }

    for (i=0,y=1;i<30;i++)
    {
        rco[i]=y;
        y=xtime(y);
    }

  /* calculate forward and reverse tables */
    for (i=0;i<256;i++)
    {
        y=fbsub[i];
        b[3]=y^xtime(y); b[2]=y;
        b[1]=y;          b[0]=xtime(y);
        ftable[i]=pack(b);

        y=rbsub[i];
        b[3]=bmul(InCo[0],y); b[2]=bmul(InCo[1],y);
        b[1]=bmul(InCo[2],y); b[0]=bmul(InCo[3],y);
        rtable[i]=pack(b);
    }
}

void gkey(int nb,int nk,char *key)
{ /* blocksize=32*nb bits. Key=32*nk bits */
  /* currently nb,bk = 4, 6 or 8          */
  /* key comes as 4*Nk bytes              */
  /* Key Scheduler. Create expanded encryption key */
    int i,j,k,m,N;
    int C1,C2,C3;
    u32 CipherKey[8];

    Nb=nb; Nk=nk;

  /* Nr is number of rounds */
    if (Nb>=Nk) Nr=6+Nb;
    else        Nr=6+Nk;

    C1=1;
    if (Nb<8) { C2=2; C3=3; }
    else      { C2=3; C3=4; }

  /* pre-calculate forward and reverse increments */
    for (m=j=0;j<nb;j++,m+=3)
    {
        fi[m]=(j+C1)%nb;
        fi[m+1]=(j+C2)%nb;
        fi[m+2]=(j+C3)%nb;
        ri[m]=(nb+j-C1)%nb;
        ri[m+1]=(nb+j-C2)%nb;
        ri[m+2]=(nb+j-C3)%nb;
    }

    N=Nb*(Nr+1);

    for (i=j=0;i<Nk;i++,j+=4)
    {
        CipherKey[i]=pack((u8 *)&key[j]);
    }
    for (i=0;i<Nk;i++) fkey[i]=CipherKey[i];
    for (j=Nk,k=0;j<N;j+=Nk,k++)
    {
        fkey[j]=fkey[j-Nk]^SubByte(ROTL24(fkey[j-1]))^rco[k];
        if (Nk<=6)
        {
            for (i=1;i<Nk && (i+j)<N;i++)
                fkey[i+j]=fkey[i+j-Nk]^fkey[i+j-1];
        }
        else
        {
            for (i=1;i<4 &&(i+j)<N;i++)
                fkey[i+j]=fkey[i+j-Nk]^fkey[i+j-1];
            if ((j+4)<N) fkey[j+4]=fkey[j+4-Nk]^SubByte(fkey[j+3]);
            for (i=5;i<Nk && (i+j)<N;i++)
                fkey[i+j]=fkey[i+j-Nk]^fkey[i+j-1];
        }

    }

 /* now for the expanded decrypt key in reverse order */

    for (j=0;j<Nb;j++) rkey[j+N-Nb]=fkey[j];
    for (i=Nb;i<N-Nb;i+=Nb)
    {
        k=N-Nb-i;
        for (j=0;j<Nb;j++) rkey[k+j]=InvMixCol(fkey[i+j]);
    }
    for (j=N-Nb;j<N;j++) rkey[j-N+Nb]=fkey[j];
}

/* There is an obvious time/space trade-off possible here.     *
 * Instead of just one ftable[], I could have 4, the other     *
 * 3 pre-rotated to save the ROTL8, ROTL16 and ROTL24 overhead */

void encrypt(char *buff)
{
    int i,j,k,m;
    u32 a[8],b[8],*x,*y,*t;

    for (i=j=0;i<Nb;i++,j+=4)
    {
        a[i]=pack((u8 *)&buff[j]);
        a[i]^=fkey[i];
    }
    k=Nb;
    x=a; y=b;

/* State alternates between a and b */
    for (i=1;i<Nr;i++)
    { /* Nr is number of rounds. May be odd. */

/* if Nb is fixed - unroll this next
   loop and hard-code in the values of fi[]  */

        for (m=j=0;j<Nb;j++,m+=3)
        { /* deal with each 32-bit element of the State */
          /* This is the time-critical bit */
            y[j]=fkey[k++]^ftable[(u8)x[j]]^
                 ROTL8(ftable[(u8)(x[fi[m]]>>8)])^
                 ROTL16(ftable[(u8)(x[fi[m+1]]>>16)])^
                 ROTL24(ftable[(u8)(x[fi[m+2]]>>24)]);
        }
        t=x; x=y; y=t;      /* swap pointers */
    }

/* Last Round - unroll if possible */
    for (m=j=0;j<Nb;j++,m+=3)
    {
        y[j]=fkey[k++]^(u32)fbsub[(u8)x[j]]^
             ROTL8((u32)fbsub[(u8)(x[fi[m]]>>8)])^
             ROTL16((u32)fbsub[(u8)(x[fi[m+1]]>>16)])^
             ROTL24((u32)fbsub[(u8)(x[fi[m+2]]>>24)]);
    }
    for (i=j=0;i<Nb;i++,j+=4)
    {
        unpack(y[i],(u8 *)&buff[j]);
        x[i]=y[i]=0;   /* clean up stack */
    }
    return;
}

void decrypt(char *buff)
{
    int i,j,k,m;
    u32 a[8],b[8],*x,*y,*t;

    for (i=j=0;i<Nb;i++,j+=4)
    {
        a[i]=pack((u8 *)&buff[j]);
        a[i]^=rkey[i];
    }
    k=Nb;
    x=a; y=b;

/* State alternates between a and b */
    for (i=1;i<Nr;i++)
    { /* Nr is number of rounds. May be odd. */

/* if Nb is fixed - unroll this next
   loop and hard-code in the values of ri[]  */

        for (m=j=0;j<Nb;j++,m+=3)
        { /* This is the time-critical bit */
            y[j]=rkey[k++]^rtable[(u8)x[j]]^
                 ROTL8(rtable[(u8)(x[ri[m]]>>8)])^
                 ROTL16(rtable[(u8)(x[ri[m+1]]>>16)])^
                 ROTL24(rtable[(u8)(x[ri[m+2]]>>24)]);
        }
        t=x; x=y; y=t;      /* swap pointers */
    }

/* Last Round - unroll if possible */
    for (m=j=0;j<Nb;j++,m+=3)
    {
        y[j]=rkey[k++]^(u32)rbsub[(u8)x[j]]^
             ROTL8((u32)rbsub[(u8)(x[ri[m]]>>8)])^
             ROTL16((u32)rbsub[(u8)(x[ri[m+1]]>>16)])^
             ROTL24((u32)rbsub[(u8)(x[ri[m+2]]>>24)]);
    }
    for (i=j=0;i<Nb;i++,j+=4)
    {
        unpack(y[i],(u8 *)&buff[j]);
        x[i]=y[i]=0;   /* clean up stack */
    }
    return;
}

void aes_set_key(u8 *key) {
  gentables();
  gkey(4, 4,(char*) key);
}

// CBC mode decryption
void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len) {
  u8 block[16];
  u8 *ctext_ptr;
  unsigned int blockno = 0, i;

  //printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len);

  for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) {
    unsigned int fraction;
    if (blockno == (len / sizeof(block))) { // last block
      fraction = len % sizeof(block);
      if (fraction == 0) break;
      memset(block, 0, sizeof(block));
    } else fraction = 16;

    //    debug_printf("block %d: fraction = %d\n", blockno, fraction);
    memcpy(block, inbuf + blockno * sizeof(block), fraction);
    decrypt((char*)block);

    if (blockno == 0) {
    	ctext_ptr = iv;
    }
    else {
    	ctext_ptr = inbuf + (blockno-1) * sizeof(block);
    }

    for(i=0; i < fraction; i++)
      outbuf[blockno * sizeof(block) + i] =
	ctext_ptr[i] ^ block[i];
    //    debug_printf("Block %d output: ", blockno);
    //    hexdump(outbuf + blockno*sizeof(block), 16);
  }
}

// CBC mode encryption
void aes_encrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len) {
  u8 block[16];
  unsigned int blockno = 0, i;

  //  debug_printf("aes_decrypt(%p, %p, %p, %lld)\n", iv, inbuf, outbuf, len);

  for (blockno = 0; blockno <= (len / sizeof(block)); blockno++) {
    unsigned int fraction;
    if (blockno == (len / sizeof(block))) { // last block
      fraction = len % sizeof(block);
      if (fraction == 0) break;
      memset(block, 0, sizeof(block));
    } else fraction = 16;

    //    debug_printf("block %d: fraction = %d\n", blockno, fraction);
    memcpy(block, inbuf + blockno * sizeof(block), fraction);

    for(i=0; i < fraction; i++)
      block[i] = inbuf[blockno * sizeof(block) + i] ^ iv[i];

    encrypt((char*)block);
    memcpy(iv, block, sizeof(block));
    memcpy(outbuf + blockno * sizeof(block), block, sizeof(block));
    //    debug_printf("Block %d output: ", blockno);
    //    hexdump(outbuf + blockno*sizeof(block), 16);
  }
}


code:
###########################################################################################
##      Created using Monkey Studio v1.8.4.0 (1.8.4.0)
##
##  Author    : Filipe Azevedo aka Nox P@sNox <pasnox@gmail.com>
##  Project   : libwbfs
##  FileName  : shared.pri
##  Date      : 2010-04-04T11:04:05
##  License   : GPL2
##  Comment   : Creating using Monkey Studio RAD
##  Home Page   : https://github.com/pasnox/qwbfsmanager
##
###########################################################################################

DEFINES *= LARGE_FILES _FILE_OFFSET_BITS=64
LIB_WBFS_TARGET = $$quote( wbfs )
LIB_WBFS_PWD    = $$PWD
LIB_WBFS_BUILD_PATH = ../build

INCLUDEPATH *= $${LIB_WBFS_PWD}
DEPENDPATH  *= $${LIB_WBFS_PWD}

win32 {
    OS  = $$lower( $$QMAKE_HOST.os )
    #OS=$$system( "uname -s" )

    OPENSSL_INSTALL_DIR = D:/Tools/OpenSSL
    isEqual( OS, "darwin" ):OPENSSL_INSTALL_DIR = $(HOME)/Win32Libraries
    else:isEqual( OS, "linux" ):OPENSSL_INSTALL_DIR = $(HOME)/.wine/drive_c/Development/OpenSSL

    *-g++*:LIBS *= -L$${OPENSSL_INSTALL_DIR}/lib -L$${OPENSSL_INSTALL_DIR}/lib/MinGW
    *-msvc*:LIBS    *= -L$${OPENSSL_INSTALL_DIR}/lib -L$${OPENSSL_INSTALL_DIR}/lib/VC
    INCLUDEPATH *= $${OPENSSL_INSTALL_DIR}/include
} else:mac {
    # Mac Brew openssl
    INCLUDEPATH *= /usr/local/Cellar/openssl/1.0.2f/include
}


code:
// Copyright 2007,2008  Segher Boessenkool  <segher@kernel.crashing.org>
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

#include "tools.h"

#include <stddef.h>	// to accommodate certain broken versions of openssl
#include <openssl/md5.h>
#include <openssl/aes.h>
#include <openssl/sha.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

#ifdef WIN32
#define snprintf _snprintf
#endif

//
// basic data types
//

u16 be16(const u8 *p)
{
	return (p[0] << 8) | p[1];
}

u32 be32(const u8 *p)
{
	return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}

u64 be64(const u8 *p)
{
	return ((u64)be32(p) << 32) | be32(p + 4);
}

u64 be34(const u8 *p)
{
	return 4 * (u64)be32(p);
}

void wbe16(u8 *p, u16 x)
{
	p[0] = (u8)(x >> 8);
	p[1] = (u8)(x);
}

void wbe32(u8 *p, u32 x)
{
	wbe16(p + 0, (u16)(x >> 16));
	wbe16(p + 2, (u16)(x));
}

void wbe64(u8 *p, u64 x)
{
	wbe32(p + 0, (u32)(x >> 32));
	wbe32(p + 4, (u32)(x));
}

//
// crypto
//

void md5(u8 *data, u32 len, u8 *hash)
{
	MD5(data, len, hash);
}

void sha(u8 *data, u32 len, u8 *hash)
{
	SHA1(data, len, hash);
}

void get_key(const char *name, u8 *key, u32 len)
{
	char path[256];
#ifndef WIN32
	char *home;
#endif
	FILE *fp;

#ifdef WIN32
	snprintf(path, sizeof path, "wii/%s", name);
#else
	home = getenv("HOME");
	if (home == 0)
		fatal("cannot find HOME");
	snprintf(path, sizeof path, "%s/.wii/%s", home, name);
#endif

	fp = fopen(path, "rb");
	if (fp == 0)
		fatal("cannot open %s", name);
	if (fread(key, len, 1, fp) != 1)
		fatal("error reading %s", name);
	fclose(fp);
}

void aes_cbc_dec(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out)
{
	AES_KEY aes_key;

	AES_set_decrypt_key(key, 128, &aes_key);
	AES_cbc_encrypt(in, out, len, &aes_key, iv, AES_DECRYPT);
}

void aes_cbc_enc(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out)
{
	AES_KEY aes_key;

	AES_set_encrypt_key(key, 128, &aes_key);
	AES_cbc_encrypt(in, out, len, &aes_key, iv, AES_ENCRYPT);
}

void decrypt_title_key(u8 *tik, u8 *title_key)
{
	u8 common_key[16];
	u8 iv[16];

	get_key("common-key", common_key, 16);

	memset(iv, 0, sizeof iv);
	memcpy(iv, tik + 0x01dc, 8);
	aes_cbc_dec(common_key, iv, tik + 0x01bf, 16, title_key);
        printf("title key: %02x %02x %02x\n",title_key[0],title_key[1],title_key[2]);
}

static u8 root_key[0x204];
static u8 *get_root_key(void)
{
	//get_key("root-key", root_key, sizeof root_key);
	return root_key;
}

static u32 get_sig_len(u8 *sig)
{
	u32 type;

	type = be32(sig);
	switch (type - 0x10000) {
	case 0:
		return 0x240;

	case 1:
		return 0x140;

	case 2:
		return 0x80;
	}

	fprintf(stderr, "get_sig_len(): unhandled sig type %08x\n", type);
	return 0;
}

static u32 get_sub_len(u8 *sub)
{
	u32 type;

	type = be32(sub + 0x40);
	switch (type) {
	case 0:
		return 0x2c0;

	case 1:
		return 0x1c0;

	case 2:
		return 0x100;
	}

	fprintf(stderr, "get_sub_len(): unhandled sub type %08x\n", type);
	return 0;
}

int check_ec(u8 *ng, u8 *ap, u8 *sig, u8 *sig_hash)
{
	u8 ap_hash[20];
	u8 *ng_Q, *ap_R, *ap_S;
	u8 *ap_Q, *sig_R, *sig_S;

	ng_Q = ng + 0x0108;
	ap_R = ap + 0x04;
	ap_S = ap + 0x22;

	SHA1(ap + 0x80, 0x100, ap_hash);

	ap_Q = ap + 0x0108;
	sig_R = sig;
	sig_S = sig + 30;

	return check_ecdsa(ng_Q, ap_R, ap_S, ap_hash)
	       && check_ecdsa(ap_Q, sig_R, sig_S, sig_hash);
}

static int check_rsa(u8 *h, u8 *sig, u8 *key, u32 n)
{
	u8 correct[0x200];
	u8 x[0x200];
	static const u8 ber[16] = "\x00\x30\x21\x30\x09\x06\x05\x2b"
	                          "\x0e\x03\x02\x1a\x05\x00\x04\x14";

//fprintf(stderr, "n = %x\n", n);
//fprintf(stderr, "key:\n");
//hexdump(key, n);
//fprintf(stderr, "sig:\n");
//hexdump(sig, n);

	correct[0] = 0;
	correct[1] = 1;
	memset(correct + 2, 0xff, n - 38);
	memcpy(correct + n - 36, ber, 16);
	memcpy(correct + n - 20, h, 20);
//fprintf(stderr, "correct is:\n");
//hexdump(correct, n);

	bn_exp(x, sig, key, n, key + n, 4);
//fprintf(stderr, "x is:\n");
//hexdump(x, n);

	if (memcmp(correct, x, n) == 0)
		return 0;

	return -5;
}

static int check_hash(u8 *h, u8 *sig, u8 *key)
{
	u32 type;

	type = be32(sig) - 0x10000;
	if (type != be32(key + 0x40))
		return -6;

	switch (type) {
	case 1:
		return check_rsa(h, sig + 4, key + 0x88, 0x100);
	}

	return -7;
}

static u8 *find_cert_in_chain(u8 *sub, u8 *cert, u32 cert_len)
{
	char parent[64];
	char *child;
	u32 sig_len, sub_len;
	u8 *p;
	u8 *issuer;

	strncpy(parent, (char*)sub, sizeof parent);
	parent[sizeof parent - 1] = 0;
	child = strrchr(parent, '-');
	if (child)
		*child++ = 0;
	else {
		*parent = 0;
		child = (char*)sub;
	}

	for (p = cert; p < cert + cert_len; p += sig_len + sub_len) {
		sig_len = get_sig_len(p);
		if (sig_len == 0)
			return 0;
		issuer = p + sig_len;
		sub_len = get_sub_len(issuer);
		if (sub_len == 0)
			return 0;

		if (strcmp(parent, (char*)issuer) == 0
		    && strcmp(child, (char*)issuer + 0x44) == 0)
			return p;
	}

	return 0;
}

int check_cert_chain(u8 *data, u32 data_len, u8 *cert, u32 cert_len)
{
	u8 *sig;
	u8 *sub;
	u32 sig_len;
	u32 sub_len;
	u8 h[20];
	u8 *key_cert;
	u8 *key;
	int ret;
	sig = data;
	sig_len = get_sig_len(sig);
	if (sig_len == 0)
		return -1;
	sub = data + sig_len;
	sub_len = data_len - sig_len;
	if (sub_len == 0)
		return -2;

	for (;;) {
                fprintf(stderr, ">>>>>> checking sig by %s...\n", sub);
                if (strcmp((char*)sub, "Root") == 0) {
			key = get_root_key();
			sha(sub, sub_len, h);
			if (be32(sig) != 0x10000)
				return -8;
			return check_rsa(h, sig + 4, key, 0x200);
		}

		key_cert = find_cert_in_chain(sub, cert, cert_len);
		if (key_cert == 0)
			return -3;

		key = key_cert + get_sig_len(key_cert);

		sha(sub, sub_len, h);
		ret = check_hash(h, sig, key);
		if (ret)
			return ret;

		sig = key_cert;
		sig_len = get_sig_len(sig);
		if (sig_len == 0)
			return -4;
		sub = sig + sig_len;
		sub_len = get_sub_len(sub);
		if (sub_len == 0)
			return -5;
	}
}

//
// compression
//

void do_yaz0(u8 *in, u32 in_size, u8 *out, u32 out_size)
{
	u32 nout;
	u8 bits;
	u32 nbits;
	u32 n, d, i;
#ifdef UNNECESSARY_STUFF
    in_size++;
#endif
	bits = 0;
	nbits = 0;
	in += 0x10;
	for (nout = 0; nout < out_size; ) {
		if (nbits == 0) {
			bits = *in++;
			nbits = 8;
		}

		if ((bits & 0x80) != 0) {
			*out++ = *in++;
			nout++;
		} else {
			n = *in++;
			d = *in++;
			d |= (n << 8) & 0xf00;
			n >>= 4;
			if (n == 0)
				n = 0x10 + *in++;
			n += 2;
			d++;

			for (i = 0; i < n; i++) {
				*out = *(out - d);
				out++;
			}
			nout += n;
		}

		nbits--;
		bits <<= 1;
	};
}

//
// error handling
//

void fatal(const char *s, ...)
{
	char message[256];
	va_list ap;

	va_start(ap, s);
	vsnprintf(message, sizeof message, s, ap);

	perror(message);

	exit(1);
}

void non_fatal(const char *s, ...)
{
	char message[256];
	va_list ap;

	va_start(ap, s);
	vsnprintf(message, sizeof message, s, ap);

	perror(message);
}

//
// output formatting
//

void print_bytes(u8 *x, u32 n)
{
	u32 i;

	for (i = 0; i < n; i++)
		fprintf(stderr, "%02x", x[i]);
}

void hexdump(u8 *x, u32 n)
{
	u32 i, j;

	for (i = 0; i < n; i += 16) {
		fprintf(stderr, "%04x:", i);
		for (j = 0; j < 16 && i + j < n; j++) {
			if ((j & 3) == 0)
				fprintf(stderr, " ");
			fprintf(stderr, "%02x", *x++);
		}
		fprintf(stderr, "\n");
	}
}

void dump_tmd(u8 *tmd)
{
	u32 i, n;
	u8 *p;

	printf("       issuer: %s\n", tmd + 0x140);
	printf("  sys_version: %016llx\n", be64(tmd + 0x0184));
	printf("     title_id: %016llx\n", be64(tmd + 0x018c));
	printf("   title_type: %08x\n", be32(tmd + 0x0194));
	printf("     group_id: %04x\n", be16(tmd + 0x0198));
	printf("title_version: %04x\n", be16(tmd + 0x01dc));
	printf(" num_contents: %04x\n", be16(tmd + 0x01de));
	printf("   boot_index: %04x\n", be16(tmd + 0x01e0));

	n = be16(tmd + 0x01de);
	p = tmd + 0x01e4;
	for (i = 0; i < n; i++) {
		printf("cid %08x  index %04x  type %04x  size %08llx\n",
		       be32(p), be16(p + 4), be16(p + 6), be64(p + 8));
		p += 0x24;
	}
}
void spinner(u64 x, u64 max)
{
	static time_t start_time;
	static u32 expected_total;
	u32 d;
	double percent;
	u32 h, m, s;

	if (x == 0) {
		start_time = time(0);
		expected_total = 300;
	}

	if (x == max) {
                d = (u32)(time(0) - start_time);
                h = d / 3600;
                m = (d / 60) % 60;
                s = d % 60;
		fprintf(stderr, "Done in  %d:%02d:%02d                   \n", h, m, s);
		return;
	}

	d = (u32)(time(0) - start_time);

	if ( (d != 0) && (x > 0) )
		expected_total = (u32)((3 * expected_total + d * max / x) / 4);

	if (expected_total > d)
		d = expected_total - d;
	else
		d = 0;

	h = d / 3600;
	m = (d / 60) % 60;
	s = d % 60;
	percent = 100.0 * x / max;

#ifdef SPINNER_MOD
	fprintf(stderr, "%5.2f%% (%c) ETA: %d:%02d:%02d  \r",
		percent, "/|\\-"[(x/10) % 4], h, m, s);
#else
	fprintf(stderr, "%5.2f|@|%d:%02d:%02d  \r",
		percent, h, m, s);
#endif

	fflush(stderr);
}

void progress(u64 x, u64 max)
{
	static time_t start_time;
	static u32 expected_total;
	u32 d;
	u32 prc,dec;
	char prg[40];
	u32 h, m, s;

	if (x == 0) {
		start_time = time(0);
		expected_total = 300;
	}

	if (x == max)
	{
		d = (u32)(time(0) - start_time);
		h = d / 3600;
		m = (d / 60) % 60;
		s = d % 60;
		fprintf(stderr, "\rDone in  %d:%02d:%02d                                     \n", h, m, s);
		return;
	}

	d = (u32)(time(0) - start_time);

	if (x != 0)
		expected_total = (u32)((3 * expected_total + d * max / x) / 4);

	if (expected_total > d)
		d = expected_total - d;
	else
		d = 0;

	h = d / 3600;
	m = (d / 60) % 60;
	s = d % 60;

	prc = (u32)((100 * x) / max);
	dec = prc % 3;
	prc /=3;

	memset(prg, 0x20, 34);
	for(d=0;d<prc;d++) prg[d]='O';
	switch(dec)
	{
	case 1:
		prg[prc]='.';
		break;

	case 2:
		prg[prc]='o';
		break;
	}

	prg[33]=0;

	fprintf(stderr, "\rETA: %d:%02d:%02d [%s]", h, m, s, prg);
	fflush(stderr);
}


code:
// Copyright 2007,2008  Segher Boessenkool  <segher@kernel.crashing.org>
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

#ifndef _TOOLS_H
#define _TOOLS_H

#ifdef WIN32
// A bit untidy for now - g3power
#define SPINNER_MOD
#endif

// basic data types
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned long long u64;

u16 be16(const u8 *p);
u32 be32(const u8 *p);
u64 be64(const u8 *p);
u64 be34(const u8 *p);

void wbe16(u8 *p, u16 x);
void wbe32(u8 *p, u32 x);
void wbe64(u8 *p, u64 x);

//#define round_down(x,n) ((x) & -(n))
#define round_up(x,n) (-(-(x) & -(n)))

// bignum
int bn_compare(u8 *a, u8 *b, u32 n);
void bn_sub_modulus(u8 *a, u8 *N, u32 n);
void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n);
void bn_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n);
void bn_inv(u8 *d, u8 *a, u8 *N, u32 n);	// only for prime N
void bn_exp(u8 *d, u8 *a, u8 *N, u32 n, u8 *e, u32 en);

// crypto
void md5(u8 *data, u32 len, u8 *hash);
void sha(u8 *data, u32 len, u8 *hash);
void get_key(const char *name, u8 *key, u32 len);
void aes_cbc_dec(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out);
void aes_cbc_enc(u8 *key, u8 *iv, u8 *in, u32 len, u8 *out);
void decrypt_title_key(u8 *tik, u8 *title_key);
int check_cert_chain(u8 *data, u32 data_len, u8 *cert, u32 cert_len);
int check_ec(u8 *ng, u8 *ap, u8 *sig, u8 *sig_hash);
void generate_ecdsa(u8 *R, u8 *S, u8 *k, u8 *hash);
int check_ecdsa(u8 *Q, u8 *R, u8 *S, u8 *hash);
void ec_priv_to_pub(u8 *k, u8 *Q);

// compression
void do_yaz0(u8 *in, u32 in_size, u8 *out, u32 out_size);

// error handling
void fatal(const char *s, ...);
void non_fatal(const char *s, ...);

// output formatting
void print_bytes(u8 *x, u32 n);
void hexdump(u8 *x, u32 n);
void dump_tmd(u8 *tmd);
void spinner(u64 x, u64 max);
void progress(u64 x, u64 max);

#endif


code:
// Copyright 2009 Kwiirk based on negentig.c:
// Copyright 2007,2008  Segher Boessenkool  <segher@kernel.crashing.org>
// Licensed under the terms of the GNU GPL, version 2
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt

#include "wiidisc.h"

void aes_set_key(u8 *key);
void aes_decrypt(u8 *iv, u8 *inbuf, u8 *outbuf, unsigned long long len);

static void _decrypt_title_key(u8 *tik, u8 *title_key)
{
	u8 common_key[16]={
                0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45,
                0x73, 0x81, 0xaa, 0xf7
        };
	u8 iv[16];

	wbfs_memset(iv, 0, sizeof iv);
	wbfs_memcpy(iv, tik + 0x01dc, 8);
        aes_set_key(common_key);
	//_aes_cbc_dec(common_key, iv, tik + 0x01bf, 16, title_key);
        aes_decrypt(iv, tik + 0x01bf,title_key,16);
}
static u32 _be32(const u8 *p)
{
	return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
}

static void disc_read(wiidisc_t *d,u32 offset, u8 *data, u32 len)
{
        if(data){
                int ret=0;
                if(len==0)
                        return ;
                ret = d->read(d->fp,offset,len,data);
                if(ret)
                        wbfs_fatal("error reading disc (disc_read)");
        }
        if(d->sector_usage_table)
        {
                u32 blockno = offset>>13;
                do
                {
                        d->sector_usage_table[blockno]=1;
                        blockno+=1;
                        if(len>0x8000)
                                len-=0x8000;
                }while(len>0x8000);
        }
}

static void partition_raw_read(wiidisc_t *d,u32 offset, u8 *data, u32 len)
{
	disc_read(d, d->partition_raw_offset + offset, data, len);
}

static void partition_read_block(wiidisc_t *d,u32 blockno, u8 *block)
{
        u8*raw = d->tmp_buffer;
	u8 iv[16];
	u32 offset;
        if(d->sector_usage_table)
                d->sector_usage_table[d->partition_block+blockno]=1;
        offset = d->partition_data_offset + ((0x8000>>2) * blockno);
        partition_raw_read(d,offset, raw, 0x8000);

        // decrypt data
        memcpy(iv, raw + 0x3d0, 16);
        aes_set_key(d->disc_key);
        aes_decrypt(iv, raw + 0x400,block,0x7c00);
}

static void partition_read(wiidisc_t *d,u32 offset, u8 *data, u32 len,int fake)
{
	u8 *block = d->tmp_buffer2;
	u32 offset_in_block;
	u32 len_in_block;
        if(fake &&  d->sector_usage_table==0)
                return;

	while(len) {
		offset_in_block = offset % (0x7c00>>2);
		len_in_block = 0x7c00 - (offset_in_block<<2);
		if (len_in_block > len)
			len_in_block = len;
                if(!fake){
                        partition_read_block(d,offset / (0x7c00>>2), block);
                        wbfs_memcpy(data, block + (offset_in_block<<2), len_in_block);
                }else
                        d->sector_usage_table[d->partition_block+(offset/(0x7c00>>2))]=1;
		data += len_in_block;
		offset += len_in_block>>2;
		len -= len_in_block;
	}
}

static u32 do_fst(wiidisc_t *d,u8 *fst, const char *names, u32 i)
{
	u32 offset;
	u32 size;
	const char *name;
	u32 j;

	name = names + (_be32(fst + 12*i) & 0x00ffffff);
	size = _be32(fst + 12*i + 8);

	if (i == 0) {
		for (j = 1; j < size && !d->extracted_buffer; ){
			j = do_fst(d,fst, names, j);
                }
		return size;
	}
        //printf("name    %s\n",name);

	if (fst[12*i]) {

		for (j = i + 1; j < size && !d->extracted_buffer; )
			j = do_fst(d,fst, names, j);

		return size;
	} else {
		offset = _be32(fst + 12*i + 4);
                if(d->extract_pathname && strcmp(name, d->extract_pathname)==0)
                {
                        d->extracted_buffer = wbfs_ioalloc(size);
                        partition_read(d,offset, d->extracted_buffer, size,0);
                }else
                        partition_read(d,offset, 0, size,1);
		return i + 1;
	}
}

static void do_files(wiidisc_t*d)
{
	u8 *b = wbfs_ioalloc(0x480); // XXX: determine actual header size
	u32 dol_offset;
	u32 fst_offset;
	u32 fst_size;
	u32 apl_offset;
	u32 apl_size;
	u8 *apl_header = wbfs_ioalloc(0x20);
	u8 *fst;
	u32 n_files;
	partition_read(d,0, b, 0x480,0);

	dol_offset = _be32(b + 0x0420);
	fst_offset = _be32(b + 0x0424);
	fst_size = _be32(b + 0x0428)<<2;

	apl_offset = 0x2440>>2;
	partition_read(d,apl_offset, apl_header, 0x20,0);
	apl_size = 0x20 + _be32(apl_header + 0x14) + _be32(apl_header + 0x18);
        // fake read dol and partition
        partition_read(d,apl_offset, 0, apl_size,1);
        partition_read(d,dol_offset, 0,  (fst_offset - dol_offset)<<2,1);

	fst = wbfs_ioalloc(fst_size);
	if (fst == 0)
		wbfs_fatal("malloc fst");
	partition_read(d,fst_offset, fst, fst_size,0);
	n_files = _be32(fst + 8);

	if (n_files > 1)
		do_fst(d,fst, (char *)fst + 12*n_files, 0);
        wbfs_iofree(b);
        wbfs_iofree(apl_header);
	wbfs_iofree(fst);
}

static void do_partition(wiidisc_t*d)
{
	u8 *tik = wbfs_ioalloc(0x2a4);
        u8 *b = wbfs_ioalloc(0x1c);
	u64 tmd_offset;
	u32 tmd_size;
	u8 *tmd;
	u64 cert_offset;
	u32 cert_size;
	u8 *cert;
	u64 h3_offset;

	// read ticket, and read some offsets and sizes
	partition_raw_read(d,0, tik, 0x2a4);
	partition_raw_read(d,0x2a4>>2, b, 0x1c);

	tmd_size = _be32(b);
	tmd_offset = _be32(b + 4);
	cert_size = _be32(b + 8);
	cert_offset = _be32(b + 0x0c);
	h3_offset = _be32(b + 0x10);
	d->partition_data_offset = _be32(b + 0x14);
        d->partition_block = (d->partition_raw_offset+d->partition_data_offset)>>13;
	tmd = wbfs_ioalloc(tmd_size);
	if (tmd == 0)
		wbfs_fatal("malloc tmd");

	partition_raw_read(d,tmd_offset, tmd, tmd_size);

	cert = wbfs_ioalloc(cert_size);
	if (cert == 0)
		wbfs_fatal("malloc cert");
	partition_raw_read(d,cert_offset, cert, cert_size);

	_decrypt_title_key(tik, d->disc_key);

	partition_raw_read(d,h3_offset, 0, 0x18000);
        wbfs_iofree(b);
        wbfs_iofree(tik);
	wbfs_iofree(cert);
	wbfs_iofree(tmd);

	do_files(d);

}
static int test_parition_skip(u32 partition_type,partition_selector_t part_sel)
{
        switch(part_sel)
        {
        case ALL_PARTITIONS:
                return 0;
        case REMOVE_UPDATE_PARTITION:
                return (partition_type==1);
        case ONLY_GAME_PARTITION:
                return (partition_type!=0);
        default:
                return (partition_type!=part_sel);
        }
}

static int do_disc(wiidisc_t*d)
{
	u8 *b = wbfs_ioalloc(0x100);
	u64 partition_offset[32]; // XXX: don't know the real maximum
	u64 partition_type[32]; // XXX: don't know the real maximum
	u32 n_partitions;
        u32 magic;
	u32 i;
	disc_read(d,0, b, 0x100);
        magic=_be32(b+24);
        if(magic!=0x5D1C9EA3){
				wbfs_iofree(b);
                wbfs_error("not a wii disc");
                return 0;
        }
	disc_read(d,0x40000>>2, b, 0x100);
	n_partitions = _be32(b);
	disc_read(d,_be32(b + 4), b, 0x100);
	for (i = 0; i < n_partitions; i++){
		partition_offset[i] = _be32(b + 8 * i);
		partition_type[i] = _be32(b + 8 * i+4);
        }
	for (i = 0; i < n_partitions; i++) {
                d->partition_raw_offset = partition_offset[i];
                if(!test_parition_skip(partition_type[i],d->part_sel))
                        do_partition(d);
	}
        wbfs_iofree(b);
	return 1;
}

wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp)
{
        wiidisc_t *d = wbfs_malloc(sizeof(wiidisc_t));
        if(!d)
                return 0;
        wbfs_memset(d,0,sizeof(wiidisc_t));
        d->read = read;
        d->fp = fp;
        d->part_sel = ALL_PARTITIONS;
        d->tmp_buffer = wbfs_ioalloc(0x8000);
        d->tmp_buffer2 = wbfs_malloc(0x8000);

        return d;
}
void wd_close_disc(wiidisc_t *d)
{
        wbfs_iofree(d->tmp_buffer);
        wbfs_free(d->tmp_buffer2);
        wbfs_free(d);
}
// returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error
// XXX pathname not implemented. files are extracted by their name.
// first file found with that name is returned.
u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname)
{
        u8 *retval = 0;
        d->extract_pathname = pathname;
        d->extracted_buffer = 0;
        d->part_sel = partition_type;
        int result = do_disc(d);
		d->extract_pathname = 0;
		d->part_sel = ALL_PARTITIONS;
		retval = result == 0 ? 0 : d->extracted_buffer;
		d->extracted_buffer = 0;
        return retval;
}

int wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table)
{
        d->sector_usage_table = usage_table;
        wbfs_memset(usage_table,0,143432*2);
        d->part_sel = selector;
        int result = do_disc(d);
		d->part_sel = ALL_PARTITIONS;
		d->sector_usage_table = 0;
		return result;
}

void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table)
{
        u8 *b = partition_table;
	u32 partition_offset;
	u32 partition_type;
	u32 n_partitions,i,j;
        u32 *b32;
        if(selector == ALL_PARTITIONS)
                return;
	n_partitions = _be32(b);
        if(_be32(b + 4)-(0x40000>>2) >0x50)
                wbfs_fatal("cannot modify this partition table. Please report the bug.");

        b += (_be32(b + 4)-(0x40000>>2))*4;
        j=0;
	for (i = 0; i < n_partitions; i++){
		partition_offset = _be32(b + 8 * i);
		partition_type = _be32(b + 8 * i+4);
                if(!test_parition_skip(partition_type,selector))
                {
                        b32 = (u32*)(b + 8 * j);
                        b32[0] = wbfs_htonl(partition_offset);
                        b32[1] = wbfs_htonl(partition_type);
                        j++;
                }
        }
        b32 = (u32*)(partition_table);
        *b32 = wbfs_htonl(j);
}


code:
#ifndef WIIDISC_H
#define WIIDISC_H

#include <stdio.h>
#include "libwbfs_os.h" // this file is provided by the project wanting to compile libwbfs and wiidisc

#ifdef __cplusplus
   extern "C" {
#endif /* __cplusplus */
#if 0 //removes extra automatic indentation by editors
   }
#endif
// callback definition. Return 1 on fatal error (callback is supposed to make retries until no hopes..)
// offset points 32bit words, count counts bytes
typedef int (*read_wiidisc_callback_t)(void*fp,u32 offset,u32 count,void*iobuf);

typedef enum{
        UPDATE_PARTITION_TYPE=0,
        GAME_PARTITION_TYPE,
        OTHER_PARTITION_TYPE,
        // value in between selects partition types of that value
        ALL_PARTITIONS=0xffffffff-3,
        REMOVE_UPDATE_PARTITION, // keeps game + channel installers
        ONLY_GAME_PARTITION,
}partition_selector_t;

typedef struct wiidisc_s
{
        read_wiidisc_callback_t read;
        void *fp;
        u8 *sector_usage_table;

        // everything points 32bit words.
        u32 disc_raw_offset;
        u32 partition_raw_offset;
        u32 partition_data_offset;
        u32 partition_data_size;
        u32 partition_block;

        u8 *tmp_buffer;
        u8 *tmp_buffer2;
        u8 disc_key[16];
        int dont_decrypt;

        partition_selector_t part_sel;

        char *extract_pathname;
        u8  *extracted_buffer;
}wiidisc_t;

wiidisc_t *wd_open_disc(read_wiidisc_callback_t read,void*fp);
void wd_close_disc(wiidisc_t *);
// returns a buffer allocated with wbfs_ioalloc() or NULL if not found of alloc error
u8 * wd_extract_file(wiidisc_t *d, partition_selector_t partition_type, char *pathname);

int wd_build_disc_usage(wiidisc_t *d, partition_selector_t selector, u8* usage_table);

// effectively remove not copied partition from the partition table.
void wd_fix_partition_table(wiidisc_t *d, partition_selector_t selector, u8* partition_table);

#if 0
{
#endif
#ifdef __cplusplus
   }
#endif /* __cplusplus */

#endif


code:
/log*
/qwbfsmanager-*-src*
/qwbfsmanager-*-*.*
/*qwbfsmanager-*.exe*


code:
[Desktop Entry]
Categories=Game;Qt;
Exec=qwbfsmanager
Name=QWBFS Manager
GenericName=QWBFS Manager
Comment=QWBFS Manager is a graphical user interface (GUI) for working with WBFS filesystem formatted hard disk drive.
Terminal=false
Type=Application
Icon=qwbfsmanager


code:
[Paths]
Prefix = qt


code:
#!/bin/sh

VERSION=$1
OS=`uname -s`

SVN_REVISION=`export LANG=C && [ -f /usr/bin/git ] && git --git-dir=../.git describe --long $VERSION`

if [ '!' -z "$VERSION" ]; then
    VERSION_STR=$VERSION
else
    VERSION_STR=$SVN_REVISION
fi

if [ -z "$VERSION" ]; then
    VERSION=`echo "$SVN_REVISION" | cut -d'-' -f1`
fi

VERSION=`echo "$VERSION" | cut -d'v' -f2`

BASE_NAME=qwbfsmanager-$VERSION_STR
FOLDER_NAME=$BASE_NAME-src
TAR_GZ_FILE=$FOLDER_NAME.tar.gz
ZIP_FILE=$FOLDER_NAME.zip
WIN_SETUP=$BASE_NAME-win32.exe
WIN_FOLDER=$BASE_NAME-win32
WIN_PACKAGE=$WIN_FOLDER.zip
MAC_PACKAGE=$BASE_NAME.dmg
CUR_PATH=$PWD
LOG_FOLDER=$CUR_PATH/log

if [ $OS = "Linux" ]; then
    WINE="wine"
    WINE_DRIVE="$HOME/.wine/drive_c"
    WINE_PROGRAM_FILES="$WINE_DRIVE/Program Files"
fi

if [ $OS = "Darwin" ]; then
    WINE="/Applications/Wine.app/Contents/Resources/bin/wine"
    WINE_DRIVE="$HOME/Wine Files/drive_c"
    WINE_PROGRAM_FILES="$WINE_DRIVE/Program Files"
fi

export OS
export VERSION
export VERSION_STR
export SVN_REVISION

# execute command and stop if fails
startCommand()
{
    echo "    -Starting command: $1"
    error=0
    eval "$1" || error=1

    if [ $error = 1 -a -z "$2" ]; then
        echo "***************************************************"
        echo "***  Error when evaluing command: $1"
        echo "***************************************************"
        finish 1
    fi
}

# delete existing file/folder $1
deleteIfExists()
{
    if [ -d "$1" ]; then
        echo "*** Deleting folder: $1"
        startCommand "rm -fr \"$1\""
    elif [ -f "$1" ]; then
        echo "*** Deleting file: $1"
        startCommand "rm -f \"$1\""
    fi
}

# export a svn path from source $1 to target $2
exportRepository()
{
    echo "*** Exporting repository: $1 to $2"

    startCommand "cd \"..\""
    startCommand "git submodule update --init --recursive > /dev/null 2>&1"
    startCommand "cd \"$CUR_PATH\""

    startCommand "rsync -a .. \"$2\" --exclude \"$2\" > /dev/null 2>&1"

    startCommand "cd \"$2\""
    if [ '!' -z "$1" ]; then
        startCommand "git clean -f -q -x -d && git reset -q --hard && git checkout -q \"$1\" > /dev/null 2>&1"
    else
        startCommand "git clean -f -X -q -d > /dev/null 2>&1"
    fi
    startCommand "cd \"$CUR_PATH\""

    startCommand "rm -fr \"$2/.git\""
}

# create a tar.gz file $1 from path $2
createTarGz()
{
    echo "*** Creating tar.gz package: $1"
    startCommand "tar czf \"$1\" \"$2\""
}

# create a zip file $1 from path $2 and extra parameters $3 and ending parameters $4
createZip()
{
    echo "*** Creating zip package: $1"

    params=

    if [ '!' -z "$3" ]; then
        params="$params \"$3\""
    fi

    if [ '!' -z "$1" ]; then
        params="$params \"$1\""
    fi

    if [ '!' -z "$2" ]; then
        params="$params \"$2\""
    fi

    if [ '!' -z "$4" ]; then
        params="$params $4"
    fi

    startCommand "zip -q -r -9 $params"
}

# crossbuild for windows
crossBuild()
{
    QMAKE="qmake"

    if [ $OS = "Linux" ]; then
        QMAKE="qmake-qt4"
        QT_VERSION="4.8.6"
        QT_WIN32_VERSION="4.8.3"
        QT_PATH="/usr"
        MKSPEC="$HOME/.qt/mkspecs/win32-x11-g++"
        QT_WIN32_PATH="$WINE_DRIVE/Development/Qt/$QT_WIN32_VERSION"
        ISCC="$WINE_PROGRAM_FILES/Inno Setup 5/ISCC.exe"
        DLLS_PATH="$WINE_DRIVE/Development/OpenSSL"
    fi

    if [ $OS = "Darwin" ]; then
        QT_VERSION="4.7.0-lgpl"
        QT_WIN32_VERSION="4.7.1"
        QT_PATH="/usr/local/Trolltech/$QT_VERSION"
        MKSPEC="$HOME/mkspecs/4.6.x/win32-osx-g++"
        QT_WIN32_PATH="/usr/local/Trolltech/win32/$QT_WIN32_VERSION"
        ISCC="$WINE_PROGRAM_FILES/Inno_Setup_5/ISCC.exe"
        DLLS_PATH="$HOME/Win32Libraries/bin"
    fi

    if [ -d "$QT_PATH" ] && [ -d "$MKSPEC" ] && [ -d "$QT_WIN32_PATH" ] && [ -f "$ISCC" ] && [ -d "$DLLS_PATH" ]; then
        echo "*** Crossbuilding for windows"

        export QT_WIN32_PATH
        export QT_WIN32_VERSION
        export DLLS_PATH
        export CROSS_WIN32_QT_PATH="$QT_WIN32_PATH"
        export QT_WINDOWS_PATH="$QT_WIN32_PATH"

        startCommand "cd \"$FOLDER_NAME\""
        startCommand "make distclean > /dev/null 2>&1" 0
        startCommand "\"$QT_PATH/bin/$QMAKE\" -spec \"$MKSPEC\" -win32 -r > /dev/null 2>&1"
        startCommand "make distclean > /dev/null 2>&1" 0
        startCommand "\"$QT_PATH/bin/$QMAKE\" -spec \"$MKSPEC\" -win32 -r > /dev/null 2>&1"
        startCommand "make -j4 > \"$CUR_PATH/log/winbuild.log\" 2>&1"
        startCommand "\"$WINE\" \"$ISCC\" \"packages/windows.iss\" > \"$CUR_PATH/log/winpackage.log\" 2>&1"
        startCommand "make distclean > /dev/null 2>&1" 0
        startCommand "cd \"$CUR_PATH\""

        if [ -f "$FOLDER_NAME/packages/$WIN_SETUP" ]; then
            startCommand "mv \"$FOLDER_NAME/packages/$WIN_SETUP\" \".\""
        fi
    fi
}

# create windows zip package
windowsZipPackage()
{
    if [ -f "$WIN_SETUP" ]; then
        echo "*** Creating windows zip package"

        # uninstall previous package
        startCommand "find \"$WINE_PROGRAM_FILES/QWBFS Manager\" -name \"unins*.exe\" -print0 | xargs -0 -I {} \"$WINE\" {} /silent > /dev/null 2>&1"

        # install the current one
        startCommand "\"$WINE\" \"$WIN_SETUP\" /silent > /dev/null 2>&1"

        # create zip
        startCommand "cp -fr \"$WINE_PROGRAM_FILES/QWBFS Manager\" \"$WIN_FOLDER\""
        startCommand "createZip \"$WIN_PACKAGE\" \"$WIN_FOLDER\" \"\" \"-x *unins*.exe -x *unins*.dat\""
        startCommand "deleteIfExists \"$WIN_FOLDER\""

        # uninstall installed package
        startCommand "find \"$WINE_PROGRAM_FILES/QWBFS Manager\" -name \"unins*.exe\" -print0 | xargs -0 -I {} \"$WINE\" {} /silent > /dev/null 2>&1"
    fi
}

# create mac os x package
macPackage()
{
    echo "*** Create Mac OS X package"

    QT_VERSION="4.7.0-lgpl"
    BUNDLE_NAME="QWBFSManager"
    BUNDLE_PATH="bin"
    BUNDLE_APP_PATH="$BUNDLE_PATH/$BUNDLE_NAME.app"
    QT_PATH="/usr/local/Trolltech/$QT_VERSION"
    QMAKE_FLAGS="\"CONFIG *= universal no_fresh_install\""

    # qt4-mac package mac port qt4 support
    if [ ! -d "$QT_PATH" ]; then
        QT_VERSION=""
        QT_PATH="/opt/local"
        QMAKE_FLAGS="\"CONFIG *= no_fresh_install\""
    fi

    startCommand "cd \"$FOLDER_NAME\""
    startCommand "make distclean > /dev/null 2>&1" 0
    startCommand "\"$QT_PATH/bin/qmake\" $QMAKE_FLAGS -r > /dev/null 2>&1"
    startCommand "make distclean > /dev/null 2>&1" 0
    startCommand "\"$QT_PATH/bin/qmake\" $QMAKE_FLAGS -r > /dev/null 2>&1"
    startCommand "make -j4 > \"$CUR_PATH/log/macbuild.log\" 2>&1"
    startCommand "make install > /dev/null 2>&1"
    startCommand "\"$QT_PATH/bin/macdeployqt\" \"$BUNDLE_APP_PATH\" -dmg > /dev/null 2>&1"
    startCommand "make distclean > /dev/null 2>&1" 0
    startCommand "cd \"$CUR_PATH\""

    if [ -f "$FOLDER_NAME/$BUNDLE_PATH/$BUNDLE_NAME.dmg" ]; then
        startCommand "mv \"$FOLDER_NAME/$BUNDLE_PATH/$BUNDLE_NAME.dmg\" \"$MAC_PACKAGE\""
    fi
}

# startup function
startup()
{
    if [ '!' -d "$LOG_FOLDER" ]; then
        echo "*** Create log folder"
        startCommand "mkdir -p \"$LOG_FOLDER\""
    fi
}

# finish function, must not use startCommand or function calling it to avoid possible unfinite loop on errors.
finish()
{
    # close wine, WineBottler & X11
    if [ $OS = "Darwin" ]; then
        startCommand "killall wine > /dev/null 2>&1" 0
        startCommand "killall WineBottler > /dev/null 2>&1" 0
        startCommand "killall X11.bin > /dev/null 2>&1" 0
    fi

    # come back to start folder
    startCommand "cd \"$CUR_PATH\"" 0

    # delete exported repository
    startCommand "rm -fr \"$FOLDER_NAME\"" 0

    echo "********** Processing release finished - Exit code: $1 **********"

    exit $1
}

# startup call
startup
# delete source folder
deleteIfExists "$FOLDER_NAME"
# delete tar.gz source
deleteIfExists "$TAR_GZ_FILE"
# delete zip source
deleteIfExists "$ZIP_FILE"
# delete win setup
deleteIfExists "$WIN_SETUP"
# delete win package
deleteIfExists "$WIN_PACKAGE"
# delete mac package
deleteIfExists "$MAC_PACKAGE"
# export the taggued version to release
exportRepository "$1" "$FOLDER_NAME"
# create tar.gz source
createTarGz "$TAR_GZ_FILE" "$FOLDER_NAME"
# create zip source
createZip "$ZIP_FILE" "$FOLDER_NAME"
# create win setup
crossBuild
# create windows zip package
windowsZipPackage
# darwin specific
if [ $OS = "Darwin" ]; then
    echo "*** Mac"
    macPackage
fi
# linux specific
if [ $OS = "Linux" ]; then
    echo "*** Linux"
fi
# finish call
finish 0


code:
#!/bin/sh

VERSION=$1
PASSWORD=$2

if [ -z "$VERSION" ]; then
  echo "Please give a version to upload as first parameter"
  exit 1
fi

if [ -z "PASSWORD" ]; then
  echo "Please give a password as second parameter"
  exit 1
fi

NAME="qwbfsmanager"

# $1 - package file path
# $2 - package summary
# $3 - package labels
googleCodeUpload() {
  echo "Uploading package '$1'..."
  googlecode_upload.pl \
    --progress \
    --user="pasnox" \
    --pass="$PASSWORD" \
    --project="qwbfs" \
    --summary="$2" \
    --label="$3" \
    --file="$1"
}

# source tar gz
if [ -f "$NAME-$VERSION-src.tar.gz" ]; then
  googleCodeUpload "$NAME-$VERSION-src.tar.gz" "Source Tgz Archive $VERSION" "OpSys-All,Type-Source,Featured"
fi

# source zip
if [ -f "$NAME-$VERSION-src.zip" ]; then
  googleCodeUpload "$NAME-$VERSION-src.zip" "Source Zip Archive $VERSION" "OpSys-All,Type-Source,Featured"
fi

# win32 zip
if [ -f "$NAME-$VERSION-win32.zip" ]; then
  googleCodeUpload "$NAME-$VERSION-win32.zip" "Windows Zip Archive $VERSION" "OpSys-Windows,Type-Archive,Featured"
fi

# win32 setup
if [ -f "setup-$NAME-$VERSION-win32.exe" ]; then
  googleCodeUpload "setup-$NAME-$VERSION-win32.exe" "Windows Installer $VERSION" "OpSys-Windows,Type-Installer,Featured"
fi


code:
; This script need to use a special Inno Setup version located here : http://jrsoftware.org/isdl.php#qsp

[CustomMessages]
QWBFS_NAME=QWBFS Manager
QWBFS_COPYRIGHTS=2010-2014 Filipe Azevedo
QWBFS_URL=https://github.com/pasnox/qwbfsmanager
QWBFS_ISSUES_URL=https://github.com/pasnox/qwbfsmanager/issues

#define QWBFS_VERSION GetEnv("VERSION")
#define QWBFS_VERSION_STR GetEnv("VERSION_STR")
#define QWBFS_SETUP_NAME "qwbfsmanager-" +QWBFS_VERSION_STR +"-win32"
#define QT_PATH "Z:\" +GetEnv("CROSS_WIN32_QT_PATH")
#define DLLS_PATH "Z:\" +GetEnv("DLLS_PATH")

[Setup]
SourceDir=..\bin
OutputDir=..\packages
OutputBaseFilename={#QWBFS_SETUP_NAME}
VersionInfoVersion={#QWBFS_VERSION}
VersionInfoCompany=Filipe Azevedo
VersionInfoDescription=Free, Fast and Powerfull cross platform Wii Backup File System Manager
VersionInfoTextVersion={#QWBFS_VERSION}
VersionInfoCopyright={cm:QWBFS_COPYRIGHTS}
AppCopyright={cm:QWBFS_COPYRIGHTS}
AppName={cm:QWBFS_NAME}
AppVerName={cm:QWBFS_NAME} {#QWBFS_VERSION}
InfoAfterFile=..\GPL-2
InfoBeforeFile=..\GPL-2
LicenseFile=..\GPL-2
ChangesAssociations=true
PrivilegesRequired=none
DefaultDirName={pf}\{cm:QWBFS_NAME}
EnableDirDoesntExistWarning=false
AllowNoIcons=true
DefaultGroupName={cm:QWBFS_NAME}
AlwaysUsePersonalGroup=true
;SetupIconFile=..\qwbfs\resources\qwbfsmanager.ico
AppPublisher={cm:QWBFS_COPYRIGHTS}
AppPublisherURL={cm:QWBFS_URL}
AppSupportURL={cm:QWBFS_ISSUES_URL}
AppVersion={#QWBFS_VERSION}
AppComments=Thanks using {cm:QWBFS_NAME}
AppContact={cm:QWBFS_ISSUES_URL}
UninstallDisplayName={cm:QWBFS_NAME}
ShowLanguageDialog=yes
UsePreviousLanguage=no

[_ISTool]
UseAbsolutePaths=false

[Files]
; Fresh framework related
Source: ..\fresh.git\translations\*.qm; DestDir: {app}\translations; Flags: promptifolder
; QWBFS Manager related files
Source: qwbfsmanager.exe; DestDir: {app}; Flags: promptifolder
Source: ..\packages\qt_windows.conf; DestDir: {app}; DestName: qt.conf; Flags: promptifolder
Source: ..\GPL-2; DestDir: {app}; Flags: promptifolder
Source: ..\translations\*.qm; DestDir: {app}\translations; Flags: promptifolder
; Qt related files
Source: {#QT_PATH}\bin\QtCore4.dll; DestDir: {app}; Flags: promptifolder
Source: {#QT_PATH}\bin\QtGui4.dll; DestDir: {app}; Flags: promptifolder
Source: {#QT_PATH}\bin\QtXml4.dll; DestDir: {app}; Flags: promptifolder
Source: {#QT_PATH}\bin\QtNetwork4.dll; DestDir: {app}; Flags: promptifolder
Source: {#QT_PATH}\plugins\accessible\*.dll; DestDir: {app}\qt\plugins\accessible; Flags: promptifolder; Excludes: *d4.dll
Source: {#QT_PATH}\plugins\codecs\*.dll; DestDir: {app}\qt\plugins\codecs; Flags: promptifolder; Excludes: *d4.dll
Source: {#QT_PATH}\plugins\graphicssystems\*.dll; DestDir: {app}\qt\plugins\graphicssystems; Flags: promptifolder; Excludes: *d4.dll
Source: {#QT_PATH}\plugins\imageformats\*.dll; DestDir: {app}\qt\plugins\imageformats; Flags: promptifolder; Excludes: *d4.dll
Source: {#QT_PATH}\translations\qt*.qm; DestDir: {app}\qt\translations; Flags: promptifolder recursesubdirs; Excludes: qt_help*.qm,qtconfig*.qm
; Dependencies related files
Source: {#QT_PATH}\bin\mingwm10.dll; DestDir: {app}; Flags: promptifolder
Source: {#QT_PATH}\bin\libgcc_s_dw2-1.dll; DestDir: {app}; Flags: promptifolder skipifsourcedoesntexist
Source: {#QT_PATH}\bin\libstdc++-6.dll; DestDir: {app}; Flags: promptifolder skipifsourcedoesntexist
Source: {#DLLS_PATH}\libeay32.dll; DestDir: {app}; Flags: promptifolder
Source: {#DLLS_PATH}\libssl32.dll; DestDir: {app}; Flags: promptifolder
Source: {#DLLS_PATH}\ssleay32.dll; DestDir: {app}; Flags: promptifolder

[Icons]
Name: {group}\{cm:QWBFS_NAME}; Filename: {app}\qwbfsmanager.exe; WorkingDir: {app}; IconFilename: {app}\qwbfsmanager.exe; IconIndex: 0
Name: {userdesktop}\{cm:QWBFS_NAME}; Filename: {app}\qwbfsmanager.exe; WorkingDir: {app}; IconFilename: {app}\qwbfsmanager.exe; IconIndex: 0
Name: {group}\Home Page; Filename: {app}\Home Page.url; WorkingDir: {app}
Name: {group}\Tracker; Filename: {app}\Tracker.url; WorkingDir: {app}

[INI]
Filename: {app}\Home Page.url; Section: InternetShortcut; Key: URL; String: {cm:QWBFS_URL}; Flags: createkeyifdoesntexist uninsdeleteentry uninsdeletesectionifempty; Components:
Filename: {app}\Tracker.url; Section: InternetShortcut; Key: URL; String: {cm:QWBFS_ISSUES_URL}; Flags: createkeyifdoesntexist uninsdeleteentry uninsdeletesectionifempty

[UninstallDelete]
Name: {app}\Home Page.url; Type: files
Name: {app}\Tracker.url; Type: files
Name: {app}\*.ini; Type: files
Name: {app}; Type: dirifempty
[Run]
Filename: {app}\qwbfsmanager.exe; WorkingDir: {app}; Flags: postinstall skipifsilent; Description: {cm:QWBFS_NAME}


code:
###########################################################################################
##      Created using Monkey Studio v1.8.4.0 (1.8.4.0)
##
##  Author    : Filipe Azevedo aka Nox P@sNox <pasnox@gmail.com>
##  Project   : qwbfs
##  FileName  : qwbfs.pro
##  Date      : 2010-04-04T11:04:05
##  License   : GPL2
##  Comment   : Creating using Monkey Studio RAD
##  Home Page   : https://github.com/pasnox/qwbfsmanager
##
###########################################################################################

TEMPLATE = subdirs
CONFIG *= ordered

BUILD_PATH = build

include( shared.pri )

win32_crossbuild {
    exists( "$(QT_WIN32_PATH)/lib/*fresh*" ):CONFIG *= fresh
} else {
    exists( "$$[QT_INSTALL_LIBS]/*fresh*" ):CONFIG *= fresh
}

fresh {
    !build_pass:message( "Using system fresh library." )
} else {
    exists( fresh.git/fresh.pro ) {
        SUBDIRS *= fresh.git/fresh.pro
        !build_pass:message( "Using bundled fresh library." )
    } else {
        !build_pass:error( "Fresh library not found - initialize the submodules using the init-repositories script." )
    }
}

SUBDIRS *= libwbfs \
    qwbfs


code:
###########################################################################################
##      Created using Monkey Studio v1.8.4.0 (1.8.4.0)
##
##  Author    : Filipe Azevedo aka Nox P@sNox <pasnox@gmail.com>
##  Project   : qwbfs
##  FileName  : installs.pri
##  Date      : 2010-04-04T11:04:05
##  License   : GPL2
##  Comment   : Creating using Monkey Studio RAD
##  Home Page   : https://github.com/pasnox/qwbfsmanager
##
###########################################################################################

unix {
    PACKAGE_PREFIX = $${PREFIX}
    isEmpty( PACKAGE_PREFIX ) {
        macx:PACKAGE_PREFIX = /Applications
        else:PACKAGE_PREFIX = /usr/local
    }

    !build_pass {
        unix:!macx:CAN_INSTALL  = 1
        else:macx:isEqual( MAC_FULL_INSTALL, 1 ):CAN_INSTALL    = 1

        isEqual( CAN_INSTALL, 1 ) {
                message( "The application will be installed to $${PACKAGE_PREFIX}" )
                message( "You can overwrite the prefix calling qmake with parameter: qmake PREFIX=/usr" )
        } else:macx {
            message( "The application bundle will not be installed into $${PACKAGE_PREFIX}." )
            message( "Call: 'qmake MAC_FULL_INSTALL=1' for install the bundle." )
        }
    }

    macx:qwbfs_translations.path    = $${DESTDIR}/$${TARGET}.app/Contents/Resources/translations
    else:qwbfs_translations.path    =   $$[QT_INSTALL_TRANSLATIONS]
    qwbfs_translations.files    = $$PWD/../translations/*.qm

    INSTALLS    = qwbfs_translations

    exists( ../fresh/fresh.pro ) {
        FRESH_LIBRARY_PATH = ../fresh
    } else:exists( ../../../fresh/fresh.pro ) {
        FRESH_LIBRARY_PATH = ../../../fresh
    }

    !isEmpty( FRESH_LIBRARY_PATH ) {
        macx:fresh_translations.path    = $${DESTDIR}/$${TARGET}.app/Contents/Resources/translations
        else:fresh_translations.path    =   $$[QT_INSTALL_TRANSLATIONS]
        fresh_translations.files    = $${FRESH_LIBRARY_PATH}/translations/*.qm

        INSTALLS    *= fresh_translations
    }

    macx {
        qwbfs_qt_translations.path  = $${DESTDIR}/$${TARGET}.app/Contents/Resources/qt/translations
        #qwbfs_qt_translations.files    = $$[QT_INSTALL_TRANSLATIONS]/qt_{\,[A-Za-z][A-Za-z]_}[A-Za-z][A-Za-z].qm
        qwbfs_qt_translations.files = $$[QT_INSTALL_TRANSLATIONS]/qt_??.qm
        qwbfs_qt_translations.files *= $$[QT_INSTALL_TRANSLATIONS]/qt_??_??.qm

        INSTALLS    *= qwbfs_qt_translations
    }
}

unix:!macx {
    # binary
    qwbfs_target.path   = $${PACKAGE_PREFIX}/bin
    qwbfs_target.files  = $${DESTDIR}/$${TARGET}
    qwbfs_target.CONFIG *= no_check_exist

    # desktop file
    qwbfs_desktop.path  = $${PACKAGE_PREFIX}/share/applications
    qwbfs_desktop.files = ../packages/QWBFSManager.desktop

    # desktop icon file
    qwbfs_desktopicon.path  = $${PACKAGE_PREFIX}/share/icons/hicolor/256x256/apps
    qwbfs_desktopicon.files = $$PWD/resources/qwbfsmanager.png

    INSTALLS    *= qwbfs_target qwbfs_desktop qwbfs_desktopicon
}

macx {
    # binary
    qwbfs_target.path   = $${PACKAGE_PREFIX}
    qwbfs_target.files  = $${DESTDIR}/$${TARGET}.app
    qwbfs_target.CONFIG *= no_check_exist

    isEqual( MAC_FULL_INSTALL, 1 ):INSTALLS *= qwbfs_target
}