From 655cc7f75cabf3a271e7dbfd4cecb9f214540817 Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sun, 12 Feb 2023 22:08:21 +0100 Subject: [PATCH] DiscIO: Add support for CleanRip-style split ISOs. --- Source/Core/DiscIO/Blob.cpp | 5 ++ Source/Core/DiscIO/Blob.h | 1 + Source/Core/DiscIO/CMakeLists.txt | 2 + Source/Core/DiscIO/SplitFileBlob.cpp | 90 ++++++++++++++++++++++++++ Source/Core/DiscIO/SplitFileBlob.h | 50 ++++++++++++++ Source/Core/DolphinLib.props | 2 + Source/Core/UICommon/GameFileCache.cpp | 2 +- 7 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 Source/Core/DiscIO/SplitFileBlob.cpp create mode 100644 Source/Core/DiscIO/SplitFileBlob.h diff --git a/Source/Core/DiscIO/Blob.cpp b/Source/Core/DiscIO/Blob.cpp index b3c9f4f115..1109b00a1b 100644 --- a/Source/Core/DiscIO/Blob.cpp +++ b/Source/Core/DiscIO/Blob.cpp @@ -19,6 +19,7 @@ #include "DiscIO/DirectoryBlob.h" #include "DiscIO/FileBlob.h" #include "DiscIO/NFSBlob.h" +#include "DiscIO/SplitFileBlob.h" #include "DiscIO/TGCBlob.h" #include "DiscIO/WIABlob.h" #include "DiscIO/WbfsBlob.h" @@ -53,6 +54,8 @@ std::string GetName(BlobType blob_type, bool translate) return translate_str("Mod"); case BlobType::NFS: return "NFS"; + case BlobType::SPLIT_PLAIN: + return translate_str("Multi-part ISO"); default: return ""; } @@ -245,6 +248,8 @@ std::unique_ptr CreateBlobReader(const std::string& filename) default: if (auto directory_blob = DirectoryBlobReader::Create(filename)) return std::move(directory_blob); + if (auto split_blob = SplitPlainFileReader::Create(filename)) + return std::move(split_blob); return PlainFileReader::Create(std::move(file)); } diff --git a/Source/Core/DiscIO/Blob.h b/Source/Core/DiscIO/Blob.h index 102c151200..1777f9fcaa 100644 --- a/Source/Core/DiscIO/Blob.h +++ b/Source/Core/DiscIO/Blob.h @@ -41,6 +41,7 @@ enum class BlobType RVZ, MOD_DESCRIPTOR, NFS, + SPLIT_PLAIN, }; // If you convert an ISO file to another format and then call GetDataSize on it, what is the result? diff --git a/Source/Core/DiscIO/CMakeLists.txt b/Source/Core/DiscIO/CMakeLists.txt index b688d7a4d5..44d4af95b8 100644 --- a/Source/Core/DiscIO/CMakeLists.txt +++ b/Source/Core/DiscIO/CMakeLists.txt @@ -36,6 +36,8 @@ add_library(discio RiivolutionPatcher.h ScrubbedBlob.cpp ScrubbedBlob.h + SplitFileBlob.cpp + SplitFileBlob.h TGCBlob.cpp TGCBlob.h Volume.cpp diff --git a/Source/Core/DiscIO/SplitFileBlob.cpp b/Source/Core/DiscIO/SplitFileBlob.cpp new file mode 100644 index 0000000000..f3a0895614 --- /dev/null +++ b/Source/Core/DiscIO/SplitFileBlob.cpp @@ -0,0 +1,90 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "DiscIO/SplitFileBlob.h" + +#include +#include +#include +#include + +#include + +#include "Common/Assert.h" +#include "Common/FileUtil.h" +#include "Common/IOFile.h" +#include "Common/MsgHandler.h" + +namespace DiscIO +{ +SplitPlainFileReader::SplitPlainFileReader(std::vector files) + : m_files(std::move(files)) +{ + m_size = 0; + for (const auto& f : m_files) + m_size += f.size; +} + +std::unique_ptr SplitPlainFileReader::Create(std::string_view first_file_path) +{ + constexpr std::string_view part0_iso = ".part0.iso"; + if (!first_file_path.ends_with(part0_iso)) + return nullptr; + + const std::string_view base_path = + first_file_path.substr(0, first_file_path.size() - part0_iso.size()); + std::vector files; + size_t index = 0; + u64 offset = 0; + while (true) + { + File::IOFile f(fmt::format("{}.part{}.iso", base_path, index), "rb"); + if (!f.IsOpen()) + break; + const u64 size = f.GetSize(); + if (size == 0) + return nullptr; + files.emplace_back(SingleFile{std::move(f), offset, size}); + offset += size; + ++index; + } + + if (files.size() < 2) + return nullptr; + + files.shrink_to_fit(); + return std::unique_ptr(new SplitPlainFileReader(std::move(files))); +} + +bool SplitPlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr) +{ + if (offset >= m_size) + return false; + + u64 current_offset = offset; + u64 rest = nbytes; + u8* out = out_ptr; + for (auto& file : m_files) + { + if (current_offset >= file.offset && current_offset < file.offset + file.size) + { + auto& f = file.file; + const u64 seek_offset = current_offset - file.offset; + const u64 current_read = std::min(file.size - seek_offset, rest); + if (!f.Seek(seek_offset, File::SeekOrigin::Begin) || !f.ReadBytes(out, current_read)) + { + f.ClearError(); + return false; + } + + rest -= current_read; + if (rest == 0) + return true; + current_offset += current_read; + out += current_read; + } + } + + return rest == 0; +} +} // namespace DiscIO diff --git a/Source/Core/DiscIO/SplitFileBlob.h b/Source/Core/DiscIO/SplitFileBlob.h new file mode 100644 index 0000000000..4b3c635150 --- /dev/null +++ b/Source/Core/DiscIO/SplitFileBlob.h @@ -0,0 +1,50 @@ +// Copyright 2023 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/IOFile.h" +#include "DiscIO/Blob.h" + +namespace DiscIO +{ +class SplitPlainFileReader final : public BlobReader +{ +public: + static std::unique_ptr Create(std::string_view first_file_path); + + BlobType GetBlobType() const override { return BlobType::SPLIT_PLAIN; } + + u64 GetRawSize() const override { return m_size; } + u64 GetDataSize() const override { return m_size; } + DataSizeType GetDataSizeType() const override { return DataSizeType::Accurate; } + + u64 GetBlockSize() const override { return 0; } + bool HasFastRandomAccessInBlock() const override { return true; } + std::string GetCompressionMethod() const override { return {}; } + std::optional GetCompressionLevel() const override { return std::nullopt; } + + bool Read(u64 offset, u64 nbytes, u8* out_ptr) override; + +private: + struct SingleFile + { + File::IOFile file; + u64 offset; + u64 size; + }; + + SplitPlainFileReader(std::vector m_files); + + std::vector m_files; + u64 m_size; +}; + +} // namespace DiscIO diff --git a/Source/Core/DolphinLib.props b/Source/Core/DolphinLib.props index 6facc4c074..c99d353df6 100644 --- a/Source/Core/DolphinLib.props +++ b/Source/Core/DolphinLib.props @@ -453,6 +453,7 @@ + @@ -1078,6 +1079,7 @@ + diff --git a/Source/Core/UICommon/GameFileCache.cpp b/Source/Core/UICommon/GameFileCache.cpp index 59c4c9a292..d9b08f63d5 100644 --- a/Source/Core/UICommon/GameFileCache.cpp +++ b/Source/Core/UICommon/GameFileCache.cpp @@ -26,7 +26,7 @@ namespace UICommon { -static constexpr u32 CACHE_REVISION = 23; // Last changed in PR 10932 +static constexpr u32 CACHE_REVISION = 24; // Last changed in PR 11557 std::vector FindAllGamePaths(const std::vector& directories_to_scan, bool recursive_scan)