dolphin/Source/Core/DiscIO/SplitFileBlob.cpp
LillyJadeKatrin 335cf4f2db Added CopyReader to BlobReader and all subclasses
A deep-copy method CopyReader has been added to BlobReader (virtual) and all of its subclasses (override). This should create a second BlobReader to open the same set of data but with an independent read pointer so that it doesn't interfere with any reads done on the original Reader.

As part of this, IOFile has added code to create a deep copy IOFile pointer onto the same file, with code based on the platform in question to find the file ID from the file pointer and open a new one. There has also been a small piece added to FileInfo to enable a deep copy, but its only subclass at this time already had a copy constructor so this was relatively minor.
2023-10-01 09:04:06 -04:00

102 lines
2.5 KiB
C++

// Copyright 2023 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DiscIO/SplitFileBlob.h"
#include <memory>
#include <string>
#include <string_view>
#include <vector>
#include <fmt/format.h>
#include "Common/Assert.h"
#include "Common/FileUtil.h"
#include "Common/IOFile.h"
#include "Common/MsgHandler.h"
namespace DiscIO
{
SplitPlainFileReader::SplitPlainFileReader(std::vector<SingleFile> files)
: m_files(std::move(files))
{
m_size = 0;
for (const auto& f : m_files)
m_size += f.size;
}
std::unique_ptr<SplitPlainFileReader> 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<SingleFile> 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<SplitPlainFileReader>(new SplitPlainFileReader(std::move(files)));
}
std::unique_ptr<BlobReader> SplitPlainFileReader::CopyReader() const
{
std::vector<SingleFile> new_files{};
for (const SingleFile& file : m_files)
{
new_files.push_back(
{.file = file.file.Duplicate("rb"), .offset = file.offset, .size = file.size});
}
return std::unique_ptr<SplitPlainFileReader>(new SplitPlainFileReader(std::move(new_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