added WBFS discIO support

This commit is contained in:
Jack Andersen 2015-07-04 12:02:37 -10:00
parent f093f633b4
commit 3501e90e2c
3 changed files with 266 additions and 37 deletions

View File

@ -35,11 +35,7 @@ public:
#endif #endif
if (!fp) if (!fp)
{ {
#if NOD_UCS2 LogModule.report(LogVisor::Error, _S("Unable to open '%s' for reading"), filepath.c_str());
LogModule.report(LogVisor::Error, L"Unable to open '%s' for reading", filepath.c_str());
#else
LogModule.report(LogVisor::Error, "Unable to open '%s' for reading", filepath.c_str());
#endif
return std::unique_ptr<IReadStream>(); return std::unique_ptr<IReadStream>();
} }
fseeko(fp, offset, SEEK_SET); fseeko(fp, offset, SEEK_SET);
@ -66,11 +62,7 @@ public:
#endif #endif
if (!fp) if (!fp)
{ {
#if NOD_UCS2 LogModule.report(LogVisor::Error, _S("Unable to open '%s' for writing"), filepath.c_str());
LogModule.report(LogVisor::Error, L"Unable to open '%s' for writing", filepath.c_str());
#else
LogModule.report(LogVisor::Error, "Unable to open '%s' for writing", filepath.c_str());
#endif
return std::unique_ptr<IWriteStream>(); return std::unique_ptr<IWriteStream>();
} }
fseeko(fp, offset, SEEK_SET); fseeko(fp, offset, SEEK_SET);

View File

@ -6,25 +6,279 @@
namespace NOD namespace NOD
{ {
#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 ALIGN_LBA(x) (((x)+p->hd_sec_sz-1)&(~(p->hd_sec_sz-1)))
static uint8_t size_to_shift(uint32_t size)
{
uint8_t ret = 0;
while (size)
{
ret++;
size>>=1;
}
return ret-1;
}
class DiscIOWBFS : public IDiscIO class DiscIOWBFS : public IDiscIO
{ {
SystemString filepath; SystemString filepath;
struct WBFSHead
{
uint32_t magic;
// parameters copied in the partition for easy dumping, and bug reports
uint32_t n_hd_sec; // total number of hd_sec in this partition
uint8_t hd_sec_sz_s; // sector size in this partition
uint8_t wbfs_sec_sz_s; // size of a wbfs sec
uint8_t padding3[2];
uint8_t disc_table[0]; // size depends on hd sector size
};
std::unique_ptr<uint8_t[]> wbfsHead;
struct WBFSDiscInfo
{
uint8_t disc_header_copy[0x100];
uint16_t wlba_table[0];
};
std::unique_ptr<uint8_t[]> wbfsDiscInfo;
struct WBFS
{
/* hdsectors, the size of the sector provided by the hosting hard drive */
uint32_t hd_sec_sz;
uint8_t hd_sec_sz_s; // the power of two of the last number
uint32_t n_hd_sec; // the number of hd sector in the wbfs partition
/* standard wii sector (0x8000 bytes) */
uint32_t wii_sec_sz;
uint8_t wii_sec_sz_s;
uint32_t n_wii_sec;
uint32_t n_wii_sec_per_disc;
/* The size of a wbfs sector */
uint32_t wbfs_sec_sz;
uint32_t wbfs_sec_sz_s;
uint16_t n_wbfs_sec; // this must fit in 16 bit!
uint16_t n_wbfs_sec_per_disc; // size of the lookup table
uint32_t part_lba;
uint16_t max_disc;
uint32_t freeblks_lba;
uint32_t *freeblks;
uint16_t disc_info_sz;
uint32_t n_disc_open;
} wbfs;
static int _wbfsReadSector(FILE* fp, uint32_t lba, uint32_t count, void* buf)
{
uint64_t off = lba;
off*=512ULL;
if (fseeko(fp, off, SEEK_SET))
{
LogModule.report(LogVisor::FatalError, "error seeking in disc partition: %lld %d", off, count);
return 1;
}
if (fread(buf, count*512ULL, 1, fp) != 1){
LogModule.report(LogVisor::FatalError, "error reading disc");
return 1;
}
return 0;
}
public: public:
DiscIOWBFS(const SystemString& fpin) DiscIOWBFS(const SystemString& fpin)
: filepath(fpin) {} : filepath(fpin)
{
/* Temporary file handle to read LBA table */
#if NOD_UCS2
FILE* fp = _wfopen(filepath.c_str(), L"rb");
#else
FILE* fp = fopen(filepath.c_str(), "rb");
#endif
WBFS* p = &wbfs;
WBFSHead tmpHead;
if (fread(&tmpHead, 1, sizeof(tmpHead), fp) != sizeof(tmpHead))
LogModule.report(LogVisor::FatalError, "unable to read WBFS head");
fseek(fp, 0, SEEK_SET);
unsigned hd_sector_size = 1 << tmpHead.hd_sec_sz_s;
unsigned num_hd_sector = SBig(tmpHead.n_hd_sec);
wbfsHead.reset(new uint8_t[hd_sector_size]);
WBFSHead* head = (WBFSHead*)wbfsHead.get();
if (fread(head, 1, hd_sector_size, fp) != hd_sector_size)
LogModule.report(LogVisor::FatalError, "unable to read WBFS head");
//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->part_lba = 0;
_wbfsReadSector(fp, p->part_lba, 1, head);
if (hd_sector_size && head->hd_sec_sz_s != size_to_shift(hd_sector_size)) {
LogModule.report(LogVisor::FatalError, "hd sector size doesn't match");
}
if (num_hd_sector && head->n_hd_sec != SBig(num_hd_sector)) {
LogModule.report(LogVisor::FatalError, "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 = SBig(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(WBFSDiscInfo) + p->n_wbfs_sec_per_disc*2);
p->freeblks_lba = (p->wbfs_sec_sz - p->n_wbfs_sec/8)>>p->hd_sec_sz_s;
p->freeblks = 0; // will alloc and read only if needed
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(WBFSHead))
p->max_disc = p->hd_sec_sz - sizeof(WBFSHead);
p->n_disc_open = 0;
int disc_info_sz_lba = p->disc_info_sz>>p->hd_sec_sz_s;
if (head->disc_table[0])
{
wbfsDiscInfo.reset(new uint8_t[p->disc_info_sz]);
if (!wbfsDiscInfo)
LogModule.report(LogVisor::FatalError, "allocating memory");
_wbfsReadSector(fp, p->part_lba+1, disc_info_sz_lba, wbfsDiscInfo.get());
p->n_disc_open++;
//for(i=0;i<p->n_wbfs_sec_per_disc;i++)
// printf("%d,",wbfs_ntohs(d->header->wlba_table[i]));
}
}
class ReadStream : public IReadStream class ReadStream : public IReadStream
{ {
friend class DiscIOWBFS; friend class DiscIOWBFS;
const DiscIOWBFS& m_parent;
FILE* fp; FILE* fp;
ReadStream(FILE* fpin) uint64_t m_offset;
: fp(fpin) {} std::unique_ptr<uint8_t[]> m_tmpBuffer;
ReadStream(const DiscIOWBFS& parent, FILE* fpin, uint64_t offset)
: m_parent(parent),
fp(fpin),
m_offset(offset),
m_tmpBuffer(new uint8_t[parent.wbfs.hd_sec_sz]) {}
~ReadStream() {fclose(fp);} ~ReadStream() {fclose(fp);}
int wbfsReadSector(uint32_t lba, uint32_t count, void* buf)
{return DiscIOWBFS::_wbfsReadSector(fp, lba, count, buf);}
int wbfsDiscRead(uint32_t offset, uint8_t *data, uint64_t len)
{
const WBFS* p = &m_parent.wbfs;
const WBFSDiscInfo* d = (WBFSDiscInfo*)m_parent.wbfsDiscInfo.get();
uint16_t wlba = offset>>(p->wbfs_sec_sz_s-2);
uint32_t iwlba_shift = p->wbfs_sec_sz_s - p->hd_sec_sz_s;
uint32_t lba_mask = (p->wbfs_sec_sz-1)>>(p->hd_sec_sz_s);
uint64_t lba = (offset>>(p->hd_sec_sz_s-2))&lba_mask;
uint64_t off = offset&((p->hd_sec_sz>>2)-1);
uint16_t iwlba = SBig(d->wlba_table[wlba]);
uint64_t len_copied;
int err = 0;
uint8_t *ptr = data;
if (unlikely(iwlba==0))
return 1;
if (unlikely(off))
{
off*=4;
err = wbfsReadSector(p->part_lba + (iwlba<<iwlba_shift) + lba, 1, m_tmpBuffer.get());
if (err)
return err;
len_copied = p->hd_sec_sz - off;
if (likely(len < len_copied))
len_copied = len;
memcpy(ptr, m_tmpBuffer.get() + off, len_copied);
len -= len_copied;
ptr += len_copied;
lba++;
if (unlikely(lba>lba_mask && len))
{
lba=0;
iwlba = SBig(d->wlba_table[++wlba]);
if (unlikely(iwlba==0))
return 1;
}
}
while (likely(len>=p->hd_sec_sz))
{
uint32_t 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 = wbfsReadSector(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 = SBig(d->wlba_table[++wlba]);
if (unlikely(iwlba==0))
return 1;
}
}
if (unlikely(len))
{
err = wbfsReadSector(p->part_lba + (iwlba<<iwlba_shift) + lba, 1, m_tmpBuffer.get());
if (err)
return err;
memcpy(ptr, m_tmpBuffer.get(), len);
}
return 0;
}
public: public:
uint64_t read(void* buf, uint64_t length) uint64_t read(void* buf, uint64_t length)
{return fread(buf, 1, length, fp);} {
uint8_t extra[4];
uint64_t rem_offset = m_offset % 4;
if (rem_offset)
{
uint64_t rem_rem = 4 - rem_offset;
if (wbfsDiscRead((unsigned int)m_offset / 4, extra, 4))
return 0;
memcpy(buf, extra + rem_offset, rem_rem);
if (wbfsDiscRead((unsigned int)m_offset / 4 + 1, (uint8_t*)buf + rem_rem, length - rem_rem))
return 0;
}
else
{
if (wbfsDiscRead((unsigned int)m_offset / 4, (uint8_t*)buf, length))
return 0;
}
m_offset += length;
return length;
}
void seek(int64_t offset, int whence) void seek(int64_t offset, int whence)
{fseeko(fp, offset, whence);} {
if (whence == SEEK_SET)
m_offset = offset;
else if (whence == SEEK_CUR)
m_offset += offset;
}
}; };
std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const std::unique_ptr<IReadStream> beginReadStream(uint64_t offset) const
{ {
@ -35,15 +289,10 @@ public:
#endif #endif
if (!fp) if (!fp)
{ {
#if NOD_UCS2 LogModule.report(LogVisor::Error, _S("Unable to open '%s' for reading"), filepath.c_str());
LogModule.report(LogVisor::Error, L"Unable to open '%s' for reading", filepath.c_str());
#else
LogModule.report(LogVisor::Error, "Unable to open '%s' for reading", filepath.c_str());
#endif
return std::unique_ptr<IReadStream>(); return std::unique_ptr<IReadStream>();
} }
fseeko(fp, offset, SEEK_SET); return std::unique_ptr<IReadStream>(new ReadStream(*this, fp, offset));
return std::unique_ptr<IReadStream>(new ReadStream(fp));
} }
class WriteStream : public IWriteStream class WriteStream : public IWriteStream
@ -66,11 +315,7 @@ public:
#endif #endif
if (!fp) if (!fp)
{ {
#if NOD_UCS2 LogModule.report(LogVisor::Error, _S("Unable to open '%s' for writing"), filepath.c_str());
LogModule.report(LogVisor::Error, L"Unable to open '%s' for writing", filepath.c_str());
#else
LogModule.report(LogVisor::Error, "Unable to open '%s' for writing", filepath.c_str());
#endif
return std::unique_ptr<IWriteStream>(); return std::unique_ptr<IWriteStream>();
} }
fseeko(fp, offset, SEEK_SET); fseeko(fp, offset, SEEK_SET);

View File

@ -20,11 +20,7 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii)
#endif #endif
if (!fp) if (!fp)
{ {
#if NOD_UCS2 LogModule.report(LogVisor::Error, _S("Unable to open '%s'"), path);
LogModule.report(LogVisor::Error, L"Unable to open '%s'", path);
#else
LogModule.report(LogVisor::Error, "Unable to open '%s'", path);
#endif
return std::unique_ptr<DiscBase>(); return std::unique_ptr<DiscBase>();
} }
@ -65,11 +61,7 @@ std::unique_ptr<DiscBase> OpenDiscFromImage(const SystemChar* path, bool& isWii)
if (!discIO) if (!discIO)
{ {
#if NOD_UCS2 LogModule.report(LogVisor::Error, _S("'%s' is not a valid image"), path);
LogModule.report(LogVisor::Error, L"'%s' is not a valid image", path);
#else
LogModule.report(LogVisor::Error, "'%s' is not a valid image", path);
#endif
return std::unique_ptr<DiscBase>(); return std::unique_ptr<DiscBase>();
} }