diff --git a/Source/Core/DiscIO/Blob.cpp b/Source/Core/DiscIO/Blob.cpp index d7aad9332a..7d30cfe212 100644 --- a/Source/Core/DiscIO/Blob.cpp +++ b/Source/Core/DiscIO/Blob.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "Common/CDUtils.h" #include "Common/CommonTypes.h" @@ -177,23 +178,31 @@ std::unique_ptr CreateBlobReader(const std::string& filename) if (cdio_is_cdrom(filename)) return DriveReader::Create(filename); - if (!File::Exists(filename)) + File::IOFile file(filename, "rb"); + u32 magic; + if (!file.ReadArray(&magic, 1)) return nullptr; - if (IsWbfsBlob(filename)) - return WbfsFileReader::Create(filename); + // Conveniently, every supported file format (except for plain disc images) starts + // with a 4-byte magic number that identifies the format, so we just need a simple + // switch statement to create the right blob type. If the magic number doesn't + // match any known magic number, we assume it's a plain disc image. If that + // assumption is wrong, the volume code that runs later will notice the error + // because the blob won't provide valid data when reading the GC/Wii disc header. - if (IsGCZBlob(filename)) - return CompressedBlobReader::Create(filename); - - if (IsCISOBlob(filename)) - return CISOFileReader::Create(filename); - - if (IsTGCBlob(filename)) - return TGCFileReader::Create(filename); - - // Still here? Assume plain file - since we know it exists due to the File::Exists check above. - return PlainFileReader::Create(filename); + switch (magic) + { + case CISO_MAGIC: + return CISOFileReader::Create(std::move(file)); + case GCZ_MAGIC: + return CompressedBlobReader::Create(std::move(file), filename); + case TGC_MAGIC: + return TGCFileReader::Create(std::move(file)); + case WBFS_MAGIC: + return WbfsFileReader::Create(std::move(file), filename); + default: + return PlainFileReader::Create(std::move(file)); + } } } // namespace diff --git a/Source/Core/DiscIO/Blob.h b/Source/Core/DiscIO/Blob.h index fa068c33ef..7f954cd65b 100644 --- a/Source/Core/DiscIO/Blob.h +++ b/Source/Core/DiscIO/Blob.h @@ -159,10 +159,10 @@ std::unique_ptr CreateBlobReader(const std::string& filename); typedef bool (*CompressCB)(const std::string& text, float percent, void* arg); -bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u32 sub_type = 0, - int sector_size = 16384, CompressCB callback = nullptr, +bool CompressFileToBlob(const std::string& infile_path, const std::string& outfile_path, + u32 sub_type = 0, int sector_size = 16384, CompressCB callback = nullptr, void* arg = nullptr); -bool DecompressBlobToFile(const std::string& infile, const std::string& outfile, +bool DecompressBlobToFile(const std::string& infile_path, const std::string& outfile_path, CompressCB callback = nullptr, void* arg = nullptr); } // namespace diff --git a/Source/Core/DiscIO/CISOBlob.cpp b/Source/Core/DiscIO/CISOBlob.cpp index 37e427793b..846952ce8a 100644 --- a/Source/Core/DiscIO/CISOBlob.cpp +++ b/Source/Core/DiscIO/CISOBlob.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "Common/CommonTypes.h" #include "Common/FileUtil.h" @@ -12,13 +13,12 @@ namespace DiscIO { -static const char CISO_MAGIC[] = "CISO"; - -CISOFileReader::CISOFileReader(std::FILE* file) : m_file(file) +CISOFileReader::CISOFileReader(File::IOFile file) : m_file(std::move(file)) { m_size = m_file.GetSize(); CISOHeader header; + m_file.Seek(0, SEEK_SET); m_file.ReadArray(&header, 1); m_block_size = header.block_size; @@ -28,13 +28,11 @@ CISOFileReader::CISOFileReader(std::FILE* file) : m_file(file) m_ciso_map[idx] = (1 == header.map[idx]) ? count++ : UNUSED_BLOCK_ID; } -std::unique_ptr CISOFileReader::Create(const std::string& filename) +std::unique_ptr CISOFileReader::Create(File::IOFile file) { - if (IsCISOBlob(filename)) - { - File::IOFile f(filename, "rb"); - return std::unique_ptr(new CISOFileReader(f.ReleaseHandle())); - } + CISOHeader header; + if (file.Seek(0, SEEK_SET) && file.ReadArray(&header, 1) && header.magic == CISO_MAGIC) + return std::unique_ptr(new CISOFileReader(std::move(file))); return nullptr; } @@ -81,13 +79,4 @@ bool CISOFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr) return true; } -bool IsCISOBlob(const std::string& filename) -{ - File::IOFile f(filename, "rb"); - - CISOHeader header; - return (f.ReadArray(&header, 1) && - std::equal(header.magic, header.magic + sizeof(header.magic), CISO_MAGIC)); -} - } // namespace diff --git a/Source/Core/DiscIO/CISOBlob.h b/Source/Core/DiscIO/CISOBlob.h index 9eed0d992a..b8c0ebd816 100644 --- a/Source/Core/DiscIO/CISOBlob.h +++ b/Source/Core/DiscIO/CISOBlob.h @@ -14,7 +14,7 @@ namespace DiscIO { -bool IsCISOBlob(const std::string& filename); +static constexpr u32 CISO_MAGIC = 0x4F534943; // "CISO" (byteswapped to little endian) static const u32 CISO_HEADER_SIZE = 0x8000; static const u32 CISO_MAP_SIZE = CISO_HEADER_SIZE - sizeof(u32) - sizeof(char) * 4; @@ -22,7 +22,7 @@ static const u32 CISO_MAP_SIZE = CISO_HEADER_SIZE - sizeof(u32) - sizeof(char) * struct CISOHeader { // "CISO" - char magic[4]; + u32 magic; // little endian u32 block_size; @@ -34,7 +34,7 @@ struct CISOHeader class CISOFileReader : public IBlobReader { public: - static std::unique_ptr Create(const std::string& filename); + static std::unique_ptr Create(File::IOFile file); BlobType GetBlobType() const override { return BlobType::CISO; } // The CISO format does not save the original file size. @@ -45,7 +45,7 @@ public: bool Read(u64 offset, u64 nbytes, u8* out_ptr) override; private: - CISOFileReader(std::FILE* file); + CISOFileReader(File::IOFile file); typedef u16 MapType; static const MapType UNUSED_BLOCK_ID = -1; diff --git a/Source/Core/DiscIO/CompressedBlob.cpp b/Source/Core/DiscIO/CompressedBlob.cpp index 680ca8d179..0cf8dbc5b3 100644 --- a/Source/Core/DiscIO/CompressedBlob.cpp +++ b/Source/Core/DiscIO/CompressedBlob.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -28,10 +29,13 @@ namespace DiscIO { -CompressedBlobReader::CompressedBlobReader(const std::string& filename) : m_file_name(filename) +bool IsGCZBlob(File::IOFile& file); + +CompressedBlobReader::CompressedBlobReader(File::IOFile file, const std::string& filename) + : m_file(std::move(file)), m_file_name(filename) { - m_file.Open(filename, "rb"); - m_file_size = File::GetSize(filename); + m_file_size = m_file.GetSize(); + m_file.Seek(0, SEEK_SET); m_file.ReadArray(&m_header, 1); SetSectorSize(m_header.block_size); @@ -53,10 +57,12 @@ CompressedBlobReader::CompressedBlobReader(const std::string& filename) : m_file m_zlib_buffer.resize(zlib_buffer_size); } -std::unique_ptr CompressedBlobReader::Create(const std::string& filename) +std::unique_ptr CompressedBlobReader::Create(File::IOFile file, + const std::string& filename) { - if (IsGCZBlob(filename)) - return std::unique_ptr(new CompressedBlobReader(filename)); + if (IsGCZBlob(file)) + return std::unique_ptr( + new CompressedBlobReader(std::move(file), filename)); return nullptr; } @@ -145,40 +151,41 @@ bool CompressedBlobReader::GetBlock(u64 block_num, u8* out_ptr) return true; } -bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u32 sub_type, - int block_size, CompressCB callback, void* arg) +bool CompressFileToBlob(const std::string& infile_path, const std::string& outfile_path, + u32 sub_type, int block_size, CompressCB callback, void* arg) { bool scrubbing = false; + File::IOFile infile(infile_path, "rb"); if (IsGCZBlob(infile)) { - PanicAlertT("\"%s\" is already compressed! Cannot compress it further.", infile.c_str()); + PanicAlertT("\"%s\" is already compressed! Cannot compress it further.", infile_path.c_str()); return false; } - File::IOFile inf(infile, "rb"); - if (!inf) + if (!infile) { - PanicAlertT("Failed to open the input file \"%s\".", infile.c_str()); + PanicAlertT("Failed to open the input file \"%s\".", infile_path.c_str()); return false; } - File::IOFile f(outfile, "wb"); - if (!f) + File::IOFile outfile(outfile_path, "wb"); + if (!outfile) { PanicAlertT("Failed to open the output file \"%s\".\n" "Check that you have permissions to write the target folder and that the media can " "be written.", - outfile.c_str()); + outfile_path.c_str()); return false; } DiscScrubber disc_scrubber; if (sub_type == 1) { - if (!disc_scrubber.SetupScrub(infile, block_size)) + if (!disc_scrubber.SetupScrub(infile_path, block_size)) { - PanicAlertT("\"%s\" failed to be scrubbed. Probably the image is corrupt.", infile.c_str()); + PanicAlertT("\"%s\" failed to be scrubbed. Probably the image is corrupt.", + infile_path.c_str()); return false; } @@ -192,10 +199,10 @@ bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u callback(GetStringT("Files opened, ready to compress."), 0, arg); CompressedBlobHeader header; - header.magic_cookie = kBlobCookie; + header.magic_cookie = GCZ_MAGIC; header.sub_type = sub_type; header.block_size = block_size; - header.data_size = File::GetSize(infile); + header.data_size = infile.GetSize(); // round upwards! header.num_blocks = (u32)((header.data_size + (block_size - 1)) / block_size); @@ -206,9 +213,9 @@ bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u std::vector in_buf(block_size); // seek past the header (we will write it at the end) - f.Seek(sizeof(CompressedBlobHeader), SEEK_CUR); + outfile.Seek(sizeof(CompressedBlobHeader), SEEK_CUR); // seek past the offset and hash tables (we will write them at the end) - f.Seek((sizeof(u64) + sizeof(u32)) * header.num_blocks, SEEK_CUR); + outfile.Seek((sizeof(u64) + sizeof(u32)) * header.num_blocks, SEEK_CUR); // Now we are ready to write compressed data! u64 position = 0; @@ -221,7 +228,7 @@ bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u { if (i % progress_monitor == 0) { - const u64 inpos = inf.Tell(); + const u64 inpos = infile.Tell(); int ratio = 0; if (inpos != 0) ratio = (int)(100 * position / inpos); @@ -241,9 +248,9 @@ bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u size_t read_bytes; if (scrubbing) - read_bytes = disc_scrubber.GetNextBlock(inf, in_buf.data()); + read_bytes = disc_scrubber.GetNextBlock(infile, in_buf.data()); else - inf.ReadArray(in_buf.data(), header.block_size, &read_bytes); + infile.ReadArray(in_buf.data(), header.block_size, &read_bytes); if (read_bytes < header.block_size) std::fill(in_buf.begin() + read_bytes, in_buf.begin() + header.block_size, 0); @@ -283,11 +290,11 @@ bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u num_compressed++; } - if (!f.WriteBytes(write_buf, write_size)) + if (!outfile.WriteBytes(write_buf, write_size)) { PanicAlertT("Failed to write the output file \"%s\".\n" "Check that you have enough space available on the target drive.", - outfile.c_str()); + outfile_path.c_str()); success = false; break; } @@ -302,16 +309,16 @@ bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u if (!success) { // Remove the incomplete output file. - f.Close(); - File::Delete(outfile); + outfile.Close(); + File::Delete(outfile_path); } else { // Okay, go back and fill in headers - f.Seek(0, SEEK_SET); - f.WriteArray(&header, 1); - f.WriteArray(offsets.data(), header.num_blocks); - f.WriteArray(hashes.data(), header.num_blocks); + outfile.Seek(0, SEEK_SET); + outfile.WriteArray(&header, 1); + outfile.WriteArray(offsets.data(), header.num_blocks); + outfile.WriteArray(hashes.data(), header.num_blocks); } // Cleanup @@ -324,29 +331,34 @@ bool CompressFileToBlob(const std::string& infile, const std::string& outfile, u return success; } -bool DecompressBlobToFile(const std::string& infile, const std::string& outfile, +bool DecompressBlobToFile(const std::string& infile_path, const std::string& outfile_path, CompressCB callback, void* arg) { - if (!IsGCZBlob(infile)) + std::unique_ptr reader; { - PanicAlertT("File not compressed"); - return false; + File::IOFile infile(infile_path, "rb"); + if (!IsGCZBlob(infile)) + { + PanicAlertT("File not compressed"); + return false; + } + + reader = CompressedBlobReader::Create(std::move(infile), infile_path); } - std::unique_ptr reader(CompressedBlobReader::Create(infile)); if (!reader) { - PanicAlertT("Failed to open the input file \"%s\".", infile.c_str()); + PanicAlertT("Failed to open the input file \"%s\".", infile_path.c_str()); return false; } - File::IOFile f(outfile, "wb"); - if (!f) + File::IOFile outfile(outfile_path, "wb"); + if (!outfile) { PanicAlertT("Failed to open the output file \"%s\".\n" "Check that you have permissions to write the target folder and that the media can " "be written.", - outfile.c_str()); + outfile_path.c_str()); return false; } @@ -372,11 +384,11 @@ bool DecompressBlobToFile(const std::string& infile, const std::string& outfile, } const size_t sz = i == num_buffers - 1 ? last_buffer_size : buffer_size; reader->Read(i * buffer_size, sz, buffer.data()); - if (!f.WriteBytes(buffer.data(), sz)) + if (!outfile.WriteBytes(buffer.data(), sz)) { PanicAlertT("Failed to write the output file \"%s\".\n" "Check that you have enough space available on the target drive.", - outfile.c_str()); + outfile_path.c_str()); success = false; break; } @@ -385,23 +397,21 @@ bool DecompressBlobToFile(const std::string& infile, const std::string& outfile, if (!success) { // Remove the incomplete output file. - f.Close(); - File::Delete(outfile); + outfile.Close(); + File::Delete(outfile_path); } else { - f.Resize(header.data_size); + outfile.Resize(header.data_size); } return true; } -bool IsGCZBlob(const std::string& filename) +bool IsGCZBlob(File::IOFile& file) { - File::IOFile f(filename, "rb"); - CompressedBlobHeader header; - return f.ReadArray(&header, 1) && (header.magic_cookie == kBlobCookie); + return file.Seek(0, SEEK_SET) && file.ReadArray(&header, 1) && header.magic_cookie == GCZ_MAGIC; } } // namespace diff --git a/Source/Core/DiscIO/CompressedBlob.h b/Source/Core/DiscIO/CompressedBlob.h index 4c33cf85be..281db6c64a 100644 --- a/Source/Core/DiscIO/CompressedBlob.h +++ b/Source/Core/DiscIO/CompressedBlob.h @@ -23,9 +23,7 @@ namespace DiscIO { -bool IsGCZBlob(const std::string& filename); - -const u32 kBlobCookie = 0xB10BC001; +static constexpr u32 GCZ_MAGIC = 0xB10BC001; // GCZ file structure: // BlobHeader @@ -46,7 +44,8 @@ struct CompressedBlobHeader // 32 bytes class CompressedBlobReader : public SectorReader { public: - static std::unique_ptr Create(const std::string& filename); + static std::unique_ptr Create(File::IOFile file, + const std::string& filename); ~CompressedBlobReader(); const CompressedBlobHeader& GetHeader() const { return m_header; } BlobType GetBlobType() const override { return BlobType::GCZ; } @@ -56,7 +55,7 @@ public: bool GetBlock(u64 block_num, u8* out_ptr) override; private: - CompressedBlobReader(const std::string& filename); + CompressedBlobReader(File::IOFile file, const std::string& filename); CompressedBlobHeader m_header; std::vector m_block_pointers; diff --git a/Source/Core/DiscIO/FileBlob.cpp b/Source/Core/DiscIO/FileBlob.cpp index 7d1dabe198..3547ef658d 100644 --- a/Source/Core/DiscIO/FileBlob.cpp +++ b/Source/Core/DiscIO/FileBlob.cpp @@ -2,22 +2,23 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "DiscIO/FileBlob.h" #include #include +#include + +#include "DiscIO/FileBlob.h" namespace DiscIO { -PlainFileReader::PlainFileReader(std::FILE* file) : m_file(file) +PlainFileReader::PlainFileReader(File::IOFile file) : m_file(std::move(file)) { m_size = m_file.GetSize(); } -std::unique_ptr PlainFileReader::Create(const std::string& filename) +std::unique_ptr PlainFileReader::Create(File::IOFile file) { - File::IOFile f(filename, "rb"); - if (f) - return std::unique_ptr(new PlainFileReader(f.ReleaseHandle())); + if (file) + return std::unique_ptr(new PlainFileReader(std::move(file))); return nullptr; } diff --git a/Source/Core/DiscIO/FileBlob.h b/Source/Core/DiscIO/FileBlob.h index 3a85799651..22cd614ec6 100644 --- a/Source/Core/DiscIO/FileBlob.h +++ b/Source/Core/DiscIO/FileBlob.h @@ -17,7 +17,7 @@ namespace DiscIO class PlainFileReader : public IBlobReader { public: - static std::unique_ptr Create(const std::string& filename); + static std::unique_ptr Create(File::IOFile file); BlobType GetBlobType() const override { return BlobType::PLAIN; } u64 GetDataSize() const override { return m_size; } @@ -25,7 +25,7 @@ public: bool Read(u64 offset, u64 nbytes, u8* out_ptr) override; private: - PlainFileReader(std::FILE* file); + PlainFileReader(File::IOFile file); File::IOFile m_file; s64 m_size; diff --git a/Source/Core/DiscIO/TGCBlob.cpp b/Source/Core/DiscIO/TGCBlob.cpp index 85a5a854aa..886bbb6a78 100644 --- a/Source/Core/DiscIO/TGCBlob.cpp +++ b/Source/Core/DiscIO/TGCBlob.cpp @@ -4,6 +4,7 @@ #include #include +#include #include "Common/CommonFuncs.h" #include "Common/FileUtil.h" @@ -56,27 +57,18 @@ void Replace32(u64 offset, u64 nbytes, u8* out_ptr, u64 replace_offset, u32 repl namespace DiscIO { -bool IsTGCBlob(const std::string& path) +std::unique_ptr TGCFileReader::Create(File::IOFile file) { - File::IOFile file(path, "rb"); - TGCHeader header; - return (file.ReadArray(&header, 1) && header.magic == Common::swap32(0xAE0F38A2)); -} - -std::unique_ptr TGCFileReader::Create(const std::string& path) -{ - if (IsTGCBlob(path)) - { - File::IOFile file(path, "rb"); + if (file.Seek(0, SEEK_SET) && file.ReadArray(&header, 1) && header.magic == TGC_MAGIC) return std::unique_ptr(new TGCFileReader(std::move(file))); - } return nullptr; } -TGCFileReader::TGCFileReader(File::IOFile&& file) : m_file(std::move(file)) +TGCFileReader::TGCFileReader(File::IOFile file) : m_file(std::move(file)) { + m_file.Seek(0, SEEK_SET); m_file.ReadArray(&m_header, 1); u32 header_size = Common::swap32(m_header.header_size); m_size = m_file.GetSize(); diff --git a/Source/Core/DiscIO/TGCBlob.h b/Source/Core/DiscIO/TGCBlob.h index c1057afc27..86c0a85033 100644 --- a/Source/Core/DiscIO/TGCBlob.h +++ b/Source/Core/DiscIO/TGCBlob.h @@ -14,7 +14,7 @@ namespace DiscIO { -bool IsTGCBlob(const std::string& path); +static constexpr u32 TGC_MAGIC = 0xA2380FAE; struct TGCHeader { @@ -40,7 +40,7 @@ struct TGCHeader class TGCFileReader final : public IBlobReader { public: - static std::unique_ptr Create(const std::string& filename); + static std::unique_ptr Create(File::IOFile file); BlobType GetBlobType() const override { return BlobType::TGC; } u64 GetDataSize() const override; @@ -48,7 +48,7 @@ public: bool Read(u64 offset, u64 nbytes, u8* out_ptr) override; private: - TGCFileReader(File::IOFile&& file); + TGCFileReader(File::IOFile file); bool InternalRead(u64 offset, u64 nbytes, u8* out_ptr); diff --git a/Source/Core/DiscIO/WbfsBlob.cpp b/Source/Core/DiscIO/WbfsBlob.cpp index 6acbe5d0b3..8c29d93202 100644 --- a/Source/Core/DiscIO/WbfsBlob.cpp +++ b/Source/Core/DiscIO/WbfsBlob.cpp @@ -7,9 +7,11 @@ #include #include #include +#include #include #include "Common/Align.h" +#include "Common/Assert.h" #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" @@ -23,20 +25,21 @@ static const u64 WII_SECTOR_SIZE = 0x8000; static const u64 WII_SECTOR_COUNT = 143432 * 2; static const u64 WII_DISC_HEADER_SIZE = 256; -WbfsFileReader::WbfsFileReader(const std::string& filename) - : m_total_files(0), m_size(0), m_good(true) +WbfsFileReader::WbfsFileReader(File::IOFile file, const std::string& path) + : m_size(0), m_good(false) { - if (filename.length() < 4 || !OpenFiles(filename) || !ReadHeader()) - { - m_good = false; + if (!AddFileToList(std::move(file))) return; - } + OpenAdditionalFiles(path); + if (!ReadHeader()) + return; + m_good = true; // Grab disc info (assume slot 0, checked in ReadHeader()) m_wlba_table.resize(m_blocks_per_disc); - m_files[0]->file.Seek(m_hd_sector_size + WII_DISC_HEADER_SIZE /*+ i * m_disc_info_size*/, - SEEK_SET); - m_files[0]->file.ReadBytes(m_wlba_table.data(), m_blocks_per_disc * sizeof(u16)); + m_files[0].file.Seek(m_hd_sector_size + WII_DISC_HEADER_SIZE /*+ i * m_disc_info_size*/, + SEEK_SET); + m_files[0].file.ReadBytes(m_wlba_table.data(), m_blocks_per_disc * sizeof(u16)); for (size_t i = 0; i < m_blocks_per_disc; i++) m_wlba_table[i] = Common::swap16(m_wlba_table[i]); } @@ -50,41 +53,46 @@ u64 WbfsFileReader::GetDataSize() const return WII_SECTOR_COUNT * WII_SECTOR_SIZE; } -bool WbfsFileReader::OpenFiles(const std::string& filename) +void WbfsFileReader::OpenAdditionalFiles(const std::string& path) { - m_total_files = 0; + if (path.length() < 4) + return; + + _assert_(m_files.size() > 0); // The code below gives .wbf0 for index 0, but it should be .wbfs while (true) { - auto new_entry = std::make_unique(); - // Replace last character with index (e.g. wbfs = wbf1) - std::string path = filename; - if (m_total_files != 0) - { - path[path.length() - 1] = '0' + m_total_files; - } - - if (!new_entry->file.Open(path, "rb")) - { - return m_total_files != 0; - } - - new_entry->base_address = m_size; - new_entry->size = new_entry->file.GetSize(); - m_size += new_entry->size; - - m_total_files++; - m_files.emplace_back(std::move(new_entry)); + if (m_files.size() >= 10) + return; + std::string current_path = path; + current_path.back() = static_cast('0' + m_files.size()); + if (!AddFileToList(File::IOFile(current_path, "rb"))) + return; } } +bool WbfsFileReader::AddFileToList(File::IOFile file) +{ + if (!file.IsOpen()) + return false; + + const u64 file_size = file.GetSize(); + m_files.emplace_back(std::move(file), m_size, file_size); + m_size += file_size; + + return true; +} + bool WbfsFileReader::ReadHeader() { // Read hd size info - m_files[0]->file.ReadBytes(&m_header, sizeof(WbfsHeader)); - m_header.hd_sector_count = Common::swap32(m_header.hd_sector_count); + m_files[0].file.Seek(0, SEEK_SET); + m_files[0].file.ReadBytes(&m_header, sizeof(WbfsHeader)); + if (m_header.magic != WBFS_MAGIC) + return false; + m_header.hd_sector_count = Common::swap32(m_header.hd_sector_count); m_hd_sector_size = 1ull << m_header.hd_sector_shift; if (m_size != (m_header.hd_sector_count * m_hd_sector_size)) @@ -138,19 +146,19 @@ File::IOFile& WbfsFileReader::SeekToCluster(u64 offset, u64* available) u64 cluster_offset = offset & (m_wbfs_sector_size - 1); u64 final_address = cluster_address + cluster_offset; - for (u32 i = 0; i != m_total_files; i++) + for (file_entry& file_entry : m_files) { - if (final_address < (m_files[i]->base_address + m_files[i]->size)) + if (final_address < (file_entry.base_address + file_entry.size)) { - m_files[i]->file.Seek(final_address - m_files[i]->base_address, SEEK_SET); + file_entry.file.Seek(final_address - file_entry.base_address, SEEK_SET); if (available) { - u64 till_end_of_file = m_files[i]->size - (final_address - m_files[i]->base_address); + u64 till_end_of_file = file_entry.size - (final_address - file_entry.base_address); u64 till_end_of_sector = m_wbfs_sector_size - cluster_offset; *available = std::min(till_end_of_file, till_end_of_sector); } - return m_files[i]->file; + return file_entry.file; } } } @@ -158,13 +166,13 @@ File::IOFile& WbfsFileReader::SeekToCluster(u64 offset, u64* available) PanicAlert("Read beyond end of disc"); if (available) *available = 0; - m_files[0]->file.Seek(0, SEEK_SET); - return m_files[0]->file; + m_files[0].file.Seek(0, SEEK_SET); + return m_files[0].file; } -std::unique_ptr WbfsFileReader::Create(const std::string& filename) +std::unique_ptr WbfsFileReader::Create(File::IOFile file, const std::string& path) { - auto reader = std::unique_ptr(new WbfsFileReader(filename)); + auto reader = std::unique_ptr(new WbfsFileReader(std::move(file), path)); if (!reader->IsGood()) reader.reset(); @@ -172,14 +180,4 @@ std::unique_ptr WbfsFileReader::Create(const std::string& filena return reader; } -bool IsWbfsBlob(const std::string& filename) -{ - File::IOFile f(filename, "rb"); - - u8 magic[4] = {0, 0, 0, 0}; - f.ReadBytes(&magic, 4); - - return (magic[0] == 'W') && (magic[1] == 'B') && (magic[2] == 'F') && (magic[3] == 'S'); -} - } // namespace diff --git a/Source/Core/DiscIO/WbfsBlob.h b/Source/Core/DiscIO/WbfsBlob.h index b7b2323950..f2c0d4bfa7 100644 --- a/Source/Core/DiscIO/WbfsBlob.h +++ b/Source/Core/DiscIO/WbfsBlob.h @@ -14,12 +14,14 @@ namespace DiscIO { +static constexpr u32 WBFS_MAGIC = 0x53464257; // "WBFS" (byteswapped to little endian) + class WbfsFileReader : public IBlobReader { public: ~WbfsFileReader(); - static std::unique_ptr Create(const std::string& filename); + static std::unique_ptr Create(File::IOFile file, const std::string& path); BlobType GetBlobType() const override { return BlobType::WBFS; } // The WBFS format does not save the original file size. @@ -31,23 +33,28 @@ public: bool Read(u64 offset, u64 nbytes, u8* out_ptr) override; private: - WbfsFileReader(const std::string& filename); + WbfsFileReader(File::IOFile file, const std::string& path); - bool OpenFiles(const std::string& filename); + void OpenAdditionalFiles(const std::string& path); + bool AddFileToList(File::IOFile file); bool ReadHeader(); File::IOFile& SeekToCluster(u64 offset, u64* available); bool IsGood() { return m_good; } struct file_entry { + file_entry(File::IOFile file_, u64 base_address_, u64 size_) + : file(std::move(file_)), base_address(base_address_), size(size_) + { + } + File::IOFile file; u64 base_address; u64 size; }; - std::vector> m_files; + std::vector m_files; - u32 m_total_files; u64 m_size; u64 m_hd_sector_size; @@ -58,7 +65,7 @@ private: #pragma pack(1) struct WbfsHeader { - char magic[4]; + u32 magic; u32 hd_sector_count; u8 hd_sector_shift; u8 wbfs_sector_shift; @@ -73,6 +80,4 @@ private: bool m_good; }; -bool IsWbfsBlob(const std::string& filename); - } // namespace