From 3501e90e2c347ee7eba2ad46dc20e61c8713d7e6 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Sat, 4 Jul 2015 12:02:37 -1000 Subject: [PATCH] added WBFS discIO support --- lib/DiscIOISO.cpp | 12 +- lib/DiscIOWBFS.cpp | 279 ++++++++++++++++++++++++++++++++++++++++++--- lib/NOD.cpp | 12 +- 3 files changed, 266 insertions(+), 37 deletions(-) diff --git a/lib/DiscIOISO.cpp b/lib/DiscIOISO.cpp index 64c12e8..0439da1 100644 --- a/lib/DiscIOISO.cpp +++ b/lib/DiscIOISO.cpp @@ -35,11 +35,7 @@ public: #endif if (!fp) { -#if NOD_UCS2 - 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 + LogModule.report(LogVisor::Error, _S("Unable to open '%s' for reading"), filepath.c_str()); return std::unique_ptr(); } fseeko(fp, offset, SEEK_SET); @@ -66,11 +62,7 @@ public: #endif if (!fp) { -#if NOD_UCS2 - 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 + LogModule.report(LogVisor::Error, _S("Unable to open '%s' for writing"), filepath.c_str()); return std::unique_ptr(); } fseeko(fp, offset, SEEK_SET); diff --git a/lib/DiscIOWBFS.cpp b/lib/DiscIOWBFS.cpp index 8fa3b20..4656c2b 100644 --- a/lib/DiscIOWBFS.cpp +++ b/lib/DiscIOWBFS.cpp @@ -6,25 +6,279 @@ 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 { 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 wbfsHead; + + struct WBFSDiscInfo + { + uint8_t disc_header_copy[0x100]; + uint16_t wlba_table[0]; + }; + std::unique_ptr 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: 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<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<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;in_wbfs_sec_per_disc;i++) + // printf("%d,",wbfs_ntohs(d->header->wlba_table[i])); + } + } class ReadStream : public IReadStream { friend class DiscIOWBFS; + const DiscIOWBFS& m_parent; FILE* fp; - ReadStream(FILE* fpin) - : fp(fpin) {} + uint64_t m_offset; + std::unique_ptr 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);} + + 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<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<hd_sec_sz_s; + ptr += nlb<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< beginReadStream(uint64_t offset) const { @@ -35,15 +289,10 @@ public: #endif if (!fp) { -#if NOD_UCS2 - 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 + LogModule.report(LogVisor::Error, _S("Unable to open '%s' for reading"), filepath.c_str()); return std::unique_ptr(); } - fseeko(fp, offset, SEEK_SET); - return std::unique_ptr(new ReadStream(fp)); + return std::unique_ptr(new ReadStream(*this, fp, offset)); } class WriteStream : public IWriteStream @@ -66,11 +315,7 @@ public: #endif if (!fp) { -#if NOD_UCS2 - 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 + LogModule.report(LogVisor::Error, _S("Unable to open '%s' for writing"), filepath.c_str()); return std::unique_ptr(); } fseeko(fp, offset, SEEK_SET); diff --git a/lib/NOD.cpp b/lib/NOD.cpp index 872f226..a0c3a1f 100644 --- a/lib/NOD.cpp +++ b/lib/NOD.cpp @@ -20,11 +20,7 @@ std::unique_ptr OpenDiscFromImage(const SystemChar* path, bool& isWii) #endif if (!fp) { -#if NOD_UCS2 - LogModule.report(LogVisor::Error, L"Unable to open '%s'", path); -#else - LogModule.report(LogVisor::Error, "Unable to open '%s'", path); -#endif + LogModule.report(LogVisor::Error, _S("Unable to open '%s'"), path); return std::unique_ptr(); } @@ -65,11 +61,7 @@ std::unique_ptr OpenDiscFromImage(const SystemChar* path, bool& isWii) if (!discIO) { -#if NOD_UCS2 - 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 + LogModule.report(LogVisor::Error, _S("'%s' is not a valid image"), path); return std::unique_ptr(); }